Skip to content

Commit 5435702

Browse files
phillip-haydonMihaZupanxoofx
authored
Fix issue where an inline code block that spans multiple lines doesn't parse correctly (#893)
* fixes issue where an inline code block that spans multiple lines doesn't get treated as code * Update src/Markdig.Tests/TestPipeTable.cs Co-authored-by: Miha Zupan <mihazupan.zupan1@gmail.com> * Apply suggestion from @MihaZupan Co-authored-by: Miha Zupan <mihazupan.zupan1@gmail.com> * Update src/Markdig.Tests/TestPipeTable.cs Co-authored-by: Miha Zupan <mihazupan.zupan1@gmail.com> * fix broken test * removed unreachable code and added more tests * Update src/Markdig.Tests/TestPipeTable.cs Co-authored-by: Miha Zupan <mihazupan.zupan1@gmail.com> * Update src/Markdig.Tests/TestPipeTable.cs Co-authored-by: Miha Zupan <mihazupan.zupan1@gmail.com> * removed uncessary inline code check * Update src/Markdig/Parsers/Inlines/CodeInlineParser.cs Co-authored-by: Miha Zupan <mihazupan.zupan1@gmail.com> --------- Co-authored-by: Miha Zupan <mihazupan.zupan1@gmail.com> Co-authored-by: Alexandre Mutel <alexandre_mutel@live.com>
1 parent 4dc0be8 commit 5435702

File tree

3 files changed

+116
-13
lines changed

3 files changed

+116
-13
lines changed

src/Markdig.Tests/TestPipeTable.cs

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
using Markdig;
12
using Markdig.Extensions.Tables;
23
using Markdig.Syntax;
4+
using Markdig.Syntax.Inlines;
35

46
namespace Markdig.Tests;
57

@@ -101,4 +103,103 @@ public void TableWithUnbalancedCodeSpanParsesWithoutDepthLimitError()
101103
Assert.That(html, Does.Contain("<table"));
102104
Assert.That(html, Does.Contain("<td>`C</td>"));
103105
}
106+
107+
[Test]
108+
public void CodeInlineWithPipeDelimitersRemainsCodeInline()
109+
{
110+
const string markdown = "`|| hidden text ||`";
111+
112+
var pipeline = new MarkdownPipelineBuilder()
113+
.UseAdvancedExtensions()
114+
.Build();
115+
116+
var document = Markdown.Parse(markdown, pipeline);
117+
118+
var codeInline = document.Descendants().OfType<CodeInline>().SingleOrDefault();
119+
Assert.IsNotNull(codeInline);
120+
Assert.That(codeInline!.Content, Is.EqualTo("|| hidden text ||"));
121+
Assert.That(document.ToHtml(), Is.EqualTo("<p><code>|| hidden text ||</code></p>\n"));
122+
}
123+
124+
[Test]
125+
public void MultiLineCodeInlineWithPipeDelimitersRendersAsCode()
126+
{
127+
string markdown =
128+
"""
129+
`
130+
|| hidden text ||
131+
`
132+
""".ReplaceLineEndings("\n");
133+
134+
var pipeline = new MarkdownPipelineBuilder()
135+
.UseAdvancedExtensions()
136+
.Build();
137+
138+
var html = Markdown.ToHtml(markdown, pipeline);
139+
140+
Assert.That(html, Is.EqualTo("<p><code>|| hidden text ||</code></p>\n"));
141+
}
142+
143+
[Test]
144+
public void TableCellWithCodeInlineRendersCorrectly()
145+
{
146+
const string markdown =
147+
"""
148+
| Count | A | B | C | D | E |
149+
|-------|---|---|---|---|---|
150+
| 0 | B | C | D | E | F |
151+
| 1 | B | `Code block` | D | E | F |
152+
| 2 | B | C | D | E | F |
153+
""";
154+
155+
var pipeline = new MarkdownPipelineBuilder()
156+
.UseAdvancedExtensions()
157+
.Build();
158+
159+
var html = Markdown.ToHtml(markdown, pipeline);
160+
161+
Assert.That(html, Does.Contain("<td><code>Code block</code></td>"));
162+
}
163+
164+
[Test]
165+
public void CodeInlineWithIndentedContentPreservesWhitespace()
166+
{
167+
const string markdown = "`\n foo\n`";
168+
169+
var pipeline = new MarkdownPipelineBuilder()
170+
.UseAdvancedExtensions()
171+
.Build();
172+
173+
var document = Markdown.Parse(markdown, pipeline);
174+
var codeInline = document.Descendants().OfType<CodeInline>().Single();
175+
176+
Assert.That(codeInline.Content, Is.EqualTo("foo"));
177+
Assert.That(Markdown.ToHtml(markdown, pipeline), Is.EqualTo("<p><code>foo</code></p>\n"));
178+
}
179+
180+
[Test]
181+
public void TableWithIndentedPipeAfterCodeInlineParsesCorrectly()
182+
{
183+
var markdown =
184+
"""
185+
`
186+
|| hidden text ||
187+
`
188+
189+
| Count | Value |
190+
|-------|-------|
191+
| 0 | B |
192+
193+
""".ReplaceLineEndings("\n");
194+
195+
var pipeline = new MarkdownPipelineBuilder()
196+
.UseAdvancedExtensions()
197+
.Build();
198+
199+
var html = Markdown.ToHtml(markdown, pipeline);
200+
201+
Assert.That(html, Does.Contain("<p><code>|| hidden text ||</code></p>"));
202+
Assert.That(html, Does.Contain("<table"));
203+
Assert.That(html, Does.Contain("<td>B</td>"));
204+
}
104205
}

src/Markdig/Helpers/HtmlHelper.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// Copyright (c) Alexandre Mutel. All rights reserved.
2-
// This file is licensed under the BSD-Clause 2 license.
2+
// This file is licensed under the BSD-Clause 2 license.
33
// See the license.txt file in the project root for more information.
44

55
using System.Diagnostics;
@@ -430,7 +430,7 @@ private static bool TryParseHtmlTagHtmlComment(ref StringSlice text, ref ValueSt
430430

431431
const string EndOfComment = "-->";
432432

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

559559
numericEntity = 0;
@@ -568,7 +568,7 @@ public static int ScanEntity<T>(T slice, out int numericEntity, out int namedEnt
568568
var start = slice.Start;
569569
char c = slice.NextChar();
570570
int counter = 0;
571-
571+
572572
if (c == '#')
573573
{
574574
c = slice.PeekChar();

src/Markdig/Parsers/Inlines/CodeInlineParser.cs

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
using System.Diagnostics;
66

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

98100
containsNewLines = true;

0 commit comments

Comments
 (0)