Skip to content

fix: better list children rendering logic#478

Draft
eszlamczyk wants to merge 16 commits into
mainfrom
fix/243/better-list-children-rendering-logic
Draft

fix: better list children rendering logic#478
eszlamczyk wants to merge 16 commits into
mainfrom
fix/243/better-list-children-rendering-logic

Conversation

@eszlamczyk

@eszlamczyk eszlamczyk commented Jul 1, 2026

Copy link
Copy Markdown
Collaborator

What/Why?

Fixes #243 — lists containing block children (fenced code blocks, nested sublists) rendered broken: markers
were lost or drawn inside code blocks, checked-task strikethrough bled across code, and code blocks ignored
list indentation.

Root cause: list styling was applied post-hoc — children rendered list-ignorant, then ListItemRenderer
stamped indent/marker metadata over the result, which broke whenever an item's first child wasn't plain text.
This PR moves list layout to a top-down model:

Shared parser (packages/core/cpp)

  • wrapListItemInlineRuns post-pass: md4c emits bare inline children for tight list items; they're now wrapped
    in synthetic Paragraph nodes (marked tight), so renderers only ever see block children. One change covers
    iOS, Android (JNI) and web (rebuilt md4c.js; web renderers needed no changes).

iOS

  • BaseRenderer is now a template method (renderNode: = snapshot scope → renderNodeContent: → restore).
    RenderContext.snapshotScope/restoreScope replaces the per-renderer @try/@finally partial restores and stops
    block-style state leaking between sibling blocks.
  • RenderContext.accumulatedIndent: ListItemRenderer pushes the item indent around its children;
    CodeBlockRenderer folds it into its paragraph style and CodeBlockBackground insets the background rect —
    code blocks in list items now render indented under the item content (CommonMark behavior).
  • Markers are self-contained ENRMListMarkerDescriptors planted on the item's first content character
    (appending when one already exists). ListMarkerDrawer draws every descriptor at its own gutter — fixes
    missing markers on code-block-first items, draws both markers (browser-style) for sublist-first items, and no
    longer duplicates markers per paragraph in loose items.

Android (mirror)

  • BlockStyleContext.accumulatedIndentCodeBlockSpan.leadingIndent (leading margin + background inset).
  • BaseListSpan split into segments around code blocks (keeps the metric-affecting span off code text), single
    drawsMarker owner per item; when the item starts with a code block/sublist, a zero-margin
    ListMarkerAnchorSpan draws the marker instead.
  • applyCheckedDecorations excludes code-block ranges — no more strikethrough across code in checked tasks;
    inline-code backgrounds and line-height spans are list-margin aware.

Not in scope (pre-existing): ordered-list start is ignored by the parser (1. + nested 2. x renders 1. 1. x).

Testing

  • e2e suites green on both platforms; targeted re-run: yarntest:e2e:mobile --include-tags list,code_block,task_list.
  • 5 new e2e fixtures: loose/tight list×code-block combos, code-block-first items, sublist-first item, checked task containing a code block (previously uncovered bug class).
  • Manual verification via example app (screenshots) (edge cases mirrored from the fixtures).

Screenshots

Before After

PR Checklist

  • Code compiles and runs on iOS
  • Code compiles and runs on Android
  • Updated documentation/README if applicable
  • Ran example app to verify changes
  • E2E tests are passing
  • Required E2E tests have been added (if applicable)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Lists not rendering correctly

1 participant