Skip to content
101 changes: 101 additions & 0 deletions src/Markdig.Tests/TestPipeTable.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using Markdig;
using Markdig.Extensions.Tables;
using Markdig.Syntax;
using Markdig.Syntax.Inlines;

namespace Markdig.Tests;

Expand Down Expand Up @@ -101,4 +103,103 @@ public void TableWithUnbalancedCodeSpanParsesWithoutDepthLimitError()
Assert.That(html, Does.Contain("<table"));
Assert.That(html, Does.Contain("<td>`C</td>"));
}

[Test]
public void CodeInlineWithPipeDelimitersRemainsCodeInline()
{
const string markdown = "`|| hidden text ||`";

var pipeline = new MarkdownPipelineBuilder()
.UseAdvancedExtensions()
.Build();

var document = Markdown.Parse(markdown, pipeline);

var codeInline = document.Descendants().OfType<CodeInline>().SingleOrDefault();
Assert.IsNotNull(codeInline);
Assert.That(codeInline!.Content, Is.EqualTo("|| hidden text ||"));
Assert.That(document.ToHtml(), Is.EqualTo("<p><code>|| hidden text ||</code></p>\n"));
}

[Test]
public void MultiLineCodeInlineWithPipeDelimitersRendersAsCode()
{
string markdown =
"""
`
|| hidden text ||
`
""".ReplaceLineEndings("\n");

var pipeline = new MarkdownPipelineBuilder()
.UseAdvancedExtensions()
.Build();

var html = Markdown.ToHtml(markdown, pipeline);

Assert.That(html, Is.EqualTo("<p><code>|| hidden text ||</code></p>\n"));
}

[Test]
public void TableCellWithCodeInlineRendersCorrectly()
{
const string markdown =
"""
| Count | A | B | C | D | E |
|-------|---|---|---|---|---|
| 0 | B | C | D | E | F |
| 1 | B | `Code block` | D | E | F |
| 2 | B | C | D | E | F |
""";

var pipeline = new MarkdownPipelineBuilder()
.UseAdvancedExtensions()
.Build();

var html = Markdown.ToHtml(markdown, pipeline);

Assert.That(html, Does.Contain("<td><code>Code block</code></td>"));
}

[Test]
public void CodeInlineWithIndentedContentPreservesWhitespace()
{
const string markdown = "`\n foo\n`";

var pipeline = new MarkdownPipelineBuilder()
.UseAdvancedExtensions()
.Build();

var document = Markdown.Parse(markdown, pipeline);
var codeInline = document.Descendants().OfType<CodeInline>().Single();

Assert.That(codeInline.Content, Is.EqualTo("foo"));
Assert.That(Markdown.ToHtml(markdown, pipeline), Is.EqualTo("<p><code>foo</code></p>\n"));
}

[Test]
public void TableWithIndentedPipeAfterCodeInlineParsesCorrectly()
{
var markdown =
"""
`
|| hidden text ||
`

| Count | Value |
|-------|-------|
| 0 | B |

""".ReplaceLineEndings("\n");

var pipeline = new MarkdownPipelineBuilder()
.UseAdvancedExtensions()
.Build();

var html = Markdown.ToHtml(markdown, pipeline);

Assert.That(html, Does.Contain("<p><code>|| hidden text ||</code></p>"));
Assert.That(html, Does.Contain("<table"));
Assert.That(html, Does.Contain("<td>B</td>"));
}
}
10 changes: 5 additions & 5 deletions src/Markdig/Helpers/HtmlHelper.cs
Original file line number Diff line number Diff line change
@@ -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.Diagnostics;
Expand Down Expand Up @@ -430,7 +430,7 @@ private static bool TryParseHtmlTagHtmlComment(ref StringSlice text, ref ValueSt

const string EndOfComment = "-->";

int endOfComment = slice.IndexOf(EndOfComment, StringComparison.Ordinal);
int endOfComment = slice.IndexOf(EndOfComment.AsSpan(), StringComparison.Ordinal);
if (endOfComment < 0)
{
return false;
Expand Down Expand Up @@ -474,7 +474,7 @@ private static bool TryParseHtmlTagProcessingInstruction(ref StringSlice text, r
public static string Unescape(string? text, bool removeBackSlash = true)
{
// Credits: code from CommonMark.NET
// Copyright (c) 2014, Kārlis Gaņģis All rights reserved.
// Copyright (c) 2014, Kārlis Gaņģis All rights reserved.
// See license for details: https://github.com/Knagis/CommonMark.NET/blob/master/LICENSE.md
if (string.IsNullOrEmpty(text))
{
Expand Down Expand Up @@ -553,7 +553,7 @@ public static string Unescape(string? text, bool removeBackSlash = true)
public static int ScanEntity<T>(T slice, out int numericEntity, out int namedEntityStart, out int namedEntityLength) where T : ICharIterator
{
// Credits: code from CommonMark.NET
// Copyright (c) 2014, Kārlis Gaņģis All rights reserved.
// Copyright (c) 2014, Kārlis Gaņģis All rights reserved.
// See license for details: https://github.com/Knagis/CommonMark.NET/blob/master/LICENSE.md

numericEntity = 0;
Expand All @@ -568,7 +568,7 @@ public static int ScanEntity<T>(T slice, out int numericEntity, out int namedEnt
var start = slice.Start;
char c = slice.NextChar();
int counter = 0;

if (c == '#')
{
c = slice.PeekChar();
Expand Down
18 changes: 10 additions & 8 deletions src/Markdig/Parsers/Inlines/CodeInlineParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

using System.Diagnostics;

using Markdig.Extensions.Tables;
using Markdig.Helpers;
using Markdig.Syntax;
using Markdig.Syntax.Inlines;
Expand Down Expand Up @@ -84,15 +85,16 @@ public override bool Match(InlineProcessor processor, ref StringSlice slice)
{
lookAhead = lookAhead.Slice(1);
}
int whitespace = 0;
while (whitespace < lookAhead.Length && (lookAhead[whitespace] == ' ' || lookAhead[whitespace] == '\t'))
if (lookAhead[0] == '|')
{
whitespace++;
}
if (whitespace < lookAhead.Length && lookAhead[whitespace] == '|')
{
slice.Start = openingStart;
return false;
// We saw the start of a code inline, but the close sticks are not present on the same line.
// If the next line starts with a pipe character, this is likely an incomplete CodeInline within a table.
// Treat it as regular text to avoid breaking the overall table shape.
if (processor.Inline != null && processor.Inline.ContainsParentOfType<PipeTableDelimiterInline>())
{
slice.Start = openingStart;
return false;
}
}

containsNewLines = true;
Expand Down