Skip to content

Conversation

@siketyan
Copy link
Member

@siketyan siketyan commented Jan 2, 2026

Summary

Closes #8605

The django-style interpolations ({{ expr }}) can be placed both inside and outside HTML tags. For example:

<a {{ expr }}>foo</a>
<a>{{ expr }}</a>

Biome have used the same syntax kind HTML_DOUBLE_TEXT_EXPRESSION for the both usages. However, it's ambiguous when converting it to a bogus node:

fn to_bogus(&self) -> Self {
match self {
kind if AnyHtmlAttribute::can_cast(*kind) => HTML_BOGUS_ATTRIBUTE,
kind if AnyHtmlElement::can_cast(*kind) => HTML_BOGUS_ELEMENT,
kind if AnyAstroFrontmatterElement::can_cast(*kind) => ASTRO_BOGUS_FRONTMATTER,
kind if AnyHtmlTextExpression::can_cast(*kind) => HTML_BOGUS_TEXT_EXPRESSION,
HTML_CLOSING_ELEMENT => HTML_BOGUS_ELEMENT,
_ => HTML_BOGUS,
}
}

The syntax kind does not have the context in where the node is placed, and it tried to place HTML_BOGUS_ATTRIBUTE under HTML_ELEMENT_LIST, which is invalid. To distinguish these, I added new syntax kinds HTML_ATTRIBUTE_DOUBLE_TEXT_EXPRESSION and HTML_ATTRIBUTE_SINGLE_TEXT_EXPRESSION, which are only used inside a HTML tag and possibly at an attribute.

Test Plan

Added a snapshot test. Plus manually tested with the reproduction in the issue.

Docs

N/A

@siketyan siketyan requested review from a team January 2, 2026 08:04
@siketyan siketyan self-assigned this Jan 2, 2026
@changeset-bot
Copy link

changeset-bot bot commented Jan 2, 2026

🦋 Changeset detected

Latest commit: f394f59

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 13 packages
Name Type
@biomejs/biome Patch
@biomejs/cli-win32-x64 Patch
@biomejs/cli-win32-arm64 Patch
@biomejs/cli-darwin-x64 Patch
@biomejs/cli-darwin-arm64 Patch
@biomejs/cli-linux-x64 Patch
@biomejs/cli-linux-arm64 Patch
@biomejs/cli-linux-x64-musl Patch
@biomejs/cli-linux-arm64-musl Patch
@biomejs/wasm-web Patch
@biomejs/wasm-bundler Patch
@biomejs/wasm-nodejs Patch
@biomejs/backend-jsonrpc Patch

Not sure what this means? Click here to learn what changesets are.

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

@github-actions github-actions bot added A-Parser Area: parser A-Formatter Area: formatter A-Tooling Area: internal tools L-HTML Language: HTML and super languages labels Jan 2, 2026
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 2, 2026

Walkthrough

This PR introduces attribute-specific HTML text-expression node kinds (HtmlAttributeDoubleTextExpression and HtmlAttributeSingleTextExpression) and updates codegen and kinds to expose them. The HTML parser now completes text expressions inside tag contexts as attribute-specific kinds. The formatter gains formatter rules and FormatRule/AsFormat/IntoFormat impls for the new nodes, and formatter match arms were updated to use the renamed variants. Whitespace/children handling was adjusted to account for bogus elements, and tests for Django-style top-level text expressions (issue-8605) were added. Changes include test harness routing for files with text expressions.

Possibly related PRs

Suggested reviewers

  • dyc3
  • ematipico

Pre-merge checks and finishing touches

✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: distinguishing interpolations inside vs. outside HTML tags to fix issue #8605.
Description check ✅ Passed The description is clearly related to the changeset, explaining the problem (ambiguous syntax kinds), the solution (new kinds for attribute context), and testing approach.
Linked Issues check ✅ Passed The PR addresses issue #8605 by preventing panic when processing Django-style HTML interpolations through new syntax kinds that distinguish attribute vs. non-attribute contexts.
Out of Scope Changes check ✅ Passed All changes align with the stated objective of distinguishing interpolations in different contexts. Minor additional fixes (HtmlBogusElement spacing, comment grammar) are reasonable incidental improvements within scope.
✨ Finishing touches
  • 📝 Generate docstrings

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

Copy link
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: 1

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a3a27a7 and 858117c.

⛔ Files ignored due to path filters (16)
  • crates/biome_html_factory/src/generated/node_factory.rs is excluded by !**/generated/**, !**/generated/** and included by **
  • crates/biome_html_factory/src/generated/syntax_factory.rs is excluded by !**/generated/**, !**/generated/** and included by **
  • crates/biome_html_parser/tests/html_specs/error/interpolation.html.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_parser/tests/html_specs/error/template-langs/django/issue-8605.html.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_parser/tests/html_specs/ok/astro/attribute_expression.astro.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_parser/tests/html_specs/ok/svelte/attach.svelte.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_parser/tests/html_specs/ok/svelte/await_multiline.svelte.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_parser/tests/html_specs/ok/svelte/complex_expressions.svelte.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_parser/tests/html_specs/ok/svelte/dynamic-prop.svelte.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_parser/tests/html_specs/ok/svelte/each_as_in_identifier.svelte.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_parser/tests/html_specs/ok/svelte/shorthand-prop.svelte.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_parser/tests/html_specs/ok/svelte/shorthand-spread-props.svelte.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_syntax/src/generated/kind.rs is excluded by !**/generated/**, !**/generated/** and included by **
  • crates/biome_html_syntax/src/generated/macros.rs is excluded by !**/generated/**, !**/generated/** and included by **
  • crates/biome_html_syntax/src/generated/nodes.rs is excluded by !**/generated/**, !**/generated/** and included by **
  • crates/biome_html_syntax/src/generated/nodes_mut.rs is excluded by !**/generated/**, !**/generated/** and included by **
📒 Files selected for processing (13)
  • .changeset/happy-cats-drive.md
  • crates/biome_html_formatter/src/generated.rs
  • crates/biome_html_formatter/src/html/any/attribute.rs
  • crates/biome_html_formatter/src/html/any/attribute_initializer.rs
  • crates/biome_html_formatter/src/html/auxiliary/attribute_double_text_expression.rs
  • crates/biome_html_formatter/src/html/auxiliary/attribute_single_text_expression.rs
  • crates/biome_html_formatter/src/html/auxiliary/mod.rs
  • crates/biome_html_formatter/src/html/lists/attribute_list.rs
  • crates/biome_html_parser/src/syntax/mod.rs
  • crates/biome_html_parser/tests/html_specs/error/template-langs/django/issue-8605.html
  • crates/biome_html_syntax/src/attr_ext.rs
  • xtask/codegen/html.ungram
  • xtask/codegen/src/html_kinds_src.rs
🧰 Additional context used
📓 Path-based instructions (1)
**/*.rs

📄 CodeRabbit inference engine (CONTRIBUTING.md)

**/*.rs: Use inline rustdoc documentation for rules, assists, and their options
Use the dbg!() macro for debugging output in Rust tests and code
Use doc tests (doctest) format with code blocks in rustdoc comments; ensure assertions pass in tests

Files:

  • crates/biome_html_formatter/src/html/auxiliary/mod.rs
  • xtask/codegen/src/html_kinds_src.rs
  • crates/biome_html_formatter/src/html/lists/attribute_list.rs
  • crates/biome_html_formatter/src/html/auxiliary/attribute_double_text_expression.rs
  • crates/biome_html_syntax/src/attr_ext.rs
  • crates/biome_html_formatter/src/html/auxiliary/attribute_single_text_expression.rs
  • crates/biome_html_parser/src/syntax/mod.rs
  • crates/biome_html_formatter/src/html/any/attribute_initializer.rs
  • crates/biome_html_formatter/src/html/any/attribute.rs
  • crates/biome_html_formatter/src/generated.rs
🧠 Learnings (34)
📓 Common learnings
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:05:20.371Z
Learning: Define `FormatHtmlSyntaxNode` struct in a `cst.rs` file implementing `FormatRule<HtmlSyntaxNode>`, `AsFormat<HtmlFormatContext>`, and `IntoFormat<HtmlFormatContext>` traits using the provided boilerplate code
📚 Learning: 2025-11-24T18:05:42.356Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_js_type_info/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:05:42.356Z
Learning: Applies to crates/biome_js_type_info/**/local_inference.rs : Implement local inference in dedicated modules to derive type definitions from expressions without context of surrounding scopes

Applied to files:

  • crates/biome_html_formatter/src/html/auxiliary/mod.rs
📚 Learning: 2025-11-24T18:06:03.545Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_parser/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:06:03.545Z
Learning: Applies to crates/biome_parser/**/*.ungram : Unions of nodes must start with `Any*`, e.g., `AnyHtmlAttribute`

Applied to files:

  • crates/biome_html_formatter/src/html/lists/attribute_list.rs
  • crates/biome_html_syntax/src/attr_ext.rs
  • crates/biome_html_parser/src/syntax/mod.rs
  • crates/biome_html_formatter/src/html/any/attribute_initializer.rs
  • crates/biome_html_formatter/src/html/any/attribute.rs
📚 Learning: 2025-12-19T12:53:30.413Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-12-19T12:53:30.413Z
Learning: Applies to crates/biome_analyze/**/biome_rule_options/lib/**/*.rs : Use `rename_all = "camelCase"` in serde derive macro for rule options

Applied to files:

  • crates/biome_html_formatter/src/html/lists/attribute_list.rs
📚 Learning: 2025-11-24T18:05:27.810Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_js_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:05:27.810Z
Learning: Applies to crates/biome_js_formatter/**/*.rs : When formatting AST nodes, use mandatory tokens from the AST instead of hardcoding token strings (e.g., use `node.l_paren_token().format()` instead of `token("(")`)

