mirror of
https://github.com/nsnail/spectre.console.git
synced 2025-06-19 13:28:16 +08:00
Update emoji support
* Add constants for emojis * Move emoji shortcode rendering to Markup * Add documentation * Add example * Add tests
This commit is contained in:

committed by
Patrik Svensson

parent
090b30f731
commit
eeb3f967b6
@ -55,5 +55,8 @@ namespace Generator.Commands
|
||||
{
|
||||
[CommandArgument(0, "<OUTPUT>")]
|
||||
public string Output { get; set; }
|
||||
|
||||
[CommandOption("-i|--input <PATH>")]
|
||||
public string Input { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
@ -10,52 +11,72 @@ using Scriban.Runtime;
|
||||
using Spectre.Cli;
|
||||
using Spectre.IO;
|
||||
using Path = Spectre.IO.Path;
|
||||
using SpectreEnvironment = Spectre.IO.Environment;
|
||||
|
||||
namespace Generator.Commands
|
||||
{
|
||||
public sealed class EmojiGeneratorCommand : AsyncCommand<GeneratorCommandSettings>
|
||||
{
|
||||
private readonly IFileSystem _fileSystem;
|
||||
|
||||
private readonly IEnvironment _environment;
|
||||
private readonly IHtmlParser _parser;
|
||||
|
||||
private readonly Dictionary<string, string> _templates = new Dictionary<string, string>
|
||||
{
|
||||
{ "Templates/Emoji.Generated.template", "Emoji.Generated.cs" },
|
||||
{ "Templates/Emoji.Json.template", "emojis.json" },
|
||||
};
|
||||
|
||||
public EmojiGeneratorCommand()
|
||||
{
|
||||
_fileSystem = new FileSystem();
|
||||
_environment = new SpectreEnvironment();
|
||||
_parser = new HtmlParser();
|
||||
}
|
||||
|
||||
public override async Task<int> ExecuteAsync(CommandContext context, GeneratorCommandSettings settings)
|
||||
{
|
||||
var output = new DirectoryPath(settings.Output);
|
||||
|
||||
if (!_fileSystem.Directory.Exists(settings.Output))
|
||||
{
|
||||
_fileSystem.Directory.Create(settings.Output);
|
||||
}
|
||||
|
||||
var templatePath = new FilePath("Templates/Emoji.Generated.template");
|
||||
var stream = await FetchEmojis(settings);
|
||||
var document = await _parser.ParseDocumentAsync(stream);
|
||||
var emojis = Emoji.Parse(document).OrderBy(x => x.Name)
|
||||
.Where(emoji => !emoji.HasCombinators)
|
||||
.ToList();
|
||||
|
||||
var emojis = await FetchEmojis("http://www.unicode.org/emoji/charts/emoji-list.html");
|
||||
// Render all templates
|
||||
foreach (var (templateFilename, outputFilename) in _templates)
|
||||
{
|
||||
var result = await RenderTemplate(new FilePath(templateFilename), emojis);
|
||||
|
||||
var result = await RenderTemplate(templatePath, emojis);
|
||||
|
||||
var outputPath = output.CombineWithFilePath(templatePath.GetFilename().ChangeExtension(".cs"));
|
||||
|
||||
await File.WriteAllTextAsync(outputPath.FullPath, result);
|
||||
var outputPath = output.CombineWithFilePath(outputFilename);
|
||||
await File.WriteAllTextAsync(outputPath.FullPath, result);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private async Task<IReadOnlyCollection<Emoji>> FetchEmojis(string url)
|
||||
private async Task<Stream> FetchEmojis(GeneratorCommandSettings settings)
|
||||
{
|
||||
using var http = new HttpClient();
|
||||
var input = string.IsNullOrEmpty(settings.Input)
|
||||
? _environment.WorkingDirectory
|
||||
: new DirectoryPath(settings.Input);
|
||||
|
||||
var htmlStream = await http.GetStreamAsync(url);
|
||||
var file = _fileSystem.File.Retrieve(input.CombineWithFilePath("emoji-list.html"));
|
||||
if (!file.Exists)
|
||||
{
|
||||
using var http = new HttpClient();
|
||||
using var httpStream = await http.GetStreamAsync("http://www.unicode.org/emoji/charts/emoji-list.html");
|
||||
using var outStream = file.OpenWrite();
|
||||
|
||||
var document = await _parser.ParseDocumentAsync(htmlStream);
|
||||
await httpStream.CopyToAsync(outStream);
|
||||
}
|
||||
|
||||
return Emoji.Parse(document).OrderBy(x => x.Name).ToList();
|
||||
return file.OpenRead();
|
||||
}
|
||||
|
||||
private static async Task<string> RenderTemplate(Path path, IReadOnlyCollection<Emoji> emojis)
|
||||
@ -63,7 +84,6 @@ namespace Generator.Commands
|
||||
var text = await File.ReadAllTextAsync(path.FullPath);
|
||||
|
||||
var template = Template.Parse(text);
|
||||
|
||||
var templateContext = new TemplateContext
|
||||
{
|
||||
// Because of the insane amount of Emojis,
|
||||
@ -72,9 +92,7 @@ namespace Generator.Commands
|
||||
};
|
||||
|
||||
var scriptObject = new ScriptObject();
|
||||
|
||||
scriptObject.Import(new { Emojis = emojis });
|
||||
|
||||
templateContext.PushGlobal(scriptObject);
|
||||
|
||||
return await template.RenderAsync(templateContext);
|
||||
|
@ -24,6 +24,9 @@
|
||||
<None Update="Templates\ColorPalette.Generated.template">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Templates\Emoji.Json.template">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Templates\Emoji.Generated.template">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
@ -31,6 +34,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AngleSharp" Version="0.14.0" />
|
||||
<PackageReference Include="Humanizer.Core" Version="2.8.26" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||
<PackageReference Include="Scriban" Version="2.1.3" />
|
||||
<PackageReference Include="Spectre.Cli" Version="0.36.1-preview.0.6" />
|
||||
|
@ -3,6 +3,7 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using AngleSharp.Dom;
|
||||
using AngleSharp.Html.Dom;
|
||||
using Humanizer;
|
||||
|
||||
namespace Generator.Models
|
||||
{
|
||||
@ -10,15 +11,22 @@ namespace Generator.Models
|
||||
{
|
||||
private static readonly string[] _headers = { "count", "code", "sample", "name" };
|
||||
|
||||
private Emoji(string code, string name)
|
||||
private Emoji(string identifier, string name, string code, string description)
|
||||
{
|
||||
Code = code;
|
||||
Identifier = identifier;
|
||||
Name = name;
|
||||
Code = code;
|
||||
Description = description;
|
||||
NormalizedCode = Code.Replace("\\U", "U+");
|
||||
HasCombinators = Code.Split(new[] { "\\U" }, System.StringSplitOptions.RemoveEmptyEntries).Length > 1;
|
||||
}
|
||||
|
||||
public string Identifier { get; set; }
|
||||
public string Code { get; }
|
||||
|
||||
public string NormalizedCode { get; }
|
||||
public string Name { get; }
|
||||
public string Description { get; set; }
|
||||
public bool HasCombinators { get; set; }
|
||||
|
||||
public static IEnumerable<Emoji> Parse(IHtmlDocument document)
|
||||
{
|
||||
@ -30,13 +38,24 @@ namespace Generator.Models
|
||||
foreach (var row in rows)
|
||||
{
|
||||
var dictionary = _headers
|
||||
.Zip(row.Cells, (header, cell) => (header, cell.TextContent.Trim()))
|
||||
.Zip(row.Cells, (header, cell) => (Header: header, cell.TextContent.Trim()))
|
||||
.ToDictionary(x => x.Item1, x => x.Item2);
|
||||
|
||||
var code = TransformCode(dictionary["code"]);
|
||||
var name = TransformName(dictionary["name"]);
|
||||
var identifier = TransformName(dictionary["name"])
|
||||
.Replace("-", "_")
|
||||
.Replace("(", string.Empty)
|
||||
.Replace(")", string.Empty);
|
||||
|
||||
yield return new Emoji(code, name);
|
||||
var description = dictionary["name"].Humanize();
|
||||
|
||||
var name = identifier
|
||||
.Replace("1st", "first")
|
||||
.Replace("2nd", "second")
|
||||
.Replace("3rd", "third")
|
||||
.Pascalize();
|
||||
|
||||
yield return new Emoji(identifier, name, code, description);
|
||||
}
|
||||
}
|
||||
|
||||
@ -48,8 +67,14 @@ namespace Generator.Models
|
||||
.Replace("\u201c", string.Empty)
|
||||
.Replace("\u201d", string.Empty)
|
||||
.Replace("\u229b", string.Empty)
|
||||
.Trim()
|
||||
.Replace(' ', '_')
|
||||
.Replace("’s", "s")
|
||||
.Replace("’", "_")
|
||||
.Replace("&", "and")
|
||||
.Replace("#", "hash")
|
||||
.Replace("*", "star")
|
||||
.Replace("!", string.Empty)
|
||||
.Trim()
|
||||
.ToLowerInvariant();
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Generated {{ date.now | date.to_string `%Y-%m-%d %k:%M` }}
|
||||
// Generated {{ date.now | date.to_string `%F %R` }}
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
|
@ -1,7 +1,7 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Generated {{ date.now | date.to_string `%Y-%m-%d %k:%M` }}
|
||||
// Generated {{ date.now | date.to_string `%F %R` }}
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
|
@ -1,7 +1,7 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Generated {{ date.now | date.to_string `%Y-%m-%d %k:%M` }}
|
||||
// Generated {{ date.now | date.to_string `%F %R` }}
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
|
@ -1,7 +1,7 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Generated {{ date.now | date.to_string `%Y-%m-%d %k:%M` }}
|
||||
// Generated {{ date.now | date.to_string `%F %R` }}
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
@ -10,20 +10,34 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace Spectre.Console
|
||||
{
|
||||
/// <summary>
|
||||
/// Utility class for working with emojis.
|
||||
/// Utility for working with emojis.
|
||||
/// </summary>
|
||||
internal static partial class Emoji
|
||||
public static partial class Emoji
|
||||
{
|
||||
private static readonly Dictionary<string, string> _emojis
|
||||
= new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase)
|
||||
{
|
||||
{{~ for emoji in emojis }} { "{{ emoji.name }}", "{{ emoji.code }}" },
|
||||
{{~ for emoji in emojis ~}}
|
||||
{ "{{ emoji.identifier }}", Emoji.Known.{{ emoji.name }} },
|
||||
{{~ end ~}}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Contains all predefined emojis.
|
||||
/// </summary>
|
||||
public static class Known
|
||||
{
|
||||
{{- for emoji in emojis }}
|
||||
/// <summary>
|
||||
/// Gets the "{{ emoji.identifier }}" emoji.
|
||||
/// Description: {{ emoji.description }}.
|
||||
/// </summary>
|
||||
public const string {{ emoji.name }} = "{{ emoji.code }}";
|
||||
{{~ end ~}}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
10
resources/scripts/Generator/Templates/Emoji.Json.template
Normal file
10
resources/scripts/Generator/Templates/Emoji.Json.template
Normal file
@ -0,0 +1,10 @@
|
||||
[
|
||||
{{~ for x in 0..(emojis.size-1) ~}}
|
||||
{
|
||||
"id": "{{ emojis[x].identifier }}",
|
||||
"name": "{{ emojis[x].name }}",
|
||||
"description": "{{ emojis[x].description }}",
|
||||
"code": "{{ emojis[x].normalized_code }}"
|
||||
}{{ if x != (emojis.size-1) }},{{ end }}
|
||||
{{~ end ~}}
|
||||
]
|
Reference in New Issue
Block a user