Skip to content

ENABLE_HEADING_ATTRIBUTES makes an ATX heading swallow the next line when it ends with a lone CR #1132

Description

@chattersley

With ENABLE_HEADING_ATTRIBUTES, an ATX heading terminated by a bare \r absorbs the following line. \n and \r\n are unaffected.

Minimal case

use pulldown_cmark::{Options, Parser};

let src = "# a\r$";
for (event, range) in Parser::new_ext(src, Options::empty()).into_offset_iter() {
    println!("{}..{} {event:?}", range.start, range.end);
}
println!("--");
for (event, range) in Parser::new_ext(src, Options::ENABLE_HEADING_ATTRIBUTES).into_offset_iter() {
    println!("{}..{} {event:?}", range.start, range.end);
}
0..4 Start(Heading { level: H1, .. })
2..3 Text("a")
0..4 End(Heading(H1))
4..5 Start(Paragraph)
4..5 Text("$")
4..5 End(Paragraph)
--
0..5 Start(Heading { level: H1, .. })
2..5 Text("a\r$")
0..5 End(Heading(H1))

The heading's range grows from 0..4 to 0..5 and its text event becomes "a\r$". The second line is gone as a block.

A variant where the heading has no text loses the line to syntax rather than to text, which is worse for a consumer that conceals markers:

"#\u{b}\r$"
  off: Heading 0..3, then Paragraph "$"
  on:  Heading 0..4, no Text event at all

Why it matters here

I maintain a Markdown editor that decorates the source in place rather than rendering it, so I use the offsets to decide which bytes are syntax. Lone CRs reach the parser routinely — the editor preserves whatever line endings a file arrives with and only converts them when asked. With this flag on, a heading followed by a CR-terminated line either absorbs that line's text into the heading or conceals it entirely.

Found by a property test over a delimiter alphabet, minimised to the five characters above. Versions: pulldown-cmark 0.13.4.

I have left the flag off for now rather than work around it, since the workaround would be re-deriving the heading's extent myself.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions