Skip to content

Commit

Permalink
feat: add markdown formatting methods
Browse files Browse the repository at this point in the history
  • Loading branch information
oliverbooth committed Aug 27, 2023
1 parent bd823ba commit 5d936b5
Show file tree
Hide file tree
Showing 3 changed files with 329 additions and 0 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- X10D: Added `TextWriter.WriteLineNoAlloc(long[, ReadOnlySpan<char>[, IFormatProvider]])`.
- X10D: Added `TextWriter.WriteLineNoAlloc(ulong[, ReadOnlySpan<char>[, IFormatProvider]])`.
- X10D: Added `string.ConcatIf`.
- X10D: Added `string.MDBold`, `string.MDCode`, `string.MDCodeBlock([string])`, `string.MDHeading(int)`,
`string.MDItalic`, `string.MDLink`, `string.MDStrikeOut`, and `string.MDUnderline` for Markdown formatting.
- X10D.Unity: Added `RaycastHit.GetComponent` and `RaycastHit.TryGetComponent`.
- X10D.Unity: Added `DebugUtility.DrawFunction`, and `DebugUtility.DrawUnjoinedPolyhedron` on which it relies.

Expand Down
122 changes: 122 additions & 0 deletions X10D.Tests/src/Text/MarkdownTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
using NUnit.Framework;
using X10D.Text;

namespace X10D.Tests.Text;

[TestFixture]
internal class MarkdownTests
{
[Test]
public void MDBold_ShouldThrowArgumentNullException_GivenNull()
{
Assert.Throws<ArgumentNullException>(() => ((string)null!).MDBold());
}

[Test]
public void MDBold_ShouldReturnBoldText_GivenText()
{
Assert.That("Hello, world!".MDBold(), Is.EqualTo("**Hello, world!**"));
}

[Test]
public void MDCode_ShouldThrowArgumentNullException_GivenNull()
{
Assert.Throws<ArgumentNullException>(() => ((string)null!).MDCode());
}

[Test]
public void MDCode_ShouldReturnCodeText_GivenText()
{
Assert.That("Hello, world!".MDCode(), Is.EqualTo("`Hello, world!`"));
}

[Test]
public void MDCodeBlock_ShouldThrowArgumentNullException_GivenNull()
{
Assert.Throws<ArgumentNullException>(() => ((string)null!).MDCodeBlock());
}

[Test]
public void MDCodeBlock_ShouldReturnCodeBlockText_GivenText()
{
Assert.That("Hello, world!".MDCodeBlock(), Is.EqualTo($"```{Environment.NewLine}Hello, world!{Environment.NewLine}```"));
}

[Test]
public void MDCodeBlock_ShouldReturnCodeBlockText_GivenTextAndLanguage()
{
Assert.That("Hello, world!".MDCodeBlock("csharp"), Is.EqualTo($"```csharp{Environment.NewLine}Hello, world!{Environment.NewLine}```"));
}

[Test]
public void MDHeading_ShouldThrowArgumentNullException_GivenNull()
{
Assert.Throws<ArgumentNullException>(() => ((string)null!).MDHeading(1));
}

[Test]
public void MDHeading_ShouldThrowArgumentOutOfRangeException_GivenInvalidHeading()
{
Assert.Throws<ArgumentOutOfRangeException>(() => "Hello, world!".MDHeading(0));
Assert.Throws<ArgumentOutOfRangeException>(() => "Hello, world!".MDHeading(7));
}

[Test]
public void MDHeading_ShouldReturnHeadingText_GivenText()
{
Assert.That("Hello, world!".MDHeading(1), Is.EqualTo("# Hello, world!"));
Assert.That("Hello, world!".MDHeading(2), Is.EqualTo("## Hello, world!"));
Assert.That("Hello, world!".MDHeading(3), Is.EqualTo("### Hello, world!"));
Assert.That("Hello, world!".MDHeading(4), Is.EqualTo("#### Hello, world!"));
Assert.That("Hello, world!".MDHeading(5), Is.EqualTo("##### Hello, world!"));
Assert.That("Hello, world!".MDHeading(6), Is.EqualTo("###### Hello, world!"));
}

[Test]
public void MDItalic_ShouldThrowArgumentNullException_GivenNull()
{
Assert.Throws<ArgumentNullException>(() => ((string)null!).MDItalic());
}

[Test]
public void MDItalic_ShouldReturnItalicTextWithAsterisk_GivenText()
{
Assert.That("Hello, world!".MDItalic(), Is.EqualTo("*Hello, world!*"));
}

[Test]
public void MDItalic_ShouldReturnItalicTextWithAsterisk_GivenText_AndFalseUnderscoreFlag()
{
Assert.That("Hello, world!".MDItalic(false), Is.EqualTo("*Hello, world!*"));
}

[Test]
public void MDItalic_ShouldReturnItalicTextWithUnderscores_GivenText_AndTrueUnderscoreFlag()
{
Assert.That("Hello, world!".MDItalic(true), Is.EqualTo("_Hello, world!_"));
}

[Test]
public void MDStrikeOut_ShouldThrowArgumentNullException_GivenNull()
{
Assert.Throws<ArgumentNullException>(() => ((string)null!).MDStrikeOut());
}

[Test]
public void MDStrikeOut_ShouldReturnStrikeOutText_GivenText()
{
Assert.That("Hello, world!".MDStrikeOut(), Is.EqualTo("~~Hello, world!~~"));
}

[Test]
public void MDUnderline_ShouldThrowArgumentNullException_GivenNull()
{
Assert.Throws<ArgumentNullException>(() => ((string)null!).MDUnderline());
}

[Test]
public void MDUnderline_ShouldReturnUnderlineText_GivenText()
{
Assert.That("Hello, world!".MDUnderline(), Is.EqualTo("__Hello, world!__"));
}
}
205 changes: 205 additions & 0 deletions X10D/src/Text/MarkdownExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
namespace X10D.Text;

