Conversation
|
WalkthroughThe PR restructures the Tailwind parser and lexer state: it removes multiple internal fields from Possibly related PRs
Suggested reviewers
Pre-merge checks and finishing touches✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
crates/biome_tailwind_syntax/src/lib.rs (1)
83-88: Stale comment contradicts the new behaviour.The comment on lines 84-85 states whitespace is intentionally not considered trivia, but the code now explicitly treats
WHITESPACEas trivia. Please update or remove this outdated comment.🔎 Suggested fix
if value.is_trivia() { - // We intentionally don't consider whitespace to be trivia because it's a required part of the syntax. - // There must be spaces between Candidates in order for tailwind to parse them. match value { TailwindSyntaxKind::NEWLINE => Ok(Self::Newline), TailwindSyntaxKind::WHITESPACE => Ok(Self::Whitespace), _ => unreachable!("Not Trivia"), }
🧹 Nitpick comments (1)
crates/biome_tailwind_parser/src/syntax/mod.rs (1)
113-124: Consider consolidating the two whitespace checks.There are two separate conditions checking for whitespace between tokens:
had_trivia_before()– trivia immediately before current token- Checking if
last_triviaposition is afterposThese seem to handle slightly different cases, but it's a bit subtle. A brief inline comment explaining why both are needed would help future maintainers.
Also, minor typo: "Theres" → "There's" (lines 115 and 122).
🔎 Typo fix
if p.source().had_trivia_before() { // Whitespace is not allowed in tailwind candidates - // Theres whitespace between these tokens, so it can't be a functional candidate + // There's whitespace between these tokens, so it can't be a functional candidate return Present(m.complete(p, TW_STATIC_CANDIDATE)); } if let Some(last_trivia) = p.source().trivia_list.last() && pos < last_trivia.text_range().start() { // Whitespace is not allowed in tailwind candidates - // Theres whitespace between these tokens, so it can't be a functional candidate + // There's whitespace between these tokens, so it can't be a functional candidate return Present(m.complete(p, TW_STATIC_CANDIDATE)); }
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (48)
crates/biome_tailwind_factory/src/generated/node_factory.rsis excluded by!**/generated/**,!**/generated/**and included by**crates/biome_tailwind_factory/src/generated/syntax_factory.rsis excluded by!**/generated/**,!**/generated/**and included by**crates/biome_tailwind_parser/tests/tailwind_specs/error/arbitrary-candidate/missing-property.txt.snapis excluded by!**/*.snapand included by**crates/biome_tailwind_parser/tests/tailwind_specs/error/arbitrary-candidate/missing-value-in-arbitrary.txt.snapis excluded by!**/*.snapand included by**crates/biome_tailwind_parser/tests/tailwind_specs/error/incomplete-arbitrary-value-0.txt.snapis excluded by!**/*.snapand included by**crates/biome_tailwind_parser/tests/tailwind_specs/error/incomplete-arbitrary-value-1.txt.snapis excluded by!**/*.snapand included by**crates/biome_tailwind_parser/tests/tailwind_specs/error/incomplete-arbitrary-value-2.txt.snapis excluded by!**/*.snapand included by**crates/biome_tailwind_parser/tests/tailwind_specs/error/incomplete-arbitrary-variant.txt.snapis excluded by!**/*.snapand included by**crates/biome_tailwind_parser/tests/tailwind_specs/error/missing-modifier-value-1.txt.snapis excluded by!**/*.snapand included by**crates/biome_tailwind_parser/tests/tailwind_specs/error/missing-modifier-value.txt.snapis excluded by!**/*.snapand included by**crates/biome_tailwind_parser/tests/tailwind_specs/error/missing-value.txt.snapis excluded by!**/*.snapand included by**crates/biome_tailwind_parser/tests/tailwind_specs/ok/brackets/gradient.txt.snapis excluded by!**/*.snapand included by**crates/biome_tailwind_parser/tests/tailwind_specs/ok/brackets/image-url.txt.snapis excluded by!**/*.snapand included by**crates/biome_tailwind_parser/tests/tailwind_specs/ok/brackets/inset.txt.snapis excluded by!**/*.snapand included by**crates/biome_tailwind_parser/tests/tailwind_specs/ok/brackets/misc-xy.txt.snapis excluded by!**/*.snapand included by**crates/biome_tailwind_parser/tests/tailwind_specs/ok/brackets/shadow.txt.snapis excluded by!**/*.snapand included by**crates/biome_tailwind_parser/tests/tailwind_specs/ok/candidates/arbitrary-candidate-0.txt.snapis excluded by!**/*.snapand included by**crates/biome_tailwind_parser/tests/tailwind_specs/ok/candidates/arbitrary-candidate-1.txt.snapis excluded by!**/*.snapand included by**crates/biome_tailwind_parser/tests/tailwind_specs/ok/candidates/arbitrary-candidate-2.txt.snapis excluded by!**/*.snapand included by**crates/biome_tailwind_parser/tests/tailwind_specs/ok/candidates/arbitrary-candidate-3.txt.snapis excluded by!**/*.snapand included by**crates/biome_tailwind_parser/tests/tailwind_specs/ok/gradients/precise-control.txt.snapis excluded by!**/*.snapand included by**crates/biome_tailwind_parser/tests/tailwind_specs/ok/gradients/simple.txt.snapis excluded by!**/*.snapand included by**crates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/2-classes.txt.snapis excluded by!**/*.snapand included by**crates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/arbitrary-value-0.txt.snapis excluded by!**/*.snapand included by**crates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/arbitrary-value-1.txt.snapis excluded by!**/*.snapand included by**crates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/base-has-dash-0.txt.snapis excluded by!**/*.snapand included by**crates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/base-has-dash-1.txt.snapis excluded by!**/*.snapand included by**crates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/base-has-dash-2.txt.snapis excluded by!**/*.snapand included by**crates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/basic-0.txt.snapis excluded by!**/*.snapand included by**crates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/basic-1.txt.snapis excluded by!**/*.snapand included by**crates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/basic-2.txt.snapis excluded by!**/*.snapand included by**crates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/border.txt.snapis excluded by!**/*.snapand included by**crates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/css-value.txt.snapis excluded by!**/*.snapand included by**crates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/important.txt.snapis excluded by!**/*.snapand included by**crates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/modifier.txt.snapis excluded by!**/*.snapand included by**crates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/multiple-spaces.txt.snapis excluded by!**/*.snapand included by**crates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/multiple.txt.snapis excluded by!**/*.snapand included by**crates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/negative.txt.snapis excluded by!**/*.snapand included by**crates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/static.txt.snapis excluded by!**/*.snapand included by**crates/biome_tailwind_parser/tests/tailwind_specs/ok/stress/stress-1.txt.snapis excluded by!**/*.snapand included by**crates/biome_tailwind_parser/tests/tailwind_specs/ok/stress/stress-2.txt.snapis excluded by!**/*.snapand included by**crates/biome_tailwind_parser/tests/tailwind_specs/ok/variants/arbitrary-variant.txt.snapis excluded by!**/*.snapand included by**crates/biome_tailwind_parser/tests/tailwind_specs/ok/variants/functional-arbirary-param.txt.snapis excluded by!**/*.snapand included by**crates/biome_tailwind_parser/tests/tailwind_specs/ok/variants/functional-named-param.txt.snapis excluded by!**/*.snapand included by**crates/biome_tailwind_parser/tests/tailwind_specs/ok/variants/hover.txt.snapis excluded by!**/*.snapand included by**crates/biome_tailwind_parser/tests/tailwind_specs/ok/variants/hover_focus.txt.snapis excluded by!**/*.snapand included by**crates/biome_tailwind_parser/tests/tailwind_specs/ok/variants/starts-with-number.txt.snapis excluded by!**/*.snapand included by**crates/biome_tailwind_syntax/src/generated/nodes.rsis excluded by!**/generated/**,!**/generated/**and included by**
📒 Files selected for processing (8)
crates/biome_tailwind_parser/src/lexer/mod.rs(0 hunks)crates/biome_tailwind_parser/src/syntax/mod.rs(4 hunks)crates/biome_tailwind_parser/src/syntax/value.rs(0 hunks)crates/biome_tailwind_parser/src/token_source.rs(4 hunks)crates/biome_tailwind_parser/tests/quick_test.rs(1 hunks)crates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/2-classes.txt(1 hunks)crates/biome_tailwind_syntax/src/lib.rs(2 hunks)xtask/codegen/tailwind.ungram(1 hunks)
💤 Files with no reviewable changes (2)
- crates/biome_tailwind_parser/src/syntax/value.rs
- crates/biome_tailwind_parser/src/lexer/mod.rs
🧰 Additional context used
📓 Path-based instructions (1)
crates/**/*.rs
📄 CodeRabbit inference engine (CONTRIBUTING.md)
Update inline rustdoc documentation for rules, assists, and their options when adding new features or changing existing features in Rust crates
Files:
crates/biome_tailwind_parser/tests/quick_test.rscrates/biome_tailwind_syntax/src/lib.rscrates/biome_tailwind_parser/src/token_source.rscrates/biome_tailwind_parser/src/syntax/mod.rs
🧠 Learnings (20)
📓 Common learnings
Learnt from: ematipico
Repo: biomejs/biome PR: 7852
File: crates/biome_css_parser/src/syntax/property/mod.rs:161-168
Timestamp: 2025-10-25T07:22:18.540Z
Learning: In the Biome CSS parser, lexer token emission should not be gated behind parser options like `is_tailwind_directives_enabled()`. The lexer must emit correct tokens regardless of parser options to enable accurate diagnostics and error messages when the syntax is used incorrectly.
📚 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_tailwind_parser/tests/quick_test.rscrates/biome_tailwind_parser/src/syntax/mod.rs
📚 Learning: 2025-12-19T12:53:30.399Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-12-19T12:53:30.399Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/**/*.rs : Avoid string allocations by comparing against `&str` or using `TokenText`
Applied to files:
crates/biome_tailwind_parser/tests/quick_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_tailwind_parser/tests/quick_test.rscrates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/2-classes.txt
📚 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 `p.eat(token)` for optional tokens, `p.expect(token)` for required tokens, `parse_rule(p).ok(p)` for optional nodes, and `parse_rule(p).or_add_diagnostic(p, error)` for required nodes
Applied to files:
crates/biome_tailwind_parser/tests/quick_test.rscrates/biome_tailwind_parser/src/syntax/mod.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 : Distinguish between `TypeData::Unknown` and `TypeData::UnknownKeyword` to measure inference effectiveness versus explicit user-provided unknown types
Applied to files:
crates/biome_tailwind_parser/tests/quick_test.rscrates/biome_tailwind_syntax/src/lib.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_tailwind_parser/tests/quick_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 error recovery in list parsing using `or_recover()` to wrap unparseable tokens in a `BOGUS_*` node and consume tokens until a recovery token is found
Applied to files:
crates/biome_tailwind_parser/tests/quick_test.rscrates/biome_tailwind_parser/src/syntax/mod.rs
📚 Learning: 2025-12-19T12:53:30.399Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-12-19T12:53:30.399Z
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_tailwind_parser/tests/quick_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_tailwind_syntax/src/lib.rscrates/biome_tailwind_parser/src/syntax/mod.rs
📚 Learning: 2025-12-12T10:11:05.564Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: CONTRIBUTING.md:0-0
Timestamp: 2025-12-12T10:11:05.564Z
Learning: Applies to crates/**/*.rs : Update inline rustdoc documentation for rules, assists, and their options when adding new features or changing existing features in Rust crates
Applied to files:
crates/biome_tailwind_syntax/src/lib.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_tailwind_parser/src/token_source.rscrates/biome_tailwind_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_tailwind_parser/src/token_source.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_tailwind_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_tailwind_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_tailwind_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 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_tailwind_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 : A parser struct must implement the `Parser` trait and save the token source, parser context, and optional parser options
Applied to files:
crates/biome_tailwind_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 return `ParsedSyntax::Absent` if the rule can't predict by the next token(s) if they form the expected node, and must not progress the parser in this case
Applied to files:
crates/biome_tailwind_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/**/lexer/mod.rs : Implement a `Lexer` trait from `biome_parser` crate for the lexer struct that consumes characters from source code and emits tokens
Applied to files:
crates/biome_tailwind_parser/src/syntax/mod.rs
⏰ 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). (11)
- GitHub Check: Check Dependencies
- GitHub Check: Lint project (depot-ubuntu-24.04-arm-16)
- GitHub Check: Test (depot-windows-2022-16)
- GitHub Check: Documentation
- GitHub Check: Test (depot-ubuntu-24.04-arm-16)
- GitHub Check: End-to-end tests
- GitHub Check: Lint project (depot-windows-2022)
- GitHub Check: Test Node.js API
- GitHub Check: Parser conformance
- GitHub Check: Bench (biome_tailwind_parser)
- GitHub Check: autofix
🔇 Additional comments (6)
crates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/2-classes.txt (1)
1-1: LGTM!Good test case for validating that two classes separated by whitespace—including a negative utility—are correctly parsed.
xtask/codegen/tailwind.ungram (1)
55-55: LGTM!The flattened
AnyTwFullCandidate*structure properly reflects that whitespace between candidates is now handled as trivia rather than explicit separators.crates/biome_tailwind_parser/tests/quick_test.rs (1)
7-7: LGTM!Solid test input for exercising negative utilities with whitespace separation.
crates/biome_tailwind_parser/src/token_source.rs (2)
17-19: LGTM on the trivia tracking implementation.Clean addition of the
had_trivia_beforestate and its accessor. The reset logic innext_non_trivia_tokenis correctly placed.Also applies to: 106-110
100-105: Therewindmethod doesn't need to restorehad_trivia_before—the flag is reset at the start of every token consumption.Since
next_non_trivia_token()resetshad_trivia_before = falseon entry (line 67), and every code path that advances the parser (viabump_with_context()) calls this method, the flag is always reset before its state matters. The checkpoint design is sound.crates/biome_tailwind_parser/src/syntax/mod.rs (1)
31-56: LGTM on the ParseNodeList implementation.Clean switch from
ParseSeparatedListtoParseNodeList. The recovery logic is well-structured.
Parser conformance results onjs/262
jsx/babel
symbols/microsoft
ts/babel
ts/microsoft
|
CodSpeed Performance ReportMerging #8513 will improve performances by 16.27%Comparing Summary
Benchmarks breakdown
Footnotes
|
This stack of pull requests is managed by Graphite. Learn more about stacking. |
98f8dc3 to
c242dce
Compare
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
crates/biome_tailwind_parser/src/syntax/mod.rs (1)
113-124: Clarify whether both trivia checks are needed.Lines 113–117 check
had_trivia_before()and lines 118–124 checktrivia_list.last()against the captured position. Both returnTW_STATIC_CANDIDATEwith identical comments. Ifhad_trivia_before()already detects trivia immediately preceding the current token, the second check may be redundant.Could you clarify when one check would succeed but not the other? If both are truly necessary, a brief comment explaining the distinction would help future maintainers.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (48)
crates/biome_tailwind_factory/src/generated/node_factory.rsis excluded by!**/generated/**,!**/generated/**and included by**crates/biome_tailwind_factory/src/generated/syntax_factory.rsis excluded by!**/generated/**,!**/generated/**and included by**crates/biome_tailwind_parser/tests/tailwind_specs/error/arbitrary-candidate/missing-property.txt.snapis excluded by!**/*.snapand included by**crates/biome_tailwind_parser/tests/tailwind_specs/error/arbitrary-candidate/missing-value-in-arbitrary.txt.snapis excluded by!**/*.snapand included by**crates/biome_tailwind_parser/tests/tailwind_specs/error/incomplete-arbitrary-value-0.txt.snapis excluded by!**/*.snapand included by**crates/biome_tailwind_parser/tests/tailwind_specs/error/incomplete-arbitrary-value-1.txt.snapis excluded by!**/*.snapand included by**crates/biome_tailwind_parser/tests/tailwind_specs/error/incomplete-arbitrary-value-2.txt.snapis excluded by!**/*.snapand included by**crates/biome_tailwind_parser/tests/tailwind_specs/error/incomplete-arbitrary-variant.txt.snapis excluded by!**/*.snapand included by**crates/biome_tailwind_parser/tests/tailwind_specs/error/missing-modifier-value-1.txt.snapis excluded by!**/*.snapand included by**crates/biome_tailwind_parser/tests/tailwind_specs/error/missing-modifier-value.txt.snapis excluded by!**/*.snapand included by**crates/biome_tailwind_parser/tests/tailwind_specs/error/missing-value.txt.snapis excluded by!**/*.snapand included by**crates/biome_tailwind_parser/tests/tailwind_specs/ok/brackets/gradient.txt.snapis excluded by!**/*.snapand included by**crates/biome_tailwind_parser/tests/tailwind_specs/ok/brackets/image-url.txt.snapis excluded by!**/*.snapand included by**crates/biome_tailwind_parser/tests/tailwind_specs/ok/brackets/inset.txt.snapis excluded by!**/*.snapand included by**crates/biome_tailwind_parser/tests/tailwind_specs/ok/brackets/misc-xy.txt.snapis excluded by!**/*.snapand included by**crates/biome_tailwind_parser/tests/tailwind_specs/ok/brackets/shadow.txt.snapis excluded by!**/*.snapand included by**crates/biome_tailwind_parser/tests/tailwind_specs/ok/candidates/arbitrary-candidate-0.txt.snapis excluded by!**/*.snapand included by**crates/biome_tailwind_parser/tests/tailwind_specs/ok/candidates/arbitrary-candidate-1.txt.snapis excluded by!**/*.snapand included by**crates/biome_tailwind_parser/tests/tailwind_specs/ok/candidates/arbitrary-candidate-2.txt.snapis excluded by!**/*.snapand included by**crates/biome_tailwind_parser/tests/tailwind_specs/ok/candidates/arbitrary-candidate-3.txt.snapis excluded by!**/*.snapand included by**crates/biome_tailwind_parser/tests/tailwind_specs/ok/gradients/precise-control.txt.snapis excluded by!**/*.snapand included by**crates/biome_tailwind_parser/tests/tailwind_specs/ok/gradients/simple.txt.snapis excluded by!**/*.snapand included by**crates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/2-classes.txt.snapis excluded by!**/*.snapand included by**crates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/arbitrary-value-0.txt.snapis excluded by!**/*.snapand included by**crates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/arbitrary-value-1.txt.snapis excluded by!**/*.snapand included by**crates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/base-has-dash-0.txt.snapis excluded by!**/*.snapand included by**crates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/base-has-dash-1.txt.snapis excluded by!**/*.snapand included by**crates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/base-has-dash-2.txt.snapis excluded by!**/*.snapand included by**crates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/basic-0.txt.snapis excluded by!**/*.snapand included by**crates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/basic-1.txt.snapis excluded by!**/*.snapand included by**crates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/basic-2.txt.snapis excluded by!**/*.snapand included by**crates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/border.txt.snapis excluded by!**/*.snapand included by**crates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/css-value.txt.snapis excluded by!**/*.snapand included by**crates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/important.txt.snapis excluded by!**/*.snapand included by**crates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/modifier.txt.snapis excluded by!**/*.snapand included by**crates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/multiple-spaces.txt.snapis excluded by!**/*.snapand included by**crates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/multiple.txt.snapis excluded by!**/*.snapand included by**crates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/negative.txt.snapis excluded by!**/*.snapand included by**crates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/static.txt.snapis excluded by!**/*.snapand included by**crates/biome_tailwind_parser/tests/tailwind_specs/ok/stress/stress-1.txt.snapis excluded by!**/*.snapand included by**crates/biome_tailwind_parser/tests/tailwind_specs/ok/stress/stress-2.txt.snapis excluded by!**/*.snapand included by**crates/biome_tailwind_parser/tests/tailwind_specs/ok/variants/arbitrary-variant.txt.snapis excluded by!**/*.snapand included by**crates/biome_tailwind_parser/tests/tailwind_specs/ok/variants/functional-arbirary-param.txt.snapis excluded by!**/*.snapand included by**crates/biome_tailwind_parser/tests/tailwind_specs/ok/variants/functional-named-param.txt.snapis excluded by!**/*.snapand included by**crates/biome_tailwind_parser/tests/tailwind_specs/ok/variants/hover.txt.snapis excluded by!**/*.snapand included by**crates/biome_tailwind_parser/tests/tailwind_specs/ok/variants/hover_focus.txt.snapis excluded by!**/*.snapand included by**crates/biome_tailwind_parser/tests/tailwind_specs/ok/variants/starts-with-number.txt.snapis excluded by!**/*.snapand included by**crates/biome_tailwind_syntax/src/generated/nodes.rsis excluded by!**/generated/**,!**/generated/**and included by**
📒 Files selected for processing (8)
crates/biome_tailwind_parser/src/lexer/mod.rs(0 hunks)crates/biome_tailwind_parser/src/syntax/mod.rs(4 hunks)crates/biome_tailwind_parser/src/syntax/value.rs(0 hunks)crates/biome_tailwind_parser/src/token_source.rs(4 hunks)crates/biome_tailwind_parser/tests/quick_test.rs(1 hunks)crates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/2-classes.txt(1 hunks)crates/biome_tailwind_syntax/src/lib.rs(2 hunks)xtask/codegen/tailwind.ungram(1 hunks)
💤 Files with no reviewable changes (2)
- crates/biome_tailwind_parser/src/syntax/value.rs
- crates/biome_tailwind_parser/src/lexer/mod.rs
🚧 Files skipped from review as they are similar to previous changes (4)
- crates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/2-classes.txt
- crates/biome_tailwind_parser/tests/quick_test.rs
- crates/biome_tailwind_syntax/src/lib.rs
- crates/biome_tailwind_parser/src/token_source.rs
🧰 Additional context used
📓 Path-based instructions (1)
crates/**/*.rs
📄 CodeRabbit inference engine (CONTRIBUTING.md)
Update inline rustdoc documentation for rules, assists, and their options when adding new features or changing existing features in Rust crates
Files:
crates/biome_tailwind_parser/src/syntax/mod.rs
🧠 Learnings (13)
📓 Common learnings
Learnt from: ematipico
Repo: biomejs/biome PR: 7852
File: crates/biome_css_parser/src/syntax/property/mod.rs:161-168
Timestamp: 2025-10-25T07:22:18.540Z
Learning: In the Biome CSS parser, lexer token emission should not be gated behind parser options like `is_tailwind_directives_enabled()`. The lexer must emit correct tokens regardless of parser options to enable accurate diagnostics and error messages when the syntax is used incorrectly.
📚 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_tailwind_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 : Implement error recovery in list parsing using `or_recover()` to wrap unparseable tokens in a `BOGUS_*` node and consume tokens until a recovery token is found
Applied to files:
crates/biome_tailwind_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 : 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_tailwind_parser/src/syntax/mod.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_tailwind_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 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_tailwind_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 : Use `p.eat(token)` for optional tokens, `p.expect(token)` for required tokens, `parse_rule(p).ok(p)` for optional nodes, and `parse_rule(p).or_add_diagnostic(p, error)` for required nodes
Applied to files:
crates/biome_tailwind_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_tailwind_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_tailwind_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 return `ParsedSyntax::Absent` if the rule can't predict by the next token(s) if they form the expected node, and must not progress the parser in this case
Applied to files:
crates/biome_tailwind_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 : A parser struct must implement the `Parser` trait and save the token source, parser context, and optional parser options
Applied to files:
crates/biome_tailwind_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 : 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_tailwind_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/**/lexer/mod.rs : Implement a `Lexer` trait from `biome_parser` crate for the lexer struct that consumes characters from source code and emits tokens
Applied to files:
crates/biome_tailwind_parser/src/syntax/mod.rs
⏰ 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). (11)
- GitHub Check: Test Node.js API
- GitHub Check: Bench (biome_tailwind_parser)
- GitHub Check: autofix
- GitHub Check: Parser conformance
- GitHub Check: End-to-end tests
- GitHub Check: Documentation
- GitHub Check: Lint project (depot-windows-2022)
- GitHub Check: Lint project (depot-ubuntu-24.04-arm-16)
- GitHub Check: Test (depot-ubuntu-24.04-arm-16)
- GitHub Check: Test (depot-windows-2022-16)
- GitHub Check: Check Dependencies
🔇 Additional comments (3)
crates/biome_tailwind_parser/src/syntax/mod.rs (2)
6-6: LGTM!Import updated appropriately for the new
ParseNodeListusage.
31-56: Clean transition toParseNodeList.The switch from
ParseSeparatedListtoParseNodeListis well-aligned with treating whitespace as trivia. The recovery logic remains sensible.xtask/codegen/tailwind.ungram (1)
55-55: Grammar simplification aligns with parser changes.Flattening the list to
AnyTwFullCandidate*is consistent with treating whitespace as trivia rather than explicit separators. Clean change.
<!-- IMPORTANT!! If you generated this PR with the help of any AI assistance, please disclose it in the PR. https://github.com/biomejs/biome/blob/main/CONTRIBUTING.md#ai-assistance-notice --> <!-- Thanks for submitting a Pull Request! We appreciate you spending the time to work on these changes. Please provide enough information so that others can review your PR. Once created, your PR will be automatically labeled according to changed files. Learn more about contributing: https://github.com/biomejs/biome/blob/main/CONTRIBUTING.md --> ## Summary <!-- Explain the **motivation** for making this change. What existing problem does the pull request solve?--> This refactors the tailwind parser to use trivia again, instead of considering whitespace to not be trivia. <!-- Link any relevant issues if necessary or include a transcript of any Discord discussion. --> <!-- If you create a user-facing change, please write a changeset: https://github.com/biomejs/biome/blob/main/CONTRIBUTING.md#writing-a-changeset (your changeset is often a good starting point for this summary as well) --> ## Test Plan <!-- What demonstrates that your implementation is correct? --> snapshots ## Docs <!-- If you're submitting a new rule or action (or an option for them), the documentation is part of the code. Make sure rules and actions have example usages, and that all options are documented. --> <!-- For other features, please submit a documentation PR to the `next` branch of our website: https://github.com/biomejs/website/. Link the PR here once it's ready. -->

Summary
This refactors the tailwind parser to use trivia again, instead of considering whitespace to not be trivia.
Test Plan
snapshots
Docs