diff --git a/src/Markdig.Tests/TestAutoLinks.cs b/src/Markdig.Tests/TestAutoLinks.cs
index 005c3d1..c72ac01 100644
--- a/src/Markdig.Tests/TestAutoLinks.cs
+++ b/src/Markdig.Tests/TestAutoLinks.cs
@@ -21,4 +21,28 @@ public class TestAutoLinks
Assert.That(html, Is.EqualTo(expected).IgnoreWhiteSpace);
}
+
+ // https://github.com/xoofx/markdig/issues/668
+ // A heading's implicit reference must not resolve inside another still-open
+ // link bracket, which would break the outer link.
+ [Test]
+ public void TestAutoIdentifierHeadingLinkDoesNotHijackNestedLinkLabel()
+ {
+ var markdown = "# Testing Markdown\n\n" +
+ "### Header\n\n" +
+ "[Testing [Header]](https://www.bing.com)\n\n" +
+ "[Testing [test]](https://www.google.com)\n";
+ var expected = "
Testing Markdown
\n" +
+ "\n" +
+ "Testing [Header]
\n" +
+ "Testing [test]
";
+
+ var pipeline = new MarkdownPipelineBuilder()
+ .UseAutoLinks()
+ .UseAutoIdentifiers()
+ .Build();
+ var html = Markdown.ToHtml(markdown, pipeline);
+
+ Assert.That(html, Is.EqualTo(expected).IgnoreWhiteSpace);
+ }
}
diff --git a/src/Markdig/Extensions/AutoIdentifiers/HeadingLinkReferenceDefinition.cs b/src/Markdig/Extensions/AutoIdentifiers/HeadingLinkReferenceDefinition.cs
index 32562cc..b57c785 100644
--- a/src/Markdig/Extensions/AutoIdentifiers/HeadingLinkReferenceDefinition.cs
+++ b/src/Markdig/Extensions/AutoIdentifiers/HeadingLinkReferenceDefinition.cs
@@ -24,4 +24,11 @@ public class HeadingLinkReferenceDefinition : LinkReferenceDefinition
/// Gets or sets the heading related to this link reference definition.
///
public HeadingBlock Heading { get; set; }
+
+ ///
+ ///
+ /// Created implicitly for every heading, so it must not resolve inside another
+ /// still-open link bracket, e.g. [Some text [Heading]](url).
+ ///
+ internal override bool AllowResolutionInsideOpenLink => false;
}
diff --git a/src/Markdig/Parsers/Inlines/LinkInlineParser.cs b/src/Markdig/Parsers/Inlines/LinkInlineParser.cs
index 83ac5db..0701ae4 100644
--- a/src/Markdig/Parsers/Inlines/LinkInlineParser.cs
+++ b/src/Markdig/Parsers/Inlines/LinkInlineParser.cs
@@ -150,6 +150,13 @@ public class LinkInlineParser : InlineParser
return false;
}
+ // An implicit definition must not hijack a bracket nested inside a
+ // still-open link, which would break the outer one.
+ if (!linkRef.AllowResolutionInsideOpenLink && HasActiveAncestorLink(parent))
+ {
+ return false;
+ }
+
Inline? link = null;
// Try to use a callback directly defined on the LinkReferenceDefinition
if (linkRef.CreateLinkInline != null)
@@ -430,6 +437,24 @@ public class LinkInlineParser : InlineParser
}
}
+ ///
+ /// Determines whether the delimiter is nested inside another link/image
+ /// delimiter that may still resolve.
+ ///
+ private static bool HasActiveAncestorLink(LinkDelimiterInline delimiter)
+ {
+ var inline = delimiter.Parent;
+ while (inline != null)
+ {
+ if (inline is LinkDelimiterInline { IsActive: true })
+ {
+ return true;
+ }
+ inline = inline.Parent;
+ }
+ return false;
+ }
+
private static void MarkParentAsInactive(Inline? inline)
{
while (inline != null)
diff --git a/src/Markdig/Syntax/LinkReferenceDefinition.cs b/src/Markdig/Syntax/LinkReferenceDefinition.cs
index 056d91d..e70a574 100644
--- a/src/Markdig/Syntax/LinkReferenceDefinition.cs
+++ b/src/Markdig/Syntax/LinkReferenceDefinition.cs
@@ -136,6 +136,16 @@ public class LinkReferenceDefinition : LeafBlock
///
public CreateLinkInlineDelegate? CreateLinkInline { get; set; }
+ ///
+ /// Gets whether this definition may resolve into a link while its label is
+ /// nested inside another still-open link or image bracket.
+ ///
+ ///
+ /// true for user-authored definitions, preserving CommonMark's "links
+ /// cannot contain links". Implicitly generated ones override it to false.
+ ///
+ internal virtual bool AllowResolutionInsideOpenLink => true;
+
///
/// Tries to the parse the specified text into a definition.
///