using System; using System.Collections.Generic; using System.Text; namespace Spectre.Console { /// /// Utility for working with emojis. /// public static partial class Emoji { private static readonly Dictionary _remappings; static Emoji() { _remappings = new Dictionary(StringComparer.OrdinalIgnoreCase); } /// /// Remaps a specific emoji tag with a new emoji. /// /// The emoji tag. /// The emoji. 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; } #if NETSTANDARD2_0 /// /// Replaces emoji markup with corresponding unicode characters. /// /// A string with emojis codes, e.g. "Hello :smiley:!". /// A string with emoji codes replaced with actual emoji. public static string Replace(string value) { return Replace(value.AsSpan()); } #endif /// /// Replaces emoji markup with corresponding unicode characters. /// /// A string with emojis codes, e.g. "Hello :smiley:!". /// A string with emoji codes replaced with actual emoji. public static string Replace(ReadOnlySpan value) { var output = new StringBuilder(); var colonPos = value.IndexOf(':'); if (colonPos == -1) { // No colons, no emoji. return what was passed in with no changes. return value.ToString(); } while ((colonPos = value.IndexOf(':')) != -1) { // Append text up to colon output.AppendSpan(value.Slice(0, colonPos)); // Set value equal to that colon and the rest of the string value = value.Slice(colonPos); // Find colon after that. if no colon, break out var nextColonPos = value.IndexOf(':', 1); if (nextColonPos == -1) { break; } // Get the emoji text minus the colons var emojiKey = value.Slice(1, nextColonPos - 1).ToString(); if (TryGetEmoji(emojiKey, out var emojiValue)) { output.Append(emojiValue); value = value.Slice(nextColonPos + 1); } else { output.Append(':'); value = value.Slice(1); } } output.AppendSpan(value); return output.ToString(); } private static bool TryGetEmoji(string emoji, out string value) { if (_remappings.TryGetValue(emoji, out var remappedEmojiValue)) { value = remappedEmojiValue; return true; } if (_emojis.TryGetValue(emoji, out var emojiValue)) { value = emojiValue; return true; } value = string.Empty; return false; } private static int IndexOf(this ReadOnlySpan span, char value, int startIndex) { var indexInSlice = span.Slice(startIndex).IndexOf(value); if (indexInSlice == -1) { return -1; } return startIndex + indexInSlice; } } }