* 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 + testpull/1355/head
@@ -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)); | |||||
} | |||||
} | } | ||||
} | } |