Skip to content

perf(md/parse): restore parser state via checkpoint - #11257

Merged
ematipico merged 2 commits into
mainfrom
perf/md-parser-state
Aug 7, 2026
Merged

perf(md/parse): restore parser state via checkpoint#11257
ematipico merged 2 commits into
mainfrom
perf/md-parser-state

Conversation

@ematipico

Copy link
Copy Markdown
Member

Summary

  • This PR fixes a problem where the checkout of the parser didn't restore its state after the lookahead.
  • Better before starting phase 2 of the parser, by making sure that the stream of events is correct and has correct ranges. This is done in debug mode only
  • Added a defensive DeferredInlineStart which makes sure that the deferred strategy isn't miused

Test Plan

Green CI

Docs

N/A

@changeset-bot

changeset-bot Bot commented Aug 6, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 8814837

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

@codspeed-hq

codspeed-hq Bot commented Aug 6, 2026

Copy link
Copy Markdown

Merging this PR will improve performance by 53.34%

⚡ 26 improved benchmarks
✅ 2 untouched benchmarks
⏩ 258 skipped benchmarks1

Performance Changes

Benchmark BASE HEAD Efficiency
synthetic/emphasis-heavy.md[cached] 2.3 ms 1.3 ms +85.45%
synthetic/inline-html.md[cached] 1,135.5 µs 621.2 µs +82.78%
synthetic/emphasis-heavy.md[uncached] 2.4 ms 1.4 ms +74.31%
synthetic/inline-html.md[uncached] 1,154.5 µs 667.7 µs +72.91%
synthetic/nested-lists.md[cached] 5.6 ms 3.3 ms +70.85%
real/readme-style.md[cached] 2.9 ms 1.7 ms +69.99%
synthetic/nested-lists.md[uncached] 5.6 ms 3.3 ms +68.82%
spec/inline-html.md[cached] 1,536.2 µs 914.3 µs +68.03%
synthetic/blockquotes-nested.md[cached] 3 ms 1.8 ms +66.53%
real/readme-style.md[uncached] 2.9 ms 1.8 ms +63.74%
spec/emphasis.md[cached] 3.1 ms 1.9 ms +63.2%
synthetic/blockquotes-nested.md[uncached] 3.1 ms 1.9 ms +63.09%
spec/inline-html.md[uncached] 1,548.4 µs 962.7 µs +60.84%
synthetic/long-paragraphs.md[cached] 2 ms 1.3 ms +59.79%
spec/emphasis.md[uncached] 3.1 ms 1.9 ms +59.58%
spec/blockquotes.md[cached] 529.4 µs 333.3 µs +58.82%
spec/autolinks.md[cached] 1,458 µs 938 µs +55.44%
synthetic/long-paragraphs.md[uncached] 2.1 ms 1.3 ms +54.69%
spec/autolinks.md[uncached] 1,453.8 µs 957.5 µs +51.83%
spec/blockquotes.md[uncached] 521.4 µs 349.2 µs +49.31%
... ... ... ... ...

ℹ️ Only the first 20 benchmarks are displayed. Go to the app to view all benchmarks.

Tip

Curious why this is faster? Comment @codspeedbot explain why this is faster on this PR, or directly use the CodSpeed MCP with your agent.


Comparing perf/md-parser-state (8814837) with main (caefe39)2

Open in CodSpeed

Footnotes

  1. 258 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 (1d24304) during the generation of this report, so caefe39 was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

Base automatically changed from refactor/two-phase-md-parse to main August 6, 2026 18:22
@ematipico
ematipico force-pushed the perf/md-parser-state branch from ce78d01 to 1b29623 Compare August 6, 2026 18:22
@coderabbitai

coderabbitai Bot commented Aug 6, 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: 173535f3-ce5d-460b-98e0-ab4a2c86e8fb

📥 Commits

Reviewing files that changed from the base of the PR and between 1d24304 and 8814837.

⛔ Files ignored due to path filters (1)
  • crates/biome_markdown_parser/tests/md_test_suite/ok/empty_atx_heading.md.snap is excluded by !**/*.snap and included by **
📒 Files selected for processing (8)
  • crates/biome_markdown_parser/src/inline_phase.rs
  • crates/biome_markdown_parser/src/lib.rs
  • crates/biome_markdown_parser/src/parser.rs
  • crates/biome_markdown_parser/src/syntax/header.rs
  • crates/biome_markdown_parser/src/syntax/inline/emphasis.rs
  • crates/biome_markdown_parser/src/syntax/mod.rs
  • crates/biome_markdown_parser/src/to_html.rs
  • crates/biome_markdown_parser/tests/md_test_suite/ok/empty_atx_heading.md
🚧 Files skipped from review as they are similar to previous changes (8)
  • crates/biome_markdown_parser/tests/md_test_suite/ok/empty_atx_heading.md
  • crates/biome_markdown_parser/src/lib.rs
  • crates/biome_markdown_parser/src/syntax/header.rs
  • crates/biome_markdown_parser/src/parser.rs
  • crates/biome_markdown_parser/src/syntax/inline/emphasis.rs
  • crates/biome_markdown_parser/src/inline_phase.rs
  • crates/biome_markdown_parser/src/syntax/mod.rs
  • crates/biome_markdown_parser/src/to_html.rs

Walkthrough

The Markdown parser now stores complete parser checkpoints and shared emphasis contexts. Paragraph and ATX heading parsing use deferred-inline start and finish methods. Deferred fragments are reparsed in place, with validation for event ranges, source boundaries, subtree completeness, token alignment, and definition counts. Invalid fragments return failure. Diagnostics and trivia merge into the existing output. No-progress recovery now emits MD_BOGUS_BLOCK and clears link-reference continuation state. Tests cover these behaviours and reference links in ATX headings.

Possibly related PRs

  • biomejs/biome#11256: This PR extends and hardens the deferred-inline parsing flow in the same parser areas.

Suggested reviewers: dyc3

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the primary change: restoring Markdown parser state through checkpoints.
Description check ✅ Passed The description explains parser state restoration, debug validation, and defensive deferred parsing changes.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch perf/md-parser-state

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (3)
crates/biome_markdown_parser/src/parser.rs (1)

532-552: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add rustdoc for the deferred-inline start/finish pair.

start_deferred_inline and finish_deferred_inline now form a paired API, and DeferredInlineStart carries the invariant that the caller must call finish_deferred_inline at the exact end of the inline region. That invariant is not recoverable from the names and types. Document it on both methods, including the fact that the recorded event_range must contain the complete subtree expected by the flavour.

As per coding guidelines: "For developer-facing comments, explain behavior, invariants, panics, module rationale, or non-obvious rationale" and "Use rustdoc documentation for documenting new features".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/biome_markdown_parser/src/parser.rs` around lines 532 - 552, Add
rustdoc to both start_deferred_inline and finish_deferred_inline documenting
that they must be used as a pair, with finish_deferred_inline called at the
exact end of the inline region. Describe that the resulting event_range must
contain the complete subtree required by the selected DeferredInlineFlavor.

Source: Coding guidelines

crates/biome_markdown_parser/src/to_html.rs (1)

2123-2126: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider a separate test for the heading case.

This assertion covers a reference link inside an ATX heading with trailing hard-break spaces. That is the new deferred AtxParagraph path, not emphasis scoping, so the enclosing test name no longer describes all of its content. A dedicated test makes the failure attribution clearer if the heading path regresses.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/biome_markdown_parser/src/to_html.rs` around lines 2123 - 2126,
Extract the ATX heading reference-link assertion from the existing test into a
dedicated test focused on deferred AtxParagraph heading rendering, leaving the
existing test scoped to emphasis behavior. Preserve the assertion input and
expected HTML exactly so regressions identify the heading path separately.
crates/biome_markdown_parser/src/inline_phase.rs (1)

336-374: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Two assertions in these tests cannot fail.

validate_deferred_inlines takes &MarkdownParserOutput, so output.events.len() and output.deferred_inlines.len() cannot change. The names promise that the output is left unchanged, but the checks are tautological, and a reader may believe the mutating path is covered. Either drop the two length assertions, or call parse_deferred_inlines with a mutable output so the "leaves output unchanged" claim is actually tested.

The name invalid_deferred_range_leaves_output_unchanged is also slightly off: 0..3 is a valid range for three events, and the rejection comes from the non-char-boundary source slice on "é".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/biome_markdown_parser/src/inline_phase.rs` around lines 336 - 374,
Remove the tautological events and deferred_inlines length assertions from
invalid_deferred_range_leaves_output_unchanged, since validate_deferred_inlines
receives an immutable output. Rename the test to describe rejection caused by
the invalid UTF-8 character boundary in the source slice, and keep the existing
validation assertion.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/biome_markdown_parser/src/inline_phase.rs`:
- Around line 72-79: Add release-safe validation to the replacement loop around
`event_range` before subtracting or consuming events, rejecting overlapping,
out-of-order, or invalid half-open ranges. On invalid input, fail gracefully
using the same behavior as the existing fragment path, while
preserving/restoring `output.events` if it has already been taken; do not rely
on the debug-only `validate_deferred_inlines` assertion.

---

Nitpick comments:
In `@crates/biome_markdown_parser/src/inline_phase.rs`:
- Around line 336-374: Remove the tautological events and deferred_inlines
length assertions from invalid_deferred_range_leaves_output_unchanged, since
validate_deferred_inlines receives an immutable output. Rename the test to
describe rejection caused by the invalid UTF-8 character boundary in the source
slice, and keep the existing validation assertion.

In `@crates/biome_markdown_parser/src/parser.rs`:
- Around line 532-552: Add rustdoc to both start_deferred_inline and
finish_deferred_inline documenting that they must be used as a pair, with
finish_deferred_inline called at the exact end of the inline region. Describe
that the resulting event_range must contain the complete subtree required by the
selected DeferredInlineFlavor.

In `@crates/biome_markdown_parser/src/to_html.rs`:
- Around line 2123-2126: Extract the ATX heading reference-link assertion from
the existing test into a dedicated test focused on deferred AtxParagraph heading
rendering, leaving the existing test scoped to emphasis behavior. Preserve the
assertion input and expected HTML exactly so regressions identify the heading
path separately.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 2eb530b2-0ae5-4a12-b2c8-c8fb0b2eb252

📥 Commits

Reviewing files that changed from the base of the PR and between c4a07bf and 1b29623.

📒 Files selected for processing (7)
  • crates/biome_markdown_parser/src/inline_phase.rs
  • crates/biome_markdown_parser/src/lib.rs
  • crates/biome_markdown_parser/src/parser.rs
  • crates/biome_markdown_parser/src/syntax/header.rs
  • crates/biome_markdown_parser/src/syntax/inline/emphasis.rs
  • crates/biome_markdown_parser/src/syntax/mod.rs
  • crates/biome_markdown_parser/src/to_html.rs

Comment thread crates/biome_markdown_parser/src/inline_phase.rs
@ematipico
ematipico force-pushed the perf/md-parser-state branch from 1b29623 to e174f9b Compare August 6, 2026 18:55
@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
crates/biome_markdown_parser/src/parser.rs (1)

483-489: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Remove the redundant doc comment.

Line 483 repeats set_emphasis_context and its return type. Remove it, or document an invariant that callers must preserve.

As per coding guidelines, “Avoid comments whose content is already recoverable from names and types.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/biome_markdown_parser/src/parser.rs` around lines 483 - 489, Remove
the redundant doc comment immediately above set_emphasis_context; do not replace
it unless documenting a non-obvious invariant callers must preserve.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@crates/biome_markdown_parser/src/parser.rs`:
- Around line 483-489: Remove the redundant doc comment immediately above
set_emphasis_context; do not replace it unless documenting a non-obvious
invariant callers must preserve.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 0c1ed4f8-2f1a-422b-856d-32d0fce07876

📥 Commits

Reviewing files that changed from the base of the PR and between c4a07bf and e174f9b.

📒 Files selected for processing (7)
  • crates/biome_markdown_parser/src/inline_phase.rs
  • crates/biome_markdown_parser/src/lib.rs
  • crates/biome_markdown_parser/src/parser.rs
  • crates/biome_markdown_parser/src/syntax/header.rs
  • crates/biome_markdown_parser/src/syntax/inline/emphasis.rs
  • crates/biome_markdown_parser/src/syntax/mod.rs
  • crates/biome_markdown_parser/src/to_html.rs
🚧 Files skipped from review as they are similar to previous changes (6)
  • crates/biome_markdown_parser/src/lib.rs
  • crates/biome_markdown_parser/src/to_html.rs
  • crates/biome_markdown_parser/src/inline_phase.rs
  • crates/biome_markdown_parser/src/syntax/inline/emphasis.rs
  • crates/biome_markdown_parser/src/syntax/mod.rs
  • crates/biome_markdown_parser/src/syntax/header.rs

@ematipico ematipico closed this Aug 7, 2026
@ematipico ematipico reopened this Aug 7, 2026
@github-actions

github-actions Bot commented Aug 7, 2026

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 L-Markdown Language: Markdown labels Aug 7, 2026
@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Parser conformance results on

js/262

Test result main count This PR count Difference
Total 53595 53595 0
Passed 52312 52312 0
Failed 1241 1241 0
Panics 42 42 0
Coverage 97.61% 97.61% 0.00%

jsx/babel

Test result main count This PR count Difference
Total 38 38 0
Passed 37 37 0
Failed 1 1 0
Panics 0 0 0
Coverage 97.37% 97.37% 0.00%

markdown/commonmark

Test result main count This PR count Difference
Total 652 652 0
Passed 652 652 0
Failed 0 0 0
Panics 0 0 0
Coverage 100.00% 100.00% 0.00%

symbols/microsoft

Test result main count This PR count Difference
Total 5467 5467 0
Passed 1915 1915 0
Failed 3552 3552 0
Panics 0 0 0
Coverage 35.03% 35.03% 0.00%

ts/babel

Test result main count This PR count Difference
Total 676 676 0
Passed 592 592 0
Failed 84 84 0
Panics 0 0 0
Coverage 87.57% 87.57% 0.00%

ts/microsoft

Test result main count This PR count Difference
Total 18876 18876 0
Passed 13010 13010 0
Failed 5865 5865 0
Panics 1 1 0
Coverage 68.92% 68.92% 0.00%

@ematipico
ematipico force-pushed the perf/md-parser-state branch from 5d57c99 to 8814837 Compare August 7, 2026 07:01
@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@ematipico
ematipico merged commit ca2e0d0 into main Aug 7, 2026
32 checks passed
@ematipico
ematipico deleted the perf/md-parser-state branch August 7, 2026 07:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants