From b024fe2b1e3b66d0a03559fb1ce1a045115aaaba Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 7 Apr 2026 11:04:34 +0000 Subject: [PATCH 1/2] fix: Markdown.ToMd preserves DirectLink and DirectImage titles When serialising a document with ToMd, link and image titles were silently dropped. A Markdown fragment such as [text](url "My Title") would round-trip as [text](url) because the `DirectLink` / `DirectImage` formatSpan cases matched the title field with a wildcard. Fix: give DirectLink and DirectImage their own branches so the optional title can be appended in CommonMark form ( \"title\"). Three new round-trip tests cover: - DirectLink with title - DirectLink without title (unchanged behaviour) - DirectImage with title All 284 Markdown tests pass. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- RELEASE_NOTES.md | 3 +++ .../MarkdownUtils.fs | 18 +++++++++++++-- tests/FSharp.Markdown.Tests/Markdown.fs | 22 +++++++++++++++++++ 3 files changed, 41 insertions(+), 2 deletions(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 33214cbc..723de0c4 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -2,6 +2,9 @@ ## [Unreleased] +### Fixed +* Fix `Markdown.ToMd` dropping link titles in `DirectLink` and `DirectImage` spans. Links with a title attribute (e.g. `[text](url "title")`) now round-trip correctly; without this fix the title was silently discarded on serialisation. + ## [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..f7b0c277 100644 --- a/src/FSharp.Formatting.Markdown/MarkdownUtils.fs +++ b/src/FSharp.Formatting.Markdown/MarkdownUtils.fs @@ -106,13 +106,27 @@ module internal MarkdownUtils = | HardLineBreak(_) -> "\n" | AnchorLink _ -> "" + | DirectLink(body, link, title, _) -> + let t = + title + |> Option.map (fun t -> sprintf " \"%s\"" (t.Replace("\"", "\\\""))) + |> Option.defaultValue "" + + "[" + formatSpans ctx body + "](" + link + t + ")" + | IndirectLink(body, _, LookupKey ctx.Links (link, _), _) - | DirectLink(body, link, _, _) | IndirectLink(body, link, _, _) -> "[" + formatSpans ctx body + "](" + link + ")" | IndirectImage(body, _, LookupKey ctx.Links (link, _), _) -> sprintf "![%s](%s)" body link | IndirectImage(body, _, key, _) -> sprintf "![%s][%s]" body key - | DirectImage(body, link, _, _) -> sprintf "![%s](%s)" body link + + | DirectImage(body, link, title, _) -> + let t = + title + |> Option.map (fun t -> sprintf " \"%s\"" (t.Replace("\"", "\\\""))) + |> Option.defaultValue "" + + sprintf "![%s](%s)" body (link + t) | Strong(body, _) -> "**" + formatSpans ctx body + "**" | InlineCode(body, _) -> "`" + body + "`" | Emphasis(body, _) -> "*" + formatSpans ctx body + "*" diff --git a/tests/FSharp.Markdown.Tests/Markdown.fs b/tests/FSharp.Markdown.Tests/Markdown.fs index 984047f7..dadd88f2 100644 --- a/tests/FSharp.Markdown.Tests/Markdown.fs +++ b/tests/FSharp.Markdown.Tests/Markdown.fs @@ -1245,10 +1245,32 @@ let ``ToMd preserves a direct link`` () = |> toMd |> should contain "[FSharp](https://fsharp.org)" +[] +let ``ToMd preserves a direct link with title`` () = + let md = "[FSharp](https://fsharp.org \"F# language\")" + let result = toMd md + result |> should contain "[FSharp](" + result |> should contain "https://fsharp.org" + result |> should contain "\"F# language\"" + +[] +let ``ToMd preserves a direct link without title unchanged`` () = + let result = "[link](http://example.com)" |> toMd + result |> should contain "[link](http://example.com)" + result |> should not' (contain "\"") + [] let ``ToMd preserves a direct image`` () = "![alt text](image.png)" |> toMd |> should contain "![alt text](image.png)" +[] +let ``ToMd preserves a direct image with title`` () = + let md = "![photo](image.png \"My Photo\")" + let result = toMd md + result |> should contain "![photo](" + result |> should contain "image.png" + result |> should contain "\"My Photo\"" + [] let ``ToMd preserves an unordered list`` () = let md = "* apple\n* banana\n* cherry" From a752a4165679372911aa693ed2b307a1ab2f0d72 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 7 Apr 2026 11:04:36 +0000 Subject: [PATCH 2/2] ci: trigger checks