From 40eebc0cd8e644688d805a917cb5e16bfcc5605a Mon Sep 17 00:00:00 2001 From: upstream Date: Sat, 19 Sep 2026 11:41:48 +0200 Subject: [PATCH] Upstream xoofx/markdig state of the touched files before baa95a9ca3 --- site/docs/extensions/auto-identifiers.md | 89 +++++++++++++++++++ src/Markdig.Tests/TestAutoLinks.cs | 25 ------ .../HeadingLinkReferenceDefinition.cs | 8 -- .../Parsers/Inlines/LinkInlineParser.cs | 26 ------ src/Markdig/Syntax/LinkReferenceDefinition.cs | 15 ---- 5 files changed, 89 insertions(+), 74 deletions(-) create mode 100644 site/docs/extensions/auto-identifiers.md diff --git a/site/docs/extensions/auto-identifiers.md b/site/docs/extensions/auto-identifiers.md new file mode 100644 index 0000000..f3aaea7 --- /dev/null +++ b/site/docs/extensions/auto-identifiers.md @@ -0,0 +1,89 @@ +--- +title: Auto-identifiers +--- + +# Auto-identifiers + +Enable with `.UseAutoIdentifiers()` (included in `UseAdvancedExtensions()`). + +This extension automatically generates `id` attributes for all headings, similar to [Pandoc auto identifiers](https://pandoc.org/MANUAL.html#extension-auto_identifiers). This is essential for linking to specific sections. + +## How it works + +Every heading gets an `id` attribute derived from its text content: + +```markdown +## Getting Started +``` + +Renders as: + +```html +

Getting Started

+``` + +You can then link to it: + +```markdown +See [Getting Started](#getting-started). +``` + +## ID generation rules + +1. Convert to lowercase +2. Remove anything that is not a letter, number, space, or hyphen +3. Replace spaces with hyphens +4. Remove leading/trailing hyphens + +**Examples:** + +{.table} +| Heading | Generated ID | +|---|---| +| `# Hello World` | `hello-world` | +| `## C# Tips & Tricks` | `c-tips--tricks` | +| `### 2. Installation` | `2-installation` | + +## Duplicate handling + +If two headings produce the same ID, a numeric suffix is appended: + +```markdown +## Section +## Section +## Section +``` + +Produces: `section`, `section-1`, `section-2`. + +## Options + +`UseAutoIdentifiers` accepts an `AutoIdentifierOptions` flags enum: + +```csharp +using Markdig.Extensions.AutoIdentifiers; + +var pipeline = new MarkdownPipelineBuilder() + .UseAutoIdentifiers(AutoIdentifierOptions.GitHub) + .Build(); +``` + +Available options: + +{.table} +| Option | Description | +|---|---| +| `AutoIdentifierOptions.Default` | Standard auto-identifier behavior | +| `AutoIdentifierOptions.GitHub` | GitHub-compatible ID generation | +| `AutoIdentifierOptions.AutoLink` | Also create a self-link anchor | +| `AutoIdentifierOptions.AllowOnlyAscii` | Strip non-ASCII characters from IDs | + +## Combining with generic attributes + +You can override the auto-generated ID using [Generic attributes](generic-attributes.md): + +```markdown +## My Heading {#custom-id} +``` + +The explicit `#custom-id` takes precedence over the auto-generated one. diff --git a/src/Markdig.Tests/TestAutoLinks.cs b/src/Markdig.Tests/TestAutoLinks.cs index e3bb239..005c3d1 100644 --- a/src/Markdig.Tests/TestAutoLinks.cs +++ b/src/Markdig.Tests/TestAutoLinks.cs @@ -21,29 +21,4 @@ public class TestAutoLinks Assert.That(html, Is.EqualTo(expected).IgnoreWhiteSpace); } - - // https://github.com/xoofx/markdig/issues/668 - // A heading's implicit "AutoLink" reference (registered by the AutoIdentifiers extension) must not - // resolve when its label is nested inside another still-open link bracket, otherwise it silently - // breaks the outer, explicitly authored 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" + - "

Header

\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 a8b6958..32562cc 100644 --- a/src/Markdig/Extensions/AutoIdentifiers/HeadingLinkReferenceDefinition.cs +++ b/src/Markdig/Extensions/AutoIdentifiers/HeadingLinkReferenceDefinition.cs @@ -24,12 +24,4 @@ public class HeadingLinkReferenceDefinition : LinkReferenceDefinition /// Gets or sets the heading related to this link reference definition. /// public HeadingBlock Heading { get; set; } - - /// - /// - /// This reference definition is created implicitly for every heading, so it must not resolve when its - /// label is nested inside another still-open link bracket, e.g. [Some text [HeadingText]](url). - /// Otherwise it would silently break the outer, explicitly authored link. - /// - internal override bool AllowResolutionInsideOpenLink => false; } diff --git a/src/Markdig/Parsers/Inlines/LinkInlineParser.cs b/src/Markdig/Parsers/Inlines/LinkInlineParser.cs index fcd1e93..83ac5db 100644 --- a/src/Markdig/Parsers/Inlines/LinkInlineParser.cs +++ b/src/Markdig/Parsers/Inlines/LinkInlineParser.cs @@ -150,14 +150,6 @@ public class LinkInlineParser : InlineParser return false; } - // Implicitly generated reference definitions (e.g. automatic heading anchors from the auto-identifier - // extension) must not hijack a bracket pair nested inside another still-open link, otherwise they would - // silently break the outer, explicitly authored link (see https://github.com/xoofx/markdig/issues/668). - if (!linkRef.AllowResolutionInsideOpenLink && HasActiveAncestorLink(parent)) - { - return false; - } - Inline? link = null; // Try to use a callback directly defined on the LinkReferenceDefinition if (linkRef.CreateLinkInline != null) @@ -438,24 +430,6 @@ public class LinkInlineParser : InlineParser } } - /// - /// Determines whether the given link delimiter is nested inside another link/image delimiter that is - /// still active, i.e. it may still be resolved into a real link/image once fully parsed. - /// - 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 c44ee06..056d91d 100644 --- a/src/Markdig/Syntax/LinkReferenceDefinition.cs +++ b/src/Markdig/Syntax/LinkReferenceDefinition.cs @@ -136,21 +136,6 @@ public class LinkReferenceDefinition : LeafBlock /// public CreateLinkInlineDelegate? CreateLinkInline { get; set; } - /// - /// Gets a value indicating whether this reference definition is allowed to resolve into a link when its - /// label is nested inside another link or image bracket that is still open (and may still become a valid - /// link/image once fully parsed). - /// - /// - /// This is true for regular, user-authored reference definitions so that CommonMark's "links cannot - /// contain links" resolution order is preserved. Implicitly generated reference definitions (e.g. the - /// automatic heading anchors created by the auto-identifier extension) override this to false so - /// they don't silently hijack a bracket pair that is part of a larger, still-open link label, such as - /// [Some text [HeadingText]](https://example.com) (see - /// https://github.com/xoofx/markdig/issues/668). - /// - internal virtual bool AllowResolutionInsideOpenLink => true; - /// /// Tries to the parse the specified text into a definition. ///