2024-05-04 00:27:17 +01:00
|
|
|
using HtmlAgilityPack;
|
2024-05-03 23:31:47 +01:00
|
|
|
using Humanizer;
|
2024-05-04 00:27:17 +01:00
|
|
|
using Markdig;
|
2024-05-03 23:31:47 +01:00
|
|
|
using Markdig.Renderers;
|
|
|
|
using Markdig.Renderers.Html;
|
|
|
|
|
|
|
|
namespace OliverBooth.Markdown.Callout;
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Represents an HTML renderer which renders a <see cref="CalloutBlock" />.
|
|
|
|
/// </summary>
|
|
|
|
internal sealed class CalloutRenderer : HtmlObjectRenderer<CalloutBlock>
|
|
|
|
{
|
2024-05-04 00:27:17 +01:00
|
|
|
private readonly MarkdownPipeline _pipeline;
|
|
|
|
|
2024-05-03 23:31:47 +01:00
|
|
|
private static readonly Dictionary<string, string> CalloutTypes = new()
|
|
|
|
{
|
|
|
|
["NOTE"] = "pencil",
|
|
|
|
["ABSTRACT"] = "clipboard-list",
|
|
|
|
["INFO"] = "info",
|
|
|
|
["TODO"] = "circle-check",
|
|
|
|
["TIP"] = "flame",
|
|
|
|
["SUCCESS"] = "check",
|
|
|
|
["QUESTION"] = "circle-help",
|
|
|
|
["WARNING"] = "triangle-alert",
|
|
|
|
["FAILURE"] = "x",
|
|
|
|
["DANGER"] = "zap",
|
|
|
|
["BUG"] = "bug",
|
|
|
|
["EXAMPLE"] = "list",
|
|
|
|
["CITE"] = "quote",
|
|
|
|
["UPDATE"] = "calendar-check",
|
|
|
|
};
|
|
|
|
|
2024-05-04 00:27:17 +01:00
|
|
|
public CalloutRenderer(MarkdownPipeline pipeline)
|
|
|
|
{
|
|
|
|
_pipeline = pipeline;
|
|
|
|
}
|
|
|
|
|
2024-05-03 23:31:47 +01:00
|
|
|
/// <inheritdoc />
|
|
|
|
protected override void Write(HtmlRenderer renderer, CalloutBlock block)
|
|
|
|
{
|
|
|
|
renderer.EnsureLine();
|
|
|
|
if (renderer.EnableHtmlForBlock)
|
|
|
|
{
|
2024-05-04 00:27:17 +01:00
|
|
|
RenderAsHtml(renderer, block, _pipeline);
|
2024-05-03 23:31:47 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
RenderAsText(renderer, block);
|
|
|
|
}
|
|
|
|
|
|
|
|
renderer.EnsureLine();
|
|
|
|
}
|
|
|
|
|
2024-05-04 00:27:17 +01:00
|
|
|
private static void RenderAsHtml(HtmlRenderer renderer, CalloutBlock block, MarkdownPipeline pipeline)
|
2024-05-03 23:31:47 +01:00
|
|
|
{
|
|
|
|
string title = block.Title.Text;
|
|
|
|
ReadOnlySpan<char> type = block.Type.AsSpan();
|
|
|
|
Span<char> upperType = stackalloc char[type.Length];
|
|
|
|
type.ToUpperInvariant(upperType);
|
|
|
|
|
|
|
|
if (!CalloutTypes.TryGetValue(upperType.ToString(), out string? lucideClass))
|
|
|
|
{
|
|
|
|
lucideClass = "pencil";
|
|
|
|
}
|
|
|
|
|
|
|
|
var typeString = type.ToString().ToLowerInvariant();
|
|
|
|
|
|
|
|
renderer.Write($"<div class=\"callout\" data-callout=\"{typeString}\">");
|
|
|
|
renderer.Write("<div class=\"callout-title\"><i data-lucide=\"");
|
|
|
|
renderer.Write(lucideClass);
|
|
|
|
renderer.Write("\"></i> ");
|
|
|
|
|
2024-05-04 00:27:17 +01:00
|
|
|
string calloutTitle = title.Length == 0 ? typeString.Humanize(LetterCasing.Sentence) : title;
|
|
|
|
WriteTitle(renderer, pipeline, calloutTitle);
|
|
|
|
|
2024-05-03 23:31:47 +01:00
|
|
|
renderer.WriteLine("</div>");
|
|
|
|
renderer.WriteChildren(block);
|
|
|
|
renderer.WriteLine("</div>");
|
|
|
|
renderer.EnsureLine();
|
|
|
|
}
|
|
|
|
|
2024-05-04 00:27:17 +01:00
|
|
|
private static void WriteTitle(TextRendererBase renderer, MarkdownPipeline pipeline, string calloutTitle)
|
|
|
|
{
|
|
|
|
string html = Markdig.Markdown.ToHtml(calloutTitle, pipeline);
|
|
|
|
var document = new HtmlDocument();
|
|
|
|
document.LoadHtml(html);
|
|
|
|
if (document.DocumentNode.FirstChild is { Name: "p" } child)
|
|
|
|
{
|
|
|
|
// ugly hack to remove <p> tag generated by Markdig
|
|
|
|
document.DocumentNode.InnerHtml = child.InnerHtml;
|
|
|
|
}
|
|
|
|
|
|
|
|
document.Save(renderer.Writer);
|
|
|
|
}
|
|
|
|
|
2024-05-03 23:31:47 +01:00
|
|
|
private static void RenderAsText(HtmlRenderer renderer, CalloutBlock block)
|
|
|
|
{
|
|
|
|
string title = block.Title.Text;
|
|
|
|
ReadOnlySpan<char> type = block.Type.AsSpan();
|
|
|
|
renderer.WriteLine(title.Length == 0 ? type.ToString().ToUpperInvariant() : title.ToUpperInvariant());
|
|
|
|
renderer.WriteChildren(block);
|
|
|
|
renderer.EnsureLine();
|
|
|
|
}
|
|
|
|
}
|