Skip to content

Commit f2b80ec

Browse files
reakaleekMpdreamz
andauthored
Add custom UseHardBreaks extension (#342)
* Add custom DisableHtmlWithExceptions extension * format * Move property position back * Add comment * Remove `DisableHtmlWithExceptions` and implement `UseHardBreaks`. The `DisableHtmlWithExceptions` logic was removed and replaced with a simpler `UseHardBreaks` implementation for handling `<br>` tags. This refactor simplifies the markdown pipeline and improves maintainability while retaining support for hard breaks. * remove folder from Tests csproj * Rename file --------- Co-authored-by: Martijn Laarman <[email protected]>
1 parent d5f8dff commit f2b80ec

File tree

3 files changed

+108
-2
lines changed

3 files changed

+108
-2
lines changed
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
// Licensed to Elasticsearch B.V under one or more agreements.
2+
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3+
// See the LICENSE file in the project root for more information
4+
5+
using Markdig;
6+
using Markdig.Helpers;
7+
using Markdig.Parsers;
8+
using Markdig.Parsers.Inlines;
9+
using Markdig.Renderers;
10+
using Markdig.Renderers.Html;
11+
using Markdig.Renderers.Html.Inlines;
12+
using Markdig.Syntax.Inlines;
13+
14+
namespace Elastic.Markdown.Myst.InlineParsers;
15+
16+
public static class HardBreakBuilderExtensions
17+
{
18+
public static MarkdownPipelineBuilder UseHardBreaks(this MarkdownPipelineBuilder pipeline)
19+
{
20+
pipeline.Extensions.AddIfNotAlready<HardBreakBuilderExtension>();
21+
return pipeline;
22+
}
23+
}
24+
25+
public class HardBreakBuilderExtension : IMarkdownExtension
26+
{
27+
public void Setup(MarkdownPipelineBuilder pipeline) =>
28+
pipeline.InlineParsers.InsertBefore<EmphasisInlineParser>(new HardBreakParser());
29+
30+
public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer) =>
31+
renderer.ObjectRenderers.InsertAfter<EmphasisInlineRenderer>(new HardBreakRenderer());
32+
}
33+
34+
public class HardBreakParser : InlineParser
35+
{
36+
public HardBreakParser() => OpeningCharacters = ['<'];
37+
38+
public override bool Match(InlineProcessor processor, ref StringSlice slice)
39+
{
40+
var span = slice.AsSpan();
41+
if (!span.StartsWith("<br"))
42+
return false;
43+
44+
var closingStart = span[3..].IndexOf('>');
45+
// we allow
46+
if (closingStart != 0)
47+
return false;
48+
49+
processor.Inline = new HardBreak();
50+
51+
var sliceEnd = slice.Start + 4; //<br + >
52+
while (slice.Start != sliceEnd)
53+
slice.SkipChar();
54+
55+
return true;
56+
}
57+
}
58+
59+
public class HardBreak : LeafInline;
60+
61+
public class HardBreakRenderer : HtmlObjectRenderer<HardBreak>
62+
{
63+
protected override void Write(HtmlRenderer renderer, HardBreak obj) =>
64+
renderer.Write("<br>");
65+
}

src/Elastic.Markdown/Myst/MarkdownParser.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ public class MarkdownParser(
5353
.UseDirectives()
5454
.UseEnhancedCodeBlocks()
5555
.DisableHtml()
56+
.UseHardBreaks()
5657
.Build();
5758

5859
public ConfigurationFile Configuration { get; } = configuration;
@@ -107,6 +108,4 @@ public MarkdownDocument Parse(string yaml, IFileInfo parent, YamlFrontMatter? ma
107108
var markdownDocument = Markdig.Markdown.Parse(yaml, Pipeline, context);
108109
return markdownDocument;
109110
}
110-
111-
112111
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// Licensed to Elasticsearch B.V under one or more agreements.
2+
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3+
// See the LICENSE file in the project root for more information
4+
5+
using FluentAssertions;
6+
using Xunit.Abstractions;
7+
8+
namespace Elastic.Markdown.Tests.Inline;
9+
10+
public class AllowBrTagTest(ITestOutputHelper output)
11+
: InlineTest(output,
12+
"Hello,<br>World!")
13+
{
14+
[Fact]
15+
public void GeneratesHtml() =>
16+
Html.Should().Contain(
17+
"<p>Hello,<br>World!</p>"
18+
);
19+
}
20+
21+
public class BrTagNeedsToBeExact(ITestOutputHelper output)
22+
: InlineTest(output,
23+
"Hello,<br >World<br />!")
24+
{
25+
[Fact]
26+
public void GeneratesHtml() =>
27+
Html.Should().Contain(
28+
"<p>Hello,&lt;br &gt;World&lt;br /&gt;!</p>"
29+
);
30+
}
31+
32+
public class DisallowSpanTag(ITestOutputHelper output)
33+
: InlineTest(output,
34+
"Hello,<span>World!</span>")
35+
{
36+
[Fact]
37+
// span tag is rendered as text
38+
public void GeneratesHtml() =>
39+
Html.Should().Contain(
40+
"<p>Hello,&lt;span&gt;World!&lt;/span&gt;</p>"
41+
);
42+
}

0 commit comments

Comments
 (0)