From ae92c606bb8639575b731daa5eac3ce513f85b4a Mon Sep 17 00:00:00 2001
From: Patrik Svensson <patrik@patriksvensson.se>
Date: Wed, 7 Oct 2020 17:26:16 +0200
Subject: [PATCH] Add support for remapping emojis

Closes #94
---
 docs/input/appendix/emojis.md | 19 ++++++++++++++++++
 examples/Emojis/Program.cs    | 14 ++++++++++---
 src/.editorconfig             |  5 ++++-
 src/Spectre.Console/Emoji.cs  | 37 +++++++++++++++++++++++++++++++++++
 4 files changed, 71 insertions(+), 4 deletions(-)

diff --git a/docs/input/appendix/emojis.md b/docs/input/appendix/emojis.md
index f650792..3aab8a2 100644
--- a/docs/input/appendix/emojis.md
+++ b/docs/input/appendix/emojis.md
@@ -30,6 +30,25 @@ var phrase = "Mmmm :birthday_cake:";
 var rendered = Emoji.Replace(phrase);
 ```
 
+# Remapping or adding an emoji
+
+Sometimes you want to remap an existing emoji, or 
+add a completely new one. For this you can use the 
+`Emoji.Remap` method. This approach works both with 
+markup strings and `Emoji.Replace`.
+
+```csharp
+// Remap the emoji
+Emoji.Remap("globe_showing_europe_africa", "😄");
+
+// Render markup
+AnsiConsole.MarkupLine("Hello :globe_showing_europe_africa:!");
+
+// Replace emojis in string
+var phrase = "Hello :globe_showing_europe_africa:!";
+var rendered = Emoji.Replace(phrase);
+```
+
 # Emojis
 
 _The images in the table below might not render correctly in your 
diff --git a/examples/Emojis/Program.cs b/examples/Emojis/Program.cs
index f4662f0..c67932f 100644
--- a/examples/Emojis/Program.cs
+++ b/examples/Emojis/Program.cs
@@ -6,11 +6,19 @@ namespace EmojiExample
     {
         public static void Main(string[] args)
         {
-            // Markup
+            // Show a known emoji
+            RenderEmoji();
+
+            // Show a remapped emoji
+            Emoji.Remap("globe_showing_europe_africa", Emoji.Known.GrinningFaceWithSmilingEyes);
+            RenderEmoji();
+        }
+
+        private static void RenderEmoji()
+        {
             AnsiConsole.Render(
                 new Panel("[yellow]Hello :globe_showing_europe_africa:![/]")
-                    .RoundedBorder()
-                    .SetHeader("Markup"));
+                    .RoundedBorder());
         }
     }
 }
diff --git a/src/.editorconfig b/src/.editorconfig
index 31bd18c..449cd6c 100644
--- a/src/.editorconfig
+++ b/src/.editorconfig
@@ -86,4 +86,7 @@ dotnet_diagnostic.RCS1057.severity = none
 dotnet_diagnostic.RCS1227.severity = none
 
 # IDE0004: Remove Unnecessary Cast
-dotnet_diagnostic.IDE0004.severity = warning
\ No newline at end of file
+dotnet_diagnostic.IDE0004.severity = warning
+
+# CA1810: Initialize reference type static fields inline
+dotnet_diagnostic.CA1810.severity = none
\ No newline at end of file
diff --git a/src/Spectre.Console/Emoji.cs b/src/Spectre.Console/Emoji.cs
index f9795c1..ab2c08c 100644
--- a/src/Spectre.Console/Emoji.cs
+++ b/src/Spectre.Console/Emoji.cs
@@ -1,3 +1,5 @@
+using System;
+using System.Collections.Generic;
 using System.Text.RegularExpressions;
 
 namespace Spectre.Console
@@ -8,6 +10,35 @@ namespace Spectre.Console
     public static partial class Emoji
     {
         private static readonly Regex _emojiCode = new Regex(@"(:(\S*?):)", RegexOptions.Compiled);
+        private static readonly Dictionary<string, string> _remappings;
+
+        static Emoji()
+        {
+            _remappings = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
+        }
+
+        /// <summary>
+        /// Remaps a specific emoji tag with a new emoji.
+        /// </summary>
+        /// <param name="tag">The emoji tag.</param>
+        /// <param name="emoji">The emoji.</param>
+        public static void Remap(string tag, string emoji)
+        {
+            if (tag is null)
+            {
+                throw new ArgumentNullException(nameof(tag));
+            }
+
+            if (emoji is null)
+            {
+                throw new ArgumentNullException(nameof(emoji));
+            }
+
+            tag = tag.TrimStart(':').TrimEnd(':');
+            emoji = emoji.TrimStart(':').TrimEnd(':');
+
+            _remappings[tag] = emoji;
+        }
 
         /// <summary>
         /// Replaces emoji markup with corresponding unicode characters.
@@ -19,6 +50,12 @@ namespace Spectre.Console
             static string ReplaceEmoji(Match match)
             {
                 var key = match.Groups[2].Value;
+
+                if (_remappings.Count > 0 && _remappings.TryGetValue(key, out var remappedEmoji))
+                {
+                    return remappedEmoji;
+                }
+
                 if (_emojis.TryGetValue(key, out var emoji))
                 {
                     return emoji;