Applied to files:

  • crates/biome_html_formatter/src/html/lists/attribute_list.rs
  • crates/biome_html_formatter/src/html/auxiliary/attribute_double_text_expression.rs
  • crates/biome_html_formatter/src/html/auxiliary/attribute_single_text_expression.rs
  • crates/biome_html_parser/src/syntax/mod.rs
  • crates/biome_html_formatter/src/html/any/attribute_initializer.rs
  • crates/biome_html_formatter/src/html/any/attribute.rs
  • crates/biome_html_formatter/src/generated.rs
📚 Learning: 2025-12-19T12:53:30.413Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-12-19T12:53:30.413Z
Learning: Applies to crates/biome_analyze/**/biome_rule_options/lib/**/*.rs : Wrap rule options fields in `Option<>` to properly track set and unset options during merge

Applied to files:

  • crates/biome_html_formatter/src/html/lists/attribute_list.rs
  • crates/biome_html_formatter/src/generated.rs
📚 Learning: 2025-11-24T18:06:03.545Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_parser/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:06:03.545Z
Learning: Applies to crates/biome_parser/**/*.ungram : Nodes that represent a list must end with the postfix `List`, e.g., `HtmlAttributeList`, and lists are mandatory (not optional) but empty by default

Applied to files:

  • crates/biome_html_formatter/src/html/lists/attribute_list.rs
📚 Learning: 2025-11-24T18:06:03.545Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_parser/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:06:03.545Z
Learning: Applies to crates/biome_parser/**/*.ungram : All grammar nodes must start with the prefix of the language, e.g., `HtmlSimpleAttribute`

Applied to files:

  • crates/biome_html_formatter/src/html/lists/attribute_list.rs
  • crates/biome_html_syntax/src/attr_ext.rs
  • crates/biome_html_formatter/src/html/auxiliary/attribute_single_text_expression.rs
  • crates/biome_html_parser/src/syntax/mod.rs
  • crates/biome_html_formatter/src/html/any/attribute.rs
📚 Learning: 2025-11-24T18:06:03.545Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_parser/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:06:03.545Z
Learning: Applies to crates/biome_parser/**/src/**/*.rs : Use `ParseSeparatedList` and `ParseNodeList` for parsing lists with error recovery to avoid infinite loops

Applied to files:

  • crates/biome_html_formatter/src/html/lists/attribute_list.rs
📚 Learning: 2025-11-24T18:05:42.356Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_js_type_info/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:05:42.356Z
Learning: Applies to crates/biome_js_type_info/**/*.rs : Use `TypeReference` instead of `Arc` for types that reference other types to avoid stale cache issues when modules are replaced

Applied to files:

  • crates/biome_html_formatter/src/html/lists/attribute_list.rs
📚 Learning: 2025-12-19T12:53:30.413Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-12-19T12:53:30.413Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/**/*.rs : Avoid string allocations by comparing against `&str` or using `TokenText`

Applied to files:

  • crates/biome_html_formatter/src/html/lists/attribute_list.rs
  • crates/biome_html_syntax/src/attr_ext.rs
  • crates/biome_html_parser/src/syntax/mod.rs
  • crates/biome_html_formatter/src/html/any/attribute.rs
📚 Learning: 2025-11-24T18:05:20.371Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:05:20.371Z
Learning: Define `FormatHtmlSyntaxNode` struct in a `cst.rs` file implementing `FormatRule<HtmlSyntaxNode>`, `AsFormat<HtmlFormatContext>`, and `IntoFormat<HtmlFormatContext>` traits using the provided boilerplate code

Applied to files:

  • crates/biome_html_formatter/src/html/lists/attribute_list.rs
  • crates/biome_html_formatter/src/html/auxiliary/attribute_double_text_expression.rs
  • crates/biome_html_formatter/src/html/auxiliary/attribute_single_text_expression.rs
  • crates/biome_html_formatter/src/html/any/attribute_initializer.rs
  • crates/biome_html_formatter/src/html/any/attribute.rs
  • crates/biome_html_formatter/src/generated.rs
📚 Learning: 2025-11-24T18:05:27.810Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_js_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:05:27.810Z
Learning: Applies to crates/biome_js_formatter/**/*.rs : Import the `FormatNode` trait and implement it for your Node when creating formatters in biome_js_formatter

Applied to files:

  • crates/biome_html_formatter/src/html/auxiliary/attribute_double_text_expression.rs
  • crates/biome_html_formatter/src/html/auxiliary/attribute_single_text_expression.rs
  • crates/biome_html_formatter/src/html/any/attribute_initializer.rs
  • crates/biome_html_formatter/src/html/any/attribute.rs
  • crates/biome_html_formatter/src/generated.rs
📚 Learning: 2025-11-24T18:05:20.371Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:05:20.371Z
Learning: Implement the `FormatNodeRule<N>` trait with `fmt_fields` as the only required method; default implementations of `fmt`, `is_suppressed`, `fmt_leading_comments`, `fmt_dangling_comments`, and `fmt_trailing_comments` are provided

