diff --git a/src/Discord.Net/Format.cs b/src/Discord.Net/Format.cs index 6c62ba4aa..2218a4767 100644 --- a/src/Discord.Net/Format.cs +++ b/src/Discord.Net/Format.cs @@ -1,23 +1,91 @@ -using System.Text; +using Discord.Net.API; +using System.Text; using System.Text.RegularExpressions; namespace Discord { - public static class Format + public static class Format { - private static readonly Regex _escapeRegex; - private static readonly MatchEvaluator _escapeEvaluator; + private static readonly string[] _patterns; + private static readonly StringBuilder _builder; static Format() { - const string innerPattern = "[_*]|~~"; - _escapeRegex = new Regex($@"(?<=^|\W)(?:{innerPattern})|(?:{innerPattern})(?=\W|$)|\\", RegexOptions.Compiled); - _escapeEvaluator = new MatchEvaluator(e => '\\' + e.Value); - } + _patterns = new string[] { "__", "_", "**", "*", "~~" }; + _builder = new StringBuilder(DiscordAPIClient.MaxMessageSize); + } /// Removes all special formatting characters from the provided text. private static string Escape(string text) - => _escapeRegex.Replace(text, _escapeEvaluator); + { + lock (_builder) + { + _builder.Clear(); + + //Escape all backslashes + for (int i = 0; i < text.Length; i++) + { + _builder.Append(text[i]); + if (text[i] == '\\') + _builder.Append('\\'); + } + + EscapeSubstring(0, _builder.Length); + + return _builder.ToString(); + } + } + private static int EscapeSubstring(int start, int end) + { + int totalAddedChars = 0; + for (int i = start; i < end + totalAddedChars; i++) + { + for (int p = 0; p < _patterns.Length; p++) + { + string pattern = _patterns[p]; + if (i + pattern.Length * 2 > _builder.Length) + continue; + int s = FindPattern(pattern, i, i + 1); + if (s == -1) continue; + int e = FindPattern(pattern, i + 1, end + totalAddedChars); + if (e == -1) continue; + + if (e - s - pattern.Length > 0) + { + //By going right to left, we dont need to adjust any offsets + for (int k = pattern.Length - 1; k >= 0; k--) + _builder.Insert(e + k, '\\'); + for (int k = pattern.Length - 1; k >= 0; k--) + _builder.Insert(s + k, '\\'); + int addedChars = pattern.Length * 2; + addedChars += EscapeSubstring(s + pattern.Length * 2, e + pattern.Length); + i = e + addedChars + pattern.Length - 1; + totalAddedChars += addedChars; + break; + } + } + } + return totalAddedChars; + } + private static int FindPattern(string pattern, int start, int end) + { + for (int j = start; j < end; j++) + { + if (_builder[j] == '\\') + { + j++; + continue; + } + for (int k = 0; k < pattern.Length; k++) + { + if (_builder[j + k] != pattern[k]) + goto nextpos; + } + return j; + nextpos:; + } + return -1; + } /// Returns a markdown-formatted string with no formatting, optionally escaping the contents. public static string Normal(string text, bool escape = true)