Skip to content

fix(md): parse space+tab indented sublists as nested list items#10703

Merged
ematipico merged 6 commits into
biomejs:mainfrom
xsourabhsharma:fix/md-space-tab-sublist
Jun 24, 2026
Merged

fix(md): parse space+tab indented sublists as nested list items#10703
ematipico merged 6 commits into
biomejs:mainfrom
xsourabhsharma:fix/md-space-tab-sublist

Conversation

@xsourabhsharma

@xsourabhsharma xsourabhsharma commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

AI assistance: This PR was developed with guidance from Claude.

Closes #10558.

Summary

Space+tab-indented sublists were parsed as lazy paragraph continuation instead of nested list items. The fix makes tab expansion column-relative per CommonMark 2.2, and detects sibling list markers at the enclosing item's marker indent.

  * Text

  	- foo
- 	- lreum
- 	- bar
+ 	- lreum
+ 	- bar

Before, only the first - foo became a list item; lreum and bar were absorbed as paragraph text. They are now three sibling <li> items.

Test Plan

  • cargo test -p biome_markdown_parser (incl. new regression test sublist_loose_space_tab_indent.md)
  • cargo test -p biome_markdown_formatter
  • cargo test -p biome_markdown_factory

All pass. Three formatter snapshots (tab.md, nested-tab.md, indent.md) were previously capturing formatter bugs caused by this parser bug; they now match Prettier.

Note: the pure 4-space indent variant ( - foo / - lreum) has a separate pre-existing issue (siblings nesting one level too deep) that is out of scope here.

