mirror of
https://github.com/nsnail/spectre.console.git
synced 2025-04-16 17:02:51 +08:00
Add Generator command to generate emoji lookup table
This commit is contained in:
parent
11d331e31d
commit
a7b7d4e556
22
resources/scripts/Generate-Emoji.ps1
Normal file
22
resources/scripts/Generate-Emoji.ps1
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
##########################################################
|
||||||
|
# Script that generates the emoji lookup table.
|
||||||
|
##########################################################
|
||||||
|
|
||||||
|
$Output = Join-Path $PSScriptRoot "Temp"
|
||||||
|
$Source = Join-Path $PSScriptRoot "/../../src/Spectre.Console"
|
||||||
|
|
||||||
|
if(!(Test-Path $Output -PathType Container)) {
|
||||||
|
New-Item -ItemType Directory -Path $Output | Out-Null
|
||||||
|
}
|
||||||
|
|
||||||
|
# Generate the files
|
||||||
|
Push-Location Generator
|
||||||
|
&dotnet run -- emoji "$Output"
|
||||||
|
if(!$?) {
|
||||||
|
Pop-Location
|
||||||
|
Throw "An error occured when generating code."
|
||||||
|
}
|
||||||
|
Pop-Location
|
||||||
|
|
||||||
|
# Copy the files to the correct location
|
||||||
|
Copy-Item (Join-Path "$Output" "Emoji.Generated.cs") -Destination "$Source/Emoji.Generated.cs"
|
@ -0,0 +1,83 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using AngleSharp.Html.Parser;
|
||||||
|
using Generator.Models;
|
||||||
|
using Scriban;
|
||||||
|
using Scriban.Runtime;
|
||||||
|
using Spectre.Cli;
|
||||||
|
using Spectre.IO;
|
||||||
|
using Path = Spectre.IO.Path;
|
||||||
|
|
||||||
|
namespace Generator.Commands
|
||||||
|
{
|
||||||
|
public sealed class EmojiGeneratorCommand : AsyncCommand<GeneratorCommandSettings>
|
||||||
|
{
|
||||||
|
private readonly IFileSystem _fileSystem;
|
||||||
|
|
||||||
|
private readonly IHtmlParser _parser;
|
||||||
|
|
||||||
|
public EmojiGeneratorCommand()
|
||||||
|
{
|
||||||
|
_fileSystem = new FileSystem();
|
||||||
|
_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 emojis = await FetchEmojis("http://www.unicode.org/emoji/charts/emoji-list.html");
|
||||||
|
|
||||||
|
var result = await RenderTemplate(templatePath, emojis);
|
||||||
|
|
||||||
|
var outputPath = output.CombineWithFilePath(templatePath.GetFilename().ChangeExtension(".cs"));
|
||||||
|
|
||||||
|
await File.WriteAllTextAsync(outputPath.FullPath, result);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<IReadOnlyCollection<Emoji>> FetchEmojis(string url)
|
||||||
|
{
|
||||||
|
using var http = new HttpClient();
|
||||||
|
|
||||||
|
var htmlStream = await http.GetStreamAsync(url);
|
||||||
|
|
||||||
|
var document = await _parser.ParseDocumentAsync(htmlStream);
|
||||||
|
|
||||||
|
return Emoji.Parse(document).OrderBy(x => x.Name).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task<string> RenderTemplate(Path path, IReadOnlyCollection<Emoji> emojis)
|
||||||
|
{
|
||||||
|
var text = await File.ReadAllTextAsync(path.FullPath);
|
||||||
|
|
||||||
|
var template = Template.Parse(text);
|
||||||
|
|
||||||
|
var templateContext = new TemplateContext
|
||||||
|
{
|
||||||
|
// Because of the insane amount of Emojis,
|
||||||
|
// we need to get rid of some secure defaults :P
|
||||||
|
LoopLimit = int.MaxValue,
|
||||||
|
};
|
||||||
|
|
||||||
|
var scriptObject = new ScriptObject();
|
||||||
|
|
||||||
|
scriptObject.Import(new { Emojis = emojis });
|
||||||
|
|
||||||
|
templateContext.PushGlobal(scriptObject);
|
||||||
|
|
||||||
|
return await template.RenderAsync(templateContext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -24,9 +24,13 @@
|
|||||||
<None Update="Templates\ColorPalette.Generated.template">
|
<None Update="Templates\ColorPalette.Generated.template">
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
</None>
|
</None>
|
||||||
|
<None Update="Templates\Emoji.Generated.template">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="AngleSharp" Version="0.14.0" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||||
<PackageReference Include="Scriban" Version="2.1.3" />
|
<PackageReference Include="Scriban" Version="2.1.3" />
|
||||||
<PackageReference Include="Spectre.Cli" Version="0.36.1-preview.0.6" />
|
<PackageReference Include="Spectre.Cli" Version="0.36.1-preview.0.6" />
|
||||||
|
70
resources/scripts/Generator/Models/Emoji.cs
Normal file
70
resources/scripts/Generator/Models/Emoji.cs
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using AngleSharp.Dom;
|
||||||
|
using AngleSharp.Html.Dom;
|
||||||
|
|
||||||
|
namespace Generator.Models
|
||||||
|
{
|
||||||
|
public class Emoji
|
||||||
|
{
|
||||||
|
private static readonly string[] _headers = { "count", "code", "sample", "name" };
|
||||||
|
|
||||||
|
private Emoji(string code, string name)
|
||||||
|
{
|
||||||
|
Code = code;
|
||||||
|
Name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Code { get; }
|
||||||
|
|
||||||
|
public string Name { get; }
|
||||||
|
|
||||||
|
public static IEnumerable<Emoji> Parse(IHtmlDocument document)
|
||||||
|
{
|
||||||
|
var rows = document
|
||||||
|
.GetNodes<IHtmlTableRowElement>(predicate: row =>
|
||||||
|
row.Cells.Length >= _headers.Length && // Filter out rows that don't have enough cells.
|
||||||
|
row.Cells.All(x => x.LocalName == TagNames.Td)); // We're only interested in td cells, not th.
|
||||||
|
|
||||||
|
foreach (var row in rows)
|
||||||
|
{
|
||||||
|
var dictionary = _headers
|
||||||
|
.Zip(row.Cells, (header, cell) => (header, cell.TextContent.Trim()))
|
||||||
|
.ToDictionary(x => x.Item1, x => x.Item2);
|
||||||
|
|
||||||
|
var code = TransformCode(dictionary["code"]);
|
||||||
|
var name = TransformName(dictionary["name"]);
|
||||||
|
|
||||||
|
yield return new Emoji(code, name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string TransformName(string name)
|
||||||
|
{
|
||||||
|
return name.Replace(":", string.Empty)
|
||||||
|
.Replace(",", string.Empty)
|
||||||
|
.Replace(".", string.Empty)
|
||||||
|
.Replace("\u201c", string.Empty)
|
||||||
|
.Replace("\u201d", string.Empty)
|
||||||
|
.Replace("\u229b", string.Empty)
|
||||||
|
.Trim()
|
||||||
|
.Replace(' ', '_')
|
||||||
|
.ToLowerInvariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string TransformCode(string code)
|
||||||
|
{
|
||||||
|
var builder = new StringBuilder();
|
||||||
|
|
||||||
|
foreach (var part in code.Split(' '))
|
||||||
|
{
|
||||||
|
builder.Append(part.Length == 6
|
||||||
|
? part.Replace("+", "0000")
|
||||||
|
: part.Replace("+", "000"));
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.ToString().Replace("U", "\\U");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -11,6 +11,7 @@ namespace Generator
|
|||||||
app.Configure(config =>
|
app.Configure(config =>
|
||||||
{
|
{
|
||||||
config.AddCommand<ColorGeneratorCommand>("colors");
|
config.AddCommand<ColorGeneratorCommand>("colors");
|
||||||
|
config.AddCommand<EmojiGeneratorCommand>("emoji");
|
||||||
});
|
});
|
||||||
|
|
||||||
return app.Run(args);
|
return app.Run(args);
|
||||||
|
@ -0,0 +1,29 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// <auto-generated>
|
||||||
|
// This code was generated by a tool.
|
||||||
|
// Generated {{ date.now | date.to_string `%Y-%m-%d %k:%M` }}
|
||||||
|
//
|
||||||
|
// Changes to this file may cause incorrect behavior and will be lost if
|
||||||
|
// the code is regenerated.
|
||||||
|
// </auto-generated>
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
|
||||||
|
namespace Spectre.Console
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Utility class for working with emojis.
|
||||||
|
/// </summary>
|
||||||
|
internal 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 }}" },
|
||||||
|
{{~ end ~}}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user