mfgames-cil/src/MfGames.Markdown/HeadingLevelTransformer.cs
D. Moonfire edf6289dee
All checks were successful
deploy / deploy (push) Successful in 12m9s
feat: added a Markdown heading level transformer
2024-04-18 23:56:05 -05:00

73 lines
2.4 KiB
C#

using Markdig.Renderers.Normalize;
using Markdig.Syntax;
using MfGames.Markdown.Exceptions;
namespace MfGames.Markdown;
/// <summary>
/// A transformer that goes through a Markdown document alters the level of the
/// headings either higher or lower..
/// </summary>
public class HeadingLevelTransformer
{
public HeadingLevelTransformer(int offset = 1)
{
this.Offset = offset;
}
/// <summary>
/// Gets or sets the value to offset the heading. A positive number would
/// increase the heading by that amount, a negative would reduce it. If
/// this would produce a negative heading, an exception is thrown. The
/// default is 1 to increase the heading level by one (H1 -> H2).
/// </summary>
public int Offset { get; } = 1;
/// <summary>
/// Parses the given input as Markdown, goes through and transforms all
/// the links, and then returns the modified Markdown.
/// </summary>
/// <param name="input">The input text as Markdown.</param>
/// <returns>Modified Markdown text.</returns>
public string? Transform(string? input)
{
// If we get a null or blank string, we return it.
if (string.IsNullOrWhiteSpace(input) || this.Offset == 0)
{
return input;
}
// Parse the Markdown into an abstract syntax tree (AST). We need the
// trivia because we want to round-trip as much as possible and it
// contains things like extra whitespace or indention.
MarkdownDocument document = Markdig.Markdown.Parse(input, true);
// Go through all the headings.
IEnumerable<HeadingBlock> blockList = document.Descendants<HeadingBlock>();
foreach (HeadingBlock block in blockList)
{
// Make sure we have sane values.
int oldLevel = block.Level;
int newLevel = oldLevel + this.Offset;
if (newLevel is < 1 or > 7)
{
throw new MarkdownHeaderOutOfRangeException(oldLevel, newLevel);
}
block.Level = newLevel;
}
// Convert the AST back into Markdown and return the results. The
// RoundtripRenderer doesn't work here, but NormalizeRenderer seems to
// allow us to modify the link above and get the results.
var writer = new StringWriter();
var renderer = new NormalizeRenderer(writer);
renderer.Write(document);
return writer.ToString();
}
}