/// <summary>
/// Markdown-related extension methods for <see cref="string" />.
/// </summary>
public static class MarkdownExtensions
{
/// <summary>
/// Formats the specified text as bold, using Markdown.
/// </summary>
/// <param name="value">The value to surround with bold.</param>
/// <returns>The formatted text.</returns>
/// <exception cref="ArgumentNullException"><paramref name="value" /> is null.</exception>
public static string MDBold(this string value)
{
if (value is null)
{
throw new ArgumentNullException(nameof(value));
}

return $"**{value}**";
}

/// <summary>
/// Formats the specified text as code, using Markdown.
/// </summary>
/// <param name="value">The value to surround with code.</param>
/// <returns>The formatted text.</returns>
/// <exception cref="ArgumentNullException"><paramref name="value" /> is null.</exception>
public static string MDCode(this string value)
{
if (value is null)
{
throw new ArgumentNullException(nameof(value));
}

return $"`{value}`";
}

/// <summary>
/// Formats the specified text as a code block, using Markdown.
/// </summary>
/// <param name="value">The value to surround with code blocks.</param>
/// <returns>The formatted text.</returns>
/// <exception cref="ArgumentNullException"><paramref name="value" /> is null.</exception>
public static string MDCodeBlock(this string value)
{
return MDCodeBlock(value, string.Empty);
}

/// <summary>
/// Formats the specified text as a code block, using Markdown.
/// </summary>
/// <param name="value">The value to surround with code blocks.</param>
/// <param name="language">The language to use for syntax highlighting.</param>
/// <returns>The formatted text.</returns>
/// <exception cref="ArgumentNullException"><paramref name="value" /> is null.</exception>
public static string MDCodeBlock(this string value, string language)
{
if (value is null)
{
throw new ArgumentNullException(nameof(value));
}

return $"```{language}{Environment.NewLine}{value}{Environment.NewLine}```";
}

/// <summary>
/// Formats the specified text as a heading, using Markdown.
/// </summary>
/// <param name="value">The value to surround with italics.</param>
/// <param name="level">The level of the heading.</param>
/// <returns>The formatted text.</returns>
/// <exception cref="ArgumentNullException"><paramref name="value" /> is null.</exception>
/// <exception cref="ArgumentOutOfRangeException"><paramref name="level" /> is less than 1 or greater than 6.</exception>
public static string MDHeading(this string value, int level)
{
if (value is null)
{
throw new ArgumentNullException(nameof(value));
}

if (level is < 1 or > 6)
{
throw new ArgumentOutOfRangeException(nameof(level));
}

return $"{'#'.Repeat(level)} {value}";
}

/// <summary>
/// Formats the specified text as italics, using Markdown.
/// </summary>
/// <param name="value">The value to surround with italics.</param>
/// <returns>The formatted text.</returns>
/// <exception cref="ArgumentNullException"><paramref name="value" /> is null.</exception>
/// <remarks>
/// Markdown has two methods of italicizing text: <c>*</c> and <c>_</c>. This method uses asterisks by default. To
/// use underscores, use <see cref="MDItalic(string, bool)" /> and pass <see langword="true" /> as the second argument.
/// </remarks>
public static string MDItalic(this string value)
{
return MDItalic(value, false);
}

/// <summary>
/// Formats the specified text as italics, using Markdown.
/// </summary>
/// <param name="value">The value to surround with italics.</param>
/// <param name="useUnderscores">Whether to use underscores instead of asterisks for italicizing.</param>
/// <returns>The formatted text.</returns>
/// <exception cref="ArgumentNullException"><paramref name="value" /> is null.</exception>
public static string MDItalic(this string value, bool useUnderscores)
{
if (value is null)
{
throw new ArgumentNullException(nameof(value));
}

return useUnderscores ? $"_{value}_" : $"*{value}*";
}

/// <summary>
/// Formats the specified text as a link, using Markdown.
/// </summary>
/// <param name="label">The label to use for the link.</param>
/// <param name="url">The URL to link to.</param>
/// <returns>The formatted text.</returns>
/// <exception cref="ArgumentNullException"><paramref name="url" /> is null.</exception>
public static string MDLink(this string? label, string url)
{
if (url is null)
{
throw new ArgumentNullException(nameof(url));
}

return string.IsNullOrWhiteSpace(label) ? url : $"[{label}]({url})";
}

/// <summary>
/// Formats the specified text as a link, using Markdown.
/// </summary>
/// <param name="label">The label to use for the link.</param>
/// <param name="url">The URL to link to.</param>
/// <returns>The formatted text.</returns>
/// <exception cref="ArgumentNullException"><paramref name="url" /> is null.</exception>
public static string MDLink(this string? label, Uri url)
{
if (url is null)
{
throw new ArgumentNullException(nameof(url));
}

return string.IsNullOrWhiteSpace(label) ? url.ToString() : $"[{label}]({url})";
}

/// <summary>
/// Formats the specified text as a link, using Markdown.
/// </summary>
/// <param name="url">The URL to link to.</param>
/// <param name="label">The label to use for the link.</param>
/// <returns>The formatted text.</returns>
/// <exception cref="ArgumentNullException"><paramref name="url" /> is null.</exception>
public static string MDLink(this Uri url, string? label)
{
if (url is null)
{
throw new ArgumentNullException(nameof(url));
}

return string.IsNullOrWhiteSpace(label) ? url.ToString() : $"[{label}]({url})";
}

/// <summary>
/// Formats the specified text as striked out, using Markdown.
/// </summary>
/// <param name="value">The value to surround with strikeout.</param>
/// <returns>The formatted text.</returns>
/// <exception cref="ArgumentNullException"><paramref name="value" /> is null.</exception>
public static string MDStrikeOut(this string value)
{
if (value is null)
{
throw new ArgumentNullException(nameof(value));
}

return $"~~{value}~~";
}

/// <summary>
/// Formats the specified text as underlined, using Markdown.
/// </summary>
/// <param name="value">The value to surround with underline.</param>
/// <returns>The formatted text.</returns>
/// <exception cref="ArgumentNullException"><paramref name="value" /> is null.</exception>
public static string MDUnderline(this string value)
{
if (value is null)
{
throw new ArgumentNullException(nameof(value));
}

return $"__{value}__";
}
}

0 comments on commit 5d936b5

Please sign in to comment.