Skip to content

feat: markdown linter - #11253

Merged
ematipico merged 7 commits into
mainfrom
feat/markdown-linter
Aug 6, 2026
Merged

feat: markdown linter#11253
ematipico merged 7 commits into
mainfrom
feat/markdown-linter

Conversation

@ematipico

Copy link
Copy Markdown
Member

Summary

This PR adds the markdown linter to our repository.

It adds one lint rule: https://github.com/markdownlint/markdownlint/blob/main/docs/RULES.md#md001---header-levels-should-only-increment-by-one-level-at-a-time

I didn't add metadata because I don't know how to link it. We can do that later.

  • Renamed MdDocument to MdRoot, in line with our nomenclature
  • Refactored how hash headers are parsed. Before, a list of one item was always created, which is... unproductive. Now the parser parses as a live list. This allows us to correctly compute the level of an header.
  • Removed a diagnostic that was emitted with seven or more headers. We now create a checkpoint, and parse as a paragraph. This matches CommonMark.

Test Plan

Added new tests

Docs

Please review the docs, it's possible they are stale (copy-paste)

@changeset-bot

changeset-bot Bot commented Aug 6, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: f1fdeba

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

github-actions Bot commented Aug 6, 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-CLI Area: CLI A-Project Area: project A-Linter Area: linter A-Parser Area: parser A-Formatter Area: formatter A-Tooling Area: internal tools A-Diagnostic Area: diagnostocis L-Markdown Language: Markdown labels Aug 6, 2026
@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

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: e3c8d95c-9f9e-4625-a712-de842874b9bd

📥 Commits

Reviewing files that changed from the base of the PR and between 7ef4a23 and f1fdeba.

