This repository has been archived on 2023-02-02. You can view files and clone it, but cannot push or open issues or pull requests.
mfgames-markdown-cil/src/MfGames.Markdown.Gemtext/Renderers/GemtextRenderer.cs

186 lines
6.6 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using Markdig.Helpers;
using Markdig.Renderers;
using Markdig.Syntax;
using MfGames.Markdown.Gemtext.Renderers.Gemtext.Blocks;
using MfGames.Markdown.Gemtext.Renderers.Gemtext.Inlines;
namespace MfGames.Markdown.Gemtext.Renderers
{
public class GemtextRenderer : TextRendererBase<GemtextRenderer>
{
/// <inheritdoc />
public GemtextRenderer(TextWriter writer)
: base(writer)
{
// Set up our default values.
this.NextFootnoteNumber = 1;
this.GatheredLinks = new List<string>();
// Default block renderers.
this.ObjectRenderers.Add(new CodeBlockRenderer());
this.ObjectRenderers.Add(new HeadingRenderer());
this.ObjectRenderers.Add(new HtmlBlockRenderer());
this.ObjectRenderers.Add(new ListRenderer());
this.ObjectRenderers.Add(new MarkdownDocumentRenderer());
this.ObjectRenderers.Add(new ParagraphRenderer());
this.ObjectRenderers.Add(new QuoteBlockRenderer());
this.ObjectRenderers.Add(new ThematicBreakRenderer());
// Default inline renderers.
this.ObjectRenderers.Add(new CodeInlineRenderer());
this.ObjectRenderers.Add(new DelimiterInlineRenderer());
this.ObjectRenderers.Add(new EmphasisInlineRenderer());
this.ObjectRenderers.Add(new HtmlEntityInlineRenderer());
this.ObjectRenderers.Add(new LineBreakInlineRenderer());
this.ObjectRenderers.Add(new LinkInlineRenderer());
this.ObjectRenderers.Add(new LiteralInlineRenderer());
}
/// <summary>
/// Gets or sets how to handle links inside paragraphs and other blocks.
/// </summary>
public BlockLinkHandling BlockLinkHandling { get; set; }
/// <summary>
/// Gets or sets the optional formatting for code inlines (backticks).
/// If this is unset, then `InlineFormatting` will be used.
/// </summary>
public InlineFormatting? CodeFormatting { get; set; }
/// <summary>
/// Gets the actual formatting for code inlines (backticks) which
/// is either `CodeInlineFormatting` or `InlineFormatting` if that
/// is not set.
/// </summary>
public InlineFormatting CodeFormattingResolved =>
this.CodeFormatting ?? this.InlineFormatting;
/// <summary>
/// Gets or sets the optional formatting for emphasis (which includes
/// italics and bolds). If this is unset, then `InlineFormatting`
/// will be used.
/// </summary>
public InlineFormatting? EmphasisFormatting { get; set; }
/// <summary>
/// Gets the actual formatting for emphasis which is either
/// `EmphasisFormatting` or `InlineFormatting` if that isn't set.
/// </summary>
public InlineFormatting EmphasisFormattingResolved =>
this.EmphasisFormatting ?? this.InlineFormatting;
/// <summary>
/// Gets or sets the formatting for how links that are gathered at the
/// end of a paragraph or document are formatted inside the paragraph.
/// </summary>
public EndLinkInlineFormatting EndLinkInlineFormatting { get; set; }
/// <summary>
/// Gets the current list of formatted links that have been gathered
/// up to this point for rendering.
/// </summary>
public List<string> GatheredLinks { get; }
/// <summary>
/// Gets or sets the formatting rule for HTML blocks.
/// </summary>
public HtmlBlockFormatting HtmlBlockFormatting { get; set; }
/// <summary>
/// Gets or sets the default formatting for all inlines.
/// </summary>
public InlineFormatting InlineFormatting { get; set; }
/// <summary>
/// An internal processing flag that determines if the rendered link
/// is inside a block or not to trigger extra handling.
/// </summary>
public bool LinkInsideBlock { get; set; }
/// <summary>
/// Gets or sets the next footnote while rendering links.
/// </summary>
public int NextFootnoteNumber { get; set; }
/// <summary>
/// Ensures there are two blank lines before an element.
/// </summary>
/// <returns></returns>
public GemtextRenderer EnsureTwoLines()
{
if (this.previousWasLine)
{
return this;
}
this.WriteLine();
this.WriteLine();
return this;
}
/// <summary>
/// A wrapper method to push the state of LinkInsideBlock while
/// performing an action.
/// </summary>
/// <param name="action">The action to perform.</param>
public void WhileLinkInsideBlock(Action action)
{
bool oldState = this.LinkInsideBlock;
this.LinkInsideBlock = true;
action();
this.LinkInsideBlock = oldState;
}
/// <summary>
/// Writes the lines of a <see cref="LeafBlock" />
/// </summary>
/// <param name="leafBlock">The leaf block.</param>
/// <param name="writeEndOfLines">if set to <c>true</c> write end of lines.</param>
/// <returns>This instance</returns>
public GemtextRenderer WriteLeafRawLines(
LeafBlock leafBlock,
bool writeEndOfLines)
{
// Make sure we have sane input.
if (leafBlock == null)
{
throw new ArgumentNullException(nameof(leafBlock));
}
// If we have nothing to write, then don't do anything. Even though
// Markdig says this can't be null, `leafBlock.Lines` may be null
// according to the comments.
// ReSharper disable once ConditionIsAlwaysTrueOrFalse
if (leafBlock.Lines.Lines == null)
{
return this;
}
// Go through the block and write out each of the lines.
StringLineGroup lines = leafBlock.Lines;
StringLine[] slices = lines.Lines;
for (int i = 0; i < lines.Count; i++)
{
if (!writeEndOfLines && i > 0)
{
this.WriteLine();
}
this.Write(ref slices[i].Slice);
if (writeEndOfLines)
{
this.WriteLine();
}
}
return this;
}
}
}