Applied to files:

  • crates/biome_html_formatter/src/html/auxiliary/attribute_double_text_expression.rs
  • crates/biome_html_formatter/src/html/auxiliary/attribute_single_text_expression.rs
  • crates/biome_html_formatter/src/html/any/attribute_initializer.rs
  • crates/biome_html_formatter/src/html/any/attribute.rs
  • crates/biome_html_formatter/src/generated.rs
📚 Learning: 2025-11-24T18:05:20.371Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:05:20.371Z
Learning: Applies to crates/biome_formatter/**/biome_*_formatter/src/lib.rs : Expose a public `format_node` function that accepts formatting options and a root syntax node, returning a `FormatResult<Formatted<Context>>` with appropriate documentation

Applied to files:

  • crates/biome_html_formatter/src/html/auxiliary/attribute_double_text_expression.rs
  • crates/biome_html_formatter/src/html/auxiliary/attribute_single_text_expression.rs
  • crates/biome_html_formatter/src/html/any/attribute_initializer.rs
  • crates/biome_html_formatter/src/html/any/attribute.rs
  • crates/biome_html_formatter/src/generated.rs
📚 Learning: 2025-11-24T18:05:27.810Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_js_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:05:27.810Z
Learning: The formatter foundation relies on using the generic `Format` trait and `FormatNode` for nodes, with creation of an intermediate IR via a series of helpers

Applied to files:

  • crates/biome_html_formatter/src/html/auxiliary/attribute_double_text_expression.rs
  • crates/biome_html_formatter/src/html/auxiliary/attribute_single_text_expression.rs
  • crates/biome_html_formatter/src/html/any/attribute_initializer.rs
  • crates/biome_html_formatter/src/html/any/attribute.rs
  • crates/biome_html_formatter/src/generated.rs
📚 Learning: 2025-11-24T18:05:20.371Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:05:20.371Z
Learning: Applies to crates/biome_formatter/**/biome_*_formatter/src/context.rs : Define `<Language>FormatContext` struct in a `context.rs` file containing `comments` and `source_map` fields, implementing `FormatContext` and `CstFormatContext` traits

Applied to files:

  • crates/biome_html_formatter/src/html/auxiliary/attribute_double_text_expression.rs
  • crates/biome_html_formatter/src/html/auxiliary/attribute_single_text_expression.rs
  • crates/biome_html_formatter/src/html/any/attribute.rs
  • crates/biome_html_formatter/src/generated.rs
📚 Learning: 2025-11-24T18:05:20.371Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:05:20.371Z
Learning: Applies to crates/biome_formatter/**/biome_*_formatter/src/lib.rs : Define a type alias `<Language>Formatter<'buf>` as `Formatter<'buf, <Language>FormatContext>` in the main formatter crate

Applied to files:

  • crates/biome_html_formatter/src/html/auxiliary/attribute_double_text_expression.rs
  • crates/biome_html_formatter/src/html/auxiliary/attribute_single_text_expression.rs
  • crates/biome_html_formatter/src/generated.rs
📚 Learning: 2025-11-24T18:05:20.371Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:05:20.371Z
Learning: Create a new formatter crate using the command `just new-crate biome_<language>_formatter` where `<language>` is the target language (e.g., `biome_html_formatter` for HTML)

Applied to files:

  • crates/biome_html_formatter/src/html/auxiliary/attribute_double_text_expression.rs
  • crates/biome_html_formatter/src/html/auxiliary/attribute_single_text_expression.rs
📚 Learning: 2025-12-21T21:15:03.796Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: CONTRIBUTING.md:0-0
Timestamp: 2025-12-21T21:15:03.796Z
Learning: Changesets should describe user-facing changes only; internal refactoring without behavior changes does not require a changeset

Applied to files:

  • .changeset/happy-cats-drive.md
📚 Learning: 2025-11-24T18:05:20.371Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:05:20.371Z
Learning: Applies to crates/biome_formatter/**/biome_*_formatter/tests/language.rs : Implement `TestFormatLanguage` trait in `tests/language.rs` for the formatter's test language

Applied to files:

  • crates/biome_html_formatter/src/html/auxiliary/attribute_single_text_expression.rs
  • crates/biome_html_formatter/src/html/any/attribute.rs
  • crates/biome_html_formatter/src/generated.rs
📚 Learning: 2025-11-24T18:05:20.371Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:05:20.371Z
Learning: Implement the `FormatLanguage` trait with `SyntaxLanguage`, `Context`, and `FormatRule` associated types for the language's formatter

Applied to files:

  • crates/biome_html_formatter/src/html/auxiliary/attribute_single_text_expression.rs
  • crates/biome_html_formatter/src/generated.rs
📚 Learning: 2025-11-24T18:06:03.545Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_parser/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:06:03.545Z
Learning: Applies to crates/biome_parser/**/src/**/*.rs : Use `ConditionalParsedSyntax` for syntax that is only valid in specific contexts (e.g., strict mode, file types, language versions) and call `or_invalid_to_bogus()` to convert to a bogus node if not supported

Applied to files:

  • crates/biome_html_parser/src/syntax/mod.rs
