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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 4 additions & 13 deletions crates/biome_tailwind_factory/src/generated/node_factory.rs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 3 additions & 7 deletions crates/biome_tailwind_factory/src/generated/syntax_factory.rs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 0 additions & 8 deletions crates/biome_tailwind_parser/src/lexer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,22 +13,14 @@ use biome_tailwind_syntax::{TailwindSyntaxKind, TextSize};
pub(crate) struct TailwindLexer<'src> {
/// Source text
source: &'src str,

/// The start byte position in the source text of the next token.
position: usize,

current_kind: TailwindSyntaxKind,

current_start: TextSize,

diagnostics: Vec<ParseDiagnostic>,

current_flags: TokenFlags,

preceding_line_break: bool,

after_newline: bool,

unicode_bom_length: usize,
}

Expand Down
25 changes: 15 additions & 10 deletions crates/biome_tailwind_parser/src/syntax/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use crate::syntax::parse_error::*;
use crate::syntax::value::parse_value;
use crate::syntax::variant::VariantList;
use crate::token_source::TailwindLexContext;
use biome_parser::parse_lists::ParseSeparatedList;
use biome_parser::parse_lists::{ParseNodeList, ParseSeparatedList};
use biome_parser::parsed_syntax::ParsedSyntax::{Absent, Present};
use biome_parser::prelude::*;
use biome_parser::{Parser, parse_recovery::ParseRecoveryTokenSet, token_set};
Expand All @@ -28,7 +28,7 @@ pub fn parse_root(p: &mut TailwindParser) {
#[derive(Default)]
struct CandidateList;

impl ParseSeparatedList for CandidateList {
impl ParseNodeList for CandidateList {
type Kind = TailwindSyntaxKind;
type Parser<'source> = TailwindParser<'source>;
const LIST_KIND: Self::Kind = TW_CANDIDATE_LIST;
Expand All @@ -37,18 +37,10 @@ impl ParseSeparatedList for CandidateList {
parse_full_candidate(p)
}

fn separating_element_kind(&mut self) -> Self::Kind {
WHITESPACE
}

fn is_at_list_end(&self, p: &mut Self::Parser<'_>) -> bool {
p.at(EOF)
}

fn allow_trailing_separating_element(&self) -> bool {
true
}

fn recover(
&mut self,
p: &mut Self::Parser<'_>,
Expand Down Expand Up @@ -107,6 +99,7 @@ fn parse_functional_or_static_candidate(p: &mut TailwindParser) -> ParsedSyntax
let m = p.start();

p.bump(TW_BASE);
let pos = p.source().position();
if p.at(T![:]) {
// Oops, this is a Variant!
m.abandon(p);
Expand All @@ -117,6 +110,18 @@ fn parse_functional_or_static_candidate(p: &mut TailwindParser) -> ParsedSyntax
if !p.at(T![-]) {
return Present(m.complete(p, TW_STATIC_CANDIDATE));
}
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
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
return Present(m.complete(p, TW_STATIC_CANDIDATE));
}

p.expect(T![-]);
match parse_value(p).or_recover_with_token_set(
Expand Down
6 changes: 0 additions & 6 deletions crates/biome_tailwind_parser/src/syntax/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,6 @@ fn parse_named_value(p: &mut TailwindParser) -> ParsedSyntax {
return Absent;
}

if p.at(T![-]) {
m.abandon(p);
p.rewind(checkpoint);
return Absent;
}

Present(m.complete(p, TW_NAMED_VALUE))
}

Expand Down
23 changes: 14 additions & 9 deletions crates/biome_tailwind_parser/src/token_source.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ pub(crate) struct TailwindTokenSource<'source> {

/// List of the skipped trivia. Needed to construct the CST and compute the non-trivia token offsets.
pub(super) trivia_list: Vec<Trivia>,

/// Whether the lexer encountered any trivia between the previous non-trivia token and the current non-trivia token.
had_trivia_before: bool,
}
pub(crate) type TailwindTokenSourceCheckpoint = TokenSourceCheckpoint<TailwindSyntaxKind>;

Expand Down Expand Up @@ -55,15 +58,14 @@ impl<'source> TailwindTokenSource<'source> {
Self {
lexer,
trivia_list: vec![],
had_trivia_before: false,
}
}

fn next_non_trivia_token(&mut self, context: TailwindLexContext, first_token: bool) {
let mut trailing = !first_token;
self.had_trivia_before = false;

// Unlike most token sources, we can't skip over trivia tokens blindly.
// This is because whitespace is invalid inside Tailwind utility classes.
// Tailwind also has no comments, so we don't need to worry about them.
loop {
let kind = self.lexer.next_token(context);

Expand All @@ -75,15 +77,13 @@ impl<'source> TailwindTokenSource<'source> {
break;
}
Ok(trivia_kind) => {
self.trivia_list
.push(Trivia::new(trivia_kind, self.current_range(), trailing));

if trivia_kind.is_newline() {
trailing = false;
// skipping over newlines is OK
continue;
}
break;

self.had_trivia_before = true;
self.trivia_list
.push(Trivia::new(trivia_kind, self.current_range(), trailing));
}
}
}
Expand All @@ -103,6 +103,11 @@ impl<'source> TailwindTokenSource<'source> {
self.trivia_list.truncate(checkpoint.trivia_len as usize);
self.lexer.rewind(checkpoint.lexer_checkpoint);
}

/// Whether the lexer encountered any trivia between the previous non-trivia token and the current non-trivia token.
pub fn had_trivia_before(&self) -> bool {
self.had_trivia_before
}
}

impl TokenSource for TailwindTokenSource<'_> {
Expand Down
2 changes: 1 addition & 1 deletion crates/biome_tailwind_parser/tests/quick_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use biome_test_utils::has_bogus_nodes_or_empty_slots;
#[ignore]
#[test]
pub fn quick_test() {
let code = r#"text-sm"#;
let code = r#"-top-4 -mb-2"#;

let root = parse_tailwind(code);
let syntax = root.syntax();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,27 +23,16 @@ TwRoot {
items: [
L_BRACKET@0..1 "[" [] [],
TW_SELECTOR@1..6 ":40px" [] [],
R_BRACKET@6..7 "]" [] [],
R_BRACKET@6..8 "]" [] [Whitespace(" ")],
TW_BASE@8..9 "w" [] [],
DASH@9..10 "-" [] [],
TW_VALUE@10..11 "5" [] [],
],
},
excl_token: missing (optional),
},
WHITESPACE@7..8 " " [] [],
TwFullCandidate {
variants: TwVariantList [],
negative_token: missing (optional),
candidate: TwFunctionalCandidate {
base_token: TW_BASE@8..9 "w" [] [],
minus_token: DASH@9..10 "-" [] [],
value: TwNamedValue {
value_token: TW_VALUE@10..12 "5" [] [Newline("\n")],
},
modifier: missing (optional),
},
excl_token: missing (optional),
},
],
eof_token: EOF@12..12 "" [] [],
eof_token: EOF@11..12 "" [Newline("\n")] [],
}
```

Expand All @@ -52,27 +41,19 @@ TwRoot {
```
0: TW_ROOT@0..12
0: (empty)
1: TW_CANDIDATE_LIST@0..12
0: TW_FULL_CANDIDATE@0..7
1: TW_CANDIDATE_LIST@0..11
0: TW_FULL_CANDIDATE@0..11
0: TW_VARIANT_LIST@0..0
1: (empty)
2: TW_BOGUS_CANDIDATE@0..7
2: TW_BOGUS_CANDIDATE@0..11
0: L_BRACKET@0..1 "[" [] []
1: TW_SELECTOR@1..6 ":40px" [] []
2: R_BRACKET@6..7 "]" [] []
3: (empty)
1: WHITESPACE@7..8 " " [] []
2: TW_FULL_CANDIDATE@8..12
0: TW_VARIANT_LIST@8..8
1: (empty)
2: TW_FUNCTIONAL_CANDIDATE@8..12
0: TW_BASE@8..9 "w" [] []
1: DASH@9..10 "-" [] []
2: TW_NAMED_VALUE@10..12
0: TW_VALUE@10..12 "5" [] [Newline("\n")]
3: (empty)
2: R_BRACKET@6..8 "]" [] [Whitespace(" ")]
3: TW_BASE@8..9 "w" [] []
4: DASH@9..10 "-" [] []
5: TW_VALUE@10..11 "5" [] []
3: (empty)
2: EOF@12..12 "" [] []
2: EOF@11..12 "" [Newline("\n")] []

