From 011f4eb9ee22795afb5042fb0a67c41ee3d825f8 Mon Sep 17 00:00:00 2001 From: Yngve Devik Hammersland Date: Sat, 13 Apr 2024 00:00:33 +0200 Subject: [PATCH 01/12] Add configuration of HtmlRenderer in pipeline builder --- src/Markdig/MarkdownExtensions.cs | 7 ++++++- src/Markdig/MarkdownPipeline.cs | 16 ++++++++++++---- src/Markdig/MarkdownPipelineBuilder.cs | 6 +++++- 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/src/Markdig/MarkdownExtensions.cs b/src/Markdig/MarkdownExtensions.cs index 185cb3b5..6b061535 100644 --- a/src/Markdig/MarkdownExtensions.cs +++ b/src/Markdig/MarkdownExtensions.cs @@ -108,7 +108,6 @@ public static MarkdownPipelineBuilder UseAlertBlocks(this MarkdownPipelineBuilde pipeline.Extensions.ReplaceOrAdd(new AlertExtension() { RenderKind = renderKind }); return pipeline; } - /// /// Uses this extension to enable autolinks from text `http://`, `https://`, `ftp://`, `mailto:`, `www.xxx.yyy` /// @@ -708,4 +707,10 @@ public static MarkdownPipelineBuilder EnableTrackTrivia(this MarkdownPipelineBui } return pipeline; } + + public static MarkdownPipelineBuilder ConfigureHtmlRenderer(this MarkdownPipelineBuilder pipeline, Action configureHtmlRenderer) + { + pipeline.ConfigureHtmlRenderer = configureHtmlRenderer; + return pipeline; + } } diff --git a/src/Markdig/MarkdownPipeline.cs b/src/Markdig/MarkdownPipeline.cs index d9667500..3971cd0b 100644 --- a/src/Markdig/MarkdownPipeline.cs +++ b/src/Markdig/MarkdownPipeline.cs @@ -25,7 +25,8 @@ internal MarkdownPipeline( BlockParserList blockParsers, InlineParserList inlineParsers, TextWriter? debugLog, - ProcessDocumentDelegate? documentProcessed) + ProcessDocumentDelegate? documentProcessed, + Action? configureHtmlRenderer) { if (blockParsers is null) ThrowHelper.ArgumentNullException(nameof(blockParsers)); if (inlineParsers is null) ThrowHelper.ArgumentNullException(nameof(inlineParsers)); @@ -35,6 +36,7 @@ internal MarkdownPipeline( InlineParsers = inlineParsers; DebugLog = debugLog; DocumentProcessed = documentProcessed; + ConfigureHtmlRenderer = configureHtmlRenderer; SelfPipeline = Extensions.Find(); } @@ -57,6 +59,8 @@ internal MarkdownPipeline( internal SelfPipelineExtension? SelfPipeline; + internal Action? ConfigureHtmlRenderer; + /// /// True to parse trivia such as whitespace, extra heading characters and unescaped /// string values. @@ -82,8 +86,8 @@ public void Setup(IMarkdownRenderer renderer) internal RentedHtmlRenderer RentHtmlRenderer(TextWriter? writer = null) { HtmlRendererCache cache = writer is null - ? _rendererCache ??= new HtmlRendererCache(this, customWriter: false) - : _rendererCacheForCustomWriter ??= new HtmlRendererCache(this, customWriter: true); + ? _rendererCache ??= new HtmlRendererCache(this, customWriter: false, ConfigureHtmlRenderer) + : _rendererCacheForCustomWriter ??= new HtmlRendererCache(this, customWriter: true, ConfigureHtmlRenderer); HtmlRenderer renderer = cache.Get(); @@ -97,17 +101,21 @@ internal RentedHtmlRenderer RentHtmlRenderer(TextWriter? writer = null) internal sealed class HtmlRendererCache( MarkdownPipeline pipeline, - bool customWriter = false) : ObjectCache + bool customWriter = false, + Action? configureRenderer = null) : ObjectCache { private static readonly FastStringWriter s_dummyWriter = new(); private readonly MarkdownPipeline _pipeline = pipeline; private readonly bool _customWriter = customWriter; + private readonly Action? ConfigureRenderer = configureRenderer; + protected override HtmlRenderer NewInstance() { TextWriter writer = _customWriter ? s_dummyWriter : new FastStringWriter(); var renderer = new HtmlRenderer(writer); + ConfigureRenderer?.Invoke(renderer); _pipeline.Setup(renderer); return renderer; } diff --git a/src/Markdig/MarkdownPipelineBuilder.cs b/src/Markdig/MarkdownPipelineBuilder.cs index 165365d8..a2e14c5d 100644 --- a/src/Markdig/MarkdownPipelineBuilder.cs +++ b/src/Markdig/MarkdownPipelineBuilder.cs @@ -7,6 +7,7 @@ using Markdig.Helpers; using Markdig.Parsers; using Markdig.Parsers.Inlines; +using Markdig.Renderers; namespace Markdig; @@ -89,6 +90,8 @@ public MarkdownPipelineBuilder() internal ProcessDocumentDelegate? GetDocumentProcessed => DocumentProcessed; + internal Action? ConfigureHtmlRenderer; + /// /// Builds a pipeline from this instance. Once the pipeline is build, it cannot be modified. /// @@ -120,7 +123,8 @@ public MarkdownPipeline Build() new BlockParserList(BlockParsers), new InlineParserList(InlineParsers), DebugLog, - GetDocumentProcessed) + GetDocumentProcessed, + ConfigureHtmlRenderer) { PreciseSourceLocation = PreciseSourceLocation, TrackTrivia = TrackTrivia, From 26064f66046d387613e8658621b53224a8640dd1 Mon Sep 17 00:00:00 2001 From: Yngve Devik Hammersland Date: Sat, 13 Apr 2024 00:46:22 +0200 Subject: [PATCH 02/12] Add support for relative base uri --- src/Markdig/MarkdownExtensions.cs | 1 + src/Markdig/Renderers/HtmlRenderer.cs | 11 ++++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/Markdig/MarkdownExtensions.cs b/src/Markdig/MarkdownExtensions.cs index 6b061535..a1b75495 100644 --- a/src/Markdig/MarkdownExtensions.cs +++ b/src/Markdig/MarkdownExtensions.cs @@ -108,6 +108,7 @@ public static MarkdownPipelineBuilder UseAlertBlocks(this MarkdownPipelineBuilde pipeline.Extensions.ReplaceOrAdd(new AlertExtension() { RenderKind = renderKind }); return pipeline; } + /// /// Uses this extension to enable autolinks from text `http://`, `https://`, `ftp://`, `mailto:`, `www.xxx.yyy` /// diff --git a/src/Markdig/Renderers/HtmlRenderer.cs b/src/Markdig/Renderers/HtmlRenderer.cs index 085a4e97..27c580d1 100644 --- a/src/Markdig/Renderers/HtmlRenderer.cs +++ b/src/Markdig/Renderers/HtmlRenderer.cs @@ -202,7 +202,16 @@ public HtmlRenderer WriteEscapeUrl(string? content) // this is the proper cross-platform way to check whether a uri is absolute or not: && Uri.TryCreate(content, UriKind.RelativeOrAbsolute, out var contentUri) && !contentUri.IsAbsoluteUri) { - content = new Uri(BaseUrl, contentUri).AbsoluteUri; + if (BaseUrl.IsAbsoluteUri) + content = new Uri(BaseUrl, contentUri).AbsoluteUri; + else + { + var builder = new UriBuilder(BaseUrl); + if (!builder.Path.EndsWith("/")) + builder.Path += "/"; + builder.Path += contentUri.ToString(); + content = builder.Uri.ToString(); + } } if (LinkRewriter != null) From de4a49c8cb95766c125bec6530b3efb2ca847551 Mon Sep 17 00:00:00 2001 From: Yngve Devik Hammersland Date: Sat, 13 Apr 2024 00:54:13 +0200 Subject: [PATCH 03/12] Redo combining of relative uris --- src/Markdig/Renderers/HtmlRenderer.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Markdig/Renderers/HtmlRenderer.cs b/src/Markdig/Renderers/HtmlRenderer.cs index 27c580d1..74bd4418 100644 --- a/src/Markdig/Renderers/HtmlRenderer.cs +++ b/src/Markdig/Renderers/HtmlRenderer.cs @@ -48,7 +48,7 @@ public HtmlRenderer(TextWriter writer) : base(writer) ObjectRenderers.Add(new EmphasisInlineRenderer()); ObjectRenderers.Add(new LineBreakInlineRenderer()); ObjectRenderers.Add(new HtmlInlineRenderer()); - ObjectRenderers.Add(new HtmlEntityInlineRenderer()); + ObjectRenderers.Add(new HtmlEntityInlineRenderer()); ObjectRenderers.Add(new LinkInlineRenderer()); ObjectRenderers.Add(new LiteralInlineRenderer()); @@ -206,11 +206,11 @@ public HtmlRenderer WriteEscapeUrl(string? content) content = new Uri(BaseUrl, contentUri).AbsoluteUri; else { - var builder = new UriBuilder(BaseUrl); - if (!builder.Path.EndsWith("/")) - builder.Path += "/"; - builder.Path += contentUri.ToString(); - content = builder.Uri.ToString(); + var baseUrl = BaseUrl.ToString(); + content = baseUrl; + if (!baseUrl.EndsWith("/")) + content += "/"; + content += contentUri.ToString(); } } From c68e734f544a2abc02d5bad85a067c8e395478bf Mon Sep 17 00:00:00 2001 From: Yngve Devik Hammersland Date: Sun, 9 Jun 2024 17:12:47 +0200 Subject: [PATCH 04/12] Rename ConfigureHtmlRenderer field to resolve name conflict --- .../TestRelativeUrlReplacement.cs | 19 +++++++++---------- src/Markdig/MarkdownExtensions.cs | 4 ++-- src/Markdig/MarkdownPipelineBuilder.cs | 4 ++-- 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/src/Markdig.Tests/TestRelativeUrlReplacement.cs b/src/Markdig.Tests/TestRelativeUrlReplacement.cs index c13ef18d..a686b1c8 100644 --- a/src/Markdig.Tests/TestRelativeUrlReplacement.cs +++ b/src/Markdig.Tests/TestRelativeUrlReplacement.cs @@ -27,18 +27,17 @@ public void ReplacesRelativeImageSources() public static void TestSpec(string baseUrl, string markdown, string expectedLink) { - var pipeline = new MarkdownPipelineBuilder().Build(); - - var writer = new StringWriter(); - var renderer = new HtmlRenderer(writer); - if (baseUrl != null) - renderer.BaseUrl = new Uri(baseUrl); - pipeline.Setup(renderer); + var pipeline = new MarkdownPipelineBuilder() + .ConfigureHtmlRenderer((r) => + { + if (baseUrl != null) + r.BaseUrl = new Uri(baseUrl); + }) + .Build(); var document = MarkdownParser.Parse(markdown, pipeline); - renderer.Render(document); - writer.Flush(); + var html = Markdown.ToHtml(document, pipeline); - Assert.That(writer.ToString(), Contains.Substring("=\"" + expectedLink + "\"")); + Assert.That(html, Contains.Substring("=\"" + expectedLink + "\"")); } } \ No newline at end of file diff --git a/src/Markdig/MarkdownExtensions.cs b/src/Markdig/MarkdownExtensions.cs index a1b75495..5473abde 100644 --- a/src/Markdig/MarkdownExtensions.cs +++ b/src/Markdig/MarkdownExtensions.cs @@ -108,7 +108,7 @@ public static MarkdownPipelineBuilder UseAlertBlocks(this MarkdownPipelineBuilde pipeline.Extensions.ReplaceOrAdd(new AlertExtension() { RenderKind = renderKind }); return pipeline; } - + /// /// Uses this extension to enable autolinks from text `http://`, `https://`, `ftp://`, `mailto:`, `www.xxx.yyy` /// @@ -711,7 +711,7 @@ public static MarkdownPipelineBuilder EnableTrackTrivia(this MarkdownPipelineBui public static MarkdownPipelineBuilder ConfigureHtmlRenderer(this MarkdownPipelineBuilder pipeline, Action configureHtmlRenderer) { - pipeline.ConfigureHtmlRenderer = configureHtmlRenderer; + pipeline.ConfigureHtmlRendererAction = configureHtmlRenderer; return pipeline; } } diff --git a/src/Markdig/MarkdownPipelineBuilder.cs b/src/Markdig/MarkdownPipelineBuilder.cs index a2e14c5d..420f5bd2 100644 --- a/src/Markdig/MarkdownPipelineBuilder.cs +++ b/src/Markdig/MarkdownPipelineBuilder.cs @@ -90,7 +90,7 @@ public MarkdownPipelineBuilder() internal ProcessDocumentDelegate? GetDocumentProcessed => DocumentProcessed; - internal Action? ConfigureHtmlRenderer; + internal Action? ConfigureHtmlRendererAction; /// /// Builds a pipeline from this instance. Once the pipeline is build, it cannot be modified. @@ -124,7 +124,7 @@ public MarkdownPipeline Build() new InlineParserList(InlineParsers), DebugLog, GetDocumentProcessed, - ConfigureHtmlRenderer) + ConfigureHtmlRendererAction) { PreciseSourceLocation = PreciseSourceLocation, TrackTrivia = TrackTrivia, From e8aa193a791f1f85bcabb25c443245d20df649de Mon Sep 17 00:00:00 2001 From: Yngve Devik Hammersland Date: Sun, 9 Jun 2024 23:36:16 +0200 Subject: [PATCH 05/12] Add POC of renderer builders --- .../TestRelativeUrlReplacement.cs | 6 +--- src/Markdig/Markdown.cs | 6 ++-- src/Markdig/MarkdownExtensions.cs | 14 ++++++-- src/Markdig/MarkdownPipeline.cs | 30 ++++++++-------- src/Markdig/MarkdownPipelineBuilder.cs | 8 ++--- src/Markdig/Renderers/HtmlRendererBuilder.cs | 35 +++++++++++++++++++ .../Renderers/IMarkdownRendererBuilder.cs | 11 ++++++ src/Markdig/Renderers/TextRendererBase.cs | 8 ++++- 8 files changed, 88 insertions(+), 30 deletions(-) create mode 100644 src/Markdig/Renderers/HtmlRendererBuilder.cs create mode 100644 src/Markdig/Renderers/IMarkdownRendererBuilder.cs diff --git a/src/Markdig.Tests/TestRelativeUrlReplacement.cs b/src/Markdig.Tests/TestRelativeUrlReplacement.cs index a686b1c8..43f249a3 100644 --- a/src/Markdig.Tests/TestRelativeUrlReplacement.cs +++ b/src/Markdig.Tests/TestRelativeUrlReplacement.cs @@ -28,11 +28,7 @@ public void ReplacesRelativeImageSources() public static void TestSpec(string baseUrl, string markdown, string expectedLink) { var pipeline = new MarkdownPipelineBuilder() - .ConfigureHtmlRenderer((r) => - { - if (baseUrl != null) - r.BaseUrl = new Uri(baseUrl); - }) + .ConfigureHtmlRenderer(b => b.UseBaseUrl(baseUrl)) .Build(); var document = MarkdownParser.Parse(markdown, pipeline); diff --git a/src/Markdig/Markdown.cs b/src/Markdig/Markdown.cs index 53dfdb78..4ecb2ce9 100644 --- a/src/Markdig/Markdown.cs +++ b/src/Markdig/Markdown.cs @@ -117,7 +117,7 @@ public static string ToHtml(this MarkdownDocument document, MarkdownPipeline? pi pipeline ??= DefaultPipeline; using var rentedRenderer = pipeline.RentHtmlRenderer(); - HtmlRenderer renderer = rentedRenderer.Instance; + var renderer = rentedRenderer.Instance; renderer.Render(document); renderer.Writer.Flush(); @@ -141,7 +141,7 @@ public static void ToHtml(this MarkdownDocument document, TextWriter writer, Mar pipeline ??= DefaultPipeline; using var rentedRenderer = pipeline.RentHtmlRenderer(writer); - HtmlRenderer renderer = rentedRenderer.Instance; + var renderer = rentedRenderer.Instance; renderer.Render(document); renderer.Writer.Flush(); @@ -166,7 +166,7 @@ public static MarkdownDocument ToHtml(string markdown, TextWriter writer, Markdo var document = MarkdownParser.Parse(markdown, pipeline, context); using var rentedRenderer = pipeline.RentHtmlRenderer(writer); - HtmlRenderer renderer = rentedRenderer.Instance; + var renderer = rentedRenderer.Instance; renderer.Render(document); writer.Flush(); diff --git a/src/Markdig/MarkdownExtensions.cs b/src/Markdig/MarkdownExtensions.cs index 5473abde..37b7a311 100644 --- a/src/Markdig/MarkdownExtensions.cs +++ b/src/Markdig/MarkdownExtensions.cs @@ -709,9 +709,19 @@ public static MarkdownPipelineBuilder EnableTrackTrivia(this MarkdownPipelineBui return pipeline; } - public static MarkdownPipelineBuilder ConfigureHtmlRenderer(this MarkdownPipelineBuilder pipeline, Action configureHtmlRenderer) + public static MarkdownPipelineBuilder ConfigureHtmlRenderer( + this MarkdownPipelineBuilder pipeline, + Func configureRenderer) { - pipeline.ConfigureHtmlRendererAction = configureHtmlRenderer; + pipeline.RendererBuilder = configureRenderer(new HtmlRendererBuilder()); + return pipeline; + } + + public static MarkdownPipelineBuilder UseRendererBuilder( + this MarkdownPipelineBuilder pipeline, + IMarkdownRendererBuilder rendererBuilder) + { + pipeline.RendererBuilder = rendererBuilder; return pipeline; } } diff --git a/src/Markdig/MarkdownPipeline.cs b/src/Markdig/MarkdownPipeline.cs index 3971cd0b..43b34856 100644 --- a/src/Markdig/MarkdownPipeline.cs +++ b/src/Markdig/MarkdownPipeline.cs @@ -1,5 +1,5 @@ // Copyright (c) Alexandre Mutel. All rights reserved. -// This file is licensed under the BSD-Clause 2 license. +// This file is licensed under the BSD-Clause 2 license. // See the license.txt file in the project root for more information. using System.IO; @@ -26,7 +26,7 @@ internal MarkdownPipeline( InlineParserList inlineParsers, TextWriter? debugLog, ProcessDocumentDelegate? documentProcessed, - Action? configureHtmlRenderer) + IMarkdownRendererBuilder? rendererBuilder) { if (blockParsers is null) ThrowHelper.ArgumentNullException(nameof(blockParsers)); if (inlineParsers is null) ThrowHelper.ArgumentNullException(nameof(inlineParsers)); @@ -36,7 +36,7 @@ internal MarkdownPipeline( InlineParsers = inlineParsers; DebugLog = debugLog; DocumentProcessed = documentProcessed; - ConfigureHtmlRenderer = configureHtmlRenderer; + RendererBuilder = rendererBuilder; SelfPipeline = Extensions.Find(); } @@ -59,7 +59,7 @@ internal MarkdownPipeline( internal SelfPipelineExtension? SelfPipeline; - internal Action? ConfigureHtmlRenderer; + internal IMarkdownRendererBuilder? RendererBuilder; /// /// True to parse trivia such as whitespace, extra heading characters and unescaped @@ -86,10 +86,10 @@ public void Setup(IMarkdownRenderer renderer) internal RentedHtmlRenderer RentHtmlRenderer(TextWriter? writer = null) { HtmlRendererCache cache = writer is null - ? _rendererCache ??= new HtmlRendererCache(this, customWriter: false, ConfigureHtmlRenderer) - : _rendererCacheForCustomWriter ??= new HtmlRendererCache(this, customWriter: true, ConfigureHtmlRenderer); + ? _rendererCache ??= new HtmlRendererCache(this, customWriter: false, RendererBuilder) + : _rendererCacheForCustomWriter ??= new HtmlRendererCache(this, customWriter: true, RendererBuilder); - HtmlRenderer renderer = cache.Get(); + TextRendererBase renderer = cache.Get(); if (writer is not null) { @@ -102,25 +102,25 @@ internal RentedHtmlRenderer RentHtmlRenderer(TextWriter? writer = null) internal sealed class HtmlRendererCache( MarkdownPipeline pipeline, bool customWriter = false, - Action? configureRenderer = null) : ObjectCache + IMarkdownRendererBuilder? rendererBuilder = null) : ObjectCache { private static readonly FastStringWriter s_dummyWriter = new(); private readonly MarkdownPipeline _pipeline = pipeline; private readonly bool _customWriter = customWriter; - private readonly Action? ConfigureRenderer = configureRenderer; + private readonly IMarkdownRendererBuilder RendererBuilder = + rendererBuilder ?? new HtmlRendererBuilder(); - protected override HtmlRenderer NewInstance() + protected override TextRendererBase NewInstance() { TextWriter writer = _customWriter ? s_dummyWriter : new FastStringWriter(); - var renderer = new HtmlRenderer(writer); - ConfigureRenderer?.Invoke(renderer); + var renderer = RendererBuilder.Build(writer); _pipeline.Setup(renderer); return renderer; } - protected override void Reset(HtmlRenderer instance) + protected override void Reset(TextRendererBase instance) { instance.ResetInternal(); @@ -138,9 +138,9 @@ protected override void Reset(HtmlRenderer instance) internal readonly struct RentedHtmlRenderer : IDisposable { private readonly HtmlRendererCache _cache; - public readonly HtmlRenderer Instance; + public readonly TextRendererBase Instance; - internal RentedHtmlRenderer(HtmlRendererCache cache, HtmlRenderer renderer) + internal RentedHtmlRenderer(HtmlRendererCache cache, TextRendererBase renderer) { _cache = cache; Instance = renderer; diff --git a/src/Markdig/MarkdownPipelineBuilder.cs b/src/Markdig/MarkdownPipelineBuilder.cs index 420f5bd2..2108a9c3 100644 --- a/src/Markdig/MarkdownPipelineBuilder.cs +++ b/src/Markdig/MarkdownPipelineBuilder.cs @@ -1,5 +1,5 @@ // Copyright (c) Alexandre Mutel. All rights reserved. -// This file is licensed under the BSD-Clause 2 license. +// This file is licensed under the BSD-Clause 2 license. // See the license.txt file in the project root for more information. using System.IO; @@ -90,7 +90,7 @@ public MarkdownPipelineBuilder() internal ProcessDocumentDelegate? GetDocumentProcessed => DocumentProcessed; - internal Action? ConfigureHtmlRendererAction; + internal IMarkdownRendererBuilder? RendererBuilder; /// /// Builds a pipeline from this instance. Once the pipeline is build, it cannot be modified. @@ -124,11 +124,11 @@ public MarkdownPipeline Build() new InlineParserList(InlineParsers), DebugLog, GetDocumentProcessed, - ConfigureHtmlRendererAction) + RendererBuilder) { PreciseSourceLocation = PreciseSourceLocation, TrackTrivia = TrackTrivia, }; return pipeline; } -} \ No newline at end of file +} diff --git a/src/Markdig/Renderers/HtmlRendererBuilder.cs b/src/Markdig/Renderers/HtmlRendererBuilder.cs new file mode 100644 index 00000000..9d578137 --- /dev/null +++ b/src/Markdig/Renderers/HtmlRendererBuilder.cs @@ -0,0 +1,35 @@ +// Copyright (c) Alexandre Mutel. All rights reserved. +// This file is licensed under the BSD-Clause 2 license. +// See the license.txt file in the project root for more information. + +using System.IO; + +namespace Markdig.Renderers; + +public class HtmlRendererBuilder : IMarkdownRendererBuilder +{ + public Uri? BaseUrl { get; private set; } + + public HtmlRenderer Build(TextWriter writer) + { + HtmlRenderer htmlRenderer = new HtmlRenderer(writer); + + if (BaseUrl != null) htmlRenderer.BaseUrl = BaseUrl; + + return htmlRenderer; + } + + TextRendererBase IMarkdownRendererBuilder.Build(TextWriter writer) => Build(writer); + + public HtmlRendererBuilder UseBaseUrl(string baseUrl) + { + BaseUrl = baseUrl != null ? new Uri(baseUrl) : null; + return this; + } + + public HtmlRendererBuilder UseBaseUrl(Uri baseUrl) + { + BaseUrl = baseUrl; + return this; + } +} diff --git a/src/Markdig/Renderers/IMarkdownRendererBuilder.cs b/src/Markdig/Renderers/IMarkdownRendererBuilder.cs new file mode 100644 index 00000000..bd6006ac --- /dev/null +++ b/src/Markdig/Renderers/IMarkdownRendererBuilder.cs @@ -0,0 +1,11 @@ +// Copyright (c) Alexandre Mutel. All rights reserved. +// This file is licensed under the BSD-Clause 2 license. +// See the license.txt file in the project root for more information. + +using System.IO; + +namespace Markdig.Renderers; + +public interface IMarkdownRendererBuilder { + TextRendererBase Build(TextWriter writer); +} diff --git a/src/Markdig/Renderers/TextRendererBase.cs b/src/Markdig/Renderers/TextRendererBase.cs index 4d428a77..9b61c4f6 100644 --- a/src/Markdig/Renderers/TextRendererBase.cs +++ b/src/Markdig/Renderers/TextRendererBase.cs @@ -61,6 +61,12 @@ public override object Render(MarkdownObject markdownObject) Write(markdownObject); return Writer; } + + internal virtual void ResetInternal() + { + + } + } /// @@ -134,7 +140,7 @@ protected internal void Reset() ResetInternal(); } - internal void ResetInternal() + internal override void ResetInternal() { _childrenDepth = 0; previousWasLine = true; From 27c64af0feaec1303e0e297a9d800db3272bdf83 Mon Sep 17 00:00:00 2001 From: Yngve Devik Hammersland Date: Thu, 13 Jun 2024 22:38:24 +0200 Subject: [PATCH 06/12] Flesh out HtmlRendererBuilder --- src/Markdig/MarkdownExtensions.cs | 10 ++++ src/Markdig/Renderers/HtmlRendererBuilder.cs | 50 +++++++++++++++++-- .../Renderers/IMarkdownRendererBuilder.cs | 11 ++++ 3 files changed, 67 insertions(+), 4 deletions(-) diff --git a/src/Markdig/MarkdownExtensions.cs b/src/Markdig/MarkdownExtensions.cs index 37b7a311..0215e2bb 100644 --- a/src/Markdig/MarkdownExtensions.cs +++ b/src/Markdig/MarkdownExtensions.cs @@ -709,6 +709,11 @@ public static MarkdownPipelineBuilder EnableTrackTrivia(this MarkdownPipelineBui return pipeline; } + /// + /// Configure the pipeline with a . + /// + /// The pipeline. + /// An action which configures the HtmlRenderer. public static MarkdownPipelineBuilder ConfigureHtmlRenderer( this MarkdownPipelineBuilder pipeline, Func configureRenderer) @@ -717,6 +722,11 @@ public static MarkdownPipelineBuilder ConfigureHtmlRenderer( return pipeline; } + /// + /// Configure the pipeline to use a to construct the renderer. + /// + /// The pipeline. + /// The builder for the renderer. public static MarkdownPipelineBuilder UseRendererBuilder( this MarkdownPipelineBuilder pipeline, IMarkdownRendererBuilder rendererBuilder) diff --git a/src/Markdig/Renderers/HtmlRendererBuilder.cs b/src/Markdig/Renderers/HtmlRendererBuilder.cs index 9d578137..36d06767 100644 --- a/src/Markdig/Renderers/HtmlRendererBuilder.cs +++ b/src/Markdig/Renderers/HtmlRendererBuilder.cs @@ -6,30 +6,72 @@ namespace Markdig.Renderers; +/// +/// This class is used with +/// to set up a pipeline with a html renderer. +/// public class HtmlRendererBuilder : IMarkdownRendererBuilder { - public Uri? BaseUrl { get; private set; } + private Uri? baseUrl; + private bool? enableHtmlEscape; + private bool? enableHtmlForBlock; + private bool? enableHtmlForInline; + private bool? useNonAsciiNoEscape; public HtmlRenderer Build(TextWriter writer) { HtmlRenderer htmlRenderer = new HtmlRenderer(writer); - if (BaseUrl != null) htmlRenderer.BaseUrl = BaseUrl; + if (baseUrl != null) htmlRenderer.BaseUrl = baseUrl; + if (enableHtmlEscape != null) htmlRenderer.EnableHtmlEscape = enableHtmlEscape.Value; + if (enableHtmlForBlock != null) htmlRenderer.EnableHtmlForBlock = enableHtmlForBlock.Value; + if (enableHtmlForInline != null) htmlRenderer.EnableHtmlForInline = enableHtmlForInline.Value; + if (useNonAsciiNoEscape != null) htmlRenderer.UseNonAsciiNoEscape = useNonAsciiNoEscape.Value; return htmlRenderer; } TextRendererBase IMarkdownRendererBuilder.Build(TextWriter writer) => Build(writer); + /// public HtmlRendererBuilder UseBaseUrl(string baseUrl) { - BaseUrl = baseUrl != null ? new Uri(baseUrl) : null; + this.baseUrl = baseUrl != null ? new Uri(baseUrl) : null; return this; } + /// public HtmlRendererBuilder UseBaseUrl(Uri baseUrl) { - BaseUrl = baseUrl; + this.baseUrl = baseUrl; + return this; + } + + /// + public HtmlRendererBuilder EnableHtmlEscape(bool enable) + { + enableHtmlEscape = enable; + return this; + } + + /// + public HtmlRendererBuilder EnableHtmlForBlock(bool enable) + { + enableHtmlForBlock = enable; + return this; + } + + /// + public HtmlRendererBuilder EnableHtmlForInline(bool enable) + { + enableHtmlForInline = enable; + return this; + } + + /// + public HtmlRendererBuilder UseNonAsciiNoEscape(bool enable) + { + useNonAsciiNoEscape = enable; return this; } } diff --git a/src/Markdig/Renderers/IMarkdownRendererBuilder.cs b/src/Markdig/Renderers/IMarkdownRendererBuilder.cs index bd6006ac..21db70c6 100644 --- a/src/Markdig/Renderers/IMarkdownRendererBuilder.cs +++ b/src/Markdig/Renderers/IMarkdownRendererBuilder.cs @@ -6,6 +6,17 @@ namespace Markdig.Renderers; +/// +/// Defines a common interface for all rendererer builders. +/// public interface IMarkdownRendererBuilder { + /// + /// Creates a new instance of a renderer for use in a pipeline. + /// + /// + /// The renderer needs to be a subclass of + /// because it is intended to be used by which depends on + /// for re-use. + /// TextRendererBase Build(TextWriter writer); } From 1211b731723835aede73ec10d4944a5286c2baad Mon Sep 17 00:00:00 2001 From: Yngve Devik Hammersland Date: Thu, 13 Jun 2024 23:27:00 +0200 Subject: [PATCH 07/12] Add NormalizeRendererBuilder --- src/Markdig/MarkdownExtensions.cs | 14 +++++ .../Normalize/NormalizeRendererBuilder.cs | 62 +++++++++++++++++++ 2 files changed, 76 insertions(+) create mode 100644 src/Markdig/Renderers/Normalize/NormalizeRendererBuilder.cs diff --git a/src/Markdig/MarkdownExtensions.cs b/src/Markdig/MarkdownExtensions.cs index 0215e2bb..3cfd0a22 100644 --- a/src/Markdig/MarkdownExtensions.cs +++ b/src/Markdig/MarkdownExtensions.cs @@ -36,6 +36,7 @@ using Markdig.Parsers; using Markdig.Parsers.Inlines; using Markdig.Renderers; +using Markdig.Renderers.Normalize; namespace Markdig; @@ -722,6 +723,19 @@ public static MarkdownPipelineBuilder ConfigureHtmlRenderer( return pipeline; } + /// + /// Configure the pipeline with a . + /// + /// The pipeline. + /// An action which configures the NormalizeRenderer. + public static MarkdownPipelineBuilder ConfigureNormalizeRenderer( + this MarkdownPipelineBuilder pipeline, + Func configureRenderer) + { + pipeline.RendererBuilder = configureRenderer(new NormalizeRendererBuilder()); + return pipeline; + } + /// /// Configure the pipeline to use a to construct the renderer. /// diff --git a/src/Markdig/Renderers/Normalize/NormalizeRendererBuilder.cs b/src/Markdig/Renderers/Normalize/NormalizeRendererBuilder.cs new file mode 100644 index 00000000..513fa844 --- /dev/null +++ b/src/Markdig/Renderers/Normalize/NormalizeRendererBuilder.cs @@ -0,0 +1,62 @@ +using System.IO; + +namespace Markdig.Renderers.Normalize; + + +/// +/// This class is used with +/// to set up a pipeline with a normalizing renderer. +/// +public class NormalizeRendererBuilder : IMarkdownRendererBuilder +{ + private readonly Lazy options = new(() => new NormalizeOptions()); + + public NormalizeRenderer Build(TextWriter writer) + { + return new NormalizeRenderer(writer, options.IsValueCreated ? options.Value : null); + } + + TextRendererBase IMarkdownRendererBuilder.Build(TextWriter writer) => Build(writer); + + /// + public NormalizeRendererBuilder UseSpaceAfterQuoteBlock(bool enable) + { + options.Value.SpaceAfterQuoteBlock = enable; + return this; + } + + /// + public NormalizeRendererBuilder UseEmptyLineAfterCodeBlock(bool enable) + { + options.Value.EmptyLineAfterCodeBlock = enable; + return this; + } + + /// + public NormalizeRendererBuilder UseEmptyLineAfterHeading(bool enable) + { + options.Value.EmptyLineAfterHeading = enable; + return this; + } + + /// + public NormalizeRendererBuilder UseEmptyLineAfterThematicBreak(bool enable) + { + options.Value.EmptyLineAfterThematicBreak = enable; + return this; + } + + /// + public NormalizeRendererBuilder UseListItemCharacter(char? character) + { + options.Value.ListItemCharacter = character; + return this; + } + + /// + public NormalizeRendererBuilder ExpandAutoLinks(bool enable) + { + options.Value.ExpandAutoLinks = enable; + return this; + } +} From 1c3afba6343dc06fff53c8e679ebdb871d7671be Mon Sep 17 00:00:00 2001 From: Yngve Devik Hammersland Date: Thu, 13 Jun 2024 23:45:02 +0200 Subject: [PATCH 08/12] Add RoundtripRendererBuilder --- src/Markdig/MarkdownExtensions.cs | 11 +++++++++++ .../Roundtrip/RoundtripRendererBuilder.cs | 19 +++++++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 src/Markdig/Renderers/Roundtrip/RoundtripRendererBuilder.cs diff --git a/src/Markdig/MarkdownExtensions.cs b/src/Markdig/MarkdownExtensions.cs index 3cfd0a22..eaa44295 100644 --- a/src/Markdig/MarkdownExtensions.cs +++ b/src/Markdig/MarkdownExtensions.cs @@ -37,6 +37,7 @@ using Markdig.Parsers.Inlines; using Markdig.Renderers; using Markdig.Renderers.Normalize; +using Markdig.Renderers.Roundtrip; namespace Markdig; @@ -736,6 +737,16 @@ public static MarkdownPipelineBuilder ConfigureNormalizeRenderer( return pipeline; } + /// + /// Configure the pipeline with a . + /// + /// The pipeline. + public static MarkdownPipelineBuilder ConfigureRoundtripRenderer(this MarkdownPipelineBuilder pipeline) + { + pipeline.RendererBuilder = new RoundtripRendererBuilder(); + return pipeline; + } + /// /// Configure the pipeline to use a to construct the renderer. /// diff --git a/src/Markdig/Renderers/Roundtrip/RoundtripRendererBuilder.cs b/src/Markdig/Renderers/Roundtrip/RoundtripRendererBuilder.cs new file mode 100644 index 00000000..6a817b62 --- /dev/null +++ b/src/Markdig/Renderers/Roundtrip/RoundtripRendererBuilder.cs @@ -0,0 +1,19 @@ +using System.IO; + +namespace Markdig.Renderers.Roundtrip; + +/// +/// This class is used with +/// to set up a pipeline with a markdown renderer. +/// +/// +/// This builder has no options since has none. +/// It is solely in use internally in +/// because can only use a renderer builder, not a renderer. +/// +class RoundtripRendererBuilder : IMarkdownRendererBuilder +{ + public RoundtripRenderer Build(TextWriter writer) => new(writer); + + TextRendererBase IMarkdownRendererBuilder.Build(TextWriter writer) => Build(writer); +} \ No newline at end of file From ef56e528e284c118d5e16fbbe6d50774b8c0ab96 Mon Sep 17 00:00:00 2001 From: Yngve Devik Hammersland Date: Thu, 13 Jun 2024 23:51:08 +0200 Subject: [PATCH 09/12] Add missing licensing headers --- src/Markdig/Renderers/Normalize/NormalizeRendererBuilder.cs | 4 ++++ src/Markdig/Renderers/Roundtrip/RoundtripRendererBuilder.cs | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/src/Markdig/Renderers/Normalize/NormalizeRendererBuilder.cs b/src/Markdig/Renderers/Normalize/NormalizeRendererBuilder.cs index 513fa844..52bf2ed0 100644 --- a/src/Markdig/Renderers/Normalize/NormalizeRendererBuilder.cs +++ b/src/Markdig/Renderers/Normalize/NormalizeRendererBuilder.cs @@ -1,3 +1,7 @@ +// Copyright (c) Alexandre Mutel. All rights reserved. +// This file is licensed under the BSD-Clause 2 license. +// See the license.txt file in the project root for more information. + using System.IO; namespace Markdig.Renderers.Normalize; diff --git a/src/Markdig/Renderers/Roundtrip/RoundtripRendererBuilder.cs b/src/Markdig/Renderers/Roundtrip/RoundtripRendererBuilder.cs index 6a817b62..89692c65 100644 --- a/src/Markdig/Renderers/Roundtrip/RoundtripRendererBuilder.cs +++ b/src/Markdig/Renderers/Roundtrip/RoundtripRendererBuilder.cs @@ -1,3 +1,7 @@ +// Copyright (c) Alexandre Mutel. All rights reserved. +// This file is licensed under the BSD-Clause 2 license. +// See the license.txt file in the project root for more information. + using System.IO; namespace Markdig.Renderers.Roundtrip; From c6521f6f036d62e9e68ccb2b2a7ff9ca805d71d7 Mon Sep 17 00:00:00 2001 From: Yngve Devik Hammersland Date: Thu, 13 Jun 2024 23:54:32 +0200 Subject: [PATCH 10/12] Refactor Configure*Renderer pipeline extension methods --- src/Markdig/MarkdownExtensions.cs | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/src/Markdig/MarkdownExtensions.cs b/src/Markdig/MarkdownExtensions.cs index eaa44295..4f6221d9 100644 --- a/src/Markdig/MarkdownExtensions.cs +++ b/src/Markdig/MarkdownExtensions.cs @@ -719,10 +719,7 @@ public static MarkdownPipelineBuilder EnableTrackTrivia(this MarkdownPipelineBui public static MarkdownPipelineBuilder ConfigureHtmlRenderer( this MarkdownPipelineBuilder pipeline, Func configureRenderer) - { - pipeline.RendererBuilder = configureRenderer(new HtmlRendererBuilder()); - return pipeline; - } + => pipeline.UseRendererBuilder(configureRenderer(new HtmlRendererBuilder())); /// /// Configure the pipeline with a . @@ -732,20 +729,14 @@ public static MarkdownPipelineBuilder ConfigureHtmlRenderer( public static MarkdownPipelineBuilder ConfigureNormalizeRenderer( this MarkdownPipelineBuilder pipeline, Func configureRenderer) - { - pipeline.RendererBuilder = configureRenderer(new NormalizeRendererBuilder()); - return pipeline; - } + => pipeline.UseRendererBuilder(configureRenderer(new NormalizeRendererBuilder())); /// /// Configure the pipeline with a . /// /// The pipeline. public static MarkdownPipelineBuilder ConfigureRoundtripRenderer(this MarkdownPipelineBuilder pipeline) - { - pipeline.RendererBuilder = new RoundtripRendererBuilder(); - return pipeline; - } + => pipeline.UseRendererBuilder(new RoundtripRendererBuilder()); /// /// Configure the pipeline to use a to construct the renderer. From d1c744c504c7ab9a919fd1529a7dd11975fa7bc2 Mon Sep 17 00:00:00 2001 From: Yngve Devik Hammersland Date: Fri, 14 Jun 2024 00:14:19 +0200 Subject: [PATCH 11/12] Add HtmlRendererBuilder.UseLinkRewriter and update a test to use it --- src/Markdig.Tests/TestLinkRewriter.cs | 14 +++++--------- src/Markdig/Renderers/HtmlRendererBuilder.cs | 9 +++++++++ 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/Markdig.Tests/TestLinkRewriter.cs b/src/Markdig.Tests/TestLinkRewriter.cs index fa2ea1df..dcaaaf7d 100644 --- a/src/Markdig.Tests/TestLinkRewriter.cs +++ b/src/Markdig.Tests/TestLinkRewriter.cs @@ -24,17 +24,13 @@ public void ReplacesRelativeImageSources() public static void TestSpec(Func linkRewriter, string markdown, string expectedLink) { - var pipeline = new MarkdownPipelineBuilder().Build(); - - var writer = new StringWriter(); - var renderer = new HtmlRenderer(writer); - renderer.LinkRewriter = linkRewriter; - pipeline.Setup(renderer); + var pipeline = new MarkdownPipelineBuilder() + .ConfigureHtmlRenderer(r => r.UseLinkRewriter(linkRewriter)) + .Build(); var document = MarkdownParser.Parse(markdown, pipeline); - renderer.Render(document); - writer.Flush(); + var html = Markdown.ToHtml(document, pipeline); - Assert.That(writer.ToString(), Contains.Substring("=\"" + expectedLink + "\"")); + Assert.That(html, Contains.Substring("=\"" + expectedLink + "\"")); } } \ No newline at end of file diff --git a/src/Markdig/Renderers/HtmlRendererBuilder.cs b/src/Markdig/Renderers/HtmlRendererBuilder.cs index 36d06767..2f7ea688 100644 --- a/src/Markdig/Renderers/HtmlRendererBuilder.cs +++ b/src/Markdig/Renderers/HtmlRendererBuilder.cs @@ -17,6 +17,7 @@ public class HtmlRendererBuilder : IMarkdownRendererBuilder private bool? enableHtmlForBlock; private bool? enableHtmlForInline; private bool? useNonAsciiNoEscape; + public Func? linkRewriter; public HtmlRenderer Build(TextWriter writer) { @@ -27,6 +28,7 @@ public HtmlRenderer Build(TextWriter writer) if (enableHtmlForBlock != null) htmlRenderer.EnableHtmlForBlock = enableHtmlForBlock.Value; if (enableHtmlForInline != null) htmlRenderer.EnableHtmlForInline = enableHtmlForInline.Value; if (useNonAsciiNoEscape != null) htmlRenderer.UseNonAsciiNoEscape = useNonAsciiNoEscape.Value; + if (linkRewriter != null) htmlRenderer.LinkRewriter = linkRewriter; return htmlRenderer; } @@ -74,4 +76,11 @@ public HtmlRendererBuilder UseNonAsciiNoEscape(bool enable) useNonAsciiNoEscape = enable; return this; } + + /// + public HtmlRendererBuilder UseLinkRewriter(Func linkRewriter) + { + this.linkRewriter = linkRewriter; + return this; + } } From 384bb9685010f46dfd410ecb2ed433077665c49b Mon Sep 17 00:00:00 2001 From: Yngve Devik Hammersland Date: Fri, 14 Jun 2024 00:35:46 +0200 Subject: [PATCH 12/12] Update a few more tests to configure renderer with the pipeline builder --- .../Inlines/TestNullCharacterInline.cs | 15 +++++++-------- src/Markdig.Tests/TestRoundtrip.cs | 16 ++++++---------- 2 files changed, 13 insertions(+), 18 deletions(-) diff --git a/src/Markdig.Tests/RoundtripSpecs/Inlines/TestNullCharacterInline.cs b/src/Markdig.Tests/RoundtripSpecs/Inlines/TestNullCharacterInline.cs index b40919e5..cf512548 100644 --- a/src/Markdig.Tests/RoundtripSpecs/Inlines/TestNullCharacterInline.cs +++ b/src/Markdig.Tests/RoundtripSpecs/Inlines/TestNullCharacterInline.cs @@ -22,16 +22,15 @@ public void Test(string value, string expected) // do not unintentionally use the expected parameter private static void RoundTrip(string markdown, string expected) { - var pipelineBuilder = new MarkdownPipelineBuilder(); - pipelineBuilder.EnableTrackTrivia(); - MarkdownPipeline pipeline = pipelineBuilder.Build(); - MarkdownDocument markdownDocument = Markdown.Parse(markdown, pipeline); - var sw = new StringWriter(); - var rr = new RoundtripRenderer(sw); + var pipeline = new MarkdownPipelineBuilder() + .EnableTrackTrivia() + .ConfigureRoundtripRenderer() + .Build(); - rr.Write(markdownDocument); + MarkdownDocument markdownDocument = Markdown.Parse(markdown, pipeline); + var result = Markdown.ToHtml(markdownDocument, pipeline); - Assert.AreEqual(expected, sw.ToString()); + Assert.AreEqual(expected, result); } } } diff --git a/src/Markdig.Tests/TestRoundtrip.cs b/src/Markdig.Tests/TestRoundtrip.cs index 24868b6b..a39a733f 100644 --- a/src/Markdig.Tests/TestRoundtrip.cs +++ b/src/Markdig.Tests/TestRoundtrip.cs @@ -12,18 +12,14 @@ internal static void TestSpec(string markdownText, string expected, string exten internal static void RoundTrip(string markdown, string context = null) { - var pipelineBuilder = new MarkdownPipelineBuilder(); - pipelineBuilder.EnableTrackTrivia(); - pipelineBuilder.UseYamlFrontMatter(); - MarkdownPipeline pipeline = pipelineBuilder.Build(); + var pipeline = new MarkdownPipelineBuilder() + .EnableTrackTrivia() + .UseYamlFrontMatter() + .ConfigureRoundtripRenderer() + .Build(); MarkdownDocument markdownDocument = Markdown.Parse(markdown, pipeline); - var sw = new StringWriter(); - var nr = new RoundtripRenderer(sw); - pipeline.Setup(nr); - nr.Write(markdownDocument); - - var result = sw.ToString(); + var result = Markdown.ToHtml(markdownDocument, pipeline); TestParser.PrintAssertExpected("", result, markdown, context); } }