Tabs were counted as a flat 4 columns and sibling list markers were only matched at the item content indent, so space+tab-indented sublist items were absorbed as lazy paragraph continuation (biomejs#10558).

Make tab expansion column-relative per CommonMark 2.2 (TAB_STOP_SPACES minus column mod TAB_STOP_SPACES) and add at_sibling_list_marker to detect sibling bullet and ordered markers at the enclosing item marker indent.

The issue input now parses as a single nested ul with three li children. Parser suite (169 spec tests incl. a new regression test), formatter suite (817 tests) and factory tests all pass. The three formatter snapshots that changed were documenting formatter bugs caused by this parser bug and now match Prettier.
@changeset-bot

changeset-bot Bot commented Jun 19, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: ab06831

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@github-actions

Copy link
Copy Markdown
Contributor

✅ Organic activity

No automation signals detected in the analyzed events.

View full analysis →

This is an automated analysis by AgentScan

@github-actions github-actions Bot added A-Parser Area: parser A-Formatter Area: formatter L-Markdown Language: Markdown labels Jun 19, 2026
@coderabbitai

coderabbitai Bot commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 11ac46cf-5e9a-4548-bd7b-afaa3317b175

📥 Commits

Reviewing files that changed from the base of the PR and between 8aa8a54 and ab06831.

📒 Files selected for processing (3)
  • crates/biome_markdown_parser/src/syntax/list.rs
  • crates/biome_markdown_parser/src/syntax/mod.rs
  • crates/biome_markdown_parser/src/token_source.rs
🚧 Files skipped from review as they are similar to previous changes (3)
  • crates/biome_markdown_parser/src/syntax/mod.rs
  • crates/biome_markdown_parser/src/token_source.rs
  • crates/biome_markdown_parser/src/syntax/list.rs

Walkthrough

The Markdown parser gains CommonMark-compliant tab-stop expansion: line_start_leading_indent now advances the running column to the next multiple of TAB_STOP_SPACES per tab rather than using a fixed 4-space increment. compute_marker_indent is extended to raw-scan leading whitespace from the physical line start when a tab is present. A new at_sibling_list_marker predicate checks whether the current position is a sibling list marker at the enclosing item's exact marker indent. at_block_interrupt is wired to call this predicate, preventing sibling markers from being consumed as lazy paragraph continuation. Three test fixtures covering space+tab, two-space+tab, and three-space+tab sublist indentation are added.

Possibly related PRs

  • biomejs/biome#9717: Modifies compute_marker_indent and list continuation/interrupt detection, directly overlapping with the same functions changed here.
  • biomejs/biome#9776: Fixes lazy continuation ownership when line indent equals a nested list marker indent, touching the same list continuation logic in list.rs.
  • biomejs/biome#10376: Adjusts sibling/parent interruption logic in at_block_interrupt to break paragraphs at enclosing marker indents, directly paralleling the change in this PR.

Suggested reviewers

  • ematipico
  • dyc3
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly matches the main change: fixing space+tab-indented sublists to parse as nested list items.
Description check ✅ Passed The description is directly about the parser bug, the CommonMark tab fix, and the new regression test.
Linked Issues check ✅ Passed The changes address #10558 by treating the example as nested bullet list items instead of lazy paragraph text.
Out of Scope Changes check ✅ Passed The extra test fixture and formatter snapshot updates are supporting the fix, with no unrelated changes apparent.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codspeed-hq

codspeed-hq Bot commented Jun 19, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 28 untouched benchmarks
⏩ 228 skipped benchmarks1


Comparing xsourabhsharma:fix/md-space-tab-sublist (ab06831) with main (f62fb8b)2

Open in CodSpeed

Footnotes

  1. 228 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

  2. No successful run was found on main (f719af6) during the generation of this report, so f62fb8b was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

@xsourabhsharma xsourabhsharma marked this pull request as draft June 19, 2026 12:53
@xsourabhsharma xsourabhsharma marked this pull request as ready for review June 20, 2026 02:40
@xsourabhsharma xsourabhsharma marked this pull request as draft June 20, 2026 03:08
compute_marker_indent now scans through leading tabs (column-relative) to the marker when the line's leading whitespace contains a tab, so 2space+tab and 3space+tab indents that expand to column 4 are measured as 4 rather than as the space count only. Pure-space lines keep the original behavior. Adds regression tests for both variants. All fuzz genLooseTabSublist cases (space+tab, 2space+tab, 3space+tab, pure tab) now parse as three sibling list items.
Replace the pure-space char-count loop with chars().count() so the loop variable is no longer unused. Behavior is unchanged.
@xsourabhsharma xsourabhsharma marked this pull request as ready for review June 20, 2026 06:53

@ematipico ematipico left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, but the docs need some. Please look after them

Comment on lines +1848 to +1852
// A sibling list item sits at the enclosing item's *marker* indent,
// which can be below its content (`required`) indent when the marker
// is followed by more than one space (or a tab). Detect siblings at
// the marker indent too, otherwise such lines are absorbed as lazy
// paragraph continuation (biomejs/biome#10558).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you understand this comment? What does the mention of "required" mean? Also, why is there a mention of an issue? We're fixing a bug.

Comment on lines +113 to +117
/// Tabs expand column-relative to the next multiple of `TAB_STOP_SPACES` (4),
/// per CommonMark §2.2. Counting a tab as a flat 4 would over-count when the
/// tab does not start at a column that is already a multiple of 4 (e.g. a
/// leading ` \t` is 4 columns, not 5), which mis-classifies space+tab-indented
/// sub-list markers as lazy paragraph continuation (biomejs/biome#10558).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you read the comment? This isn't documentation, it's Claude Code writing a diary. Please reword it so it's sounds like proper documentation. There's also no need to track the issue. We're fixing a bug.

@xsourabhsharma

Copy link
Copy Markdown
Contributor Author

give me 20min i think need to do myself.

@ematipico ematipico merged commit 13ee358 into biomejs:main Jun 24, 2026
31 checks passed
@xsourabhsharma xsourabhsharma deleted the fix/md-space-tab-sublist branch June 24, 2026 12:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-Formatter Area: formatter A-Parser Area: parser L-Markdown Language: Markdown

Projects

None yet

Development

Successfully merging this pull request may close these issues.

🐛 [md] Incorrect parsing of sub lists

2 participants