-
-
Notifications
You must be signed in to change notification settings - Fork 475
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: basic class selector parsing #307
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,138 @@ | ||
use crate::parser::CssParser; | ||
use biome_css_syntax::CssSyntaxKind::*; | ||
use biome_css_syntax::{CssSyntaxKind, T}; | ||
use biome_parser::diagnostic::expected_any; | ||
use biome_parser::parse_recovery::ParseRecovery; | ||
use biome_parser::prelude::ParsedSyntax::{Absent, Present}; | ||
use biome_parser::prelude::{ParseDiagnostic, ParsedSyntax, ToDiagnostic}; | ||
use biome_parser::{token_set, Parser, ParserProgress, TokenSet}; | ||
use biome_rowan::TextRange; | ||
|
||
const SELECTOR_RECOVERY_SET: TokenSet<CssSyntaxKind> = token_set![T!['{'], T!['}'],]; | ||
const BODY_RECOVERY_SET: TokenSet<CssSyntaxKind> = | ||
SELECTOR_RECOVERY_SET.union(token_set![T![.], T![*],]); | ||
|
||
pub(crate) fn parse_root(p: &mut CssParser) { | ||
let m = p.start(); | ||
|
||
parse_rules_list(p).expect("Parse rule list, handle this case"); | ||
|
||
m.complete(p, CSS_ROOT); | ||
} | ||
|
||
pub(crate) fn parse_rules_list(p: &mut CssParser) -> ParsedSyntax { | ||
let rules = p.start(); | ||
while !p.at(EOF) { | ||
match p.cur() { | ||
T![.] => { | ||
parse_rule(p).expect("Parse rule, handle this case properly"); | ||
} | ||
_ => return Absent, | ||
} | ||
} | ||
|
||
let completed = rules.complete(p, CSS_RULE_LIST); | ||
|
||
Present(completed) | ||
} | ||
|
||
pub(crate) fn parse_rule(p: &mut CssParser) -> ParsedSyntax { | ||
let m = p.start(); | ||
if parse_selector_list(p) | ||
.or_recover( | ||
p, | ||
&ParseRecovery::new(CSS_BOGUS_PATTERN, SELECTOR_RECOVERY_SET), | ||
expect_pattern, | ||
) | ||
.is_err() | ||
{ | ||
m.abandon(p); | ||
return Absent; | ||
} | ||
|
||
if parse_css_block(p) | ||
.or_recover( | ||
p, | ||
&ParseRecovery::new(CSS_BOGUS_BODY, BODY_RECOVERY_SET), | ||
expect_block, | ||
) | ||
.is_err() | ||
{ | ||
m.abandon(p); | ||
return Absent; | ||
} | ||
|
||
let completed = m.complete(p, CSS_RULE); | ||
Present(completed) | ||
} | ||
|
||
pub(crate) fn parse_selector_list(p: &mut CssParser) -> ParsedSyntax { | ||
let m = p.start(); | ||
let mut progress = ParserProgress::default(); | ||
|
||
while !p.at(EOF) && !p.at(T!['{']) { | ||
progress.assert_progressing(p); | ||
|
||
match p.cur() { | ||
T![.] => { | ||
parse_css_selector_pattern(p).expect("Handle this case"); | ||
} | ||
|
||
_ => { | ||
return Absent; | ||
} | ||
} | ||
} | ||
if p.at(EOF) { | ||
m.abandon(p); | ||
return Absent; | ||
} | ||
|
||
Present(m.complete(p, CSS_SELECTOR_LIST)) | ||
} | ||
|
||
#[inline] | ||
pub(crate) fn parse_css_selector_pattern(p: &mut CssParser) -> ParsedSyntax { | ||
if !p.at(T![.]) { | ||
return Absent; | ||
} | ||
let m = p.start(); | ||
|
||
p.bump(T![.]); | ||
|
||
match p.cur() { | ||
IDENT => { | ||
let m = p.start(); | ||
p.bump(IDENT); | ||
m.complete(p, CSS_IDENTIFIER); | ||
} | ||
|
||
_ => { | ||
m.abandon(p); | ||
return Absent; | ||
} | ||
} | ||
|
||
Present(m.complete(p, CSS_CLASS_SELECTOR_PATTERN)) | ||
} | ||
|
||
pub(crate) fn parse_css_block(p: &mut CssParser) -> ParsedSyntax { | ||
if !p.at(T!['{']) { | ||
return Absent; | ||
} | ||
let m = p.start(); | ||
p.expect(T!['{']); | ||
let list = p.start(); | ||
list.complete(p, CSS_DECLARATION_LIST); | ||
p.expect(T!['}']); | ||
|
||
Present(m.complete(p, CSS_BLOCK)) | ||
} | ||
|
||
fn expect_pattern(p: &CssParser, range: TextRange) -> ParseDiagnostic { | ||
expected_any(&["selector pattern"], range).into_diagnostic(p) | ||
} | ||
|
||
fn expect_block(p: &CssParser, range: TextRange) -> ParseDiagnostic { | ||
expected_any(&["body"], range).into_diagnostic(p) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
.action { |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
--- | ||
source: crates/biome_css_parser/tests/spec_test.rs | ||
expression: snapshot | ||
--- | ||
|
||
## Input | ||
|
||
```css | ||
.action { | ||
|
||
``` | ||
|
||
|
||
## AST | ||
|
||
``` | ||
CssRoot { | ||
rules: CssRuleList [ | ||
CssRule { | ||
prelude: CssSelectorList [ | ||
CssClassSelectorPattern { | ||
dot_token: [email protected] "." [] [], | ||
name: CssIdentifier { | ||
value_token: [email protected] "action" [] [Whitespace(" ")], | ||
}, | ||
}, | ||
], | ||
block: CssBlock { | ||
l_curly_token: [email protected] "{" [] [], | ||
declaration_list: CssDeclarationList [], | ||
r_curly_token: missing (required), | ||
}, | ||
}, | ||
], | ||
eof_token: [email protected] "" [Newline("\n")] [], | ||
} | ||
``` | ||
|
||
## CST | ||
|
||
``` | ||
0: [email protected] | ||
0: [email protected] | ||
0: [email protected] | ||
0: [email protected] | ||
0: [email protected] | ||
0: [email protected] "." [] [] | ||
1: [email protected] | ||
0: [email protected] "action" [] [Whitespace(" ")] | ||
1: [email protected] | ||
0: [email protected] "{" [] [] | ||
1: [email protected] | ||
2: (empty) | ||
1: [email protected] "" [Newline("\n")] [] | ||
|
||
``` | ||
|
||
## Diagnostics | ||
|
||
``` | ||
css_unfinished_block.css:2:1 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ | ||
|
||
× expected `}` but instead the file ends | ||
|
||
1 │ .action { | ||
> 2 │ | ||
│ | ||
|
||
i the file ends here | ||
|
||
1 │ .action { | ||
> 2 │ | ||
│ | ||
|
||
``` | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
.action {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you please share your strategy for situations where we might need to abandon and return as absent, rather than completing and returning bogus kind?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't have a clear strategy in mind yet, but I've had some ideas while starting this work, still it's best if we discuss them.
For rules, I thought we could bogus+recover in the prelude and in the block.
This would allow us to parse cases like:
.686 {} .bar {}
Although, I'm not sure about the prelude of the rule.
Or maybe we should have a big
CssBogusRule
? What do you think?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with you, let's try to use a recovery strategy.