Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 55 additions & 11 deletions .claude/skills/formatter-development/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: formatter-development
description: Guide for implementing formatting rules using Biome's IR-based formatter infrastructure. Use when implementing formatting for new syntax nodes, handling comments in formatted output, or comparing Biome's formatting against Prettier for JavaScript, CSS, JSON, HTML, or other languages.
description: Guide for implementing formatting rules using Biome's IR-based formatter infrastructure. Use when implementing formatting for new syntax nodes, handling comments in formatted output, writing or debugging formatter snapshot tests, diagnosing idempotency failures, or comparing Biome's formatting against Prettier for JavaScript, CSS, JSON, HTML, Markdown, or other languages.
compatibility: Designed for coding agents working on the Biome codebase (github.com/biomejs/biome).
---

Expand Down Expand Up @@ -156,6 +156,60 @@ echo 'const x = 1' | bun packages/prettier-compare/bin/prettier-compare.js --reb

**Always use `--rebuild`** to ensure WASM bundle matches your Rust changes.

### Format and Build

After changes:

```shell
just f # Format Rust code
just l # Lint
just gen-formatter # Regenerate formatter infrastructure if needed
Comment on lines +163 to +166
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot Apr 16, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Use the language-specific gen-formatter command here.

just gen-formatter nudges readers towards the wrong invocation; this doc should match the repo workflow and spell out the <lang> argument.

Suggested tweak
-just gen-formatter  # Regenerate formatter infrastructure if needed
+just gen-formatter <lang>  # Regenerate formatter infrastructure if needed

As per coding guidelines, Run 'just gen-formatter <lang>' before opening a PR when making changes to formatter implementations in *_formatter crates.

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

In @.claude/skills/formatter-development/SKILL.md around lines 163 - 166, The
docs currently show a generic invocation "just gen-formatter" which is
incorrect; update the commands block and any references to use the
language-specific form "just gen-formatter <lang>" (e.g., replace the listed
`just gen-formatter` line and any guidance text) and add the explicit guidance
sentence "Run 'just gen-formatter <lang>' before opening a PR when making
changes to formatter implementations in *_formatter crates." to ensure the
README matches the repo workflow; look for the commands block containing `just
f`, `just l`, `just gen-formatter` and the surrounding paragraph in SKILL.md to
make this change.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

@coderabbitai did you verify your claim? I believe gen-formatter doesn't accept a lang argument

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.

🧩 Analysis chain

Let me check the actual justfile to verify this.


🏁 Script executed:

#!/bin/bash
grep -A 10 "gen-formatter" justfile 2>/dev/null || grep -A 10 "gen-formatter" Justfile 2>/dev/null

Length of output: 304


@ematipico, you're right — I should have verified before flagging. The justfile defines:

gen-formatter *args='':
  cargo run -p xtask_codegen -- formatter {{args}}

The *args='' makes the argument optional, so just gen-formatter (no lang) is perfectly valid. My suggestion was wrong, and the existing doc is correct as-is. I'll update the learning.

Sorry for the noise! 🐇


⛔ Learnings removed
Learnt from: CR
Repo: biomejs/biome PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-23T19:15:52.743Z
Learning: Applies to crates/*_formatter/src/**/*.rs : Run 'just gen-formatter <lang>' before opening a PR when making changes to formatter implementations in `*_formatter` crates.

🧠 Learnings used
Learnt from: CR
Repo: biomejs/biome PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-23T19:15:52.743Z
Learning: Applies to crates/*_formatter/src/**/*.rs : Run 'just gen-formatter <lang>' before opening a PR when making changes to formatter implementations in `*_formatter` crates.

Learnt from: CR
Repo: biomejs/biome PR: 0
File: CONTRIBUTING.md:0-0
Timestamp: 2026-04-06T21:31:01.863Z
Learning: Applies to **/*.{rs,toml} : Run `just f` (alias for `just format`) to format Rust and TOML files before committing and opening a PR

Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2026-04-07T12:58:59.356Z
Learning: Run `just f` to format code and `just l` to lint code before submitting rule changes

Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_formatter/CONTRIBUTING.md:0-0
Timestamp: 2026-02-05T16:31:32.222Z
Learning: Run `just gen-formatter` to automatically generate formatter boilerplate code in `crates/biome_<language>_formatter/src/` with module structure and default `FormatNodeRule<N>` implementations

Learnt from: CR
Repo: biomejs/biome PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-23T19:15:52.743Z
Learning: Applies to **/*.{rs,ts,tsx,js,jsx,json,md} : Always run 'just f' to format code and 'just l' to lint code before committing, ensuring all code follows project standards.

Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_formatter/CONTRIBUTING.md:0-0
Timestamp: 2026-02-05T16:31:32.222Z
Learning: Create formatter crate structure using `just new-crate biome_<language>_formatter` command

Learnt from: CR
Repo: biomejs/biome PR: 0
File: CONTRIBUTING.md:0-0
Timestamp: 2026-04-06T21:31:01.863Z
Learning: Applies to **/*.rs : Run `just l` (alias for `just lint`) to lint the whole project before committing and opening a PR

Learnt from: CR
Repo: biomejs/biome PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-23T19:15:52.743Z
Learning: Applies to crates/*_analyze/src/**/*.rs : Run 'just gen-rules' and 'just gen-configuration' before opening a PR when making changes to lint rules in `*_analyze` crates.

Learnt from: CR
Repo: biomejs/biome PR: 0
File: CONTRIBUTING.md:0-0
Timestamp: 2026-04-06T21:31:01.863Z
Learning: Applies to packages/biomejs/** : When developing Node.js packages, build packages after implementing features/bug fixes so tests run against compiled files; follow the build steps: install tools, build backend-jsonrpc, build WASM, and link bindings

Learnt from: CR
Repo: biomejs/biome PR: 0
File: CONTRIBUTING.md:0-0
Timestamp: 2026-04-06T21:31:01.863Z
Learning: Run `just gen-bindings` when working around the workspace to regenerate code

Learnt from: CR
Repo: biomejs/biome PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-23T19:15:52.743Z
Learning: Applies to **/*.ungram : Run 'just gen-grammar <lang>' before opening a PR when making changes to Grammar `.ungram` files to regenerate the parser and syntax.

Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_formatter/CONTRIBUTING.md:0-0
Timestamp: 2026-02-05T16:31:32.222Z
Learning: Applies to crates/biome_formatter/**/tests/spec_test.rs : In formatter test files, use `tests_macros::gen_tests!` macro to auto-generate test functions for spec files in `tests/specs/<language>/**/*` directory

Learnt from: tidefield
Repo: biomejs/biome PR: 9693
File: crates/biome_markdown_formatter/src/markdown/auxiliary/bullet.rs:24-24
Timestamp: 2026-04-02T09:34:17.898Z
Learning: In `crates/biome_markdown_formatter/` (and biome formatter crates generally), `format_verbatim_node` is only acceptable as initial scaffolding boilerplate when a formatter is first set up. Once active formatting features are being implemented, no new calls to `format_verbatim_node` should be introduced — new feature code must format the node's fields explicitly instead. Per ematipico: its use in feature code is considered a bug. Per tidefield: this applies to all new formatting feature work in biome_markdown_formatter.

Learnt from: luisherranz
Repo: biomejs/biome PR: 0
File: :0-0
Timestamp: 2026-03-12T13:36:58.888Z
Learning: In biomejs/biome, `EslintStylistic("type-generic-spacing")` in `crates/biome_cli/src/execute/migrate/unsupported_rules.rs` is NOT related to the `delimiterSpacing` formatter option. It controls spacing around generic type brackets differently and should not be mapped to `FormatterOption("delimiterSpacing")`.

Learnt from: dyc3
Repo: biomejs/biome PR: 8291
File: crates/biome_html_formatter/tests/specs/prettier/vue/html-vue/elastic-header.html:10-10
Timestamp: 2025-12-04T13:29:49.287Z
Learning: Files under `crates/biome_html_formatter/tests/specs/prettier` are test fixtures synced from Prettier and should not receive detailed code quality reviews (e.g., HTTP vs HTTPS, formatting suggestions, etc.). These files are test data meant to validate formatter behavior and should be preserved as-is.

Learnt from: CR
Repo: biomejs/biome PR: 0
File: CONTRIBUTING.md:0-0
Timestamp: 2026-04-06T21:31:01.863Z
Learning: Applies to .changeset/** : Write changeset descriptions concisely and clearly (1-3 sentences), using past tense for actions performed and present tense for Biome behavior; include examples with inline code snippets or code blocks for new rules, formatter changes, or parser changes

Learnt from: CR
Repo: biomejs/biome PR: 0
File: CONTRIBUTING.md:0-0
Timestamp: 2026-04-06T21:31:01.863Z
Learning: Applies to crates/biome_formatter/**/*.rs : Follow technical guidelines and patterns documented in the biome_formatter CONTRIBUTING.md for formatter implementation and testing

Learnt from: CR
Repo: biomejs/biome PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-23T19:15:52.743Z
Learning: Applies to .changeset/*.md : Write changeset descriptions for end users, not developers. For bug fixes, start with 'Fixed [`#NUMBER`](issue link): ...'. For new features, describe what the feature does and why users care.

