Skip to content

feat(md/fmt): better formatting for some cases#9917

Merged
ematipico merged 1 commit intomainfrom
feat/markdown-fmt
Apr 11, 2026
Merged

feat(md/fmt): better formatting for some cases#9917
ematipico merged 1 commit intomainfrom
feat/markdown-fmt

Conversation

@ematipico
Copy link
Copy Markdown
Member

Summary

Closes #9481

This improves MD formatting in various areas:

  • italic formatting: when inside an image reference, we keep the fence as is
  • newlines are now trimmed from the document
  • auto links like inside a link component are now trimmed. It falls back into trim all otherwise
  • quote block now add a space if there are no spaces, or if there are only white spaces
  • parsing of hardlines

Test Plan

Many prettier tests have been deleted.

Added new tests

Docs

@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Apr 11, 2026

⚠️ No Changeset found

Latest commit: 2e73ebb

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 added A-Parser Area: parser A-Formatter Area: formatter L-Markdown Language: Markdown labels Apr 11, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 11, 2026

Walkthrough

This PR makes targeted formatting and parsing changes in the Markdown formatter and lexer: block and inline block lists now support a trim option and propagate trim behaviour to newline formatting; a new TrimMode::AutoLinkLike and a TextPrintMode predicate enable angle-bracket auto-link-like trimming; several auxiliary formatters (inline-image destination, inline-italic, reference-image, reference-link-label, quote-prefix) were rewritten to use structural field formatting or adjusted to preserve original syntax in specific ancestors; the newline formatter gained options to emit removed tokens conditionally; the lexer’s hard-line-break detection now treats trailing spaces at end-of-input as a potential hard break. Tests were added/expanded for inline images, italics, links and top-of-file newlines.

Possibly related PRs

Suggested reviewers

  • dyc3
🚥 Pre-merge checks | ✅ 3 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Title check ❓ Inconclusive The title is vague and doesn't clearly convey the specific improvements. Terms like 'better formatting for some cases' are non-descriptive. Consider using a more descriptive title that highlights key changes, e.g. 'feat(md/fmt): improve hard-line, newline, and auto-link formatting' or similar.
✅ Passed checks (3 passed)
Check name Status Explanation
Description check ✅ Passed The description clearly relates to the changeset, providing specific details about formatting improvements and linking to issue #9481.
Linked Issues check ✅ Passed The PR addresses issue #9481's requirement to trim trailing hard-line markers when followed by empty lines, implementing improved newline trimming and hard-line parsing.
Out of Scope Changes check ✅ Passed All changes relate to the linked issue: document newline trimming, auto-link formatting, italic preservation in references, quote spacing, and hard-line parsing improvements.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/markdown-fmt

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

@ematipico ematipico requested review from a team April 11, 2026 08:27
@github-actions
Copy link
Copy Markdown
Contributor

Parser conformance results on

js/262

Test result main count This PR count Difference
Total 53178 53178 0
Passed 51958 51958 0
Failed 1178 1178 0
Panics 42 42 0
Coverage 97.71% 97.71% 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 640 640 0
Passed 569 569 0
Failed 71 71 0
Panics 0 0 0
Coverage 88.91% 88.91% 0.00%

ts/microsoft

Test result main count This PR count Difference
Total 18876 18876 0
Passed 13014 13014 0
Failed 5861 5861 0
Panics 1 1 0
Coverage 68.94% 68.94% 0.00%

@codspeed-hq
Copy link
Copy Markdown

codspeed-hq bot commented Apr 11, 2026

Merging this PR will not alter performance

✅ 28 untouched benchmarks
⏩ 228 skipped benchmarks1


Comparing feat/markdown-fmt (2e73ebb) with main (4d9ac51)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 (3f4d103) during the generation of this report, so 4d9ac51 was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (3)
crates/biome_markdown_formatter/tests/quick_test.rs (1)

24-32: Consider asserting idempotence, not just printing it.

You already do the second pass — adding an equality assertion would turn this from a debug helper into a guardrail. Logs are charming; assertions are loyal.

Possible follow-up tweak
-    let output2 = result2.unwrap();
-    eprintln!("Re-formatted:\n{}", output2.print().unwrap().as_code());
+    let output2 = result2.unwrap().print().unwrap();
+    eprintln!("Re-formatted:\n{}", output2.as_code());
+    assert_eq!(output.as_code(), output2.as_code());
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/biome_markdown_formatter/tests/quick_test.rs` around lines 24 - 32,
Add an assertion to enforce idempotence by comparing the first-format result
with the second-format result instead of only printing them: after computing
output (from format_node -> output.print().as_code()) and output2 (from second
format_node -> output2.print().as_code()), assert they are equal (or assert
their printed strings are equal) so the test fails if formatting isn't stable;
reference the existing variables and functions parse_markdown, reparse.syntax(),
biome_formatter::format_node, output, output2, and their .print().as_code()
results when adding the equality check.
crates/biome_markdown_parser/src/lexer/mod.rs (1)

1210-1233: Doc contract is now slightly stale.

is_potential_hard_line_break now returns true for EOF as well, but the doc still says “followed by a newline”. Worth aligning to avoid future surprises.

Suggested doc tweak
-    /// Returns true if there are 2+ spaces followed by a newline.
+    /// Returns true if there are 2+ spaces followed by a newline,
+    /// or EOF (to split trailing spaces from preceding text).
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/biome_markdown_parser/src/lexer/mod.rs` around lines 1210 - 1233, The
doc comment for is_potential_hard_line_break is stale: the function treats EOF
(trailing spaces at end-of-file) as a valid "potential hard line break" and
returns true for EOF as well as newline/CR, but the comment still says "followed
by a newline". Update the doc comment on fn is_potential_hard_line_break to
state that it returns true for 2+ spaces followed by a newline, CR, or EOF (to
reflect the special-case behavior for trailing spaces), and keep the existing
explanation about splitting trailing spaces for formatter/idempotency.
crates/biome_markdown_formatter/tests/specs/markdown/newline.md (1)

1-6: Useful test, but add the exact #9481 shape too.

This covers leading-newline trimming well. I’d also add a case with a hard-line marker immediately followed by an empty line, so the regression is pinned directly (and future maintainers sleep better).

As per coding guidelines: "All code changes MUST include appropriate tests ... and bug fixes require tests that reproduce and validate the fix."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/biome_markdown_formatter/tests/specs/markdown/newline.md` around lines
1 - 6, Add a second test case to the existing newline.md spec that reproduces
the exact `#9481` shape: include a content block starting with a hard-line marker
(a line that ends with the Markdown hard line-break marker, e.g., two trailing
spaces) immediately followed by an empty line, then the header text; ensure this
file (crates/biome_markdown_formatter/tests/specs/markdown/newline.md) contains
both the original leading-newline trimming case and the new hard-line+empty-line
case so the formatter's handling of a hard-line marker followed by a blank line
is explicitly tested.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@crates/biome_markdown_formatter/src/markdown/lists/inline_item_list.rs`:
- Around line 103-126: The current auto-link detection inspects raw first/last
inline items, so wrap detection should instead find the first and last non-empty
items before testing for '<' and '>' to avoid missing links wrapped in
surrounding whitespace; change the logic that builds items and computes
starts_with_lt / ends_with_gt to locate the first/last non-empty AnyMdInline
(ignoring whitespace/textual tokens that are trimmed) and test those with
AnyMdInline::MdTextual and value_token().is_ok_and(|tok| tok.text() == "<" /
">") to set is_auto_link, leaving the rest of the branch (calling
fmt_trim_all(node, f) when not auto-link and the joiner entry behavior) intact.

In `@crates/biome_markdown_formatter/src/shared.rs`:
- Around line 38-40: Fix the tiny typo in the TrimMode::AutoLinkLike doc
comment: change the phrase "If no link has been detected, if falls back to
[Self::All]" to "If no link has been detected, it falls back to [Self::All]" in
the TrimMode::AutoLinkLike documentation string so the wording is correct.

---

Nitpick comments:
In `@crates/biome_markdown_formatter/tests/quick_test.rs`:
- Around line 24-32: Add an assertion to enforce idempotence by comparing the
first-format result with the second-format result instead of only printing them:
after computing output (from format_node -> output.print().as_code()) and
output2 (from second format_node -> output2.print().as_code()), assert they are
equal (or assert their printed strings are equal) so the test fails if
formatting isn't stable; reference the existing variables and functions
parse_markdown, reparse.syntax(), biome_formatter::format_node, output, output2,
and their .print().as_code() results when adding the equality check.

In `@crates/biome_markdown_formatter/tests/specs/markdown/newline.md`:
- Around line 1-6: Add a second test case to the existing newline.md spec that
reproduces the exact `#9481` shape: include a content block starting with a
hard-line marker (a line that ends with the Markdown hard line-break marker,
e.g., two trailing spaces) immediately followed by an empty line, then the
header text; ensure this file
(crates/biome_markdown_formatter/tests/specs/markdown/newline.md) contains both
the original leading-newline trimming case and the new hard-line+empty-line case
so the formatter's handling of a hard-line marker followed by a blank line is
explicitly tested.

In `@crates/biome_markdown_parser/src/lexer/mod.rs`:
- Around line 1210-1233: The doc comment for is_potential_hard_line_break is
stale: the function treats EOF (trailing spaces at end-of-file) as a valid
"potential hard line break" and returns true for EOF as well as newline/CR, but
the comment still says "followed by a newline". Update the doc comment on fn
is_potential_hard_line_break to state that it returns true for 2+ spaces
followed by a newline, CR, or EOF (to reflect the special-case behavior for
trailing spaces), and keep the existing explanation about splitting trailing
spaces for formatter/idempotency.
🪄 Autofix (Beta)

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: 302d9516-10db-4df1-8a6c-7602a8e25717

📥 Commits

Reviewing files that changed from the base of the PR and between 4d9ac51 and 2bd35ff.

⛔ Files ignored due to path filters (55)
  • crates/biome_markdown_formatter/tests/specs/markdown/blockquote.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/markdown/hard_line.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/markdown/inline_image.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/markdown/inline_italic.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/markdown/inline_links.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/markdown/newline.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/blockquote/code.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/blockquote/nested.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/blockquote/notext-end.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/code/additional-space.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/code/mdn-auth-api.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/code/mdn-background-1.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/code/mdn-background-2.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/code/mdn-background-3.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/code/mdn-background-4.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/code/mdn-background-5.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/code/mdn-background-6.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/code/mdn-background-7.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/code/mdn-background-8.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/code/mdn-background-9.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/code/mdn-filter-1.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/code/mdn-filter-2.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/code/mdn-font-face-1.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/code/mdn-font-face-2.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/code/mdn-grid-auto-columns.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/code/mdn-import.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/code/mdn-mask-image.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/code/mdn-padding-1.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/code/mdn-padding-2.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/code/mdn-transform.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/code/mdn-unicode-range.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/link/encodedLink.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/liquid/unbalanced-mismatched-braces.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/liquid/unbalanced-object-close-only.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/liquid/unbalanced-object-open-only.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/liquid/unbalanced-template-close-only.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/liquid/unbalanced-template-open-only.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/list/combined-lists.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/list/issue-7846.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/long-table/long-table.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/markdown/real-world-case.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/multiparser-json/invalid-json.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/spec/commonmark-0.30-example-337.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/spec/example-187.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/spec/example-189.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/spec/example-211.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/spec/example-220.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/spec/example-48.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/spec/example-543.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/spec/example-65.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/spec/example-85.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/table/issue-15572.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/wiki-link/extra-brackets-leading.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/wiki-link/extra-brackets-trailing.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/wiki-link/extra-brackets.md.snap is excluded by !**/*.snap and included by **
📒 Files selected for processing (16)
  • crates/biome_markdown_formatter/src/markdown/auxiliary/document.rs
  • crates/biome_markdown_formatter/src/markdown/auxiliary/inline_image.rs
  • crates/biome_markdown_formatter/src/markdown/auxiliary/inline_italic.rs
  • crates/biome_markdown_formatter/src/markdown/auxiliary/newline.rs
  • crates/biome_markdown_formatter/src/markdown/auxiliary/quote_prefix.rs
  • crates/biome_markdown_formatter/src/markdown/auxiliary/reference_image.rs
  • crates/biome_markdown_formatter/src/markdown/auxiliary/reference_link_label.rs
  • crates/biome_markdown_formatter/src/markdown/lists/block_list.rs
  • crates/biome_markdown_formatter/src/markdown/lists/inline_item_list.rs
  • crates/biome_markdown_formatter/src/shared.rs
  • crates/biome_markdown_formatter/tests/quick_test.rs
  • crates/biome_markdown_formatter/tests/specs/markdown/inline_image.md
  • crates/biome_markdown_formatter/tests/specs/markdown/inline_italic.md
  • crates/biome_markdown_formatter/tests/specs/markdown/inline_links.md
  • crates/biome_markdown_formatter/tests/specs/markdown/newline.md
  • crates/biome_markdown_parser/src/lexer/mod.rs

Comment thread crates/biome_markdown_formatter/src/shared.rs
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (2)
crates/biome_markdown_formatter/src/shared.rs (1)

38-40: ⚠️ Potential issue | 🟡 Minor

Fix the tiny doc typo in TrimMode::AutoLinkLike.

“if falls back” should be “it falls back”.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/biome_markdown_formatter/src/shared.rs` around lines 38 - 40, Typo in
the doc comment for the enum variant TrimMode::AutoLinkLike: change "if falls
back to [Self::All]" to "it falls back to [Self::All]". Locate the TrimMode enum
definition and update the docstring above the AutoLinkLike variant to replace
"if" with "it" so the sentence reads correctly.
crates/biome_markdown_formatter/src/markdown/lists/inline_item_list.rs (1)

105-126: ⚠️ Potential issue | 🟠 Major

Detect and strip auto-link wrappers using first/last non-empty items.

Current logic checks raw list edges, so inputs like ( <https://example.com> ) can miss the auto-link path and keep angle brackets. Use content boundaries for both detection and wrapper removal.

Suggested fix
-        let starts_with_lt = matches!(items.first(), Some(AnyMdInline::MdTextual(t)) if t.value_token().is_ok_and(|tok| tok.text() == "<"));
-        let ends_with_gt = matches!(items.last(), Some(AnyMdInline::MdTextual(t)) if t.value_token().is_ok_and(|tok| tok.text() == ">"));
-
-        let is_auto_link = starts_with_lt && ends_with_gt && items.len() > 2;
+        let is_content = |item: &AnyMdInline| match item {
+            AnyMdInline::MdTextual(text) => !text.is_empty().unwrap_or_default(),
+            AnyMdInline::MdHardLine(_) => false,
+            _ => true,
+        };
+
+        let first_content = items.iter().position(&is_content);
+        let last_content = items.iter().rposition(is_content);
+
+        let starts_with_lt = first_content
+            .and_then(|index| items.get(index))
+            .is_some_and(|item| {
+                matches!(item, AnyMdInline::MdTextual(t) if t.value_token().is_ok_and(|tok| tok.text() == "<"))
+            });
+        let ends_with_gt = last_content
+            .and_then(|index| items.get(index))
+            .is_some_and(|item| {
+                matches!(item, AnyMdInline::MdTextual(t) if t.value_token().is_ok_and(|tok| tok.text() == ">"))
+            });
+
+        let Some((first_content, last_content)) = first_content.zip(last_content) else {
+            return self.fmt_trim_all(node, f);
+        };
+        let is_auto_link = starts_with_lt && ends_with_gt && last_content > first_content + 1;
@@
-        for (index, item) in items.iter().enumerate() {
-            if (index == 0 || index == items.len() - 1)
+        for (index, item) in items.iter().enumerate() {
+            if (index == first_content || index == last_content)
                 && let AnyMdInline::MdTextual(text) = item
             {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/biome_markdown_formatter/src/markdown/lists/inline_item_list.rs`
around lines 105 - 126, The auto-link detection and removal currently use raw
first/last list elements (starts_with_lt / ends_with_gt and is_auto_link) which
fails when leading/trailing whitespace or wrapper nodes exist; update detection
to find the first and last non-empty textual items (e.g., locate the first and
last AnyMdInline::MdTextual whose value_token().is_ok_and(|tok|
!tok.text().trim().is_empty())) and use those indices to set starts_with_lt /
ends_with_gt and to decide is_auto_link; when building the output with joiner,
skip or strip only those identified wrapper text nodes by index (instead of
assuming items[0] and items[len-1]) and still call
text.format().with_options(FormatMdTextualOptions { should_remove: true,
..Default::default() }) for the actual wrapper nodes, otherwise fall back to
self.fmt_trim_all(node, f).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@crates/biome_markdown_formatter/src/markdown/lists/inline_item_list.rs`:
- Around line 105-126: The auto-link detection and removal currently use raw
first/last list elements (starts_with_lt / ends_with_gt and is_auto_link) which
fails when leading/trailing whitespace or wrapper nodes exist; update detection
to find the first and last non-empty textual items (e.g., locate the first and
last AnyMdInline::MdTextual whose value_token().is_ok_and(|tok|
!tok.text().trim().is_empty())) and use those indices to set starts_with_lt /
ends_with_gt and to decide is_auto_link; when building the output with joiner,
skip or strip only those identified wrapper text nodes by index (instead of
assuming items[0] and items[len-1]) and still call
text.format().with_options(FormatMdTextualOptions { should_remove: true,
..Default::default() }) for the actual wrapper nodes, otherwise fall back to
self.fmt_trim_all(node, f).

In `@crates/biome_markdown_formatter/src/shared.rs`:
- Around line 38-40: Typo in the doc comment for the enum variant
TrimMode::AutoLinkLike: change "if falls back to [Self::All]" to "it falls back
to [Self::All]". Locate the TrimMode enum definition and update the docstring
above the AutoLinkLike variant to replace "if" with "it" so the sentence reads
correctly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 06154209-009d-470e-9715-2f5c97eed2bc

📥 Commits

Reviewing files that changed from the base of the PR and between 2bd35ff and 2e73ebb.

⛔ Files ignored due to path filters (55)
  • crates/biome_markdown_formatter/tests/specs/markdown/blockquote.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/markdown/hard_line.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/markdown/inline_image.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/markdown/inline_italic.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/markdown/inline_links.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/markdown/newline.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/blockquote/code.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/blockquote/nested.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/blockquote/notext-end.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/code/additional-space.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/code/mdn-auth-api.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/code/mdn-background-1.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/code/mdn-background-2.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/code/mdn-background-3.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/code/mdn-background-4.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/code/mdn-background-5.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/code/mdn-background-6.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/code/mdn-background-7.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/code/mdn-background-8.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/code/mdn-background-9.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/code/mdn-filter-1.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/code/mdn-filter-2.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/code/mdn-font-face-1.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/code/mdn-font-face-2.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/code/mdn-grid-auto-columns.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/code/mdn-import.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/code/mdn-mask-image.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/code/mdn-padding-1.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/code/mdn-padding-2.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/code/mdn-transform.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/code/mdn-unicode-range.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/link/encodedLink.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/liquid/unbalanced-mismatched-braces.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/liquid/unbalanced-object-close-only.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/liquid/unbalanced-object-open-only.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/liquid/unbalanced-template-close-only.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/liquid/unbalanced-template-open-only.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/list/combined-lists.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/list/issue-7846.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/long-table/long-table.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/markdown/real-world-case.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/multiparser-json/invalid-json.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/spec/commonmark-0.30-example-337.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/spec/example-187.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/spec/example-189.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/spec/example-211.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/spec/example-220.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/spec/example-48.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/spec/example-543.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/spec/example-65.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/spec/example-85.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/table/issue-15572.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/wiki-link/extra-brackets-leading.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/wiki-link/extra-brackets-trailing.md.snap is excluded by !**/*.snap and included by **
  • crates/biome_markdown_formatter/tests/specs/prettier/markdown/wiki-link/extra-brackets.md.snap is excluded by !**/*.snap and included by **
📒 Files selected for processing (16)
  • crates/biome_markdown_formatter/src/markdown/auxiliary/document.rs
  • crates/biome_markdown_formatter/src/markdown/auxiliary/inline_image.rs
  • crates/biome_markdown_formatter/src/markdown/auxiliary/inline_italic.rs
  • crates/biome_markdown_formatter/src/markdown/auxiliary/newline.rs
  • crates/biome_markdown_formatter/src/markdown/auxiliary/quote_prefix.rs
  • crates/biome_markdown_formatter/src/markdown/auxiliary/reference_image.rs
  • crates/biome_markdown_formatter/src/markdown/auxiliary/reference_link_label.rs
  • crates/biome_markdown_formatter/src/markdown/lists/block_list.rs
  • crates/biome_markdown_formatter/src/markdown/lists/inline_item_list.rs
  • crates/biome_markdown_formatter/src/shared.rs
  • crates/biome_markdown_formatter/tests/quick_test.rs
  • crates/biome_markdown_formatter/tests/specs/markdown/inline_image.md
  • crates/biome_markdown_formatter/tests/specs/markdown/inline_italic.md
  • crates/biome_markdown_formatter/tests/specs/markdown/inline_links.md
  • crates/biome_markdown_formatter/tests/specs/markdown/newline.md
  • crates/biome_markdown_parser/src/lexer/mod.rs
✅ Files skipped from review due to trivial changes (5)
  • crates/biome_markdown_formatter/tests/specs/markdown/inline_links.md
  • crates/biome_markdown_formatter/tests/specs/markdown/newline.md
  • crates/biome_markdown_formatter/tests/quick_test.rs
  • crates/biome_markdown_formatter/tests/specs/markdown/inline_image.md
  • crates/biome_markdown_formatter/tests/specs/markdown/inline_italic.md
🚧 Files skipped from review as they are similar to previous changes (9)
  • crates/biome_markdown_formatter/src/markdown/auxiliary/inline_image.rs
  • crates/biome_markdown_formatter/src/markdown/auxiliary/quote_prefix.rs
  • crates/biome_markdown_formatter/src/markdown/auxiliary/document.rs
  • crates/biome_markdown_formatter/src/markdown/auxiliary/reference_link_label.rs
  • crates/biome_markdown_parser/src/lexer/mod.rs
  • crates/biome_markdown_formatter/src/markdown/auxiliary/reference_image.rs
  • crates/biome_markdown_formatter/src/markdown/auxiliary/inline_italic.rs
  • crates/biome_markdown_formatter/src/markdown/lists/block_list.rs
  • crates/biome_markdown_formatter/src/markdown/auxiliary/newline.rs

Copy link
Copy Markdown
Contributor

@dyc3 dyc3 left a comment

Choose a reason for hiding this comment

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

Nice progress on the prettier snapshots

@ematipico ematipico merged commit 79db7f9 into main Apr 11, 2026
17 checks passed
@ematipico ematipico deleted the feat/markdown-fmt branch April 11, 2026 16:15
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.

Markdown formatter: trim trailing hard-line spaces when followed by an empty line

2 participants