diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index aab352f09..83b2b30ba 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,3 +1,6 @@ +## 15.0.3 +* Fixes Markdown parser gets multiple-underscores-inside-italics wrong [#389](https://github.com/fsprojects/FSharp.Formatting/issues/389) + ## 15.0.2 * Trim the `--fscoptions` before passing them as `otherflags`. ([comment #616](https://github.com/fsprojects/FSharp.Formatting/issues/616#issuecomment-1200877765)) diff --git a/src/FSharp.Formatting.Markdown/MarkdownParser.fs b/src/FSharp.Formatting.Markdown/MarkdownParser.fs index bd541846c..ae24b9048 100644 --- a/src/FSharp.Formatting.Markdown/MarkdownParser.fs +++ b/src/FSharp.Formatting.Markdown/MarkdownParser.fs @@ -69,6 +69,18 @@ let inline (|EscapedLatexInlineMathChar|_|) input = | '\\' :: (('$') as c) :: rest -> Some(c, rest) | _ -> None +/// Succeeds when the specified character list starts with a letter or number +let inline (|AlphaNum|_|) input = + let re = """^[a-zA-Z0-9]""" + let match' = Regex.Match(Array.ofList input |> String, re) + + if match'.Success then + let entity = match'.Value + let _, rest = List.splitAt entity.Length input + Some(char entity, rest) + else + None + /// Matches a list if it starts with a sub-list that is delimited /// using the specified delimiters. Returns a wrapped list and the rest. /// @@ -79,7 +91,12 @@ let (|DelimitedMarkdown|_|) bracket input = let rec loop acc = function | EscapedChar (x, xs) -> loop (x :: '\\' :: acc) xs - | input when List.startsWith endl input -> Some(List.rev acc, input) + | input when List.startsWith endl input -> + let rest = List.skip bracket.Length input + + match rest with + | AlphaNum (x, xs) -> loop (x :: endl @ acc) xs + | _ -> Some(List.rev acc, input) | x :: xs -> loop (x :: acc) xs | [] -> None // If it starts with 'startl', let's search for 'endl' @@ -90,7 +107,6 @@ let (|DelimitedMarkdown|_|) bracket input = else None - /// This is similar to `List.Delimited`, but it skips over Latex inline math characters. let (|DelimitedLatexDisplayMath|_|) bracket input = let _startl, endl = bracket, bracket diff --git a/tests/FSharp.Markdown.Tests/Markdown.fs b/tests/FSharp.Markdown.Tests/Markdown.fs index 416fe31a7..0106bbcd2 100644 --- a/tests/FSharp.Markdown.Tests/Markdown.fs +++ b/tests/FSharp.Markdown.Tests/Markdown.fs @@ -790,3 +790,90 @@ let ``Parse blockquote with three leading spaces`` () = ) ] (Markdown.Parse doc).Paragraphs |> shouldEqual expected + +[] +let ``Underscore inside italic is preserved`` () = + let doc = "_fsharp_space_after_comma_" + + let expected = + [ Paragraph( + [ Emphasis( + [ Literal( + "fsharp_space_after_comma", + Some( + { StartLine = 1 + StartColumn = 0 + EndLine = 1 + EndColumn = 24 } + ) + ) ], + Some( + { StartLine = 1 + StartColumn = 0 + EndLine = 1 + EndColumn = 26 } + ) + ) ], + Some( + { StartLine = 1 + StartColumn = 0 + EndLine = 1 + EndColumn = 26 } + ) + ) ] + + (Markdown.Parse doc).Paragraphs |> shouldEqual expected + +[] +let ``Underscores inside word in heading`` () = + let doc = + """ +### fsharp_bar_before_discriminated_union_declaration + +Always use a bar before every case in the declaration of a discriminated union. +""" + + let expected = + [ Heading( + 3, + [ Literal( + "fsharp_bar_before_discriminated_union_declaration", + Some + { StartLine = 2 + StartColumn = 4 + EndLine = 2 + EndColumn = 53 } + ) ], + Some + { StartLine = 2 + StartColumn = 0 + EndLine = 2 + EndColumn = 53 } + ) + Paragraph( + [ Literal( + "Always use a bar before every case in the declaration of a discriminated union.", + Some + { StartLine = 4 + StartColumn = 0 + EndLine = 4 + EndColumn = 79 } + ) ], + Some + { StartLine = 4 + StartColumn = 0 + EndLine = 4 + EndColumn = 79 } + ) ] + + (Markdown.Parse doc).Paragraphs |> shouldEqual expected + +[] +let ``Underscore inside italic and bold near punctuation is preserved`` () = + let doc = "This is **bold_bold**, and this _italic_; and _this_too_: again." + + let expected = + "

This is bold_bold, and this italic; and this_too: again.

\r\n" + |> properNewLines + + Markdown.ToHtml doc |> shouldEqual expected