```

Expand All @@ -81,16 +62,16 @@ TwRoot {
```
missing-property.txt:1:1 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

× Expected a candidate but instead found '[:40px]'.
× Expected a candidate but instead found '[:40px] w-5'.

> 1 │ [:40px] w-5
│ ^^^^^^^
│ ^^^^^^^^^^^
2 │

i Expected a candidate here.

> 1 │ [:40px] w-5
│ ^^^^^^^
│ ^^^^^^^^^^^
2 │

```
Original file line number Diff line number Diff line change
Expand Up @@ -23,27 +23,16 @@ TwRoot {
items: [
L_BRACKET@0..1 "[" [] [],
TW_SELECTOR@1..7 "width:" [] [],
R_BRACKET@7..8 "]" [] [],
R_BRACKET@7..9 "]" [] [Whitespace(" ")],
TW_BASE@9..10 "w" [] [],
DASH@10..11 "-" [] [],
TW_VALUE@11..12 "5" [] [],
],
},
excl_token: missing (optional),
},
WHITESPACE@8..9 " " [] [],
TwFullCandidate {
variants: TwVariantList [],
negative_token: missing (optional),
candidate: TwFunctionalCandidate {
base_token: TW_BASE@9..10 "w" [] [],
minus_token: DASH@10..11 "-" [] [],
value: TwNamedValue {
value_token: TW_VALUE@11..13 "5" [] [Newline("\n")],
},
modifier: missing (optional),
},
excl_token: missing (optional),
},
],
eof_token: EOF@13..13 "" [] [],
eof_token: EOF@12..13 "" [Newline("\n")] [],
}
```

