mattercontrol/MatterControlLib/Utilities/MarkdigAgg/AggRenderer.cs
2022-08-29 00:47:01 -07:00

253 lines
5.9 KiB
C#

// Copyright (c) 2016-2017 Nicolas Musset. All rights reserved.
// This file is licensed under the MIT license.
// See the LICENSE.md file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using Markdig.Agg;
using Markdig.Helpers;
using Markdig.Renderers.Agg;
using Markdig.Renderers.Agg.Inlines;
using Markdig.Syntax;
using MatterHackers.Agg.UI;
using MatterHackers.MatterControl;
namespace Markdig.Renderers
{
public class TextWordX : TextWidget
{
public TextWordX(ThemeConfig theme)
: base("", pointSize: 10, textColor: theme.TextColor)
{
this.AutoExpandBoundsToText = true;
}
}
public class TextSpaceX : TextWidget, ISkipIfFirst
{
public TextSpaceX(ThemeConfig theme)
: base("", pointSize: 10, textColor: theme.TextColor)
{
this.AutoExpandBoundsToText = true;
}
}
public class LineBreakX : GuiWidget, IHardBreak
{
public LineBreakX()
{
}
}
/// <summary>
/// Agg renderer for a Markdown <see cref="AggMarkdownDocument"/> object.
/// </summary>
/// <seealso cref="RendererBase" />
public class AggRenderer : RendererBase
{
private readonly Stack<GuiWidget> stack = new Stack<GuiWidget>();
private char[] buffer;
private ThemeConfig theme;
public GuiWidget RootWidget { get; }
public Uri BaseUri { get; set; } = new Uri("https://www.matterhackers.com/");
public List<MarkdownDocumentLink> ChildLinks { get; internal set; }
public AggRenderer(ThemeConfig theme)
{
this.theme = theme;
}
public AggRenderer(GuiWidget rootWidget)
{
this.theme = ApplicationController.Instance.Theme;
buffer = new char[1024];
RootWidget = rootWidget;
stack.Push(rootWidget);
// Default block renderers
ObjectRenderers.Add(new AggCodeBlockRenderer(theme));
ObjectRenderers.Add(new AggListRenderer(theme));
ObjectRenderers.Add(new AggHeadingRenderer());
ObjectRenderers.Add(new AggParagraphRenderer());
ObjectRenderers.Add(new AggQuoteBlockRenderer());
ObjectRenderers.Add(new AggThematicBreakRenderer());
// Default inline renderers
ObjectRenderers.Add(new AggAutolinkInlineRenderer());
ObjectRenderers.Add(new AggCodeInlineRenderer(theme));
ObjectRenderers.Add(new AggDelimiterInlineRenderer());
ObjectRenderers.Add(new AggEmphasisInlineRenderer());
ObjectRenderers.Add(new AggLineBreakInlineRenderer());
ObjectRenderers.Add(new AggLinkInlineRenderer());
ObjectRenderers.Add(new AggLiteralInlineRenderer());
ObjectRenderers.Add(new AggMatchingTextRenderer(theme));
// Extension renderers
ObjectRenderers.Add(new AggTableRenderer());
//ObjectRenderers.Add(new AggTaskListRenderer());
}
/// <inheritdoc/>
public override object Render(MarkdownObject markdownObject)
{
Write(markdownObject);
UiThread.RunOnIdle(() =>
{
// TODO: investigate why this is required, layout should have already done this
// but it didn't. markdown that looks like the following will not layout correctly without this
// string badLayoutMarkdown = "I [s]()\n\nT";
if (RootWidget?.Parent?.Parent != null)
{
RootWidget.Parent.Parent.Width = RootWidget.Parent.Parent.Width - 1;
}
});
return RootWidget;
}
/// <summary>
/// Writes the inlines of a leaf inline.
/// </summary>
/// <param name="leafBlock">The leaf block.</param>
/// <returns>This instance</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteLeafInline(LeafBlock leafBlock)
{
if (leafBlock == null) throw new ArgumentNullException(nameof(leafBlock));
var inline = (Syntax.Inlines.Inline)leafBlock.Inline;
while (inline != null)
{
Write(inline);
inline = inline.NextSibling;
}
}
/// <summary>
/// Writes the lines of a <see cref="LeafBlock"/>
/// </summary>
/// <param name="leafBlock">The leaf block.</param>
public void WriteLeafRawLines(LeafBlock leafBlock)
{
if (leafBlock == null) throw new ArgumentNullException(nameof(leafBlock));
if (leafBlock.Lines.Lines != null)
{
var lines = leafBlock.Lines;
var slices = lines.Lines;
for (var i = 0; i < lines.Count; i++)
{
if (i != 0)
//if (stack.Peek() is FlowLayoutWidget)
//{
// this.Pop();
// this.Push(new ParagraphX());
//}
WriteInline(new LineBreakX()); // new LineBreak());
WriteText(ref slices[i].Slice);
}
}
}
internal void Push(GuiWidget o)
{
stack.Push(o);
}
internal void Pop()
{
var popped = stack.Pop();
if (stack.Count > 0)
{
var top = stack.Peek();
top.AddChild(popped);
}
}
internal void WriteBlock(GuiWidget block)
{
stack.Peek().AddChild(block);
}
internal void WriteInline(GuiWidget inline)
{
AddInline(stack.Peek(), inline);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void WriteText(ref StringSlice slice)
{
if (slice.Start > slice.End)
{
return;
}
WriteText(slice.Text, slice.Start, slice.Length);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void WriteText(string text)
{
var words = text.Split(' ');
bool first = true;
foreach (var word in words)
{
if (!first)
{
WriteInline(new TextSpaceX(theme)
{
Text = " "
});
}
if (word.Length > 0)
{
WriteInline(new TextWordX(theme)
{
Text = word
});
}
first = false;
}
}
internal void WriteText(string text, int offset, int length)
{
if (text == null)
{
return;
}
if (offset == 0 && text.Length == length)
{
WriteText(text);
}
else
{
if (length > buffer.Length)
{
buffer = text.ToCharArray();
WriteText(new string(buffer, offset, length));
}
else
{
text.CopyTo(offset, buffer, 0, length);
WriteText(new string(buffer, 0, length));
}
}
}
private static void AddInline(GuiWidget parent, GuiWidget inline)
{
parent.AddChild(inline);
}
}
}