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 { /// public GemtextRenderer(TextWriter writer) : base(writer) { // Set up our default values. this.NextFootnoteNumber = 1; this.GatheredLinks = new List(); // 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()); } /// /// Gets or sets how to handle links inside paragraphs and other blocks. /// public BlockLinkHandling BlockLinkHandling { get; set; } /// /// Gets or sets the optional formatting for code inlines (backticks). /// If this is unset, then `InlineFormatting` will be used. /// public InlineFormatting? CodeFormatting { get; set; } /// /// Gets the actual formatting for code inlines (backticks) which /// is either `CodeInlineFormatting` or `InlineFormatting` if that /// is not set. /// public InlineFormatting CodeFormattingResolved => this.CodeFormatting ?? this.InlineFormatting; /// /// Gets or sets the optional formatting for emphasis (which includes /// italics and bolds). If this is unset, then `InlineFormatting` /// will be used. /// public InlineFormatting? EmphasisFormatting { get; set; } /// /// Gets the actual formatting for emphasis which is either /// `EmphasisFormatting` or `InlineFormatting` if that isn't set. /// public InlineFormatting EmphasisFormattingResolved => this.EmphasisFormatting ?? this.InlineFormatting; /// /// Gets or sets the formatting for how links that are gathered at the /// end of a paragraph or document are formatted inside the paragraph. /// public EndLinkInlineFormatting EndLinkInlineFormatting { get; set; } /// /// Gets the current list of formatted links that have been gathered /// up to this point for rendering. /// public List GatheredLinks { get; } /// /// Gets or sets the formatting rule for HTML blocks. /// public HtmlBlockFormatting HtmlBlockFormatting { get; set; } /// /// Gets or sets the default formatting for all inlines. /// public InlineFormatting InlineFormatting { get; set; } /// /// An internal processing flag that determines if the rendered link /// is inside a block or not to trigger extra handling. /// public bool LinkInsideBlock { get; set; } /// /// Gets or sets the next footnote while rendering links. /// public int NextFootnoteNumber { get; set; } /// /// Ensures there are two blank lines before an element. /// /// public GemtextRenderer EnsureTwoLines() { if (this.previousWasLine) { return this; } this.WriteLine(); this.WriteLine(); return this; } /// /// A wrapper method to push the state of LinkInsideBlock while /// performing an action. /// /// The action to perform. public void WhileLinkInsideBlock(Action action) { bool oldState = this.LinkInsideBlock; this.LinkInsideBlock = true; action(); this.LinkInsideBlock = oldState; } /// /// Writes the lines of a /// /// The leaf block. /// if set to true write end of lines. /// This instance 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; } } }