📚 Learning: 2025-11-24T18:06:03.545Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_parser/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:06:03.545Z
Learning: Applies to crates/biome_parser/**/*.ungram : Nodes for enclosing syntax errors must have the `Bogus` word, e.g., `HtmlBogusAttribute`, and must be part of a variant

Applied to files:

  • crates/biome_html_parser/src/syntax/mod.rs
📚 Learning: 2025-11-24T18:06:03.545Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_parser/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:06:03.545Z
Learning: Applies to crates/biome_parser/**/src/**/*.rs : Parse rules must take a mutable reference to the parser as their only parameter and return a `ParsedSyntax`

Applied to files:

  • crates/biome_html_parser/src/syntax/mod.rs
📚 Learning: 2025-11-24T18:06:03.545Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_parser/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:06:03.545Z
Learning: Applies to crates/biome_parser/**/src/**/*.rs : Parse rule functions must be prefixed with `parse_` and use the name defined in the grammar file, e.g., `parse_for_statement` or `parse_expression`

Applied to files:

  • crates/biome_html_parser/src/syntax/mod.rs
📚 Learning: 2025-12-19T12:53:30.413Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-12-19T12:53:30.413Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/**/*.rs : Prefix line with `#` in documentation code examples sparingly; prefer concise complete snippets

Applied to files:

  • crates/biome_html_parser/src/syntax/mod.rs
📚 Learning: 2025-12-19T12:53:30.413Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-12-19T12:53:30.413Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/**/*.rs : Assist rules should detect refactoring opportunities and emit code action signals

Applied to files:

  • crates/biome_html_parser/src/syntax/mod.rs
📚 Learning: 2025-11-09T12:47:46.298Z
Learnt from: ematipico
Repo: biomejs/biome PR: 8031
File: crates/biome_html_parser/src/syntax/svelte.rs:140-147
Timestamp: 2025-11-09T12:47:46.298Z
Learning: In the Biome HTML parser, `expect` and `expect_with_context` consume the current token and then lex the next token. The context parameter in `expect_with_context` controls how the next token (after the consumed one) is lexed, not the current token being consumed. For example, in Svelte parsing, after `bump_with_context(T!["{:"], HtmlLexContext::Svelte)`, the next token is already lexed in the Svelte context, so `expect(T![else])` is sufficient unless the token after `else` also needs to be lexed in a specific context.

Applied to files:

  • crates/biome_html_parser/src/syntax/mod.rs
📚 Learning: 2025-12-19T11:31:06.239Z
Learnt from: ematipico
Repo: biomejs/biome PR: 8509
File: crates/biome_html_parser/src/syntax/svelte.rs:465-467
Timestamp: 2025-12-19T11:31:06.239Z
Learning: In the Biome HTML parser, `parse_single_text_expression_content` does not advance the parser if the current token is empty. This is intentional to support cases where an expression is optional. When calling this function, callers must check if `p.cur_text().is_empty()` after the call and manually advance the parser with `p.bump_remap(HTML_LITERAL)` if needed.

Applied to files:

  • crates/biome_html_parser/src/syntax/mod.rs
📚 Learning: 2025-11-24T18:05:20.371Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:05:20.371Z
Learning: Implement the `IntoFormat<Context>` trait in `lib.rs` with implementations for `SyntaxResult` and `Option` types as part of the formatter infrastructure

Applied to files:

  • crates/biome_html_formatter/src/html/any/attribute_initializer.rs
  • crates/biome_html_formatter/src/generated.rs
📚 Learning: 2025-11-24T18:05:20.371Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:05:20.371Z
Learning: Implement the `AsFormat<Context>` trait in `lib.rs` with generic implementations for references, `SyntaxResult`, and `Option` types as provided in the formatter boilerplate code

Applied to files:

  • crates/biome_html_formatter/src/html/any/attribute_initializer.rs
  • crates/biome_html_formatter/src/generated.rs
📚 Learning: 2025-11-24T18:05:27.810Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_js_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:05:27.810Z
Learning: Applies to crates/biome_js_formatter/**/*.rs : Do not attempt to 'fix' the code; if a token/node is known to be mandatory but is missing, return `None` instead

Applied to files:

  • crates/biome_html_formatter/src/html/any/attribute.rs
📚 Learning: 2025-12-04T13:29:49.287Z
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.

Applied to files:

  • crates/biome_html_parser/tests/html_specs/error/template-langs/django/issue-8605.html
🧬 Code graph analysis (5)
crates/biome_html_formatter/src/html/lists/attribute_list.rs (2)
crates/biome_html_formatter/src/generated.rs (28)
  • format (28-33)
  • format (66-71)
  • format (104-109)
  • format (126-128)
  • format (155-157)
  • format (187-192)
  • format (209-211)
  • format (238-243)
  • format (276-281)
  • format (314-319)
  • format (352-357)
  • format (390-396)
  • format (430-435)
  • format (468-473)
  • format (506-511)
  • format (538-543)
  • fmt (14-20)
  • fmt (52-58)
  • fmt (90-96)
  • fmt (123-123)
  • fmt (141-147)
  • fmt (173-179)
  • fmt (206-206)
  • fmt (224-230)
  • fmt (262-268)
  • fmt (300-306)
  • fmt (338-344)
  • fmt (376-382)
crates/biome_html_formatter/src/html/any/attribute.rs (1)
  • fmt (9-18)
crates/biome_html_formatter/src/html/auxiliary/attribute_double_text_expression.rs (3)
crates/biome_html_formatter/src/html/auxiliary/attribute_single_text_expression.rs (1)
  • fmt_fields (11-30)
crates/biome_html_formatter/src/generated.rs (16)
  • format (28-33)
  • format (66-71)
  • format (104-109)
  • format (126-128)
  • format (155-157)
  • format (187-192)
  • format (209-211)
  • format (238-243)
  • format (276-281)
  • format (314-319)
  • format (352-357)
  • format (390-396)
  • format (430-435)
  • format (468-473)
  • format (506-511)
  • format (538-543)
crates/biome_formatter/src/builders.rs (1)
  • space (606-608)
crates/biome_html_formatter/src/html/auxiliary/attribute_single_text_expression.rs (2)
crates/biome_html_formatter/src/html/auxiliary/attribute_double_text_expression.rs (1)
  • fmt_fields (11-32)
crates/biome_html_formatter/src/generated.rs (16)
  • format (28-33)
  • format (66-71)
  • format (104-109)
  • format (126-128)
  • format (155-157)
  • format (187-192)
  • format (209-211)
  • format (238-243)
  • format (276-281)
  • format (314-319)
  • format (352-357)
  • format (390-396)
  • format (430-435)
  • format (468-473)
  • format (506-511)
  • format (538-543)
crates/biome_html_parser/src/syntax/mod.rs (3)
crates/biome_formatter/src/lib.rs (2)
  • context (971-973)
  • context (2133-2135)
crates/biome_html_parser/src/parser.rs (1)
  • context (90-92)
crates/biome_parser/src/lib.rs (1)
  • context (155-155)
crates/biome_html_formatter/src/html/any/attribute.rs (1)
crates/biome_service/src/file_handlers/html.rs (3)
  • node (718-718)
  • node (730-730)
  • node (737-737)
🪛 LanguageTool
.changeset/happy-cats-drive.md

[misspelling] ~5-~5: Use “an” instead of ‘a’ if the following word starts with a vowel sound, e.g. ‘an article’, ‘an hour’.
Context: ... }}or{ expr }`) at the top level of a HTML document no longer causes panickin...

(EN_A_VS_AN)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (10)
  • GitHub Check: Lint project (depot-ubuntu-24.04-arm-16)
  • GitHub Check: Lint project (depot-windows-2022)
  • GitHub Check: Documentation
  • GitHub Check: Check Dependencies
  • GitHub Check: Test (depot-windows-2022-16)
  • GitHub Check: Test (depot-ubuntu-24.04-arm-16)
  • GitHub Check: End-to-end tests
  • GitHub Check: Test Node.js API
  • GitHub Check: autofix
  • GitHub Check: Parser conformance
🔇 Additional comments (15)
crates/biome_html_parser/tests/html_specs/error/template-langs/django/issue-8605.html (1)

1-1: LGTM! Minimal reproduction case for issue #8605.

This test file appropriately captures the scenario of a Django template expression at the document root, which previously caused incorrect bogus node generation.

xtask/codegen/src/html_kinds_src.rs (1)

80-81: LGTM! New node kinds properly added.

The addition of HTML_ATTRIBUTE_DOUBLE_TEXT_EXPRESSION and HTML_ATTRIBUTE_SINGLE_TEXT_EXPRESSION correctly distinguishes attribute-context interpolations from content-context ones, addressing the root cause of issue #8605.

crates/biome_html_formatter/src/html/any/attribute.rs (1)

13-14: LGTM! Generated formatter properly updated.

The match arms correctly handle the new HtmlAttributeDoubleTextExpression and HtmlAttributeSingleTextExpression variants, delegating formatting appropriately.

crates/biome_html_formatter/src/html/any/attribute_initializer.rs (1)

11-13: LGTM! Generated formatter correctly updated.

The variant name change to HtmlAttributeSingleTextExpression aligns with the new attribute-context node kinds.

crates/biome_html_formatter/src/html/auxiliary/mod.rs (1)

4-4: LGTM! Generated module declarations correctly added.

The new attribute_double_text_expression and attribute_single_text_expression modules are properly exposed, maintaining alphabetical ordering.

Also applies to: 7-7

crates/biome_html_syntax/src/attr_ext.rs (1)

11-11: LGTM!

Variant rename correctly reflects the new attribute-specific text expression type introduced in this PR.

crates/biome_html_formatter/src/html/lists/attribute_list.rs (1)

63-68: LGTM!

Match arms updated to use the new attribute-specific text expression variants. Formatting delegation remains consistent.

crates/biome_html_parser/src/syntax/mod.rs (2)

561-565: LGTM!

Context-aware completion correctly distinguishes double text expressions inside tags (attributes) from those in regular content, fixing the bogus node issue described in #8605.


612-616: LGTM!

Context-aware completion for single text expressions mirrors the double expression logic, maintaining consistency.

crates/biome_html_formatter/src/html/auxiliary/attribute_single_text_expression.rs (1)

22-29: Formatter implementation looks correct.

The formatting matches the pattern for other node rules. Note: Unlike HtmlAttributeDoubleTextExpression which formats with spaces around the expression ({{ expr }}), this omits spaces ({expr}), which appears intentional for distinguishing single from double brace syntax.

xtask/codegen/html.ungram (3)

165-166: LGTM!

AnyHtmlAttribute alternatives correctly reference the new attribute-specific text expression node types.


187-187: LGTM!

AnyHtmlAttributeInitializer correctly uses HtmlAttributeSingleTextExpression as the alternative to HtmlString.


190-204: LGTM!

Grammar definitions for HtmlAttributeDoubleTextExpression and HtmlAttributeSingleTextExpression are well-structured and follow naming conventions. The documentation comments clearly indicate their usage contexts.

crates/biome_html_formatter/src/html/auxiliary/attribute_double_text_expression.rs (1)

1-33: LGTM!

The formatter implementation is clean and follows the established patterns correctly. The spacing around the expression ({{ expr }}) is consistent with Django/Jinja2 template formatting conventions and appropriately differs from the single-text-expression variant.

crates/biome_html_formatter/src/generated.rs (1)

123-135: LGTM!

The generated formatting trait implementations for both new attribute text expression types follow the correct pattern and properly delegate to their respective formatter modules.

Also applies to: 206-218

siketyan and others added 2 commits January 2, 2026 17:08
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Copy link
Member

@ematipico ematipico left a comment

Choose a reason for hiding this comment

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

It seems there have been some regressions in the formatted


}} reprehenderit voluptates minus {{console.log( short_interpolation )}} nemo.</div>
}}
reprehenderit voluptates minus {{console.log( short_interpolation )}}nemo.
Copy link
Member

@ematipico ematipico Jan 2, 2026

Choose a reason for hiding this comment

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

This is a regression. There's no more space after }} anymore

Copy link
Member Author

Choose a reason for hiding this comment

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

Fixed in 81cd283, I am not sure this is the right way

Comment on lines 91 to 92
Fuga magnam facilis. Voluptatem quaerat porro.
{{
Copy link
Member

Choose a reason for hiding this comment

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

Newline before {{. It seems to be a regression

Copy link
Member Author

Choose a reason for hiding this comment

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

Also fixed in 81cd283

Copy link
Member

Choose a reason for hiding this comment

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

I think it would be useful to have also a test that passes

Copy link
Member Author

Choose a reason for hiding this comment

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

Added in f394f59

@ematipico
Copy link
Member

Is it possible that your local main wasn't updated? We recently fixed those regressions

@siketyan siketyan requested a review from ematipico January 2, 2026 16:24
Copy link
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: 0

🧹 Nitpick comments (1)
crates/biome_html_parser/tests/spec_test.rs (1)

34-40: Nice path-based test routing!

The conditional override correctly enables text expression parsing for tests organised under "with-text-expressions" directories. The logic is sound and aligns well with the test fixture structure.

Optional: Consider adding a brief comment

A one-line comment explaining the override would help future maintainers:

 let mut file_source = HtmlFileSource::try_from(test_case_path).unwrap_or_default();
+// Enable text expression parsing (e.g., Django-style {{ expr }}) for dedicated test fixtures
 if test_case_path
     .iter()
     .any(|segment| segment == "with-text-expressions")
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 81cd283 and f394f59.

⛔ Files ignored due to path filters (1)
  • crates/biome_html_parser/tests/html_specs/ok/with-text-expressions/issue-8605.html.snap is excluded by !**/*.snap and included by **
📒 Files selected for processing (2)
  • crates/biome_html_parser/tests/html_specs/ok/with-text-expressions/issue-8605.html
  • crates/biome_html_parser/tests/spec_test.rs
✅ Files skipped from review due to trivial changes (1)
  • crates/biome_html_parser/tests/html_specs/ok/with-text-expressions/issue-8605.html
🧰 Additional context used
📓 Path-based instructions (1)
**/*.rs

📄 CodeRabbit inference engine (CONTRIBUTING.md)

**/*.rs: Use inline rustdoc documentation for rules, assists, and their options
Use the dbg!() macro for debugging output in Rust tests and code
Use doc tests (doctest) format with code blocks in rustdoc comments; ensure assertions pass in tests

Files:

  • crates/biome_html_parser/tests/spec_test.rs
🧠 Learnings (11)
📓 Common learnings
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:05:20.371Z
Learning: Define `FormatHtmlSyntaxNode` struct in a `cst.rs` file implementing `FormatRule<HtmlSyntaxNode>`, `AsFormat<HtmlFormatContext>`, and `IntoFormat<HtmlFormatContext>` traits using the provided boilerplate code
📚 Learning: 2025-11-24T18:05:20.371Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:05:20.371Z
Learning: Applies to crates/biome_formatter/**/biome_*_formatter/tests/spec_tests.rs : Use the `tests_macros::gen_tests!` macro in `spec_tests.rs` to generate test functions for each specification file matching the pattern `tests/specs/<language>/**/*.<ext>`

Applied to files:

  • crates/biome_html_parser/tests/spec_test.rs
📚 Learning: 2025-12-04T13:29:49.287Z
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.

Applied to files:

  • crates/biome_html_parser/tests/spec_test.rs
📚 Learning: 2025-11-24T18:06:03.545Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_parser/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:06:03.545Z
Learning: Applies to crates/biome_parser/**/src/**/*.rs : Implement a token source struct that wraps the lexer and implements `TokenSourceWithBufferedLexer` and `LexerWithCheckpoint` for lookahead and re-lexing capabilities

Applied to files:

  • crates/biome_html_parser/tests/spec_test.rs
📚 Learning: 2025-11-24T18:05:20.371Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:05:20.371Z
Learning: Applies to crates/biome_formatter/**/biome_*_formatter/tests/language.rs : Implement `TestFormatLanguage` trait in `tests/language.rs` for the formatter's test language

Applied to files:

  • crates/biome_html_parser/tests/spec_test.rs
📚 Learning: 2025-11-24T18:06:03.545Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_parser/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:06:03.545Z
Learning: Applies to crates/biome_parser/**/src/**/*.rs : Use `ConditionalParsedSyntax` for syntax that is only valid in specific contexts (e.g., strict mode, file types, language versions) and call `or_invalid_to_bogus()` to convert to a bogus node if not supported

Applied to files:

  • crates/biome_html_parser/tests/spec_test.rs
📚 Learning: 2025-11-24T18:06:12.048Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_service/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:06:12.048Z
Learning: Applies to crates/biome_service/src/workspace/watcher.tests.rs : Implement watcher tests for workspace methods in watcher.tests.rs and end-to-end tests in LSP tests

Applied to files:

  • crates/biome_html_parser/tests/spec_test.rs
📚 Learning: 2025-11-24T18:06:03.545Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_parser/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:06:03.545Z
Learning: Applies to crates/biome_parser/**/src/**/*.rs : A parser struct must implement the `Parser` trait and save the token source, parser context, and optional parser options

Applied to files:

  • crates/biome_html_parser/tests/spec_test.rs
📚 Learning: 2025-11-24T18:05:20.371Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:05:20.371Z
Learning: Create test infrastructure with `tests/specs` folder structure and `spec_test.rs`, `spec_tests.rs`, and `language.rs` files in test directories

Applied to files:

  • crates/biome_html_parser/tests/spec_test.rs
📚 Learning: 2025-11-24T18:05:20.371Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:05:20.371Z
Learning: Applies to crates/biome_formatter/**/biome_*_formatter/src/context.rs : Define `<Language>FormatContext` struct in a `context.rs` file containing `comments` and `source_map` fields, implementing `FormatContext` and `CstFormatContext` traits

Applied to files:

  • crates/biome_html_parser/tests/spec_test.rs
📚 Learning: 2025-11-24T18:05:20.371Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:05:20.371Z
Learning: Applies to crates/biome_formatter/**/biome_*_formatter/Cargo.toml : Include development dependencies in `Cargo.toml` for formatter tests: `biome_formatter_test`, `biome_<language>_factory`, `biome_<language>_parser`, `biome_parser`, `biome_service`, `countme`, `iai`, `quickcheck`, `quickcheck_macros`, and `tests_macros`

Applied to files:

  • crates/biome_html_parser/tests/spec_test.rs
🧬 Code graph analysis (1)
crates/biome_html_parser/tests/spec_test.rs (1)
crates/biome_html_syntax/src/file_source.rs (2)
  • try_from (146-157)
  • html_with_text_expressions (79-83)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (10)
  • GitHub Check: Lint project (depot-ubuntu-24.04-arm-16)
  • GitHub Check: Lint project (depot-windows-2022)
  • GitHub Check: Check Dependencies
  • GitHub Check: Test (depot-windows-2022-16)
  • GitHub Check: Test (depot-ubuntu-24.04-arm-16)
  • GitHub Check: Documentation
  • GitHub Check: End-to-end tests
  • GitHub Check: Test Node.js API
  • GitHub Check: autofix
  • GitHub Check: Parser conformance

@ematipico ematipico merged commit 16fd71d into biomejs:main Feb 6, 2026
14 checks passed
@github-actions github-actions bot mentioned this pull request Feb 6, 2026
@coderabbitai coderabbitai bot mentioned this pull request Feb 7, 2026
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 A-Tooling Area: internal tools L-HTML Language: HTML and super languages

Projects

None yet

Development

Successfully merging this pull request may close these issues.

🐛 Panic when processing Django-style HTML syntax

2 participants