Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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 @@ -14,6 +14,7 @@
* Strip `#if SYMBOL` / `#endif // SYMBOL` marker lines from `LiterateCode` source before syntax-highlighting so they do not appear in formatted output. [#693](https://github.com/fsprojects/FSharp.Formatting/issues/693)
* Fix incorrect column ranges for inline spans (links, images, inline code) in the Markdown parser β€” spans and subsequent literals now report correct `StartColumn`/`EndColumn` values. [#744](https://github.com/fsprojects/FSharp.Formatting/issues/744)
* Normalize `--projects` paths to absolute paths before passing to the project cracker, fixing failures when relative paths are supplied. [#793](https://github.com/fsprojects/FSharp.Formatting/issues/793)
* Improve CommonMark compliance for ATX headings: reject `#` not followed by a space (e.g. `#NoSpace` is now a paragraph), reject more than 6 `#` characters as a heading, support 0–3 leading spaces before the opening `#` sequence, and fix empty content when the entire header body is a closing `###` sequence. [#191](https://github.com/fsprojects/FSharp.Formatting/issues/191)

### Changed
* Update FCS to 43.10.100. [#935](https://github.com/fsprojects/FSharp.Formatting/pull/966)
Expand Down
54 changes: 41 additions & 13 deletions src/FSharp.Formatting.Markdown/MarkdownParser.fs
Original file line number Diff line number Diff line change
Expand Up @@ -572,21 +572,49 @@
| ((StringPosition.TrimBoth header) as line1) :: ((StringPosition.TrimEnd(StringPosition.EqualsRepeated("-",
MarkdownRange.zero))) as line2) :: rest ->
Some(2, header, [ line1; line2 ], rest)
| (StringPosition.StartsWithRepeated "#" (n, StringPosition.TrimBoth(header, ln)) as line1) :: rest ->
let header =
// Drop "##" at the end, but only when it is preceded by some whitespace
// (For example "## Hello F#" should be "Hello F#")
if header.EndsWith '#' then
let noHash = header.TrimEnd [| '#' |]

if noHash.Length > 0 && Char.IsWhiteSpace(noHash.Chars(noHash.Length - 1)) then
noHash
| ((line1text, ln1) as line1) :: rest ->
// ATX heading (CommonMark): optional 0–3 leading spaces, then 1–6 '#' characters,
// then a space or end of line (a tab or other char after '#' is not valid).
let mutable i = 0

while i < 3 && i < line1text.Length && line1text.[i] = ' ' do
i <- i + 1

let hstart = i

while i < line1text.Length && line1text.[i] = '#' do
i <- i + 1

let n = i - hstart

if n < 1 || n > 6 || (i < line1text.Length && line1text.[i] <> ' ') then
None
else
let contentStart = if i < line1text.Length then i + 1 else i
let content = (line1text.Substring(contentStart)).TrimEnd()
// Remove optional closing sequence of '#' preceded by space (or empty '#'-only content).
// For example "## Hello F#" keeps the '#' because it is not preceded by a space.
let header =
if content.EndsWith('#') then
let noHash = content.TrimEnd([| '#' |])

if noHash = "" || (noHash.Length > 0 && noHash.[noHash.Length - 1] = ' ') then
Comment thread Dismissed
noHash.Trim()
else
content.Trim()
else
header
else
header
content.Trim()

let rawContent = line1text.Substring(contentStart)
let leadingContentSpaces = rawContent.Length - rawContent.TrimStart(' ').Length
let headerStart = ln1.StartColumn + contentStart + leadingContentSpaces

let headerLn =
{ ln1 with
StartColumn = headerStart
EndColumn = headerStart + header.Length }

Some(n, (header, ln), [ line1 ], rest)
Some(n, (header, headerLn), [ line1 ], rest)
| _rest -> None

let (|YamlFrontmatter|_|) lines =
Expand Down
2 changes: 1 addition & 1 deletion tests/Benchmarks/testfiles/mstest-0.1/line-endings-cr.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<h2>Header</h2>
<p>##Header</p>

<hr />

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<h2>Header</h2>
<p>##Header</p>

<hr />

Expand Down
2 changes: 1 addition & 1 deletion tests/Benchmarks/testfiles/mstest-0.1/line-endings-lf.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<h2>Header</h2>
<p>##Header</p>

<hr />

Expand Down
13 changes: 6 additions & 7 deletions tests/Benchmarks/testfiles/php-markdown/Tight blocks.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,19 @@
* ciao</p>

<p>Paragraph and 1 space:
* ciao</p>
* ciao</p>

<p>Paragraph and 3 spaces:
* ciao</p>
* ciao</p>

<p>Paragraph and 4 spaces:
* ciao</p>

<p>Paragraph before header:</p>
* ciao</p>

<h1>Header</h1>
<p>Paragraph before header:
#Header</p>

<p>Paragraph before blockquote:</p>

<blockquote>
<p>Some quote.</p>
<p>Some quote.</p>
</blockquote>
12 changes: 11 additions & 1 deletion tests/FSharp.Markdown.Tests/CommonMarkSpecTest.fs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,17 @@ open FSharp.Formatting.Markdown
let properNewLines (text: string) =
text.Replace("\r\n", "\n").Replace("\n", System.Environment.NewLine)

let enabledSections = [ "Fenced code blocks"; "Indented code blocks"; "Paragraphs"; "Precedence"; "Tabs" ]
let enabledSections =
[ "Fenced code blocks"
"Indented code blocks"
"Paragraphs"
"Precedence"
"Tabs"
"Blank lines"
"Inlines"
"Soft line breaks"
"Textual content"
"ATX headings" ]

let getTests () =
sample
Expand Down
4 changes: 2 additions & 2 deletions tests/FSharp.Markdown.Tests/Markdown.fs
Original file line number Diff line number Diff line change
Expand Up @@ -582,15 +582,15 @@ let ``Transform bulleted lists correctly`` () =
let ``Transform header 1 correctly`` () =
let doc = "#Header 1\nHeader 1\r\n========"

let expected = "<h1>Header 1</h1>\r\n<h1>Header 1</h1>\r\n" |> properNewLines
let expected = "<p>#Header 1</p>\r\n<h1>Header 1</h1>\r\n" |> properNewLines

Markdown.ToHtml doc |> shouldEqual expected

[<Test>]
let ``Transform header 2 correctly`` () =
let doc = "##Header 2\nHeader 2\r\n--------"

let expected = "<h2>Header 2</h2>\r\n<h2>Header 2</h2>\r\n" |> properNewLines
let expected = "<p>##Header 2</p>\r\n<h2>Header 2</h2>\r\n" |> properNewLines

Markdown.ToHtml doc |> shouldEqual expected

Expand Down