Skip to content

fix: keep a list item's non-paragraph first child nested on save - #248

Merged
Azganoth merged 1 commit into
mainfrom
bug/list-item-first-block-nesting
Aug 16, 2026
Merged

fix: keep a list item's non-paragraph first child nested on save#248
Azganoth merged 1 commit into
mainfrom
bug/list-item-first-block-nesting

Conversation

@Azganoth

Copy link
Copy Markdown
Owner

Summary

  • A list item whose first child is a non-paragraph block lost its nesting on save. The item serialized as a bare marker, a blank line, and the indented block, and CommonMark allows an item to begin with at most one blank line, so the next open produced an empty item and a detached top-level block.
  • The cause is upstream of serialization. The list_item schema is paragraph block*, so parsing such an item fills an empty paragraph ahead of the block, and the serializer faithfully writes that paragraph as the blank marker line. Serializing the item without the filled paragraph puts the block back on the marker line, which reparses inside the item.
  • Task items keep the filled paragraph. GFM writes the checkbox into the item's first paragraph and drops it wherever that paragraph is absent, and no GFM shape expresses a task item whose first block is a fence, so those keep today's behavior rather than trading nesting loss for checkbox loss.
  • The override targets the GFM task extension's schema slice: extendSchema creates a new slice rather than updating the commonmark one, so an override on listItemSchema never reaches the schema the editor holds.

Related Issue

Closes #247

Verification

markdownCompatibility.test.tsx covers a list item whose first child is fenced code, a table, a blockquote, a nested list, a heading, or a thematic break, under both ordered and unordered markers, each asserting the source serializes back unchanged. Indented code is covered separately since it normalizes to fenced, and an empty list item is asserted to stay a bare marker.

Convergence over corpus/commonmark/, corpus/gfm/, and corpus/isolated/end-of-file/ was measured with the prototype from #135, run against the branch and not committed: 13 of 16 files converged before the change and all 16 after, with commonmark/lists-and-blockquotes.md, commonmark/text-and-breaks.md, and gfm/tables.md as the three that moved. The same run covered the fourteen first-child cases above; one converged before, all fourteen after.

Not verified: no manual pass in the desktop app. The save path writes getMilkdownEditorMarkdown output verbatim, which is the function the tests call.

Notes

  • The filled paragraph still exists in the document, so such an item continues to show an empty first line in the editor. Rendering is out of scope here, per the issue.
  • An empty first paragraph typed inside a list item that holds further blocks is now dropped on save. CommonMark cannot express it, and today that document already loses everything after it.
  • Changing the schema to block+ would remove the filled paragraph at the source, but Milkdown's own splitListItem, liftListItem, and sinkListItem assume the paragraph-first shape. Not attempted here.
  • The convergence test over corpus/ stays with Determine whether the Markdown corpus can drive automated round-trip tests #135 as the follow-up that guards this fix.

@Azganoth Azganoth added the Bug Something isn't working label Aug 16, 2026
@Azganoth Azganoth self-assigned this Aug 16, 2026
@Azganoth
Azganoth merged commit 7fe59df into main Aug 16, 2026
3 checks passed
@Azganoth
Azganoth deleted the bug/list-item-first-block-nesting branch August 16, 2026 07:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

A list item's non-paragraph first child loses its nesting on save

1 participant