Learnt from: CR
Repo: biomejs/biome PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-23T19:15:52.743Z
Learning: Applies to **/*.{rs,ts,tsx,js} : All code changes MUST include appropriate tests: lint rules require snapshot tests in 'tests/specs/{group}/{rule}/', formatters require snapshot tests with valid/invalid cases, parsers require test files covering valid and error cases, and bug fixes require tests that reproduce and validate the fix.

Learnt from: CR
Repo: biomejs/biome PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-23T19:15:52.743Z
Learning: Use concise, precise wording in PR summaries - don't overload reviewers with unnecessary information. Accept verbose summaries only for major refactors, architectural changes, complex features, or breaking changes.

Learnt from: CR
Repo: biomejs/biome PR: 0
File: CONTRIBUTING.md:0-0
Timestamp: 2026-04-06T21:31:01.863Z
Learning: Applies to .changeset/** : For changeset descriptions of bug fixes, reference the issue with a link (e.g., 'Fixed [`#4444`](...)'); for rule references, include links to the rule documentation (e.g., '[useAwesomeThing](https://biomejs.dev/linter/rules/use-awesome-thing/)')

Learnt from: jfmcdowell
Repo: biomejs/biome PR: 0
File: :0-0
Timestamp: 2026-03-30T19:37:09.297Z
Learning: In `crates/biome_markdown_parser/src/syntax/fenced_code_block.rs`, there is an intentional asymmetry between opening and closing fence indent stripping inside list items:
- **Opening fence**: `check_continuation_indent` (called before the fenced code block parser runs) already consumes the list continuation indent, so the opener only needs to emit the remaining 0–3 CommonMark-allowed fence spaces (`MAX_BLOCK_PREFIX_INDENT`). Stripping `list_item_required_indent` in the opener is NOT needed.
- **Closing fence**: processes the raw line without prior consumption, so it must strip `list_item_required_indent + MAX_BLOCK_PREFIX_INDENT` itself (the fix introduced in PR `#9730`).

Learnt from: luisherranz
Repo: biomejs/biome PR: 9719
File: crates/biome_js_formatter/src/js/expressions/call_arguments.rs:0-0
Timestamp: 2026-04-10T15:40:45.681Z
Learning: In biomejs/biome PR `#9719`, the `delimiterSpacing` option for call arguments in `crates/biome_js_formatter/src/js/expressions/call_arguments.rs` intentionally emits spaces inside the parens whenever the opening/closing paren and its adjacent argument stay on the same line — even if the argument's inner content (e.g., a function body) is multiline. Spaces are only suppressed when the argument itself breaks to its own line (i.e., the paren and the argument are NOT on the same line). This applies uniformly to the special-case paths (test calls, React hooks, AMD/CJS, multiline-template-only). Do NOT flag unconditional `space()` calls in these paths as a bug for multiline cases.

Learnt from: tidefield
Repo: biomejs/biome PR: 9480
File: crates/biome_markdown_formatter/src/markdown/auxiliary/hard_line.rs:11-31
Timestamp: 2026-03-14T11:30:13.488Z
Learning: In `crates/biome_markdown_formatter/src/markdown/auxiliary/hard_line.rs`, `FormatMdHardLine::fmt_fields` is responsible for emitting `hard_line_break()` itself (not the caller). As of PR `#9480` (building on `#9331` by ematipico), the function branches on the token text: if it ends with `\` it emits `format_removed(&token)` + `text("\\", ...)` + `hard_line_break()`; otherwise it normalises to two trailing spaces via `format_removed(&token)` + `text("  ", ...)` + `hard_line_break()`. The callers in `inline_item_list.rs` and `paragraph.rs` do NOT add an extra `hard_line_break()`.

```

## Testing infrastructure

The testing infrastructure of the formatters is divided in two main pieces. Internal and external.

The testing infrastructure is designed for catching idempotency cases, which means that each file inside the infrastructure is designed to fail if:
- the final **printed** output differs on a second formatting run.
- the final **IR** output differs on a second formatting run.

**Both must be fixed.**

Run `cargo t` twice. The first run may write or update snapshots; the second run re-formats the just-written output and confirms it is stable. Skipping the second run hides idempotency bugs — a broken formatter can look green on a single pass because the snapshot it wrote matches itself.

### `quick_test.rs`

- Use `quick_test.rs` inside the crate for testing theories and formatting.
- Only modify the `source` string literal inside the test. Do not change the parse/format/assert scaffolding around it — that scaffolding already verifies idempotency and prints the CST and IR you need for debugging.

### External infra

The external infra relies on a human pulling the tests inside the repository, inside the folder `<crate>/tests/prettier`.

Once the tests are ported, the infrastructure produces two files for each original file:
- `<file_name>.<ext>.prettier-snap` which contains the output generated by Prettier at the moment the test was ported.
- `<file_name>.<ext>.snap` which contains three sections
- the input source
- the list of diffs between Prettier and Biome
- the output generated by Biome

The `.snap` file is only created when Biome's output differs from Prettier's. When the two agree, no `.snap` file is written.

The absence of a `.snap` file is **positive** — it means Biome matches Prettier for that input.

### Internal infrastructure

The internal infrastructure relies on creating new test files. For each test, place two snippets in the same file:
- a piece of source code **already formatted** the way Biome should produce it
- the same code in an **unformatted** shape

After running the formatter, both snippets should produce identical output. That identity proves the formatter converges on a canonical form and is idempotent.

**Always create new test cases when implementing a feature or fixing a bug.** Internal tests exercise the exact shape you care about and survive even if the Prettier corpus changes.

**Do not rely on Prettier `.snap` files disappearing as proof of correctness.** A missing `.snap` only means Biome and Prettier agree on that specific ported input. It does not cover the edge cases you introduced — write internal tests for those, and do not delete a Prettier `.snap` to make a diff "go away".

### Create Snapshot Tests

Create test files in `tests/specs/` organized by feature:
Expand Down Expand Up @@ -219,16 +273,6 @@ Create `options.json` in the test folder:

This applies to all test files in that folder.

### Format and Build

After changes:

```shell
just f # Format Rust code
just l # Lint
just gen-formatter # Regenerate formatter infrastructure if needed
```

## Tips

- **format_verbatim_node**: Initial generated code uses this - replace it with proper IR as you implement formatting
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ dhat-heap.json
/crates/biome_css_formatter/report.*
/crates/biome_graphql_formatter/report.*
/crates/biome_js_formatter/report.*
/crates/biome_markdown_formatter/report.*
/crates/biome_html_formatter/report.*
/crates/biome_json_formatter/report.*
/crates/biome_json_formatter/report_incompatible.*

Expand Down
21 changes: 3 additions & 18 deletions crates/biome_html_formatter/src/html/any/attribute_initializer.rs
Original file line number Diff line number Diff line change
@@ -1,32 +1,17 @@
//! This is a generated file. Don't modify it by hand! Run 'cargo codegen formatter' to re-generate the file.

use crate::prelude::*;
use biome_formatter::FormatRuleWithOptions;
use biome_html_syntax::AnyHtmlAttributeInitializer;
#[derive(Debug, Clone, Default)]
pub(crate) struct FormatAnyHtmlAttributeInitializer {
compact: bool,
}

pub(crate) struct FormatAnyHtmlAttributeInitializer;
impl FormatRule<AnyHtmlAttributeInitializer> for FormatAnyHtmlAttributeInitializer {
type Context = HtmlFormatContext;
fn fmt(&self, node: &AnyHtmlAttributeInitializer, f: &mut HtmlFormatter) -> FormatResult<()> {
match node {
AnyHtmlAttributeInitializer::HtmlAttributeSingleTextExpression(node) => {
node.format().with_options(self.compact).fmt(f)
}
AnyHtmlAttributeInitializer::HtmlString(node) => {
node.format().with_options(self.compact).fmt(f)
node.format().fmt(f)
}
AnyHtmlAttributeInitializer::HtmlString(node) => node.format().fmt(f),
}
}
}

impl FormatRuleWithOptions<AnyHtmlAttributeInitializer> for FormatAnyHtmlAttributeInitializer {
type Options = bool;

fn with_options(mut self, options: Self::Options) -> Self {
self.compact = options;
self
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::fmt::Debug;

use crate::prelude::*;
use crate::shared::FmtAnyAttributeInitializer;
use biome_formatter::{CstFormatContext, FormatRuleWithOptions, write};
use biome_html_syntax::{
AnyHtmlAttributeInitializer, HtmlAttributeInitializerClause,
Expand Down Expand Up @@ -155,10 +156,11 @@ impl FormatNodeRule<HtmlAttributeInitializerClause> for FormatHtmlAttributeIniti
CompactKind::Remove => {
let eq_token = eq_token.clone()?;
let value = value.clone()?;
write!(
f,
[format_removed(&eq_token), value.format().with_options(true),]
)?;
let fmt = FmtAnyAttributeInitializer {
node: value,
compact: true,
};
write!(f, [format_removed(&eq_token), &fmt,])?;
Ok(())
}
}
Expand Down
1 change: 1 addition & 0 deletions crates/biome_html_formatter/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ mod generated;
mod html;
pub(crate) mod prelude;
pub(crate) mod separated;
pub(crate) mod shared;
mod svelte;
mod trivia;
pub mod utils;
Expand Down
42 changes: 42 additions & 0 deletions crates/biome_html_formatter/src/shared.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
use crate::AsFormat;
use crate::context::HtmlFormatContext;
use biome_formatter::formatter::Formatter;
use biome_formatter::{Format, FormatResult};
use biome_html_syntax::{AnyHtmlAttributeInitializer, AnySvelteBindingProperty};

pub(crate) struct FmtAnyAttributeInitializer {
pub(crate) node: AnyHtmlAttributeInitializer,
pub(crate) compact: bool,
}

impl Format<HtmlFormatContext> for FmtAnyAttributeInitializer {
fn fmt(&self, f: &mut Formatter<HtmlFormatContext>) -> FormatResult<()> {
match &self.node {
AnyHtmlAttributeInitializer::HtmlAttributeSingleTextExpression(node) => {
node.format().with_options(self.compact).fmt(f)
}
AnyHtmlAttributeInitializer::HtmlString(node) => {
node.format().with_options(self.compact).fmt(f)
}
}
}
}

pub(crate) struct FmtAnySvelteBindingProperty {
pub(crate) node: AnySvelteBindingProperty,
pub(crate) compact: bool,
}

impl Format<HtmlFormatContext> for FmtAnySvelteBindingProperty {
fn fmt(&self, f: &mut Formatter<HtmlFormatContext>) -> FormatResult<()> {
match &self.node {
AnySvelteBindingProperty::SvelteLiteral(node) => {
node.format().with_options(self.compact).fmt(f)
}
AnySvelteBindingProperty::SvelteMemberProperty(node) => node.format().fmt(f),
AnySvelteBindingProperty::SvelteName(node) => {
node.format().with_options(self.compact).fmt(f)
}
}
}
}
24 changes: 3 additions & 21 deletions crates/biome_html_formatter/src/svelte/any/binding_property.rs
Original file line number Diff line number Diff line change
@@ -1,34 +1,16 @@
//! This is a generated file. Don't modify it by hand! Run 'cargo codegen formatter' to re-generate the file.

use crate::prelude::*;
use biome_formatter::FormatRuleWithOptions;
use biome_html_syntax::AnySvelteBindingProperty;
#[derive(Debug, Clone, Default)]
pub(crate) struct FormatAnySvelteBindingProperty {
/// Whether it should be formatted in compact mode. In compact mode, all tokens and children
/// are removed
pub compact: bool,
}
pub(crate) struct FormatAnySvelteBindingProperty;
impl FormatRule<AnySvelteBindingProperty> for FormatAnySvelteBindingProperty {
type Context = HtmlFormatContext;
fn fmt(&self, node: &AnySvelteBindingProperty, f: &mut HtmlFormatter) -> FormatResult<()> {
match node {
AnySvelteBindingProperty::SvelteLiteral(node) => {
node.format().with_options(self.compact).fmt(f)
}
AnySvelteBindingProperty::SvelteLiteral(node) => node.format().fmt(f),
AnySvelteBindingProperty::SvelteMemberProperty(node) => node.format().fmt(f),
AnySvelteBindingProperty::SvelteName(node) => {
node.format().with_options(self.compact).fmt(f)
}
AnySvelteBindingProperty::SvelteName(node) => node.format().fmt(f),
}
}
}

impl FormatRuleWithOptions<AnySvelteBindingProperty> for FormatAnySvelteBindingProperty {
type Options = bool;

fn with_options(mut self, options: Self::Options) -> Self {
self.compact = options;
self
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use crate::html::auxiliary::attribute_initializer_clause::{
CompactKind, FormatHtmlAttributeInitializerClauseOptions,
};
use crate::prelude::*;
use crate::shared::FmtAnySvelteBindingProperty;
use biome_formatter::{FormatRuleWithOptions, write};
use biome_html_syntax::{
AnySvelteBindingProperty, SvelteDirectiveValue, SvelteDirectiveValueFields,
Expand Down Expand Up @@ -67,6 +68,10 @@ impl FormatSvelteDirectiveValue {
return Ok(false);
}

let property = FmtAnySvelteBindingProperty {
node: property,
compact: true,
};
write!(
f,
[
Expand All @@ -79,7 +84,7 @@ impl FormatSvelteDirectiveValue {
tag_name: None
}
),
property.format().with_options(true),
&property,
modifiers.format().with_options(true)
]
)?;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ impl FormatNodeRule<MdAutolink> for FormatMdAutolink {
.format()
.with_options(FormatMdFormatInlineItemListOptions {
print_mode: TextPrintMode::Trim(TrimMode::NormalizeWords),
keep_fences_in_italics: false
keep_fences_in_italics: false,
inside_list: false,
}),
r_angle_token.format()
]
Expand Down
13 changes: 11 additions & 2 deletions crates/biome_markdown_formatter/src/markdown/auxiliary/bullet.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
use crate::markdown::auxiliary::list_marker_prefix::FormatMdListMarkerPrefixOptions;
use crate::markdown::lists::block_list::FormatMdBlockListOptions;
use crate::prelude::*;
use crate::shared::TextPrintMode;
use biome_formatter::write;
use biome_markdown_syntax::{
AnyMdBlock, AnyMdLeafBlock, MarkdownSyntaxKind, MdBullet, MdBulletFields,
};

#[derive(Debug, Clone, Default)]
pub(crate) struct FormatMdBullet;
impl FormatNodeRule<MdBullet> for FormatMdBullet {
Expand All @@ -19,6 +22,7 @@ impl FormatNodeRule<MdBullet> for FormatMdBullet {
// but still format content through child formatters.
let target_marker = if marker.kind() == MarkdownSyntaxKind::MINUS
|| first_block_is_dash_thematic_break(&content)
|| marker.kind() == MarkdownSyntaxKind::MD_ORDERED_LIST_MARKER
{
None
} else {
Expand All @@ -31,8 +35,13 @@ impl FormatNodeRule<MdBullet> for FormatMdBullet {
.format()
.with_options(FormatMdListMarkerPrefixOptions { target_marker })]
)?;
content.format().fmt(f)?;
Ok(())
write!(
f,
[content.format().with_options(FormatMdBlockListOptions {
paragraph_print_mode: TextPrintMode::trim_keep_leading_spaces(),
trim: false,
})]
)
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::markdown::lists::block_list::FormatMdBlockListOptions;
use crate::prelude::*;
use crate::shared::TextPrintMode;
use biome_formatter::write;
use biome_markdown_syntax::{MdDocument, MdDocumentFields};

Expand All @@ -20,9 +21,10 @@ impl FormatNodeRule<MdDocument> for FormatMdDocument {
write!(
f,
[
value
.format()
.with_options(FormatMdBlockListOptions { trim: true }),
value.format().with_options(FormatMdBlockListOptions {
paragraph_print_mode: TextPrintMode::Pristine,
trim: true
}),
format_removed(&eof_token?)
]
)?;
Expand Down
Loading
Loading