Expand All @@ -52,27 +41,19 @@ TwRoot {
```
0: TW_ROOT@0..13
0: (empty)
1: TW_CANDIDATE_LIST@0..13
0: TW_FULL_CANDIDATE@0..8
1: TW_CANDIDATE_LIST@0..12
0: TW_FULL_CANDIDATE@0..12
0: TW_VARIANT_LIST@0..0
1: (empty)
2: TW_BOGUS_CANDIDATE@0..8
2: TW_BOGUS_CANDIDATE@0..12
0: L_BRACKET@0..1 "[" [] []
1: TW_SELECTOR@1..7 "width:" [] []
2: R_BRACKET@7..8 "]" [] []
3: (empty)
1: WHITESPACE@8..9 " " [] []
2: TW_FULL_CANDIDATE@9..13
0: TW_VARIANT_LIST@9..9
1: (empty)
2: TW_FUNCTIONAL_CANDIDATE@9..13
0: TW_BASE@9..10 "w" [] []
1: DASH@10..11 "-" [] []
2: TW_NAMED_VALUE@11..13
0: TW_VALUE@11..13 "5" [] [Newline("\n")]
3: (empty)
2: R_BRACKET@7..9 "]" [] [Whitespace(" ")]
3: TW_BASE@9..10 "w" [] []
4: DASH@10..11 "-" [] []
5: TW_VALUE@11..12 "5" [] []
3: (empty)
2: EOF@13..13 "" [] []
2: EOF@12..13 "" [Newline("\n")] []

```

Expand All @@ -81,16 +62,16 @@ TwRoot {
```
missing-value-in-arbitrary.txt:1:1 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

× Expected a candidate but instead found '[width:]'.
× Expected a candidate but instead found '[width:] w-5'.

> 1 │ [width:] w-5
│ ^^^^^^^^
│ ^^^^^^^^^^^^
2 │

i Expected a candidate here.

> 1 │ [width:] w-5
│ ^^^^^^^^
│ ^^^^^^^^^^^^
2 │

```
Loading
Loading