Skip to content

Invalid rendering of a list followed by another block #371

@yannham

Description

@yannham

We're using comrak to build a markdown document programmatically and output it to a file (as markdown). At some point, we produce a list of items, where each item is a code snippet contained in a NodeValue::Code.

The NodeValue::Code nodes are directly added to the list item's children, which are themselves added to the NodeValue::List node. Then some other content is appended.

When rendered with format_markdown, comrak doesn't properly insert a new line as a separation between the list and the next element. If the next element turns out to be e.g. a Paragraph, then it is rendered as:

- `foo : Number`
- `foo | Even`
rest

instead of

- `foo : Number`
- `foo | Even`

rest

This is an issue, because the first one is different semantically, being equivalent under the commonmark spec to:

- `foo : Number`
- `foo | Even` rest

Here is a debug print (in comrak 0.17.0) of the AST being invalidly rendered:

test2.log

Looking at the rendering code in cm.rs, I found that strange bit:

comrak/src/cm.rs

Lines 407 to 421 in 26ad754

fn format_list(&mut self, node: &'a AstNode<'a>, entering: bool) {
if !entering
&& match node.next_sibling() {
Some(next_sibling) => matches!(
next_sibling.data.borrow().value,
NodeValue::CodeBlock(..) | NodeValue::List(..)
),
_ => false,
}
{
self.cr();
write!(self, "<!-- end list -->").unwrap();
self.blankline();
}
}

It turns out the renderer only inserts a newline after a list when the next node is code or another list. I don't really see why: we should always insert a new line I believe (excepted when the list is the last element of the children of a block).

However, the bug above doesn't occur when parsing and re-emitting markdown from a source file, e.g. with the comrak binary. After some investigation, it seemss that comrak always parse list items as NodeValue::Paragraph. Paragraph does insert a new line after itself:

self.blankline();

However, when the list is tight, I think this bunch of code cancels out the newline inserted by paragraphs:

self.need_cr = 1;
. I'm not 100% sure, but I feel like for some reason the last item doesn't (although the in_tight_list_item doesn't seem to depend on the position of the item in the list). Or some other mechanism which let the self.need_cr to 2 and properly insert the required line.

In any case, one possible workaround is to always wrap the content of list items in a NodeValue::Paragraph. However, it feels a bit fragile, and I think the renderer should always emit a new line, instead of relying on the unspoken invariant that list items are wrapped in paragraphs to function properly.

If maintainers agree with the baseline, I can tentatively submit a patch.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions