Skip to content

Conversation

@dyc3
Copy link
Contributor

@dyc3 dyc3 commented Nov 1, 2025

Summary

Putting a - in front of a candidate makes it a negative value.

Test Plan

Added tests.
Also tested against tailwind's playground: https://play.tailwindcss.com/gMIIXZnNPl

Docs

no changeset because this parser isn't used anywhere yet

@changeset-bot
Copy link

changeset-bot bot commented Nov 1, 2025

⚠️ No Changeset found

Latest commit: 5b79888

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

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

@github-actions github-actions bot added A-Parser Area: parser A-Tooling Area: internal tools labels Nov 1, 2025
@github-actions
Copy link
Contributor

github-actions bot commented Nov 1, 2025

Parser conformance results on

js/262

Test result main count This PR count Difference
Total 50828 50828 0
Passed 49624 49624 0
Failed 1162 1162 0
Panics 42 42 0
Coverage 97.63% 97.63% 0.00%

jsx/babel

Test result main count This PR count Difference
Total 40 40 0
Passed 37 37 0
Failed 3 3 0
Panics 0 0 0
Coverage 92.50% 92.50% 0.00%

symbols/microsoft

Test result main count This PR count Difference
Total 6320 6320 0
Passed 2106 2106 0
Failed 4214 4214 0
Panics 0 0 0
Coverage 33.32% 33.32% 0.00%

ts/babel

Test result main count This PR count Difference
Total 835 835 0
Passed 742 742 0
Failed 93 93 0
Panics 0 0 0
Coverage 88.86% 88.86% 0.00%

ts/microsoft

Test result main count This PR count Difference
Total 18813 18813 0
Passed 14065 14065 0
Failed 4747 4747 0
Panics 1 1 0
Coverage 74.76% 74.76% 0.00%

@codspeed-hq
Copy link

codspeed-hq bot commented Nov 1, 2025

CodSpeed Performance Report

Merging #7937 will not alter performance

Comparing dyc3/tw-parse-negative-candidates (5b79888) with main (69cecec)1

Summary

✅ 53 untouched
⏩ 85 skipped2

Footnotes

  1. No successful run was found on main (a35c496) during the generation of this report, so 69cecec was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

  2. 85 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 1, 2025

Walkthrough

Adds support for negative Tailwind modifiers (e.g. -mt-4). Introduces TailwindLexContext::SawNegative and a consume_token_saw_negative path in the Tailwind lexer, adds a prev_byte_at(&self, offset: usize) -> Option<u8> method to the Lexer<'src> trait, updates parser grammar and recovery to recognise leading - on full candidates, extends TwFullCandidate in the ungram with an optional negative field, and adds lexer + spec tests and new gradient spec fixtures.

Possibly related PRs

Suggested labels

L-Tailwind

Suggested reviewers

  • ematipico
  • denbezrukov

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Title Check ✅ Passed The pull request title "feat(parse/tailwind): parse negative candidates, other misc cleanup/refactors" directly maps to the primary changes in the changeset. The raw_summary confirms multiple modifications across the Tailwind parser components specifically to support negative candidate parsing: the lexer gains a SawNegative context, the token source adds the SawNegative variant, syntax parsing handles minus signs, and new test cases validate negative candidates like "-top-4". The title is concise, specific, and clearly conveys the main feature addition without unnecessary noise.
Description Check ✅ Passed The pull request description explains the motivation ("Putting a - in front of a candidate makes it a negative value") and directly relates to the implementation details shown in the changeset. It references added tests and validates the implementation against Tailwind's playground, providing concrete evidence of testing. The description adequately contextualises the changes, including the rationale for omitting a changeset because the parser isn't yet in use.
Docstring Coverage ✅ Passed Docstring coverage is 81.25% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch dyc3/tw-parse-negative-candidates

📜 Recent 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 e0ee9c0 and 5b79888.

⛔ Files ignored due to path filters (43)
  • crates/biome_tailwind_factory/src/generated/node_factory.rs is excluded by !**/generated/**, !**/generated/** and included by **
  • crates/biome_tailwind_factory/src/generated/syntax_factory.rs is excluded by !**/generated/**, !**/generated/** and included by **
  • crates/biome_tailwind_parser/tests/tailwind_specs/error/arbitrary-candidate/missing-property.txt.snap is excluded by !**/*.snap and included by **
  • crates/biome_tailwind_parser/tests/tailwind_specs/error/arbitrary-candidate/missing-value-in-arbitrary.txt.snap is excluded by !**/*.snap and included by **
  • crates/biome_tailwind_parser/tests/tailwind_specs/error/incomplete-arbitrary-value-0.txt.snap is excluded by !**/*.snap and included by **
  • crates/biome_tailwind_parser/tests/tailwind_specs/error/incomplete-arbitrary-value-1.txt.snap is excluded by !**/*.snap and included by **
  • crates/biome_tailwind_parser/tests/tailwind_specs/error/incomplete-arbitrary-value-2.txt.snap is excluded by !**/*.snap and included by **
  • crates/biome_tailwind_parser/tests/tailwind_specs/error/incomplete-arbitrary-variant.txt.snap is excluded by !**/*.snap and included by **
  • crates/biome_tailwind_parser/tests/tailwind_specs/error/missing-modifier-value-1.txt.snap is excluded by !**/*.snap and included by **
  • crates/biome_tailwind_parser/tests/tailwind_specs/error/missing-modifier-value.txt.snap is excluded by !**/*.snap and included by **
  • crates/biome_tailwind_parser/tests/tailwind_specs/error/missing-value.txt.snap is excluded by !**/*.snap and included by **
  • crates/biome_tailwind_parser/tests/tailwind_specs/ok/brackets/gradient.txt.snap is excluded by !**/*.snap and included by **
  • crates/biome_tailwind_parser/tests/tailwind_specs/ok/brackets/image-url.txt.snap is excluded by !**/*.snap and included by **
  • crates/biome_tailwind_parser/tests/tailwind_specs/ok/brackets/shadow.txt.snap is excluded by !**/*.snap and included by **
  • crates/biome_tailwind_parser/tests/tailwind_specs/ok/candidates/arbitrary-candidate-0.txt.snap is excluded by !**/*.snap and included by **
  • crates/biome_tailwind_parser/tests/tailwind_specs/ok/candidates/arbitrary-candidate-1.txt.snap is excluded by !**/*.snap and included by **
  • crates/biome_tailwind_parser/tests/tailwind_specs/ok/candidates/arbitrary-candidate-2.txt.snap is excluded by !**/*.snap and included by **
  • crates/biome_tailwind_parser/tests/tailwind_specs/ok/candidates/arbitrary-candidate-3.txt.snap is excluded by !**/*.snap and included by **
  • crates/biome_tailwind_parser/tests/tailwind_specs/ok/gradients/precise-control.txt.snap is excluded by !**/*.snap and included by **
  • crates/biome_tailwind_parser/tests/tailwind_specs/ok/gradients/simple.txt.snap is excluded by !**/*.snap and included by **
  • crates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/arbitrary-value-0.txt.snap is excluded by !**/*.snap and included by **
  • crates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/arbitrary-value-1.txt.snap is excluded by !**/*.snap and included by **
  • crates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/base-has-dash-0.txt.snap is excluded by !**/*.snap and included by **
  • crates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/base-has-dash-1.txt.snap is excluded by !**/*.snap and included by **
  • crates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/base-has-dash-2.txt.snap is excluded by !**/*.snap and included by **
  • crates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/basic-0.txt.snap is excluded by !**/*.snap and included by **
  • crates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/basic-1.txt.snap is excluded by !**/*.snap and included by **
  • crates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/css-value.txt.snap is excluded by !**/*.snap and included by **
  • crates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/important.txt.snap is excluded by !**/*.snap and included by **
  • crates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/modifier.txt.snap is excluded by !**/*.snap and included by **
  • crates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/multiple-spaces.txt.snap is excluded by !**/*.snap and included by **
  • crates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/multiple.txt.snap is excluded by !**/*.snap and included by **
  • crates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/negative.txt.snap is excluded by !**/*.snap and included by **
  • crates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/static.txt.snap is excluded by !**/*.snap and included by **
  • crates/biome_tailwind_parser/tests/tailwind_specs/ok/stress/stress-1.txt.snap is excluded by !**/*.snap and included by **
  • crates/biome_tailwind_parser/tests/tailwind_specs/ok/stress/stress-2.txt.snap is excluded by !**/*.snap and included by **
  • crates/biome_tailwind_parser/tests/tailwind_specs/ok/variants/arbitrary-variant.txt.snap is excluded by !**/*.snap and included by **
  • crates/biome_tailwind_parser/tests/tailwind_specs/ok/variants/functional-arbirary-param.txt.snap is excluded by !**/*.snap and included by **
  • crates/biome_tailwind_parser/tests/tailwind_specs/ok/variants/functional-named-param.txt.snap is excluded by !**/*.snap and included by **
  • crates/biome_tailwind_parser/tests/tailwind_specs/ok/variants/hover.txt.snap is excluded by !**/*.snap and included by **
  • crates/biome_tailwind_parser/tests/tailwind_specs/ok/variants/hover_focus.txt.snap is excluded by !**/*.snap and included by **
  • crates/biome_tailwind_syntax/src/generated/nodes.rs is excluded by !**/generated/**, !**/generated/** and included by **
  • crates/biome_tailwind_syntax/src/generated/nodes_mut.rs is excluded by !**/generated/**, !**/generated/** and included by **
📒 Files selected for processing (11)
  • .github/labeler.yml (1 hunks)
  • crates/biome_parser/src/lexer.rs (2 hunks)
  • crates/biome_tailwind_parser/src/lexer/mod.rs (2 hunks)
  • crates/biome_tailwind_parser/src/lexer/tests.rs (1 hunks)
  • crates/biome_tailwind_parser/src/syntax/mod.rs (4 hunks)
  • crates/biome_tailwind_parser/src/syntax/variant.rs (1 hunks)
  • crates/biome_tailwind_parser/src/token_source.rs (1 hunks)
  • crates/biome_tailwind_parser/tests/tailwind_specs/ok/gradients/precise-control.txt (1 hunks)
  • crates/biome_tailwind_parser/tests/tailwind_specs/ok/gradients/simple.txt (1 hunks)
  • crates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/negative.txt (1 hunks)
  • xtask/codegen/tailwind.ungram (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (6)
  • xtask/codegen/tailwind.ungram
  • crates/biome_tailwind_parser/src/lexer/mod.rs
  • crates/biome_tailwind_parser/tests/tailwind_specs/ok/gradients/simple.txt
  • .github/labeler.yml
  • crates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/negative.txt
  • crates/biome_parser/src/lexer.rs
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{rs,toml}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Format Rust and TOML files before committing (e.g., via just f)

Files:

  • crates/biome_tailwind_parser/src/lexer/tests.rs
  • crates/biome_tailwind_parser/src/token_source.rs
  • crates/biome_tailwind_parser/src/syntax/variant.rs
  • crates/biome_tailwind_parser/src/syntax/mod.rs
**/*.rs

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Document rules, assists, and their options with inline rustdoc in the Rust source

Files:

  • crates/biome_tailwind_parser/src/lexer/tests.rs
  • crates/biome_tailwind_parser/src/token_source.rs
  • crates/biome_tailwind_parser/src/syntax/variant.rs
  • crates/biome_tailwind_parser/src/syntax/mod.rs
🧠 Learnings (15)
📚 Learning: 2025-10-15T09:22:15.851Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:22:15.851Z
Learning: Applies to crates/biome_formatter/tests/spec_tests.rs : In tests/spec_tests.rs, generate tests with `tests_macros::gen_tests! {"tests/specs/html/**/*.html", crate::spec_test::run, ""}`

Applied to files:

  • crates/biome_tailwind_parser/tests/tailwind_specs/ok/gradients/precise-control.txt
📚 Learning: 2025-10-15T09:22:15.851Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:22:15.851Z
Learning: Applies to crates/biome_formatter/tests/language.rs : Create tests/language.rs defining `HtmlTestFormatLanguage` and implement the TestFormatLanguage trait

Applied to files:

  • crates/biome_tailwind_parser/src/lexer/tests.rs
📚 Learning: 2025-10-15T09:22:46.002Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_js_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:22:46.002Z
Learning: Applies to crates/biome_js_formatter/**/*.rs : Do not attempt to fix code; if a mandatory token/node is missing, return `None` instead

Applied to files:

  • crates/biome_tailwind_parser/src/syntax/variant.rs
  • crates/biome_tailwind_parser/src/syntax/mod.rs
📚 Learning: 2025-10-24T21:24:58.650Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-10-24T21:24:58.650Z
Learning: Applies to crates/biome_analyze/crates/*_analyze/**/src/**/lint/**/*.rs : Use options/full_options/use_options code block modifiers as specified to demonstrate configuration in docs; keep modifier order consistent

Applied to files:

  • crates/biome_tailwind_parser/src/syntax/variant.rs
  • crates/biome_tailwind_parser/src/syntax/mod.rs
📚 Learning: 2025-10-24T21:24:58.650Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-10-24T21:24:58.650Z
Learning: Applies to crates/biome_analyze/crates/*_analyze/**/src/**/lint/**/*.rs : Avoid deep indentation by using combinators (map, filter, and_then) rather than nested if-let/unwrap chains

Applied to files:

  • crates/biome_tailwind_parser/src/syntax/variant.rs
📚 Learning: 2025-10-15T09:22:15.851Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:22:15.851Z
Learning: Applies to crates/biome_formatter/src/**/*.rs : After generation, remove usages of `format_verbatim_node` and implement real formatting with biome_formatter utilities

Applied to files:

  • crates/biome_tailwind_parser/src/syntax/variant.rs
📚 Learning: 2025-10-24T21:24:58.650Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-10-24T21:24:58.650Z
Learning: Applies to crates/biome_analyze/crates/*_analyze/**/src/**/lint/**/*.rs : Prefer transforming Result to Option with .ok()? inside run when Signals is Option, and use let-else when Signals is a Vec/collection

Applied to files:

  • crates/biome_tailwind_parser/src/syntax/variant.rs
📚 Learning: 2025-10-24T21:24:58.650Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-10-24T21:24:58.650Z
Learning: Applies to crates/biome_analyze/crates/biome_rule_options/lib/**/*.rs : Prefer Box<[Box<str>]> over Vec<String> for options string lists to reduce memory usage

Applied to files:

  • crates/biome_tailwind_parser/src/syntax/variant.rs
📚 Learning: 2025-10-15T09:24:31.042Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_parser/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:24:31.042Z
Learning: Applies to crates/biome_parser/xtask/codegen/*.ungram : Bogus nodes must be part of a variant/union (e.g., AnyHtmlAttribute includes HtmlBogusAttribute)

Applied to files:

  • crates/biome_tailwind_parser/src/syntax/variant.rs
📚 Learning: 2025-10-15T09:24:31.042Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_parser/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:24:31.042Z
Learning: Parse rules should return ParsedSyntax; return Absent without consuming tokens when the node cannot be predicted

Applied to files:

  • crates/biome_tailwind_parser/src/syntax/variant.rs
📚 Learning: 2025-10-15T09:22:46.002Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_js_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:22:46.002Z
Learning: Applies to crates/biome_js_formatter/**/*.rs : For non-mandatory tokens, use the provided helper constructors (e.g., `token`, `space_token`, `dynamic_token`)

Applied to files:

  • crates/biome_tailwind_parser/src/syntax/mod.rs
📚 Learning: 2025-10-15T09:22:46.002Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_js_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:22:46.002Z
Learning: Applies to crates/biome_js_formatter/**/*.rs : When a token is mandatory and present in the AST, use the AST-provided token (e.g., `node.l_paren_token().format()`) instead of emitting a static token

Applied to files:

  • crates/biome_tailwind_parser/src/syntax/mod.rs
📚 Learning: 2025-10-24T21:24:58.650Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-10-24T21:24:58.650Z
Learning: Applies to crates/biome_analyze/crates/*_analyze/**/src/**/lint/**/*.rs : Avoid avoidable string allocations; compare against &str or TokenText rather than allocating Strings

Applied to files:

  • crates/biome_tailwind_parser/src/syntax/mod.rs
📚 Learning: 2025-10-15T09:24:31.042Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_parser/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:24:31.042Z
Learning: Lexer must implement the biome_parser::Lexer trait

Applied to files:

  • crates/biome_tailwind_parser/src/syntax/mod.rs
📚 Learning: 2025-10-15T09:24:31.042Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_parser/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:24:31.042Z
Learning: Use p.eat for optional tokens, p.expect for required tokens; use .ok() for optional nodes and .or_add_diagnostic(...) for required nodes

Applied to files:

  • crates/biome_tailwind_parser/src/syntax/mod.rs
🧬 Code graph analysis (1)
crates/biome_tailwind_parser/src/syntax/mod.rs (2)
crates/biome_tailwind_parser/src/parser.rs (1)
  • new (19-24)
crates/biome_tailwind_parser/src/syntax/value.rs (1)
  • parse_value (9-17)
⏰ 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). (20)
  • GitHub Check: Bench (biome_css_analyze)
  • GitHub Check: Bench (biome_css_formatter)
  • GitHub Check: Bench (biome_css_parser)
  • GitHub Check: Test Node.js API
  • GitHub Check: Documentation
  • GitHub Check: Test (depot-windows-2022-16)
  • GitHub Check: Test (depot-ubuntu-24.04-arm-16)
  • GitHub Check: Lint project (depot-windows-2022)
  • GitHub Check: Lint project (depot-ubuntu-24.04-arm-16)
  • GitHub Check: Check Dependencies
  • GitHub Check: autofix
  • GitHub Check: Bench (biome_js_analyze)
  • GitHub Check: Bench (biome_json_formatter)
  • GitHub Check: Bench (biome_js_parser)
  • GitHub Check: Bench (biome_js_formatter)
  • GitHub Check: Parser conformance
  • GitHub Check: Bench (biome_graphql_formatter)
  • GitHub Check: Bench (biome_json_parser)
  • GitHub Check: Bench (biome_graphql_parser)
  • GitHub Check: Bench (biome_json_analyze)
🔇 Additional comments (7)
crates/biome_tailwind_parser/tests/tailwind_specs/ok/gradients/precise-control.txt (1)

1-1: Test fixture looks good.

The gradient utility string is valid Tailwind syntax and should parse correctly. The fixture complements the other gradient tests and fits well into the ok/gradients/ test category.

crates/biome_tailwind_parser/src/syntax/variant.rs (1)

82-85: LGTM! Guard correctly prevents negative variants.

The early return with Absent properly ensures variants cannot start with a negative sign, leaving the caller free to parse it as a negative full candidate instead.

crates/biome_tailwind_parser/src/lexer/tests.rs (1)

195-203: LGTM! Test correctly validates SawNegative context.

The test properly exercises the lexer with a leading minus sign and verifies the expected token sequence.

crates/biome_tailwind_parser/src/token_source.rs (1)

25-26: LGTM! Well-documented context variant.

The SawNegative variant is clearly documented and properly integrates with the existing lexer context mechanism.

crates/biome_tailwind_parser/src/syntax/mod.rs (3)

72-74: LGTM! Correct handling of negative candidates.

The pre-check properly consumes the leading - with SawNegative context after variants are parsed, allowing the lexer to expect a basename next.


121-121: LGTM! More explicit token expectation.

Using expect instead of bump is better practice, making the requirement for the dash token explicit.


202-202: Verify recovery approach consistency for modifiers.

The modifier recovery explicitly includes NEWLINE in the token set, whilst other recovery sets (lines 60, 81, 125) use .enable_recovery_on_line_break() instead. Is this intentional, or should modifiers follow the same pattern?


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: 0

🧹 Nitpick comments (1)
crates/biome_tailwind_parser/src/lexer/mod.rs (1)

74-87: Consider reducing duplication.

The consume_token_saw_negative method duplicates much of the logic from consume_token (lines 49-72). Whilst the duplication may be intentional for performance or clarity, you could potentially extract shared logic into helper methods to improve maintainability.

📜 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 a35c496 and e0ee9c0.

⛔ Files ignored due to path filters (43)
  • crates/biome_tailwind_factory/src/generated/node_factory.rs is excluded by !**/generated/**, !**/generated/** and included by **
  • crates/biome_tailwind_factory/src/generated/syntax_factory.rs is excluded by !**/generated/**, !**/generated/** and included by **
  • crates/biome_tailwind_parser/tests/tailwind_specs/error/arbitrary-candidate/missing-property.txt.snap is excluded by !**/*.snap and included by **
  • crates/biome_tailwind_parser/tests/tailwind_specs/error/arbitrary-candidate/missing-value-in-arbitrary.txt.snap is excluded by !**/*.snap and included by **
  • crates/biome_tailwind_parser/tests/tailwind_specs/error/incomplete-arbitrary-value-0.txt.snap is excluded by !**/*.snap and included by **
  • crates/biome_tailwind_parser/tests/tailwind_specs/error/incomplete-arbitrary-value-1.txt.snap is excluded by !**/*.snap and included by **
  • crates/biome_tailwind_parser/tests/tailwind_specs/error/incomplete-arbitrary-value-2.txt.snap is excluded by !**/*.snap and included by **
  • crates/biome_tailwind_parser/tests/tailwind_specs/error/incomplete-arbitrary-variant.txt.snap is excluded by !**/*.snap and included by **
  • crates/biome_tailwind_parser/tests/tailwind_specs/error/missing-modifier-value-1.txt.snap is excluded by !**/*.snap and included by **
  • crates/biome_tailwind_parser/tests/tailwind_specs/error/missing-modifier-value.txt.snap is excluded by !**/*.snap and included by **
  • crates/biome_tailwind_parser/tests/tailwind_specs/error/missing-value.txt.snap is excluded by !**/*.snap and included by **
  • crates/biome_tailwind_parser/tests/tailwind_specs/ok/brackets/gradient.txt.snap is excluded by !**/*.snap and included by **
  • crates/biome_tailwind_parser/tests/tailwind_specs/ok/brackets/image-url.txt.snap is excluded by !**/*.snap and included by **
  • crates/biome_tailwind_parser/tests/tailwind_specs/ok/brackets/shadow.txt.snap is excluded by !**/*.snap and included by **
  • crates/biome_tailwind_parser/tests/tailwind_specs/ok/candidates/arbitrary-candidate-0.txt.snap is excluded by !**/*.snap and included by **
  • crates/biome_tailwind_parser/tests/tailwind_specs/ok/candidates/arbitrary-candidate-1.txt.snap is excluded by !**/*.snap and included by **
  • crates/biome_tailwind_parser/tests/tailwind_specs/ok/candidates/arbitrary-candidate-2.txt.snap is excluded by !**/*.snap and included by **
  • crates/biome_tailwind_parser/tests/tailwind_specs/ok/candidates/arbitrary-candidate-3.txt.snap is excluded by !**/*.snap and included by **
  • crates/biome_tailwind_parser/tests/tailwind_specs/ok/gradients/precise-control.txt.snap is excluded by !**/*.snap and included by **
  • crates/biome_tailwind_parser/tests/tailwind_specs/ok/gradients/simple.txt.snap is excluded by !**/*.snap and included by **
  • crates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/arbitrary-value-0.txt.snap is excluded by !**/*.snap and included by **
  • crates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/arbitrary-value-1.txt.snap is excluded by !**/*.snap and included by **
  • crates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/base-has-dash-0.txt.snap is excluded by !**/*.snap and included by **
  • crates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/base-has-dash-1.txt.snap is excluded by !**/*.snap and included by **
  • crates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/base-has-dash-2.txt.snap is excluded by !**/*.snap and included by **
  • crates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/basic-0.txt.snap is excluded by !**/*.snap and included by **
  • crates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/basic-1.txt.snap is excluded by !**/*.snap and included by **
  • crates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/css-value.txt.snap is excluded by !**/*.snap and included by **
  • crates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/important.txt.snap is excluded by !**/*.snap and included by **
  • crates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/modifier.txt.snap is excluded by !**/*.snap and included by **
  • crates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/multiple-spaces.txt.snap is excluded by !**/*.snap and included by **
  • crates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/multiple.txt.snap is excluded by !**/*.snap and included by **
  • crates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/negative.txt.snap is excluded by !**/*.snap and included by **
  • crates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/static.txt.snap is excluded by !**/*.snap and included by **
  • crates/biome_tailwind_parser/tests/tailwind_specs/ok/stress/stress-1.txt.snap is excluded by !**/*.snap and included by **
  • crates/biome_tailwind_parser/tests/tailwind_specs/ok/stress/stress-2.txt.snap is excluded by !**/*.snap and included by **
  • crates/biome_tailwind_parser/tests/tailwind_specs/ok/variants/arbitrary-variant.txt.snap is excluded by !**/*.snap and included by **
  • crates/biome_tailwind_parser/tests/tailwind_specs/ok/variants/functional-arbirary-param.txt.snap is excluded by !**/*.snap and included by **
  • crates/biome_tailwind_parser/tests/tailwind_specs/ok/variants/functional-named-param.txt.snap is excluded by !**/*.snap and included by **
  • crates/biome_tailwind_parser/tests/tailwind_specs/ok/variants/hover.txt.snap is excluded by !**/*.snap and included by **
  • crates/biome_tailwind_parser/tests/tailwind_specs/ok/variants/hover_focus.txt.snap is excluded by !**/*.snap and included by **
  • crates/biome_tailwind_syntax/src/generated/nodes.rs is excluded by !**/generated/**, !**/generated/** and included by **
  • crates/biome_tailwind_syntax/src/generated/nodes_mut.rs is excluded by !**/generated/**, !**/generated/** and included by **
📒 Files selected for processing (11)
  • .github/labeler.yml (1 hunks)
  • crates/biome_parser/src/lexer.rs (2 hunks)
  • crates/biome_tailwind_parser/src/lexer/mod.rs (2 hunks)
  • crates/biome_tailwind_parser/src/lexer/tests.rs (1 hunks)
  • crates/biome_tailwind_parser/src/syntax/mod.rs (4 hunks)
  • crates/biome_tailwind_parser/src/syntax/variant.rs (1 hunks)
  • crates/biome_tailwind_parser/src/token_source.rs (1 hunks)
  • crates/biome_tailwind_parser/tests/tailwind_specs/ok/gradients/precise-control.txt (1 hunks)
  • crates/biome_tailwind_parser/tests/tailwind_specs/ok/gradients/simple.txt (1 hunks)
  • crates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/negative.txt (1 hunks)
  • xtask/codegen/tailwind.ungram (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{rs,toml}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Format Rust and TOML files before committing (e.g., via just f)

Files:

  • crates/biome_tailwind_parser/src/syntax/variant.rs
  • crates/biome_tailwind_parser/src/token_source.rs
  • crates/biome_parser/src/lexer.rs
  • crates/biome_tailwind_parser/src/syntax/mod.rs
  • crates/biome_tailwind_parser/src/lexer/tests.rs
  • crates/biome_tailwind_parser/src/lexer/mod.rs
**/*.rs

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Document rules, assists, and their options with inline rustdoc in the Rust source

Files:

  • crates/biome_tailwind_parser/src/syntax/variant.rs
  • crates/biome_tailwind_parser/src/token_source.rs
  • crates/biome_parser/src/lexer.rs
  • crates/biome_tailwind_parser/src/syntax/mod.rs
  • crates/biome_tailwind_parser/src/lexer/tests.rs
  • crates/biome_tailwind_parser/src/lexer/mod.rs
🧠 Learnings (34)
📚 Learning: 2025-10-15T09:22:46.002Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_js_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:22:46.002Z
Learning: Applies to crates/biome_js_formatter/**/*.rs : Do not attempt to fix code; if a mandatory token/node is missing, return `None` instead

Applied to files:

  • crates/biome_tailwind_parser/src/syntax/variant.rs
  • crates/biome_tailwind_parser/src/syntax/mod.rs
  • crates/biome_tailwind_parser/src/lexer/mod.rs
📚 Learning: 2025-10-24T21:24:58.650Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-10-24T21:24:58.650Z
Learning: Applies to crates/biome_analyze/crates/*_analyze/**/src/**/lint/**/*.rs : Use options/full_options/use_options code block modifiers as specified to demonstrate configuration in docs; keep modifier order consistent

Applied to files:

  • crates/biome_tailwind_parser/src/syntax/variant.rs
  • .github/labeler.yml
  • crates/biome_tailwind_parser/src/syntax/mod.rs
📚 Learning: 2025-10-24T21:24:58.650Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-10-24T21:24:58.650Z
Learning: Applies to crates/biome_analyze/crates/*_analyze/**/src/**/lint/**/*.rs : Avoid deep indentation by using combinators (map, filter, and_then) rather than nested if-let/unwrap chains

Applied to files:

  • crates/biome_tailwind_parser/src/syntax/variant.rs
📚 Learning: 2025-10-15T09:22:15.851Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:22:15.851Z
Learning: Applies to crates/biome_formatter/src/**/*.rs : After generation, remove usages of `format_verbatim_node` and implement real formatting with biome_formatter utilities

Applied to files:

  • crates/biome_tailwind_parser/src/syntax/variant.rs
📚 Learning: 2025-10-15T09:24:31.042Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_parser/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:24:31.042Z
Learning: Parse rules should return ParsedSyntax; return Absent without consuming tokens when the node cannot be predicted

Applied to files:

  • crates/biome_tailwind_parser/src/syntax/variant.rs
📚 Learning: 2025-10-24T21:24:58.650Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-10-24T21:24:58.650Z
Learning: Applies to crates/biome_analyze/crates/*_analyze/**/src/lint/nursery/**/*.rs : Place all new rules in the nursery group

Applied to files:

  • .github/labeler.yml
📚 Learning: 2025-10-24T21:24:58.650Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-10-24T21:24:58.650Z
Learning: Applies to crates/biome_analyze/crates/*_analyze/**/src/**/lint/**/*.rs : In declare_lint_rule! macros, set version: "next"

Applied to files:

  • .github/labeler.yml
📚 Learning: 2025-10-24T21:24:58.650Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-10-24T21:24:58.650Z
Learning: Applies to crates/biome_analyze/crates/*_analyze/**/src/**/lint/**/*.rs : Rule documentation: first paragraph must be a single line summary

Applied to files:

  • .github/labeler.yml
📚 Learning: 2025-10-24T21:24:58.650Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-10-24T21:24:58.650Z
Learning: Applies to crates/biome_analyze/crates/*_analyze/**/src/**/lint/style/**/*.rs : Rules in style group must have severity: info or warn (prefer info if unsure)

Applied to files:

  • .github/labeler.yml
📚 Learning: 2025-10-24T21:24:58.650Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-10-24T21:24:58.650Z
Learning: Applies to crates/biome_analyze/crates/*_analyze/**/src/**/lint/**/*.rs : Set the language field in declare_lint_rule! to the most appropriate dialect (js/jsx/ts/tsx)

Applied to files:

  • .github/labeler.yml
📚 Learning: 2025-10-24T21:24:58.650Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-10-24T21:24:58.650Z
Learning: Applies to crates/biome_analyze/crates/*_analyze/**/src/**/lint/performance/**/*.rs : Rules in performance group must have severity: warn

Applied to files:

  • .github/labeler.yml
📚 Learning: 2025-10-24T21:24:58.650Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-10-24T21:24:58.650Z
Learning: Applies to crates/biome_analyze/crates/*_analyze/**/src/**/lint/**/*.rs : If a rule is inspired by an external source, populate sources in declare_lint_rule! with RuleSource::{Eslint,...} and same()/inspired()

Applied to files:

  • .github/labeler.yml
📚 Learning: 2025-10-24T21:24:58.650Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-10-24T21:24:58.650Z
Learning: Applies to crates/biome_analyze/crates/biome_rule_options/lib/**/*.rs : Place per-rule options types in biome_rule_options crate under lib/, one file per rule

Applied to files:

  • .github/labeler.yml
📚 Learning: 2025-10-24T21:24:58.650Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-10-24T21:24:58.650Z
Learning: Applies to crates/biome_analyze/crates/*_analyze/**/src/**/lint/suspicious/**/*.rs : Rules in suspicious group must have severity: warn or error (prefer warn if unsure)

Applied to files:

  • .github/labeler.yml
📚 Learning: 2025-10-24T21:24:58.650Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-10-24T21:24:58.650Z
Learning: Applies to crates/biome_analyze/crates/*_analyze/**/src/**/lint/**/*.rs : If a rule provides a code action, add fix_kind to declare_lint_rule! (FixKind::Safe or ::Unsafe)

Applied to files:

  • .github/labeler.yml
📚 Learning: 2025-10-15T09:24:31.042Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_parser/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:24:31.042Z
Learning: Applies to crates/biome_parser/crates/biome_*_{syntax,factory}/** : Create per-language crates biome_<lang>_syntax and biome_<lang>_factory under crates/

Applied to files:

  • .github/labeler.yml
📚 Learning: 2025-10-15T09:25:05.698Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_service/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:25:05.698Z
Learning: Applies to crates/biome_service/../biome_lsp/src/server.tests.rs : Keep end-to-end LSP tests in ../biome_lsp/src/server.tests.rs

Applied to files:

  • .github/labeler.yml
📚 Learning: 2025-10-24T21:24:58.650Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-10-24T21:24:58.650Z
Learning: Applies to crates/biome_analyze/crates/*_analyze/**/src/**/lint/**/*.rs : For cross-file analyses, use custom visitors/Queryable to emit matches during main traversal to avoid redundant passes

Applied to files:

  • .github/labeler.yml
📚 Learning: 2025-10-15T09:20:45.587Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_aria_metadata/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:20:45.587Z
Learning: Applies to crates/biome_aria_metadata/**/{build.rs,aria-data.json} : Keep aria-data.json at the expected location/name because build.rs uses it to generate ARIA metadata

Applied to files:

  • .github/labeler.yml
📚 Learning: 2025-10-24T21:24:58.650Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-10-24T21:24:58.650Z
Learning: Applies to crates/biome_analyze/crates/*_analyze/tests/quick_test.rs : Quick test lives in biome_js_analyze/tests/quick_test.rs; unignore #[ignore] and set rule filter and SOURCE for ad-hoc runs

Applied to files:

  • .github/labeler.yml
📚 Learning: 2025-10-26T15:28:00.951Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: CONTRIBUTING.md:0-0
Timestamp: 2025-10-26T15:28:00.951Z
Learning: Applies to **/*.{rs,toml} : Format Rust and TOML files before committing (e.g., via `just f`)

Applied to files:

  • .github/labeler.yml
📚 Learning: 2025-10-15T09:22:15.851Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:22:15.851Z
Learning: Applies to crates/biome_formatter/**/Cargo.toml : Add the specified dev-dependencies under [dev-dependencies] for the test infrastructure

Applied to files:

  • .github/labeler.yml
📚 Learning: 2025-10-15T09:24:31.042Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_parser/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:24:31.042Z
Learning: Lexer must implement the biome_parser::Lexer trait

Applied to files:

  • crates/biome_parser/src/lexer.rs
  • crates/biome_tailwind_parser/src/syntax/mod.rs
📚 Learning: 2025-10-15T09:24:31.042Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_parser/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:24:31.042Z
Learning: Applies to crates/biome_parser/crates/**/src/lexer/mod.rs : Create a lexer module at crates/<parser_crate>/src/lexer/mod.rs

Applied to files:

  • crates/biome_parser/src/lexer.rs
  • crates/biome_tailwind_parser/src/lexer/mod.rs
📚 Learning: 2025-10-15T09:22:15.851Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:22:15.851Z
Learning: Applies to crates/biome_formatter/tests/spec_tests.rs : In tests/spec_tests.rs, generate tests with `tests_macros::gen_tests! {"tests/specs/html/**/*.html", crate::spec_test::run, ""}`

Applied to files:

  • crates/biome_tailwind_parser/tests/tailwind_specs/ok/gradients/simple.txt
  • crates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/negative.txt
📚 Learning: 2025-10-15T09:22:15.851Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:22:15.851Z
Learning: Applies to crates/biome_formatter/tests/** : Create a tests directory containing a specs subfolder and the files spec_test.rs, spec_tests.rs, and language.rs

Applied to files:

  • crates/biome_tailwind_parser/tests/tailwind_specs/ok/gradients/simple.txt
  • crates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/negative.txt
📚 Learning: 2025-10-15T09:22:46.002Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_js_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:22:46.002Z
Learning: Applies to crates/biome_js_formatter/**/*.rs : For non-mandatory tokens, use the provided helper constructors (e.g., `token`, `space_token`, `dynamic_token`)

Applied to files:

  • crates/biome_tailwind_parser/src/syntax/mod.rs
  • crates/biome_tailwind_parser/src/lexer/mod.rs
📚 Learning: 2025-10-15T09:22:46.002Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_js_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:22:46.002Z
Learning: Applies to crates/biome_js_formatter/**/*.rs : When a token is mandatory and present in the AST, use the AST-provided token (e.g., `node.l_paren_token().format()`) instead of emitting a static token

Applied to files:

  • crates/biome_tailwind_parser/src/syntax/mod.rs
  • crates/biome_tailwind_parser/src/lexer/mod.rs
📚 Learning: 2025-10-24T21:24:58.650Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-10-24T21:24:58.650Z
Learning: Applies to crates/biome_analyze/crates/*_analyze/**/src/**/lint/**/*.rs : Avoid avoidable string allocations; compare against &str or TokenText rather than allocating Strings

Applied to files:

  • crates/biome_tailwind_parser/src/syntax/mod.rs
  • crates/biome_tailwind_parser/src/lexer/mod.rs
📚 Learning: 2025-10-15T09:24:31.042Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_parser/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:24:31.042Z
Learning: Use p.eat for optional tokens, p.expect for required tokens; use .ok() for optional nodes and .or_add_diagnostic(...) for required nodes

Applied to files:

  • crates/biome_tailwind_parser/src/syntax/mod.rs
📚 Learning: 2025-10-15T09:22:15.851Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:22:15.851Z
Learning: Applies to crates/biome_formatter/tests/language.rs : Create tests/language.rs defining `HtmlTestFormatLanguage` and implement the TestFormatLanguage trait

Applied to files:

  • crates/biome_tailwind_parser/src/lexer/tests.rs
📚 Learning: 2025-10-25T07:22:18.540Z
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.

Applied to files:

  • crates/biome_tailwind_parser/src/lexer/mod.rs
📚 Learning: 2025-10-24T21:24:58.650Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-10-24T21:24:58.650Z
Learning: Applies to crates/biome_analyze/crates/*_analyze/tests/specs/**/{invalid*,valid*}.* : Name snapshot test files with invalid* and valid* prefixes to indicate reported vs non-reported cases

Applied to files:

  • crates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/negative.txt
📚 Learning: 2025-10-15T09:22:15.851Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:22:15.851Z
Learning: Applies to crates/biome_formatter/tests/specs/html/**/*.html : Place HTML test cases under tests/specs/html as .html files discovered by the test macro

Applied to files:

  • crates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/negative.txt
🧬 Code graph analysis (2)
crates/biome_tailwind_parser/src/syntax/mod.rs (1)
crates/biome_tailwind_parser/src/syntax/value.rs (1)
  • parse_value (9-17)
crates/biome_tailwind_parser/src/lexer/mod.rs (2)
crates/biome_parser/src/lexer.rs (2)
  • current (30-30)
  • current (536-542)
crates/biome_tailwind_parser/src/token_source.rs (1)
  • current (111-113)
⏰ 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). (19)
  • GitHub Check: Documentation
  • GitHub Check: Bench (biome_css_analyze)
  • GitHub Check: Bench (biome_css_parser)
  • GitHub Check: Test (depot-ubuntu-24.04-arm-16)
  • GitHub Check: Bench (biome_css_formatter)
  • GitHub Check: Test (depot-windows-2022-16)
  • GitHub Check: Lint project (depot-windows-2022)
  • GitHub Check: Parser conformance
  • GitHub Check: Test Node.js API
  • GitHub Check: autofix
  • GitHub Check: Check Dependencies
  • GitHub Check: Bench (biome_graphql_parser)
  • GitHub Check: Bench (biome_js_analyze)
  • GitHub Check: Bench (biome_js_parser)
  • GitHub Check: Bench (biome_js_formatter)
  • GitHub Check: Bench (biome_graphql_formatter)
  • GitHub Check: Bench (biome_json_parser)
  • GitHub Check: Bench (biome_json_formatter)
  • GitHub Check: Bench (biome_json_analyze)
🔇 Additional comments (16)
crates/biome_tailwind_parser/tests/tailwind_specs/ok/gradients/precise-control.txt (1)

1-1: LGTM!

Valid Tailwind gradient syntax for testing precise percentage control.

.github/labeler.yml (1)

114-117: LGTM!

Labeler rule follows the established pattern and correctly targets Tailwind-related crates.

crates/biome_tailwind_parser/tests/tailwind_specs/ok/gradients/simple.txt (1)

1-1: LGTM!

Valid Tailwind gradient syntax for testing basic gradient parsing.

xtask/codegen/tailwind.ungram (1)

62-62: LGTM!

The optional negative field correctly models Tailwind's negative utility syntax (e.g., -mt-4). Placement before the candidate is logical.

crates/biome_parser/src/lexer.rs (1)

223-233: LGTM!

The prev_byte_at method is correctly implemented with proper boundary checks. It mirrors byte_at for backwards lookups, which is useful for the negative token context handling.

crates/biome_tailwind_parser/tests/tailwind_specs/ok/simple/negative.txt (1)

1-1: LGTM!

Test data correctly demonstrates negative Tailwind utilities, including variants with negatives (e.g., hover:-mt-2).

crates/biome_tailwind_parser/src/lexer/tests.rs (1)

195-203: LGTM!

Test correctly validates the SawNegative context, expecting a DASH token followed by the base identifier. Well-structured.

crates/biome_tailwind_parser/src/syntax/variant.rs (1)

82-85: LGTM!

Correct guard: variants cannot start with a negative sign. The negative prefix belongs to the candidate level (e.g., in hover:-mt-2, the - is part of the candidate, not the variant).

crates/biome_tailwind_parser/src/token_source.rs (1)

25-26: LGTM!

Clear documentation and a sensible addition to handle negative Tailwind candidates.

crates/biome_tailwind_parser/src/lexer/mod.rs (1)

270-270: LGTM!

Correctly routes the SawNegative context to the new consumption path.

crates/biome_tailwind_parser/src/syntax/mod.rs (6)

72-74: LGTM!

Correctly handles the negative prefix by switching to the SawNegative context.


59-60: LGTM!

Cleaner recovery pattern using enable_recovery_on_line_break() instead of explicitly including NEWLINE/EOF in the token set.


80-81: LGTM!

Consistent recovery pattern with the candidate list recovery above.


121-121: LGTM!

Using expect instead of bump is more defensive and follows parser conventions for required tokens.


124-125: LGTM!

Recovery set properly updated with line break recovery enabled.


202-202: Verify the recovery pattern inconsistency.

Unlike the other recovery token set changes (lines 59-60, 80-81, 124-125), this one retains NEWLINE in the token set and doesn't call enable_recovery_on_line_break(). Is this intentional for modifier recovery, or should it follow the same pattern as the others?

@dyc3 dyc3 force-pushed the dyc3/tw-parse-negative-candidates branch from e0ee9c0 to 5b79888 Compare November 2, 2025 13:48
@dyc3 dyc3 merged commit 04f4973 into main Nov 2, 2025
25 checks passed
@dyc3 dyc3 deleted the dyc3/tw-parse-negative-candidates branch November 2, 2025 17:59
@dyc3 dyc3 added the L-Tailwind Language: Tailwind CSS label Nov 2, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-Parser Area: parser A-Tooling Area: internal tools L-Tailwind Language: Tailwind CSS

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants