* Implement Quote Formatting Adds support for block quote text formatting. This feature is currently only implemented in the Canary client. This formatting adds a "> " to each new line from the input text. * add > char to character sanitization * change assumptions around whitespace strings * add blockquote (>>>) formatting + testtags/2.2.0
| @@ -1,10 +1,12 @@ | |||||
| using System.Text; | |||||
| namespace Discord | namespace Discord | ||||
| { | { | ||||
| /// <summary> A helper class for formatting characters. </summary> | /// <summary> A helper class for formatting characters. </summary> | ||||
| public static class Format | public static class Format | ||||
| { | { | ||||
| // Characters which need escaping | // Characters which need escaping | ||||
| private static readonly string[] SensitiveCharacters = { "\\", "*", "_", "~", "`", "|" }; | |||||
| private static readonly string[] SensitiveCharacters = { "\\", "*", "_", "~", "`", "|", ">" }; | |||||
| /// <summary> Returns a markdown-formatted string with bold formatting. </summary> | /// <summary> Returns a markdown-formatted string with bold formatting. </summary> | ||||
| public static string Bold(string text) => $"**{text}**"; | public static string Bold(string text) => $"**{text}**"; | ||||
| @@ -37,5 +39,57 @@ namespace Discord | |||||
| text = text.Replace(unsafeChar, $"\\{unsafeChar}"); | text = text.Replace(unsafeChar, $"\\{unsafeChar}"); | ||||
| return text; | return text; | ||||
| } | } | ||||
| /// <summary> | |||||
| /// Formats a string as a quote. | |||||
| /// </summary> | |||||
| /// <param name="text">The text to format.</param> | |||||
| /// <returns>Gets the formatted quote text.</returns> | |||||
| public static string Quote(string text) | |||||
| { | |||||
| // do not modify null or whitespace text | |||||
| // whitespace does not get quoted properly | |||||
| if (string.IsNullOrWhiteSpace(text)) | |||||
| return text; | |||||
| StringBuilder result = new StringBuilder(); | |||||
| int startIndex = 0; | |||||
| int newLineIndex; | |||||
| do | |||||
| { | |||||
| newLineIndex = text.IndexOf('\n', startIndex); | |||||
| if (newLineIndex == -1) | |||||
| { | |||||
| // read the rest of the string | |||||
| var str = text.Substring(startIndex); | |||||
| result.Append($"> {str}"); | |||||
| } | |||||
| else | |||||
| { | |||||
| // read until the next newline | |||||
| var str = text.Substring(startIndex, newLineIndex - startIndex); | |||||
| result.Append($"> {str}\n"); | |||||
| } | |||||
| startIndex = newLineIndex + 1; | |||||
| } | |||||
| while (newLineIndex != -1 && startIndex != text.Length); | |||||
| return result.ToString(); | |||||
| } | |||||
| /// <summary> | |||||
| /// Formats a string as a block quote. | |||||
| /// </summary> | |||||
| /// <param name="text">The text to format.</param> | |||||
| /// <returns>Gets the formatted block quote text.</returns> | |||||
| public static string BlockQuote(string text) | |||||
| { | |||||
| // do not modify null or whitespace | |||||
| if (string.IsNullOrWhiteSpace(text)) | |||||
| return text; | |||||
| return $">>> {text}"; | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| @@ -14,6 +14,7 @@ namespace Discord | |||||
| [InlineData(@"~text~", @"\~text\~")] | [InlineData(@"~text~", @"\~text\~")] | ||||
| [InlineData(@"`text`", @"\`text\`")] | [InlineData(@"`text`", @"\`text\`")] | ||||
| [InlineData(@"_text_", @"\_text\_")] | [InlineData(@"_text_", @"\_text\_")] | ||||
| [InlineData(@"> text", @"\> text")] | |||||
| public void Sanitize(string input, string expected) | public void Sanitize(string input, string expected) | ||||
| { | { | ||||
| Assert.Equal(expected, Format.Sanitize(input)); | Assert.Equal(expected, Format.Sanitize(input)); | ||||
| @@ -28,5 +29,35 @@ namespace Discord | |||||
| Assert.Equal("```cs\ntest\n```", Format.Code("test", "cs")); | Assert.Equal("```cs\ntest\n```", Format.Code("test", "cs")); | ||||
| Assert.Equal("```cs\nanother\none\n```", Format.Code("another\none", "cs")); | Assert.Equal("```cs\nanother\none\n```", Format.Code("another\none", "cs")); | ||||
| } | } | ||||
| [Fact] | |||||
| public void QuoteNullString() | |||||
| { | |||||
| Assert.Null(Format.Quote(null)); | |||||
| } | |||||
| [Theory] | |||||
| [InlineData("", "")] | |||||
| [InlineData("\n", "\n")] | |||||
| [InlineData("foo\n\nbar", "> foo\n> \n> bar")] | |||||
| [InlineData("input", "> input")] // single line | |||||
| // should work with CR or CRLF | |||||
| [InlineData("inb4\ngreentext", "> inb4\n> greentext")] | |||||
| [InlineData("inb4\r\ngreentext", "> inb4\r\n> greentext")] | |||||
| public void Quote(string input, string expected) | |||||
| { | |||||
| Assert.Equal(expected, Format.Quote(input)); | |||||
| } | |||||
| [Theory] | |||||
| [InlineData(null, null)] | |||||
| [InlineData("", "")] | |||||
| [InlineData("\n", "\n")] | |||||
| [InlineData("foo\n\nbar", ">>> foo\n\nbar")] | |||||
| [InlineData("input", ">>> input")] // single line | |||||
| // should work with CR or CRLF | |||||
| [InlineData("inb4\ngreentext", ">>> inb4\ngreentext")] | |||||
| [InlineData("inb4\r\ngreentext", ">>> inb4\r\ngreentext")] | |||||
| public void BlockQuote(string input, string expected) | |||||
| { | |||||
| Assert.Equal(expected, Format.BlockQuote(input)); | |||||
| } | |||||
| } | } | ||||
| } | } | ||||