From 0446959623380d14a028bcdf0160eb8bf93b49d9 Mon Sep 17 00:00:00 2001 From: Jason Nelson Date: Fri, 9 Feb 2024 12:54:30 -0800 Subject: [PATCH 1/3] Add TextRendererBase.Write(char c, int count) method, and eliminate various string allocations --- .../Renderers/Normalize/CodeBlockRenderer.cs | 8 +++---- .../Renderers/Normalize/HeadingRenderer.cs | 19 ++++++++++------ .../Inlines/EmphasisInlineRenderer.cs | 5 ++--- .../Renderers/Roundtrip/CodeBlockRenderer.cs | 8 +++---- .../Renderers/Roundtrip/HeadingRenderer.cs | 22 +++++++++++-------- .../Roundtrip/Inlines/CodeInlineRenderer.cs | 5 ++--- .../Inlines/EmphasisInlineRenderer.cs | 5 ++--- src/Markdig/Renderers/TextRendererBase.cs | 19 ++++++++++++++++ 8 files changed, 57 insertions(+), 34 deletions(-) diff --git a/src/Markdig/Renderers/Normalize/CodeBlockRenderer.cs b/src/Markdig/Renderers/Normalize/CodeBlockRenderer.cs index 624ece86c..d6529e54c 100644 --- a/src/Markdig/Renderers/Normalize/CodeBlockRenderer.cs +++ b/src/Markdig/Renderers/Normalize/CodeBlockRenderer.cs @@ -18,9 +18,9 @@ protected override void Write(NormalizeRenderer renderer, CodeBlock obj) { if (obj is FencedCodeBlock fencedCodeBlock) { - var fencedCharCount = Math.Min(fencedCodeBlock.OpeningFencedCharCount, fencedCodeBlock.ClosingFencedCharCount); - var opening = new string(fencedCodeBlock.FencedChar, fencedCharCount); - renderer.Write(opening); + int fencedCharCount = Math.Min(fencedCodeBlock.OpeningFencedCharCount, fencedCodeBlock.ClosingFencedCharCount); + + renderer.Write(fencedCodeBlock.FencedChar, fencedCharCount); if (fencedCodeBlock.Info != null) { renderer.Write(fencedCodeBlock.Info); @@ -41,7 +41,7 @@ protected override void Write(NormalizeRenderer renderer, CodeBlock obj) renderer.WriteLine(); renderer.WriteLeafRawLines(obj, true); - renderer.Write(opening); + renderer.Write(fencedCodeBlock.FencedChar, fencedCharCount); } else { diff --git a/src/Markdig/Renderers/Normalize/HeadingRenderer.cs b/src/Markdig/Renderers/Normalize/HeadingRenderer.cs index 8155eddd6..0cf56c1bd 100644 --- a/src/Markdig/Renderers/Normalize/HeadingRenderer.cs +++ b/src/Markdig/Renderers/Normalize/HeadingRenderer.cs @@ -12,22 +12,27 @@ namespace Markdig.Renderers.Normalize; /// public class HeadingRenderer : NormalizeObjectRenderer { - private static readonly string[] HeadingTexts = { + private static readonly string[] HeadingTexts = [ "#", "##", "###", "####", "#####", "######", - }; + ]; protected override void Write(NormalizeRenderer renderer, HeadingBlock obj) - { - var headingText = obj.Level > 0 && obj.Level <= 6 - ? HeadingTexts[obj.Level - 1] - : new string('#', obj.Level); + { + if (obj.Level is > 0 and <= 6) + { + renderer.Write(HeadingTexts[obj.Level - 1]); + } + else + { + renderer.Write('#', obj.Level); + } - renderer.Write(headingText).Write(' '); + renderer.Write(' '); renderer.WriteLeafInline(obj); renderer.FinishBlock(renderer.Options.EmptyLineAfterHeading); diff --git a/src/Markdig/Renderers/Normalize/Inlines/EmphasisInlineRenderer.cs b/src/Markdig/Renderers/Normalize/Inlines/EmphasisInlineRenderer.cs index 3e6a4328a..071951cd0 100644 --- a/src/Markdig/Renderers/Normalize/Inlines/EmphasisInlineRenderer.cs +++ b/src/Markdig/Renderers/Normalize/Inlines/EmphasisInlineRenderer.cs @@ -14,9 +14,8 @@ public class EmphasisInlineRenderer : NormalizeObjectRenderer { protected override void Write(NormalizeRenderer renderer, EmphasisInline obj) { - var emphasisText = new string(obj.DelimiterChar, obj.DelimiterCount); - renderer.Write(emphasisText); + renderer.Write(obj.DelimiterChar, obj.DelimiterCount); renderer.WriteChildren(obj); - renderer.Write(emphasisText); + renderer.Write(obj.DelimiterChar, obj.DelimiterCount); } } \ No newline at end of file diff --git a/src/Markdig/Renderers/Roundtrip/CodeBlockRenderer.cs b/src/Markdig/Renderers/Roundtrip/CodeBlockRenderer.cs index 50d7dd9b6..5c90c4d28 100644 --- a/src/Markdig/Renderers/Roundtrip/CodeBlockRenderer.cs +++ b/src/Markdig/Renderers/Roundtrip/CodeBlockRenderer.cs @@ -19,8 +19,7 @@ protected override void Write(RoundtripRenderer renderer, CodeBlock obj) if (obj is FencedCodeBlock fencedCodeBlock) { renderer.Write(obj.TriviaBefore); - var opening = new string(fencedCodeBlock.FencedChar, fencedCodeBlock.OpeningFencedCharCount); - renderer.Write(opening); + renderer.Write(fencedCodeBlock.FencedChar, fencedCodeBlock.OpeningFencedCharCount); if (!fencedCodeBlock.TriviaAfterFencedChar.IsEmpty) { @@ -56,9 +55,8 @@ protected override void Write(RoundtripRenderer renderer, CodeBlock obj) renderer.WriteLeafRawLines(obj); renderer.Write(fencedCodeBlock.TriviaBeforeClosingFence); - var closing = new string(fencedCodeBlock.FencedChar, fencedCodeBlock.ClosingFencedCharCount); - renderer.Write(closing); - if (!string.IsNullOrEmpty(closing)) + renderer.Write(fencedCodeBlock.FencedChar, fencedCodeBlock.ClosingFencedCharCount); + if (fencedCodeBlock.ClosingFencedCharCount > 0) { // See example 207: "> ```\nfoo\n```" renderer.WriteLine(obj.NewLine); diff --git a/src/Markdig/Renderers/Roundtrip/HeadingRenderer.cs b/src/Markdig/Renderers/Roundtrip/HeadingRenderer.cs index 4bef06872..c99a17eef 100644 --- a/src/Markdig/Renderers/Roundtrip/HeadingRenderer.cs +++ b/src/Markdig/Renderers/Roundtrip/HeadingRenderer.cs @@ -12,14 +12,14 @@ namespace Markdig.Renderers.Roundtrip; /// public class HeadingRenderer : RoundtripObjectRenderer { - private static readonly string[] HeadingTexts = { + private static readonly string[] HeadingTexts = [ "#", "##", "###", "####", "#####", "######", - }; + ]; protected override void Write(RoundtripRenderer renderer, HeadingBlock obj) { @@ -28,12 +28,11 @@ protected override void Write(RoundtripRenderer renderer, HeadingBlock obj) renderer.RenderLinesBefore(obj); var headingChar = obj.Level == 1 ? '=' : '-'; - var line = new string(headingChar, obj.HeaderCharCount); renderer.WriteLeafInline(obj); renderer.WriteLine(obj.SetextNewline); renderer.Write(obj.TriviaBefore); - renderer.Write(line); + renderer.Write(headingChar, obj.HeaderCharCount); renderer.WriteLine(obj.NewLine); renderer.Write(obj.TriviaAfter); @@ -43,12 +42,17 @@ protected override void Write(RoundtripRenderer renderer, HeadingBlock obj) { renderer.RenderLinesBefore(obj); - var headingText = obj.Level > 0 && obj.Level <= 6 - ? HeadingTexts[obj.Level - 1] - : new string('#', obj.Level); - renderer.Write(obj.TriviaBefore); - renderer.Write(headingText); + + if (obj.Level is > 0 and <= 6) + { + renderer.Write(HeadingTexts[obj.Level - 1]); + } + else + { + renderer.Write('#', obj.Level); + } + renderer.Write(obj.TriviaAfterAtxHeaderChar); renderer.WriteLeafInline(obj); renderer.Write(obj.TriviaAfter); diff --git a/src/Markdig/Renderers/Roundtrip/Inlines/CodeInlineRenderer.cs b/src/Markdig/Renderers/Roundtrip/Inlines/CodeInlineRenderer.cs index b4d03a2bb..2d149775b 100644 --- a/src/Markdig/Renderers/Roundtrip/Inlines/CodeInlineRenderer.cs +++ b/src/Markdig/Renderers/Roundtrip/Inlines/CodeInlineRenderer.cs @@ -14,12 +14,11 @@ public class CodeInlineRenderer : RoundtripObjectRenderer { protected override void Write(RoundtripRenderer renderer, CodeInline obj) { - var delimiterRun = new string(obj.Delimiter, obj.DelimiterCount); - renderer.Write(delimiterRun); + renderer.Write(obj.Delimiter, obj.DelimiterCount); if (!obj.ContentSpan.IsEmpty) { renderer.Write(obj.ContentWithTrivia); } - renderer.Write(delimiterRun); + renderer.Write(obj.Delimiter, obj.DelimiterCount); } } \ No newline at end of file diff --git a/src/Markdig/Renderers/Roundtrip/Inlines/EmphasisInlineRenderer.cs b/src/Markdig/Renderers/Roundtrip/Inlines/EmphasisInlineRenderer.cs index 5e61801ef..293919a3e 100644 --- a/src/Markdig/Renderers/Roundtrip/Inlines/EmphasisInlineRenderer.cs +++ b/src/Markdig/Renderers/Roundtrip/Inlines/EmphasisInlineRenderer.cs @@ -14,9 +14,8 @@ public class EmphasisInlineRenderer : RoundtripObjectRenderer { protected override void Write(RoundtripRenderer renderer, EmphasisInline obj) { - var emphasisText = new string(obj.DelimiterChar, obj.DelimiterCount); - renderer.Write(emphasisText); + renderer.Write(obj.DelimiterChar, obj.DelimiterCount); renderer.WriteChildren(obj); - renderer.Write(emphasisText); + renderer.Write(obj.DelimiterChar, obj.DelimiterCount); } } \ No newline at end of file diff --git a/src/Markdig/Renderers/TextRendererBase.cs b/src/Markdig/Renderers/TextRendererBase.cs index ea9b31748..4d428a770 100644 --- a/src/Markdig/Renderers/TextRendererBase.cs +++ b/src/Markdig/Renderers/TextRendererBase.cs @@ -211,6 +211,25 @@ public T Write(string? content) return (T)this; } + /// + /// Writes the specified char repeated a specified number of times. + /// + /// The char to write. + /// The number of times to write the char. + /// This instance + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal T Write(char c, int count) + { + WriteIndent(); + + for (int i = 0; i < count; i++) + { + Writer.Write(c); + } + + return (T)this; + } + /// /// Writes the specified slice. /// From 6a15c804bc62330a3949e7a61bf275bb422b675e Mon Sep 17 00:00:00 2001 From: Jason Nelson Date: Fri, 9 Feb 2024 12:54:52 -0800 Subject: [PATCH 2/3] Add test coverage for headlines with > 6 # characters --- src/Markdig.Tests/TestNormalize.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/Markdig.Tests/TestNormalize.cs b/src/Markdig.Tests/TestNormalize.cs index adef0691d..df8be3f71 100644 --- a/src/Markdig.Tests/TestNormalize.cs +++ b/src/Markdig.Tests/TestNormalize.cs @@ -51,6 +51,16 @@ public void SyntaxHeadline() }); } + [Test] + public void SyntaxHeadlineLevel7() + { + AssertSyntax("####### Headline", new HeadingBlock(null) { + HeaderChar = '#', + Level = 7, + Inline = new ContainerInline().AppendChild(new LiteralInline("Headline")), + }); + } + [Test] public void SyntaxParagraph() { From 2ca05ccad7a1db91a6631f8df44bd1f4a8228e84 Mon Sep 17 00:00:00 2001 From: Jason Nelson Date: Fri, 9 Feb 2024 13:13:31 -0800 Subject: [PATCH 3/3] Eliminate string allocation in CodeInlineRenderer --- .../Renderers/Normalize/Inlines/CodeInlineRenderer.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Markdig/Renderers/Normalize/Inlines/CodeInlineRenderer.cs b/src/Markdig/Renderers/Normalize/Inlines/CodeInlineRenderer.cs index 7fdf42536..aaa38d8e7 100644 --- a/src/Markdig/Renderers/Normalize/Inlines/CodeInlineRenderer.cs +++ b/src/Markdig/Renderers/Normalize/Inlines/CodeInlineRenderer.cs @@ -31,8 +31,8 @@ protected override void Write(NormalizeRenderer renderer, CodeInline obj) if (delimiterCount < count) delimiterCount = count; } - var delimiterRun = new string(obj.Delimiter, delimiterCount + 1); - renderer.Write(delimiterRun); + + renderer.Write(obj.Delimiter, delimiterCount + 1); if (content.Length != 0) { if (content[0] == obj.Delimiter) @@ -49,6 +49,6 @@ protected override void Write(NormalizeRenderer renderer, CodeInline obj) { renderer.Write(' '); } - renderer.Write(delimiterRun); + renderer.Write(obj.Delimiter, delimiterCount + 1); } } \ No newline at end of file