From 9d76b5f10eaf87dca5537e2eb267b2acc92ab0ec Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 8 Apr 2026 11:00:55 +0000 Subject: [PATCH 1/2] fix: Markdown.ToMd preserves unresolved IndirectLink in reference notation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a document contains a reference-style link whose key is not defined in the link dictionary (e.g. '[text][key]' with no '[key]: url' definition), the previous code emitted '[text]([key])' — treating the raw '[key]' string (including brackets) as the URL, producing broken Markdown. Root cause: the three-way OR pattern IndirectLink(resolved) | DirectLink | IndirectLink(fallthrough) bound 'link' to the 'original' field (position 2) of IndirectLink for the fallthrough arm. 'original' is the raw key text with surrounding brackets (e.g. '[unknown-key]'), not a URL. Fix: split the fallthrough into a dedicated pattern arm that uses the 'key' field (position 3 — the clean key string without brackets) and emits the correct '[text][key]' reference notation. Added 2 tests: - unresolved indirect link emits [text][key] - resolved indirect link (reference present) resolves to direct form All 283 Markdown tests pass. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- RELEASE_NOTES.md | 3 +++ .../MarkdownUtils.fs | 5 ++-- tests/FSharp.Markdown.Tests/Markdown.fs | 24 +++++++++++++++++++ 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 33214cbc..056880af 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -2,6 +2,9 @@ ## [Unreleased] +### Fixed +* Fix `Markdown.ToMd` emitting invalid `[text]([key])` for unresolved reference-style links. Unresolved `IndirectLink` spans now serialise as `[text][key]` (preserving the reference notation) instead of incorrectly inserting the key string with its brackets as a URL. + ## [22.0.0] - 2026-04-03 ### Fixed diff --git a/src/FSharp.Formatting.Markdown/MarkdownUtils.fs b/src/FSharp.Formatting.Markdown/MarkdownUtils.fs index 59bf09a3..5f2c34f6 100644 --- a/src/FSharp.Formatting.Markdown/MarkdownUtils.fs +++ b/src/FSharp.Formatting.Markdown/MarkdownUtils.fs @@ -107,8 +107,9 @@ module internal MarkdownUtils = | AnchorLink _ -> "" | IndirectLink(body, _, LookupKey ctx.Links (link, _), _) - | DirectLink(body, link, _, _) - | IndirectLink(body, link, _, _) -> "[" + formatSpans ctx body + "](" + link + ")" + | DirectLink(body, link, _, _) -> "[" + formatSpans ctx body + "](" + link + ")" + // Unresolved reference link: emit [text][key] to preserve the reference notation + | IndirectLink(body, _, key, _) -> "[" + formatSpans ctx body + "][" + key + "]" | IndirectImage(body, _, LookupKey ctx.Links (link, _), _) -> sprintf "![%s](%s)" body link | IndirectImage(body, _, key, _) -> sprintf "![%s][%s]" body key diff --git a/tests/FSharp.Markdown.Tests/Markdown.fs b/tests/FSharp.Markdown.Tests/Markdown.fs index 984047f7..d884d98c 100644 --- a/tests/FSharp.Markdown.Tests/Markdown.fs +++ b/tests/FSharp.Markdown.Tests/Markdown.fs @@ -1371,3 +1371,27 @@ let ``ToMd round-trip: indirect image with unresolved reference`` () = let result = Markdown.ToMd(doc) // When key is not resolved, should preserve the indirect form result |> should contain "![alt text][unknown-ref]" + +// -------------------------------------------------------------------------------------- +// ToMd round-trip: unresolved indirect links +// -------------------------------------------------------------------------------------- + +[] +let ``ToMd preserves unresolved indirect link in reference notation`` () = + // Indirect link with NO matching reference definition in the document. + // Before fix: emitted [link text]([unknown-key]) — invalid Markdown. + // After fix: emits [link text][unknown-key] — valid reference link. + let input = "[link text][unknown-key]" + let doc = Markdown.Parse(input) + let result = Markdown.ToMd(doc) + // Must use reference notation, not inline notation with [key] as the URL + result |> should contain "[link text][unknown-key]" + result |> should not' (contain "[link text]([") + +[] +let ``ToMd resolves indirect link when reference is present`` () = + // Indirect link whose reference definition IS present — key resolves to a URL. + let input = "[FSharp][fs-link]\n\n[fs-link]: https://fsharp.org" + let result = toMd input + result |> should contain "[FSharp](" + result |> should contain "https://fsharp.org" From 6d6cc1349d50f96a9c9afe80fea9ee946c594837 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 8 Apr 2026 11:00:58 +0000 Subject: [PATCH 2/2] ci: trigger checks