diff --git a/src/Elastic.Markdown/Myst/InlineParsers/HardBreakParser.cs b/src/Elastic.Markdown/Myst/InlineParsers/HardBreakParser.cs new file mode 100644 index 000000000..1ec836ed6 --- /dev/null +++ b/src/Elastic.Markdown/Myst/InlineParsers/HardBreakParser.cs @@ -0,0 +1,65 @@ +// Licensed to Elasticsearch B.V under one or more agreements. +// Elasticsearch B.V licenses this file to you under the Apache 2.0 License. +// See the LICENSE file in the project root for more information + +using Markdig; +using Markdig.Helpers; +using Markdig.Parsers; +using Markdig.Parsers.Inlines; +using Markdig.Renderers; +using Markdig.Renderers.Html; +using Markdig.Renderers.Html.Inlines; +using Markdig.Syntax.Inlines; + +namespace Elastic.Markdown.Myst.InlineParsers; + +public static class HardBreakBuilderExtensions +{ + public static MarkdownPipelineBuilder UseHardBreaks(this MarkdownPipelineBuilder pipeline) + { + pipeline.Extensions.AddIfNotAlready(); + return pipeline; + } +} + +public class HardBreakBuilderExtension : IMarkdownExtension +{ + public void Setup(MarkdownPipelineBuilder pipeline) => + pipeline.InlineParsers.InsertBefore(new HardBreakParser()); + + public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer) => + renderer.ObjectRenderers.InsertAfter(new HardBreakRenderer()); +} + +public class HardBreakParser : InlineParser +{ + public HardBreakParser() => OpeningCharacters = ['<']; + + public override bool Match(InlineProcessor processor, ref StringSlice slice) + { + var span = slice.AsSpan(); + if (!span.StartsWith("'); + // we allow + if (closingStart != 0) + return false; + + processor.Inline = new HardBreak(); + + var sliceEnd = slice.Start + 4; //
+ while (slice.Start != sliceEnd) + slice.SkipChar(); + + return true; + } +} + +public class HardBreak : LeafInline; + +public class HardBreakRenderer : HtmlObjectRenderer +{ + protected override void Write(HtmlRenderer renderer, HardBreak obj) => + renderer.Write("
"); +} diff --git a/src/Elastic.Markdown/Myst/MarkdownParser.cs b/src/Elastic.Markdown/Myst/MarkdownParser.cs index 152fb62a4..e6ce64f69 100644 --- a/src/Elastic.Markdown/Myst/MarkdownParser.cs +++ b/src/Elastic.Markdown/Myst/MarkdownParser.cs @@ -53,6 +53,7 @@ public class MarkdownParser( .UseDirectives() .UseEnhancedCodeBlocks() .DisableHtml() + .UseHardBreaks() .Build(); public ConfigurationFile Configuration { get; } = configuration; @@ -107,6 +108,4 @@ public MarkdownDocument Parse(string yaml, IFileInfo parent, YamlFrontMatter? ma var markdownDocument = Markdig.Markdown.Parse(yaml, Pipeline, context); return markdownDocument; } - - } diff --git a/tests/Elastic.Markdown.Tests/Inline/HardBreakTests.cs b/tests/Elastic.Markdown.Tests/Inline/HardBreakTests.cs new file mode 100644 index 000000000..1d761c8c0 --- /dev/null +++ b/tests/Elastic.Markdown.Tests/Inline/HardBreakTests.cs @@ -0,0 +1,42 @@ +// Licensed to Elasticsearch B.V under one or more agreements. +// Elasticsearch B.V licenses this file to you under the Apache 2.0 License. +// See the LICENSE file in the project root for more information + +using FluentAssertions; +using Xunit.Abstractions; + +namespace Elastic.Markdown.Tests.Inline; + +public class AllowBrTagTest(ITestOutputHelper output) + : InlineTest(output, + "Hello,
World!") +{ + [Fact] + public void GeneratesHtml() => + Html.Should().Contain( + "

Hello,
World!

" + ); +} + +public class BrTagNeedsToBeExact(ITestOutputHelper output) + : InlineTest(output, + "Hello,
World
!") +{ + [Fact] + public void GeneratesHtml() => + Html.Should().Contain( + "

Hello,<br >World<br />!

" + ); +} + +public class DisallowSpanTag(ITestOutputHelper output) + : InlineTest(output, + "Hello,World!") +{ + [Fact] + // span tag is rendered as text + public void GeneratesHtml() => + Html.Should().Contain( + "

Hello,<span>World!</span>

" + ); +}