mirror of
				https://github.com/nsnail/spectre.console.git
				synced 2025-10-31 00:59:26 +08:00 
			
		
		
		
	Add Generator command to generate emoji lookup table
This commit is contained in:
		 Kristian Hellang
					Kristian Hellang
				
			
				
					committed by
					
						 Patrik Svensson
						Patrik Svensson
					
				
			
			
				
	
			
			
			 Patrik Svensson
						Patrik Svensson
					
				
			
						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"> | ||||
|       <CopyToOutputDirectory>Always</CopyToOutputDirectory> | ||||
|     </None> | ||||
|     <None Update="Templates\Emoji.Generated.template"> | ||||
|       <CopyToOutputDirectory>Always</CopyToOutputDirectory> | ||||
|     </None> | ||||
|   </ItemGroup> | ||||
|  | ||||
|   <ItemGroup> | ||||
|     <PackageReference Include="AngleSharp" Version="0.14.0" /> | ||||
|     <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" /> | ||||
|   | ||||
							
								
								
									
										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 => | ||||
|             { | ||||
|                 config.AddCommand<ColorGeneratorCommand>("colors"); | ||||
|                 config.AddCommand<EmojiGeneratorCommand>("emoji"); | ||||
|             }); | ||||
|  | ||||
|             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 ~}} | ||||
|         }; | ||||
|     } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user