Skip to content

Commit e0ee9c0

Browse files
committed
feat(parse/tailwind): parse negative candidates, other misc cleanup/refactors
1 parent a35c496 commit e0ee9c0

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

54 files changed

+813
-145
lines changed

.github/labeler.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,3 +110,8 @@ L-Grit:
110110
- changed-files:
111111
- any-glob-to-any-file:
112112
- crates/biome_grit_*/**
113+
114+
L-Tailwind:
115+
- changed-files:
116+
- any-glob-to-any-file:
117+
- crates/biome_tailwind_*/**

crates/biome_parser/src/lexer.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,8 @@ pub trait Lexer<'src> {
201201
}
202202

203203
/// Returns the byte at position `self.position + offset` or `None` if it is out of bounds.
204+
///
205+
/// See also: [`Self::prev_byte_at`]
204206
#[inline]
205207
fn byte_at(&self, offset: usize) -> Option<u8> {
206208
self.source()
@@ -218,6 +220,18 @@ pub trait Lexer<'src> {
218220
}
219221
}
220222

223+
/// Returns the byte at position `self.position - offset` or `None` if it is out of bounds. Looks backwards instead of forwards.
224+
#[inline]
225+
fn prev_byte_at(&self, offset: usize) -> Option<u8> {
226+
if offset > self.position() {
227+
return None;
228+
}
229+
self.source()
230+
.as_bytes()
231+
.get(self.position() - offset)
232+
.copied()
233+
}
234+
221235
#[inline]
222236
fn text_position(&self) -> TextSize {
223237
TextSize::try_from(self.position()).expect("Input to be smaller than 4 GB")

crates/biome_tailwind_factory/src/generated/node_factory.rs

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/biome_tailwind_factory/src/generated/syntax_factory.rs

Lines changed: 8 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/biome_tailwind_parser/src/lexer/mod.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,21 @@ impl<'src> TailwindLexer<'src> {
7171
}
7272
}
7373

74+
fn consume_token_saw_negative(&mut self, current: u8) -> TailwindSyntaxKind {
75+
match current {
76+
b'\n' | b'\r' | b'\t' | b' ' => self.consume_newline_or_whitespaces(),
77+
bracket @ (b'[' | b']' | b'(' | b')') => self.consume_bracket(bracket),
78+
_ if self.current_kind == T!['['] => self.consume_bracketed_thing(TW_SELECTOR, b']'),
79+
_ if self.current_kind == T!['('] => self.consume_bracketed_thing(TW_VALUE, b')'),
80+
b':' => self.consume_byte(T![:]),
81+
b'-' => self.consume_byte(T![-]),
82+
b'!' => self.consume_byte(T![!]),
83+
b'/' => self.consume_byte(T![/]),
84+
_ if current.is_ascii_alphabetic() => self.consume_base(),
85+
_ => self.consume_unexpected_character(),
86+
}
87+
}
88+
7489
/// Consume a token in the arbitrary context
7590
fn consume_token_arbitrary(&mut self, current: u8) -> TailwindSyntaxKind {
7691
match current {
@@ -252,6 +267,7 @@ impl<'src> Lexer<'src> for TailwindLexer<'src> {
252267
match self.current_byte() {
253268
Some(current) => match context {
254269
TailwindLexContext::Regular => self.consume_token(current),
270+
TailwindLexContext::SawNegative => self.consume_token_saw_negative(current),
255271
TailwindLexContext::Arbitrary => self.consume_token_arbitrary(current),
256272
TailwindLexContext::ArbitraryVariant => {
257273
self.consume_token_arbitrary_variant(current)

crates/biome_tailwind_parser/src/lexer/tests.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,3 +191,13 @@ fn variant_multiple_full() {
191191
TW_VALUE:7
192192
);
193193
}
194+
195+
#[test]
196+
fn negative() {
197+
assert_lex!(
198+
TailwindLexContext::SawNegative,
199+
"-mt",
200+
DASH:1,
201+
TW_BASE:2,
202+
);
203+
}

crates/biome_tailwind_parser/src/syntax/mod.rs

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,8 @@ impl ParseSeparatedList for CandidateList {
5656
) -> biome_parser::parse_recovery::RecoveryResult {
5757
parsed_element.or_recover_with_token_set(
5858
p,
59-
&ParseRecoveryTokenSet::new(TW_BOGUS_CANDIDATE, token_set![WHITESPACE, NEWLINE, EOF]),
59+
&ParseRecoveryTokenSet::new(TW_BOGUS_CANDIDATE, token_set![WHITESPACE])
60+
.enable_recovery_on_line_break(),
6061
expected_candidate,
6162
)
6263
}
@@ -68,11 +69,16 @@ fn parse_full_candidate(p: &mut TailwindParser) -> ParsedSyntax {
6869

6970
VariantList.parse_list(p);
7071

72+
if p.at(T![-]) {
73+
p.bump_with_context(T![-], TailwindLexContext::SawNegative);
74+
}
75+
7176
let candidate = parse_arbitrary_candidate(p)
7277
.or_else(|| parse_functional_or_static_candidate(p))
7378
.or_recover_with_token_set(
7479
p,
75-
&ParseRecoveryTokenSet::new(TW_BOGUS_CANDIDATE, token_set![WHITESPACE, NEWLINE, EOF]),
80+
&ParseRecoveryTokenSet::new(TW_BOGUS_CANDIDATE, token_set![WHITESPACE])
81+
.enable_recovery_on_line_break(),
7682
expected_candidate,
7783
);
7884

@@ -112,10 +118,11 @@ fn parse_functional_or_static_candidate(p: &mut TailwindParser) -> ParsedSyntax
112118
return Present(m.complete(p, TW_STATIC_CANDIDATE));
113119
}
114120

115-
p.bump(DASH);
121+
p.expect(T![-]);
116122
match parse_value(p).or_recover_with_token_set(
117123
p,
118-
&ParseRecoveryTokenSet::new(TW_BOGUS_VALUE, token_set![WHITESPACE, NEWLINE, T![!], EOF]),
124+
&ParseRecoveryTokenSet::new(TW_BOGUS_VALUE, token_set![WHITESPACE, T![!]])
125+
.enable_recovery_on_line_break(),
119126
expected_value,
120127
) {
121128
Ok(_) => {}
@@ -192,10 +199,7 @@ fn parse_modifier(p: &mut TailwindParser) -> ParsedSyntax {
192199
}
193200
match parse_value(p).or_recover_with_token_set(
194201
p,
195-
&ParseRecoveryTokenSet::new(
196-
TW_BOGUS_MODIFIER,
197-
token_set![WHITESPACE, NEWLINE, T![!], EOF],
198-
),
202+
&ParseRecoveryTokenSet::new(TW_BOGUS_MODIFIER, token_set![WHITESPACE, NEWLINE, T![!]]),
199203
expected_value,
200204
) {
201205
Ok(_) => {}

crates/biome_tailwind_parser/src/syntax/variant.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,10 @@ impl ParseSeparatedList for VariantList {
7979
}
8080

8181
pub(crate) fn parse_variant(p: &mut TailwindParser) -> ParsedSyntax {
82+
if p.at(T![-]) {
83+
// variants can't start with a negative sign
84+
return Absent;
85+
}
8286
if p.at(T!['[']) {
8387
return parse_arbitrary_variant(p);
8488
}

crates/biome_tailwind_parser/src/token_source.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ pub(crate) enum TailwindLexContext {
2222
/// The default state.
2323
#[default]
2424
Regular,
25+
/// The parser just encountered a `-` before a basename, e.g. in `-mt-4`. That meant that the next token should be a basename.
26+
SawNegative,
2527
/// The lexer has encountered a `[` and the parser has yet to encounter the matching `]`.
2628
Arbitrary,
2729
/// Like Arbitrary, but specifically for arbitrary variants.

crates/biome_tailwind_parser/tests/tailwind_specs/error/arbitrary-candidate/missing-property.txt.snap

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ TwRoot {
1818
candidates: TwCandidateList [
1919
TwFullCandidate {
2020
variants: TwVariantList [],
21+
negative_token: missing (optional),
2122
candidate: TwBogusCandidate {
2223
items: [
2324
L_BRACKET@0..1 "[" [] [],
@@ -30,6 +31,7 @@ TwRoot {
3031
WHITESPACE@7..8 " " [] [],
3132
TwFullCandidate {
3233
variants: TwVariantList [],
34+
negative_token: missing (optional),
3335
candidate: TwFunctionalCandidate {
3436
base_token: TW_BASE@8..9 "w" [] [],
3537
minus_token: DASH@9..10 "-" [] [],
@@ -53,21 +55,23 @@ TwRoot {
5355
5456
5557
56-
58+
1: (empty)
59+
5760
0: [email protected] "[" [] []
5861
1: [email protected] ":40px" [] []
5962
2: [email protected] "]" [] []
60-
2: (empty)
63+
3: (empty)
6164
1: [email protected] " " [] []
6265
6366
64-
67+
1: (empty)
68+
6569
0: [email protected] "w" [] []
6670
1: [email protected] "-" [] []
6771
6872
0: [email protected] "5" [] [Newline("\n")]
6973
3: (empty)
70-
2: (empty)
74+
3: (empty)
7175
2: [email protected] "" [] []
7276
7377
```

0 commit comments

Comments
 (0)