Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
* Bump `System.Memory` transitive-dependency pin from 4.5.5 to 4.6.3.0

### Fixed
* Fix `Markdown.ToMd` converting tight lists (no blank lines between items) into loose lists by emitting a blank line after every item. Tight lists now round-trip correctly without inter-item blank lines.
* Fix `Markdown.ToMd` serialising `HardLineBreak` as a bare newline instead of two trailing spaces + newline. The correct CommonMark representation `" \n"` is now emitted, so hard line breaks survive a round-trip through `ToMd`.
* Fix `Markdown.ToMd` serialising `HorizontalRule` as 23 hyphens regardless of the character used in the source. It now emits exactly three characters matching the parsed character (`---`, `***`, or `___`), giving faithful round-trips.
* Remove stray `printfn` debug output emitted to stdout when `Markdown.ToMd` encountered an unrecognised paragraph type.
Expand Down
22 changes: 22 additions & 0 deletions src/FSharp.Formatting.Markdown/MarkdownUtils.fs
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,13 @@ module internal MarkdownUtils =

yield ""
| ListBlock(Unordered, paragraphsl, _) ->
// A tight list has exactly one Span per item (no blank lines between items).
let isTight =
paragraphsl
|> List.forall (function
| [ Span _ ] -> true
| _ -> false)

for paragraphs in paragraphsl do
for (i, paragraph) in List.indexed paragraphs do
let lines = formatParagraph ctx paragraph
Expand All @@ -210,8 +217,19 @@ module internal MarkdownUtils =
else
yield " " + line

if not isTight then
yield ""

if isTight then
yield ""
| ListBlock(Ordered, paragraphsl, _) ->
// A tight list has exactly one Span per item (no blank lines between items).
let isTight =
paragraphsl
|> List.forall (function
| [ Span _ ] -> true
| _ -> false)

for (n, paragraphs) in List.indexed paragraphsl do
for (i, paragraph) in List.indexed paragraphs do
let lines = formatParagraph ctx paragraph
Expand All @@ -223,7 +241,11 @@ module internal MarkdownUtils =
else
yield " " + line

if not isTight then
yield ""

if isTight then
yield ""
| TableBlock(headers, alignments, rows, _) ->

match headers with
Expand Down
25 changes: 25 additions & 0 deletions tests/FSharp.Markdown.Tests/Markdown.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1303,6 +1303,31 @@ let ``ToMd preserves an unordered list`` () =
result |> should contain "banana"
result |> should contain "cherry"

[<Test>]
let ``ToMd preserves tight unordered list without blank lines between items`` () =
// A tight list should not gain blank lines between items on round-trip.
let md = "* apple\n* banana\n* cherry"
let result = toMd md
// Tight list: no blank line between consecutive items
result |> should not' (contain "* apple\n\n* banana")

[<Test>]
let ``ToMd preserves tight ordered list without blank lines between items`` () =
// A tight ordered list should not gain blank lines between items on round-trip.
let md = "1. first\n2. second\n3. third"
let result = toMd md
// Tight list: no blank line between consecutive items
result |> should not' (contain "1. first\n\n2. second")

[<Test>]
let ``ToMd preserves loose list with blank lines between items`` () =
// A loose list (items separated by blank lines) should keep blank lines.
let md = "* alpha\n\n* beta\n\n* gamma"
let result = toMd md
result |> should contain "alpha"
result |> should contain "beta"
result |> should contain "gamma"

[<Test>]
let ``ToMd preserves emphasis (italic) text`` () =
// Emphasis must serialise as *...* not **...** (bold)
Expand Down