⛔ Files ignored due to path filters (2)
  • crates/biome_configuration/src/analyzer/linter/rules.rs is excluded by !**/rules.rs and included by **
  • crates/biome_configuration/src/generated/linter_options_check.rs is excluded by !**/generated/**, !**/generated/** and included by **
📒 Files selected for processing (1)
  • xtask/codegen/src/generate_configuration.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • xtask/codegen/src/generate_configuration.rs

Walkthrough

This change adds Markdown analysis, linting, suppression handling, code actions, fix-all support, rule registration, configuration, and tests. It adds the UseConsistentHeaderLevel rule. It renames the Markdown syntax root from MdDocument to MdRoot. It updates ATX heading tokenisation, parsing, HTML rendering, formatting, fuzzing, and coverage checks. It enables Markdown rule discovery in service, CLI, documentation, SARIF, and rule-check workflows.

Possibly related PRs

Suggested reviewers: dyc3

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely identifies the primary change: adding Markdown linting support.
Description check ✅ Passed The description directly explains the Markdown linter, its rule, parser changes, and added tests.
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 feat/markdown-linter

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

@github-actions

github-actions Bot commented Aug 6, 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%

@codspeed-hq

codspeed-hq Bot commented Aug 6, 2026

Copy link
Copy Markdown

Merging this PR will regress 1 benchmark

⚡ 9 improved benchmarks
❌ 1 regressed benchmark
✅ 265 untouched benchmarks
🆕 3 new benchmarks
⏩ 4 skipped benchmarks1

Warning

Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

Benchmark BASE HEAD Efficiency
bench_index_d_ts_salsa_invalidated[react/index.d.ts] 8.2 ms 10.3 ms -20.53%
bench_normalize_local_alias 99.2 µs 62.7 µs +58.12%
tsconfig_json 626.7 µs 440.2 µs +42.37%
index_d_ts[@next/font/google/index.d.ts] 276.8 ms 236.1 ms +17.23%
index_d_ts[react/index.d.ts] 19.8 ms 17 ms +16.8%
bench_index_d_ts_salsa_end_to_end[@next/font/google/index.d.ts] 333.8 ms 294.1 ms +13.5%
index_d_ts[RedisCommander.d.ts] 131.9 ms 119.8 ms +10.09%
bench_index_d_ts_salsa_incremental_first_run 554.2 ms 508.3 ms +9.04%
bench_index_d_ts_salsa_end_to_end[RedisCommander.d.ts] 149.3 ms 137.6 ms +8.51%
json_analyzer[package-lock_11240515041603712709.json] 29 ms 27.4 ms +5.91%
🆕 ascii N/A 10.9 µs N/A
🆕 sanitized N/A 322 µs N/A
🆕 unicode_without_colors N/A 441.9 µs N/A

Tip

Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.


Comparing feat/markdown-linter (f1fdeba) with main (f1f72fe)

Open in CodSpeed

Footnotes

  1. 4 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.

@codspeed-hq

codspeed-hq Bot commented Aug 6, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 28 untouched benchmarks
⏩ 258 skipped benchmarks1


Comparing feat/markdown-linter (68a7ef4) with main (f1f72fe)

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.

@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: 6

🧹 Nitpick comments (4)
crates/biome_service/src/file_handlers/md.rs (2)

430-450: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use the destructured locals consistently.

Pass path, language, and workspace.as_ref() instead of accessing the original params fields. Bind language in the pattern. This matches the other handlers and removes unnecessary field access.

🤖 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_service/src/file_handlers/md.rs` around lines 430 - 450, Update
the analyzer setup around AnalyzerVisitorBuilder to destructure and bind
language, then consistently pass the local path, language, and
workspace.as_ref() values instead of accessing params fields. Preserve the
existing rule and project configuration behavior.

511-522: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Pass the analyser cache through FixAllParams

WorkspaceServer already owns the analyser cache, but fix_file_state does not pass it to FixAllParams. Add the field and use .with_cache(...) in analyser-backed fix-all handlers, including Markdown.

🤖 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_service/src/file_handlers/md.rs` around lines 511 - 522, Add the
analyser cache to FixAllParams and propagate WorkspaceServer’s owned cache into
it via .with_cache(...) in every analyser-backed fix-all handler, including
fix_file_state’s Markdown path around AnalyzerVisitorBuilder. Ensure the
existing cache instance is reused rather than creating a new one.
crates/biome_markdown_analyze/src/lib.rs (1)

170-179: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Use dbg!() for test debugging output.

Replace these eprintln!() calls with dbg!() or remove the ignored debugging test. The Rust testing guideline requires dbg!() for debugging output.

Proposed change
-                    eprintln!("{text}");
+                    dbg!(text);
@@
-                    eprintln!("{new_code}");
+                    dbg!(new_code);

As per coding guidelines, “Use the dbg!() macro for debugging Rust code during testing”.

🤖 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_analyze/src/lib.rs` around lines 170 - 179, Replace the
two test-debugging `eprintln!` calls in the diagnostic and action-output loops
with `dbg!` calls, or remove the ignored debugging test if this output is no
longer needed. Preserve the existing diagnostic and mutation behavior.

Source: Coding guidelines

crates/biome_markdown_analyze/tests/quick_test.rs (1)

11-53: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Replace or remove this ignored test.

The test enables suspicious/noDuplicateFontNames against CSS text. It does not test the Markdown analyser or nursery/useConsistentHeaderLevel.

If this test remains, enable the Markdown rule, use Markdown input, and assert its diagnostics. Remove the reviewer-directed comment and debug output when the test becomes automated.

As per coding guidelines, Rust changes need appropriate tests, use dbg!() for test debugging, and comments must state a code purpose.

🤖 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_analyze/tests/quick_test.rs` around lines 11 - 53,
Replace or remove the ignored quick_test; if retained, configure the Markdown
analyser with nursery/useConsistentHeaderLevel, provide Markdown heading input,
and assert the expected diagnostics. Convert it into an automated test by
removing the reviewer-directed comment and debug eprintln output, using dbg!
only for temporary debugging, and asserting the collected error_ranges.

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.

Inline comments:
In
`@crates/biome_markdown_analyze/src/lint/nursery/use_consistent_header_level.rs`:
- Around line 20-25: Correct the rustdoc example for the consistent-header-level
rule so the documented Markdown actually skips a heading level and produces the
expected diagnostic; update the second heading in the example while preserving
the existing expect_diagnostic annotation and example structure.

In `@crates/biome_markdown_analyze/src/suppression_action.rs`:
- Around line 60-78: Update the suppression comment construction in the token
replacement flow to use an HTML comment form (`<!-- ... -->`) instead of the
Markdown heading form, while retaining TriviaPieceKind::SingleLineComment. Add a
regression fixture covering inline suppression and verify the generated result
does not contain an MdHeader.

In `@crates/biome_markdown_parser/src/lexer/mod.rs`:
- Around line 1471-1489: Add regression cases to the tests covering the
is_in_atx_opening_sequence branch: verify a container-prefixed heading such as
“> ### heading” is recognized correctly, while non-heading text such as “text
### heading” is not. Extend the existing multiple_hashes coverage without
changing lexer behavior.

In `@crates/biome_service/src/configuration.rs`:
- Around line 971-979: Update ProjectScanComputer::compute to call
biome_markdown_analyze::visit_registry(&mut self) under the lang_md feature,
alongside the other language registry calls, so Markdown Project and Types rules
influence ScanKind. Add a regression test covering the Markdown scan requirement
path.

In `@crates/biome_service/src/file_handlers/md.rs`:
- Line 429: Update the debug_span! label in the Markdown handler to identify
Markdown processing instead of JSON, while preserving the existing range and
path fields.

In `@crates/biome_test_utils/src/lib.rs`:
- Around line 1006-1007: Update the exclusion comment above the file-type match
to explain that Markdown supports HTML comments, but the parser represents them
as MD_HTML_LITERAL rather than comment trivia; leave the exclusion list and
behavior unchanged.

---

Nitpick comments:
In `@crates/biome_markdown_analyze/src/lib.rs`:
- Around line 170-179: Replace the two test-debugging `eprintln!` calls in the
diagnostic and action-output loops with `dbg!` calls, or remove the ignored
debugging test if this output is no longer needed. Preserve the existing
diagnostic and mutation behavior.

In `@crates/biome_markdown_analyze/tests/quick_test.rs`:
- Around line 11-53: Replace or remove the ignored quick_test; if retained,
configure the Markdown analyser with nursery/useConsistentHeaderLevel, provide
Markdown heading input, and assert the expected diagnostics. Convert it into an
automated test by removing the reviewer-directed comment and debug eprintln
output, using dbg! only for temporary debugging, and asserting the collected
error_ranges.

In `@crates/biome_service/src/file_handlers/md.rs`:
- Around line 430-450: Update the analyzer setup around AnalyzerVisitorBuilder
to destructure and bind language, then consistently pass the local path,
language, and workspace.as_ref() values instead of accessing params fields.
Preserve the existing rule and project configuration behavior.
- Around line 511-522: Add the analyser cache to FixAllParams and propagate
WorkspaceServer’s owned cache into it via .with_cache(...) in every
analyser-backed fix-all handler, including fix_file_state’s Markdown path around
AnalyzerVisitorBuilder. Ensure the existing cache instance is reused rather than
creating a new one.
🪄 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: 42507852-60f2-406d-a69d-4b5d261a94a6

📥 Commits

Reviewing files that changed from the base of the PR and between bd0b68d and 50deed3.

⛔ Files ignored due to path filters (174)
  • Cargo.lock is excluded by !**/*.lock and included by **
  • crates/biome_diagnostics_categories/src/categories.rs is excluded by !**/categories.rs and included by **
  • crates/biome_markdown_analyze/src/lint/nursery.rs is excluded by !**/nursery.rs and included by **
  • crates/biome_markdown_analyze/tests/specs/nursery/useConsistentHeaderLevel/headers_bad.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_analyze/tests/specs/nursery/useConsistentHeaderLevel/headers_good.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_analyze/tests/specs/nursery/useConsistentHeaderLevel/invalid.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_analyze/tests/specs/nursery/useConsistentHeaderLevel/valid.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_factory/src/generated/node_factory.rs is excluded by !**/generated/**, !**/generated/** and included by **
  • crates/biome_markdown_factory/src/generated/syntax_factory.rs is excluded by !**/generated/**, !**/generated/** and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/error/quote_nesting_too_deep.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/atx_heading_trailing_hash.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/autolinks.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/block_quote.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/block_quote_grouping.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/block_quote_ordered_list_interrupt.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/block_quote_ordered_sublist_after_list_item.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/block_quote_tab_separated.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/block_quote_thematic_break_after_list.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/blockquote_ends_at_unprefixed_ordered_list.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/blockquote_inside_list.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/blockquote_tab_indented_bullet.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/blockquote_tab_indented_bullet_siblings.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/blockquoted_fence_as_bullet_items.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/bom.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/bullet_list.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/bullet_list_loose_after_empty_item.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/bullet_list_space_tab_space.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/bullet_list_split_after_empty_item_marker_change.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/bullet_list_split_after_empty_item_paragraph.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/bullet_list_tab_separated.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/bullet_to_ordered_split.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/edge_cases.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/emphasis_complex.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/emphasis_crossing.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/emphasis_edge_cases.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/emphasis_flanking.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/emphasis_link_text.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/emphasis_multibyte.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/entity_references.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/fence_as_bullet_items.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/fenced_code_advanced.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/fenced_code_after_list_not_absorbed.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/fenced_code_block.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/fenced_code_blockquote_eof_after_prefix.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/fenced_code_document_indented_fences.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/fenced_code_empty_content.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/fenced_code_eof_after_fence.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/fenced_code_in_blockquote.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/fenced_code_in_list.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/fenced_code_indentation.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/fenced_code_info_backtick.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/hard_break_in_blockquote.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/hard_break_in_list_item.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/hard_break_nested_quote_in_list.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/hard_line_break.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/hard_line_break_paragraph_split.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/header.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/header_in_list.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/html_block.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/html_block_in_list.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/html_block_in_list_continuation.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/indent_code_block.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/indented_code_blank_lines.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/indented_code_in_list_vs_continuation.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/inline_elements.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/inline_html.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/inline_html_edge_cases.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/inline_html_invalid.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/inline_link_destination_title.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/inline_link_whitespace.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/lazy_continuation.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/lazy_continuation_at_marker_indent.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/lazy_continuation_emphasis_in_list.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/link_definition.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/link_definition_edge_cases.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/link_definition_indented_continuation.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/link_definition_invalid.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/link_reference_before_thematic_break.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/link_reference_definition_paragraph_continuation.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/link_reference_inside_quote_continuation.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/link_reference_inside_quote_nested_quote_list.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/link_reference_then_quoted_ordered_list.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/list_blank_lines_between_items.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/list_continuation_edge_cases.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/list_in_blockquote.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/list_indentation.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/list_interrupt_bullet.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/list_interrupt_empty_bullet.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/list_interrupt_ordered.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/list_item_tab_indented_fenced_code.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/list_lazy_link_reference_continuation.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/list_link_reference_before_thematic_break.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/list_link_reference_tab_non_thematic_break.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/list_link_reference_tab_thematic_break.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/list_marker_trailing_spaces.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/list_no_interrupt_empty_bullet.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/list_no_interrupt_ordered_2.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/list_tightness.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/mixed_bullet_markers_split.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/mixed_markers_after_fenced_code.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/mixed_markers_heading_in_list.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/mixed_markers_paragraph.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/mixed_markers_setext.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/mixed_markers_thematic_break.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/mixed_markers_three_after_heading_in_list.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/mixed_ordered_delimiters_split.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/multi_backtick_code.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/multibyte_emphasis_in_blockquote.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/multiline_label.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/multiline_label_reference.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/multiline_list.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/multiline_open_tag_blockquote_marker.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/nested_bullet_indent_tokens.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/nested_lazy_continuation_loose_item.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/nested_lazy_continuation_same_marker_trailing.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/nested_lazy_continuation_trailing_paragraph.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/nested_list_blank_line_siblings.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/nested_list_double_blank.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/nested_list_double_blank_siblings.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/nested_list_interrupt_after_newline.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/nested_list_lazy_continuation_before_fence.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/nested_list_lazy_continuation_before_link_reference.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/nested_list_tab_indented_siblings.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/nested_list_triple_blank_siblings.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/nested_quote.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/ordered_list.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/ordered_list_link_reference_tab_thematic_break.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/ordered_list_loose_after_empty_item.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/ordered_list_split_after_empty_item_delimiter_change.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/ordered_marker_non_one_in_bullet_item.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/ordered_marker_non_one_top_level.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/ordered_marker_one_starts_sublist_in_bullet.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/ordered_sublist_at_content_column.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/ordered_sublist_post_marker_wide_space.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/ordered_sublist_post_marker_wide_space_delimiter_cross.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/ordered_to_bullet_split.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/paragraph.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/paragraph_interruption.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/paren_depth_limit.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/quote_link_reference_before_thematic_break.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/quote_list_link_reference_before_thematic_break.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/quote_list_link_reference_tab_non_thematic_break.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/quote_pre_marker_indent.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/quote_textual_marker_parity.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/quoted_code_indented_bullet_stays_lazy.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/quoted_ordered_marker_non_one_no_interrupt.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/quoted_space_indented_bullet_interrupts_paragraph.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/reference_link_not_implemented.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/reference_links.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/setext_heading.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/setext_heading_edge_cases.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/setext_heading_in_blockquote.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/setext_heading_inside_list.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/setext_heading_negative.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/single_item_lists_marker_split.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/sublist_loose_space_tab_indent.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/sublist_loose_three_space_tab_indent.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/sublist_loose_two_space_tab_indent.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/thematic_break_block.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/thematic_break_in_list.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/tilde_fence_info_backtick.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/too_many_hashes.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/unclosed_bold.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/unclosed_code_span.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/unclosed_emphasis.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/unclosed_image.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/unclosed_link.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/unclosed_reference_image_label.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/unclosed_reference_link_label.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_parser/tests/md_test_suite/ok/unterminated_code_fence.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_syntax/src/generated/kind.rs is excluded by !**/generated/**, !**/generated/** and included by **
  • crates/biome_markdown_syntax/src/generated/macros.rs is excluded by !**/generated/**, !**/generated/** and included by **
  • crates/biome_markdown_syntax/src/generated/nodes.rs is excluded by !**/generated/**, !**/generated/** and included by **
  • crates/biome_markdown_syntax/src/generated/nodes_mut.rs is excluded by !**/generated/**, !**/generated/** and included by **
📒 Files selected for processing (52)
  • Cargo.toml
  • crates/biome_cli/Cargo.toml
  • crates/biome_cli/src/reporter/sarif.rs
  • crates/biome_configuration/Cargo.toml
  • crates/biome_configuration/src/markdown.rs
  • crates/biome_configuration_macros/Cargo.toml
  • crates/biome_configuration_macros/src/lib.rs
  • crates/biome_configuration_macros/src/visitors.rs
  • crates/biome_markdown_analyze/Cargo.toml
  • crates/biome_markdown_analyze/build.rs
  • crates/biome_markdown_analyze/src/assist.rs
  • crates/biome_markdown_analyze/src/lib.rs
  • crates/biome_markdown_analyze/src/lint.rs
  • crates/biome_markdown_analyze/src/lint/nursery/use_consistent_header_level.rs
  • crates/biome_markdown_analyze/src/registry.rs
  • crates/biome_markdown_analyze/src/suppression_action.rs
  • crates/biome_markdown_analyze/tests/quick_test.rs
  • crates/biome_markdown_analyze/tests/spec_tests.rs
  • crates/biome_markdown_analyze/tests/specs/nursery/useConsistentHeaderLevel/headers_bad.md
  • crates/biome_markdown_analyze/tests/specs/nursery/useConsistentHeaderLevel/headers_good.md
  • crates/biome_markdown_analyze/tests/specs/nursery/useConsistentHeaderLevel/invalid.md
  • crates/biome_markdown_analyze/tests/specs/nursery/useConsistentHeaderLevel/valid.md
  • crates/biome_markdown_factory/src/make.rs
  • crates/biome_markdown_formatter/src/generated.rs
  • crates/biome_markdown_formatter/src/markdown/auxiliary/mod.rs
  • crates/biome_markdown_formatter/src/markdown/auxiliary/root.rs
  • crates/biome_markdown_formatter/tests/spec_test.rs
  • crates/biome_markdown_parser/src/lexer/mod.rs
  • crates/biome_markdown_parser/src/lexer/tests.rs
  • crates/biome_markdown_parser/src/lib.rs
  • crates/biome_markdown_parser/src/syntax/header.rs
  • crates/biome_markdown_parser/src/syntax/list.rs
  • crates/biome_markdown_parser/src/syntax/mod.rs
  • crates/biome_markdown_parser/src/syntax/parse_error.rs
  • crates/biome_markdown_parser/src/to_html.rs
  • crates/biome_markdown_parser/tests/fuzz_differential.rs
  • crates/biome_markdown_parser/tests/md_test_suite/ok/too_many_hashes.md
  • crates/biome_markdown_parser/tests/spec_test.rs
  • crates/biome_markdown_syntax/src/header_ext.rs
  • crates/biome_markdown_syntax/src/lib.rs
  • crates/biome_markdown_syntax/src/syntax_node.rs
  • crates/biome_rule_options/src/lib.rs
  • crates/biome_rule_options/src/use_consistent_header_level.rs
  • crates/biome_service/Cargo.toml
  • crates/biome_service/src/configuration.rs
  • crates/biome_service/src/documentation/mod.rs
  • crates/biome_service/src/file_handlers/md.rs
  • crates/biome_test_utils/src/lib.rs
  • xtask/codegen/markdown.ungram
  • xtask/codegen/src/generate_analyzer.rs
  • xtask/codegen/src/markdown_kinds_src.rs
  • xtask/coverage/src/markdown/commonmark.rs
💤 Files with no reviewable changes (1)
  • crates/biome_markdown_parser/src/syntax/parse_error.rs

Comment thread crates/biome_markdown_analyze/src/lint/nursery/use_consistent_header_level.rs Outdated
Comment on lines +60 to +78
let suppression_comment = format!("# {suppression_text}: {suppression_reason}");
let suppression_comment = suppression_comment.as_str();
let trivia = [
(TriviaPieceKind::SingleLineComment, suppression_comment),
(TriviaPieceKind::Newline, "\n"),
];
if leading_whitespaces.is_empty() {
new_token = new_token.with_leading_trivia(trivia);
}
// Token is indented
else {
let mut trivia = trivia.to_vec();

for w in leading_whitespaces.iter() {
trivia.push((TriviaPieceKind::Whitespace, w.text()));
}
new_token = new_token.with_leading_trivia(trivia);
}
mutation.replace_token_transfer_trivia(token_to_apply_suppression, new_token);

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.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n -C 3 'enum\s+TriviaPieceKind|SingleLineComment|MultiLineComment' crates/biome_rowan
rg -n -C 3 'biome-ignore|suppression|MdHeader' crates/biome_markdown_analyze/tests

Repository: biomejs/biome

Length of output: 10137


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- suppression action ---'
cat -n crates/biome_markdown_analyze/src/suppression_action.rs | sed -n '1,150p'

printf '%s\n' '--- markdown comment handling ---'
rg -n -C 4 'HtmlComment|HTMLComment|html.?comment|MultiLineComment|SingleLineComment' crates/biome_markdown_parser crates/biome_markdown_syntax crates/biome_markdown_analyze

printf '%s\n' '--- suppression fixtures and helpers ---'
find crates/biome_markdown_analyze/tests -maxdepth 4 -type f | sort | sed -n '1,160p'
rg -n -C 5 'check_code_action|run_suppression_test|suppression' crates/biome_markdown_analyze/tests crates/biome_markdown_analyze/src

Repository: biomejs/biome

Length of output: 25863


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- HTML comment parser ---'
cat -n crates/biome_markdown_parser/src/syntax/inline/html.rs | sed -n '1,130p'

printf '%s\n' '--- Markdown lexer and trivia construction ---'
rg -n -C 5 'TriviaPieceKind|trivia|html_comment|HtmlComment|HTML' crates/biome_markdown_parser/src crates/biome_markdown_syntax/src

printf '%s\n' '--- tracked suppression fixtures ---'
git ls-files 'crates/biome_markdown_analyze/tests/*' | rg 'suppression|\\.snap$' || true

printf '%s\n' '--- suppression test body ---'
cat -n crates/biome_markdown_analyze/tests/spec_tests.rs | sed -n '200,310p'

Repository: biomejs/biome

Length of output: 50370


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- Markdown lexer files ---'
find crates/biome_markdown_parser/src -maxdepth 3 -type f | sort | rg 'lexer|inline|block|token'

printf '%s\n' '--- lexer token definitions and hash handling ---'
rg -n -C 4 'MD_HEADER|HASH|b.\\#.|starts_with.*#|TriviaPieceKind|is_trivia' \
  crates/biome_markdown_parser/src/lexer crates/biome_markdown_parser/src \
  -g '*.rs' | head -n 240

printf '%s\n' '--- inline HTML parse production ---'
rg -n -C 8 'MD_INLINE_HTML|is_inline_html|inline_html' crates/biome_markdown_parser/src -g '*.rs' | head -n 240

printf '%s\n' '--- suppression fixture directories ---'
find crates/biome_markdown_analyze/tests -type d -print | sort

Repository: biomejs/biome

Length of output: 37768


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
from pathlib import Path
import re

action = Path("crates/biome_markdown_analyze/src/suppression_action.rs").read_text()
trivia = Path("crates/biome_rowan/src/syntax/trivia.rs").read_text()
lexer_tests = Path("crates/biome_markdown_parser/src/lexer/tests.rs").read_text()
header = Path("crates/biome_markdown_parser/src/syntax/header.rs").read_text()
html = Path("crates/biome_markdown_parser/src/syntax/inline/html.rs").read_text()

assert 'format!("# {suppression_text}: {suppression_reason}")' in action
assert 'TriviaPieceKind::SingleLineComment' in action

assert '/// Comment that does not contain any line breaks' in trivia
assert '/// Comment that contains at least one line break' in trivia

assert '"#",' in lexer_tests and 'HASH:1' in lexer_tests
assert 'if !p.at_line_start() && !p.at_start_of_input()' in header
assert 'MD_HEADER' in header

assert 'bytes.starts_with(b"<!--")' in html
assert 'find("-->")' in html
assert 'MD_INLINE_HTML' in html

print("The current suppression text is lexed as HASH at line start.")
print("Markdown parses a line-start HASH as MD_HEADER.")
print("The proposed HTML comment is parsed as MD_INLINE_HTML.")
print("TriviaPieceKind::MultiLineComment requires a line break; the proposed comment has none.")
PY

Repository: biomejs/biome

Length of output: 403


Generate an HTML comment for inline suppression.

# {suppression_text}: ... is parsed as an MdHeader at line start. Use <!-- ... --> and keep TriviaPieceKind::SingleLineComment, because the comment contains no line break. Add a regression fixture that ensures suppression does not add an MdHeader.

🤖 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_analyze/src/suppression_action.rs` around lines 60 -
78, Update the suppression comment construction in the token replacement flow to
use an HTML comment form (`<!-- ... -->`) instead of the Markdown heading form,
while retaining TriviaPieceKind::SingleLineComment. Add a regression fixture
covering inline suppression and verify the generated result does not contain an
MdHeader.

Source: Coding guidelines

Comment on lines +1471 to +1489
self.after_newline
|| self.is_at_container_line_start()
|| self.is_in_atx_opening_sequence()
|| self.is_at_atx_closing_sequence()
}

/// Returns true when earlier hashes on the line form an ATX opening sequence.
fn is_in_atx_opening_sequence(&self) -> bool {
let before = &self.source[..self.position];
let line_start = before.rfind(['\n', '\r']).map_or(0, |pos| pos + 1);
let line_prefix = &before[line_start..];
let Some(hash_start) = line_prefix.find('#') else {
return false;
};

line_prefix[..hash_start]
.bytes()
.all(|b| is_space_or_tab_byte(b) || b == b'>')
&& line_prefix[hash_start..].bytes().all(|b| b == b'#')

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.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Add regression coverage for the ATX opening-sequence branch.

multiple_hashes only checks a bare ### sequence. Add cases for a container-prefixed heading such as > ### heading and for non-heading text such as text ### heading.

As per coding guidelines, “All code changes must include appropriate tests”.

🤖 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/lexer/mod.rs` around lines 1471 - 1489, Add
regression cases to the tests covering the is_in_atx_opening_sequence branch:
verify a container-prefixed heading such as “> ### heading” is recognized
correctly, while non-heading text such as “text ### heading” is not. Extend the
existing multiple_hashes coverage without changing lexer behavior.

Source: Coding guidelines

Comment on lines +971 to +979
#[cfg(feature = "lang_md")]
impl RegistryVisitor<MarkdownLanguage> for ProjectScanComputer<'_> {
fn record_rule<R>(&mut self)
where
R: Rule<Options: Default, Query: Queryable<Language = MarkdownLanguage, Output: Clone>>
+ 'static,
{
self.check_rule::<R, MarkdownLanguage>();
}

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.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Register the Markdown registry in ProjectScanComputer::compute.

Line 972 adds the visitor, but Lines 869-877 do not call biome_markdown_analyze::visit_registry(&mut self). Markdown rules with Project or Types domains will not affect ScanKind.

Add the feature-gated registry call beside the other language registries. Add a regression test for the Markdown scan requirement path.

Proposed fix
         #[cfg(feature = "lang_html")]
         biome_html_analyze::visit_registry(&mut self);
+        #[cfg(feature = "lang_md")]
+        biome_markdown_analyze::visit_registry(&mut self);
🤖 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_service/src/configuration.rs` around lines 971 - 979, Update
ProjectScanComputer::compute to call biome_markdown_analyze::visit_registry(&mut
self) under the lang_md feature, alongside the other language registry calls, so
Markdown Project and Types rules influence ScanKind. Add a regression test
covering the Markdown scan requirement path.

analyzer_cache,
} = params;

let _ = debug_span!("Code actions JSON", range =? range, path =? path).entered();

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.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Fix the span label.

The span reports "Code actions JSON" inside the Markdown handler. Traces for Markdown files then look like JSON work.

🔧 Proposed fix
-    let _ = debug_span!("Code actions JSON",  range =? range, path =? path).entered();
+    let _ = debug_span!("Code actions Markdown", range =? range, path =? path).entered();
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let _ = debug_span!("Code actions JSON", range =? range, path =? path).entered();
let _ = debug_span!("Code actions Markdown", range =? range, path =? path).entered();
🤖 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_service/src/file_handlers/md.rs` at line 429, Update the
debug_span! label in the Markdown handler to identify Markdown processing
instead of JSON, while preserving the existing range and path fields.

Comment on lines 1006 to +1007
// Excluded files types which cannot contain comment in the source code
"snap" | "json" | "jsonc" | "svelte" | "vue" | "astro" | "html" => false,
"snap" | "json" | "jsonc" | "svelte" | "vue" | "astro" | "html" | "md" => false,

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.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check whether the Markdown lexer produces comment trivia.
rg -n -C3 'TriviaPieceKind::(Single|Multi)LineComment|Comments' crates/biome_markdown_parser/src

# Inspect the Markdown spec fixtures for expectation markers.
fd -t f -e md . crates/biome_markdown_analyze/tests/specs --exec sh -c 'echo "== $1"; cat "$1"' _ {}

Repository: biomejs/biome

Length of output: 788


🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '== utility context =='
sed -n '960,1025p' crates/biome_test_utils/src/lib.rs

printf '%s\n' '== Markdown parser structure =='
ast-grep outline crates/biome_markdown_parser/src
rg -n -i 'html comment|comment|trivia|InlineHtml|Html' crates/biome_markdown_parser crates/biome_markdown_analyze

printf '%s\n' '== expectation handling =='
rg -n -C4 'should (not )?generate diagnostics|excluded|extension|file.*type|comment' crates/biome_test_utils crates/biome_markdown_analyze/tests

Repository: biomejs/biome

Length of output: 50372


🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '== expectation implementation =='
sed -n '1000,1075p' crates/biome_test_utils/src/lib.rs
sed -n '175,205p' crates/biome_markdown_analyze/tests/spec_tests.rs

printf '%s\n' '== Markdown token and HTML-comment handling =='
sed -n '1,130p' crates/biome_markdown_parser/src/lexer/mod.rs
sed -n '1,180p' crates/biome_markdown_parser/src/syntax/inline/html.rs
sed -n '1,130p' crates/biome_markdown_parser/src/syntax/html_block.rs

printf '%s\n' '== comment token definitions =='
rg -n -C3 'Comment|comment|Html|HTML' crates/biome_markdown_parser/src/lexer crates/biome_markdown_parser/src/syntax crates/biome_markdown_parser/src/token_source.rs

Repository: biomejs/biome

Length of output: 50371


Document Markdown’s exclusion rationale. Markdown supports HTML comments, but the parser stores them as MD_HTML_LITERAL, not comment trivia. Replace the current “cannot contain comment” wording with this reason.

🤖 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_test_utils/src/lib.rs` around lines 1006 - 1007, Update the
exclusion comment above the file-type match to explain that Markdown supports
HTML comments, but the parser represents them as MD_HTML_LITERAL rather than
comment trivia; leave the exclusion list and behavior unchanged.

@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.

♻️ Duplicate comments (1)
crates/biome_markdown_analyze/src/lint/nursery/use_consistent_header_level.rs (1)

20-32: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Use executable Markdown heading examples.

The documentation parser preserves \# Header 1. The Markdown parser then treats it as paragraph content. The invalid fixture has only a level-three header, so expect_diagnostic fails. Use level-two and level-four headings instead. The valid fixture should also contain two real consecutive headings.

Proposed fix
-    /// \# Header 1
+    /// ## Header 2
     ///
-    /// ### Header 3
+    /// #### Header 4
@@
-    /// \# Header 1
+    /// ## Header 2
     ///
-    /// ## Header 2
+    /// ### Header 3

As per coding guidelines, use rustdoc documentation for documenting new rule behaviour accurately.

🤖 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_analyze/src/lint/nursery/use_consistent_header_level.rs`
around lines 20 - 32, Update the rustdoc examples for the heading-level rule so
escaped `\#` markers become executable Markdown headings: use level-two and
level-four headings in the invalid `expect_diagnostic` fixture, and make the
valid fixture contain two real consecutive headings with the intended levels.

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.

Duplicate comments:
In
`@crates/biome_markdown_analyze/src/lint/nursery/use_consistent_header_level.rs`:
- Around line 20-32: Update the rustdoc examples for the heading-level rule so
escaped `\#` markers become executable Markdown headings: use level-two and
level-four headings in the invalid `expect_diagnostic` fixture, and make the
valid fixture contain two real consecutive headings with the intended levels.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: ea8f4db6-c1d6-41a6-a887-478a9384ddec

📥 Commits

Reviewing files that changed from the base of the PR and between 50deed3 and d328203.

⛔ Files ignored due to path filters (2)
  • Cargo.lock is excluded by !**/*.lock and included by **
  • packages/@biomejs/backend-jsonrpc/src/workspace.ts is excluded by !**/backend-jsonrpc/src/workspace.ts and included by **
📒 Files selected for processing (4)
  • crates/biome_markdown_analyze/src/lint/nursery/use_consistent_header_level.rs
  • crates/biome_markdown_analyze/tests/spec_tests.rs
  • xtask/rules_check/Cargo.toml
  • xtask/rules_check/src/lib.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • crates/biome_markdown_analyze/tests/spec_tests.rs

@ematipico
ematipico force-pushed the feat/markdown-linter branch from 19f5552 to 7ef4a23 Compare August 6, 2026 12:31
@ematipico

Copy link
Copy Markdown
Member Author

Suppressions haven't been implemented yet. I will take a bit more time because they are particular here.

Unfortunately, we can't even test this rule via rule docs because in Rust the # in one line is the "hidden line" operator. I'll have to come up with something.

@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

🤖 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 `@xtask/codegen/src/generate_configuration.rs`:
- Around line 282-283: Update generate_rules_configuration to invoke
biome_markdown_analyze::visit_registry for both its lint and assist visitors,
matching the existing calls in generate_rule_options. Keep the visitor
registrations in generate_rule_options unchanged so generated linter and assist
configurations include Markdown rules.
🪄 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: 692d10ce-5661-4dc4-9a04-fcb1f666259b

📥 Commits

Reviewing files that changed from the base of the PR and between 1fb564b and 7ef4a23.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock and included by **
📒 Files selected for processing (3)
  • crates/biome_markdown_analyze/src/lint/nursery/use_consistent_header_level.rs
  • xtask/codegen/Cargo.toml
  • xtask/codegen/src/generate_configuration.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • crates/biome_markdown_analyze/src/lint/nursery/use_consistent_header_level.rs

Comment thread xtask/codegen/src/generate_configuration.rs
@ematipico
ematipico merged commit 51997e6 into main Aug 6, 2026
42 of 43 checks passed
@ematipico
ematipico deleted the feat/markdown-linter branch August 6, 2026 13:14
@Netail

Netail commented Aug 6, 2026

Copy link
Copy Markdown
Member

Isn't the official markdown syntax naming Heading instead of Header?

@ematipico

Copy link
Copy Markdown
Member Author

Isn't the official markdown syntax naming Heading instead of Header?

I don't know, but what kind changes do you want? Feel free to suggest them and I will create a PR

@Netail

Netail commented Aug 7, 2026

Copy link
Copy Markdown
Member

MdHeader -> MdHeading

useConsistentHeaderLevel -> useConsistentHeadingLevel

If that makes sense ofc 😅

@ematipico

Copy link
Copy Markdown
Member Author

Yeah it does. I'll shoot a PR sson

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

Labels

A-CLI Area: CLI A-Diagnostic Area: diagnostocis A-Formatter Area: formatter A-Linter Area: linter A-Parser Area: parser A-Project Area: project A-Tooling Area: internal tools L-Markdown Language: Markdown

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants