diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 2bf01b6d2..83d5e94b6 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,4 +1,9 @@ +## 17.2.0 + +* Resolve markdown links in raw html [#769](https://github.com/fsprojects/FSharp.Formatting/issues/769) + ## 17.1.0 + * [Add syntax highlighting to API docs](https://github.com/fsprojects/FSharp.Formatting/pull/780) ## 17.0.0 diff --git a/src/FSharp.Formatting.Markdown/MarkdownUtils.fs b/src/FSharp.Formatting.Markdown/MarkdownUtils.fs index 9e7bf9828..a6c5f529a 100644 --- a/src/FSharp.Formatting.Markdown/MarkdownUtils.fs +++ b/src/FSharp.Formatting.Markdown/MarkdownUtils.fs @@ -5,6 +5,8 @@ namespace rec FSharp.Formatting.Markdown open System.Collections.Generic +open System.Linq +open System.Xml.Linq open FSharp.Formatting.Templating module internal MarkdownUtils = @@ -315,7 +317,36 @@ module internal MarkdownUtils = ) | OtherBlock (lines: (string * MarkdownRange) list, range) -> OtherBlock(lines |> List.map (fun (line, range) -> (mapText f line, range)), range) - | InlineHtmlBlock (code, count, range) -> InlineHtmlBlock(mapText f code, count, range) + | InlineHtmlBlock (code, count, range) -> + try + let fText, _, fLink = f + + if code.StartsWith("
{code}{tempRoot}>") + // ends-with is XPath 2.0 only, https://stackoverflow.com/questions/1525299/xpath-and-xslt-2-0-for-net + let attributes = + match System.Xml.XPath.Extensions.XPathEvaluate(element, "//*/@*[contains(., '.md')]") with + | :? System.Collections.IEnumerable as enumerable -> + enumerable |> Enumerable.Cast|> Seq.toArray + | _ -> Array.empty + + if Array.isEmpty attributes then + InlineHtmlBlock(fText code, count, range) + else + for attribute in attributes do + if attribute.Value.EndsWith(".md") then + attribute.SetValue(fLink attribute.Value) + + let html = element.Elements() |> Seq.map string |> String.concat "" |> fText + InlineHtmlBlock(html, count, range) + with ex -> + InlineHtmlBlock(mapText f code, count, range) // NOTE: substitutions are not currently applied to embedded LiterateParagraph which are in any case eliminated // before substitutions are applied. diff --git a/tests/FSharp.Markdown.Tests/Markdown.fs b/tests/FSharp.Markdown.Tests/Markdown.fs index 5239ddd0e..7899d29b9 100644 --- a/tests/FSharp.Markdown.Tests/Markdown.fs +++ b/tests/FSharp.Markdown.Tests/Markdown.fs @@ -981,3 +981,61 @@ let ``Do not close emphasis if second * is preceded by punctuation and followed let doc2 = "*(*foo*)*" let actual2 = " (foo)
\r\n" |> properNewLines Markdown.ToHtml doc2 |> shouldEqual actual2 + +[] +let ``Replace relative markdown file in anchor href attribute`` () = + let doc = "my link" + let mdlinkResolver _ = Some "./other-file.html" + + let actual = + "my link\r\n" + |> properNewLines + + Markdown.ToHtml(doc, mdlinkResolver = mdlinkResolver) |> shouldEqual actual + +[ ] +let ``Replace relative markdown file in multiple anchors`` () = + let doc = "link onelink two" + let mdlinkResolver _ = Some "./other-file.html" + + let actual = + "link onelink two\r\n" + |> properNewLines + + Markdown.ToHtml(doc, mdlinkResolver = mdlinkResolver) |> shouldEqual actual + +[ ] +let ``Replace relative markdown file in multiple attributes`` () = + let doc = "d" + let mdlinkResolver _ = Some "./other-file.html" + let actual = "d\r\n" |> properNewLines + Markdown.ToHtml(doc, mdlinkResolver = mdlinkResolver) |> shouldEqual actual + +[ ] +let ``Replace relative markdown file in custom attribute`` () = + let doc = " " + let mdlinkResolver _ = Some "./other-file.html" + + let actual = + " \r\n" + |> properNewLines + + Markdown.ToHtml(doc, mdlinkResolver = mdlinkResolver) |> shouldEqual actual + +[ ] +let ``Don't replace links in generated code block`` () = + let doc = " content" + let mdlinkResolver _ = failwith "should not be reached!" + let actual = "content\r\n" |> properNewLines + Markdown.ToHtml(doc, mdlinkResolver = mdlinkResolver) |> shouldEqual actual + +[] +let ``Don't replace links in generated code block in table`` () = + let doc = " content
" + let mdlinkResolver _ = failwith "should not be reached!" + + let actual = + "content
\r\n" + |> properNewLines + + Markdown.ToHtml(doc, mdlinkResolver = mdlinkResolver) |> shouldEqual actual