Skip to content

Commit

Permalink
feat(css_parser): CSS Parser pseudo element selector #268 (#839)
Browse files Browse the repository at this point in the history
  • Loading branch information
denbezrukov committed Nov 24, 2023
1 parent ca576c8 commit 98aeb72
Show file tree
Hide file tree
Showing 15 changed files with 1,405 additions and 13 deletions.
10 changes: 10 additions & 0 deletions crates/biome_css_factory/src/generated/node_factory.rs

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

1 change: 1 addition & 0 deletions crates/biome_css_factory/src/generated/syntax_factory.rs

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

2 changes: 2 additions & 0 deletions crates/biome_css_parser/src/lexer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -762,6 +762,8 @@ impl<'src> CssLexer<'src> {
b"nth-last-of-type" => NTHLASTOFTYPE_KW,
b"nth-col" => NTHCOL_KW,
b"nth-last-col" => NTHLASTCOL_KW,
b"ltr" => LTR_KW,
b"rtl" => RTL_KW,
_ => IDENT,
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
use crate::parser::CssParser;
use crate::syntax::parse_error::expected_identifier;
use crate::syntax::parse_regular_identifier;
use crate::syntax::selector::parse_selector_function_close_token;
use crate::syntax::selector::SELECTOR_FUNCTION_RECOVERY_SET;
use biome_css_syntax::CssSyntaxKind::CSS_PSEUDO_CLASS_FUNCTION_IDENTIFIER;
use biome_css_syntax::CssSyntaxKind::*;
use biome_css_syntax::{CssSyntaxKind, T};
use biome_css_syntax::{CssSyntaxKind, TextRange, T};
use biome_parser::diagnostic::{expected_any, ParseDiagnostic};
use biome_parser::parse_recovery::ParseRecovery;
use biome_parser::parsed_syntax::ParsedSyntax;
use biome_parser::parsed_syntax::ParsedSyntax::{Absent, Present};
use biome_parser::{token_set, Parser, TokenSet};

const PSEUDO_CLASS_FUNCTION_IDENTIFIER_SET: TokenSet<CssSyntaxKind> = token_set![DIR_KW];

#[inline]
pub(crate) fn is_at_pseudo_class_function_identifier(p: &mut CssParser) -> bool {
p.at_ts(PSEUDO_CLASS_FUNCTION_IDENTIFIER_SET) && p.nth_at(1, T!['('])
Expand All @@ -24,10 +24,74 @@ pub(crate) fn parse_pseudo_class_function_identifier(p: &mut CssParser) -> Parse

let m = p.start();

parse_regular_identifier(p).or_add_diagnostic(p, expected_identifier);
// we don't need to check if the identifier is valid, because we already did that
parse_regular_identifier(p).ok();
p.bump(T!['(']);
parse_regular_identifier(p).or_add_diagnostic(p, expected_identifier);
parse_selector_function_close_token(p);

Present(m.complete(p, CSS_PSEUDO_CLASS_FUNCTION_IDENTIFIER))
let kind = if is_at_dir_parameter_identifier(p) {
let identifier = parse_regular_identifier(p);

if eat_or_recover_function_close_token(p, identifier) {
CSS_PSEUDO_CLASS_FUNCTION_IDENTIFIER
} else {
CSS_BOGUS_PSEUDO_CLASS
}
} else {
recover_invalid_dir_parameter_identifier(p);
p.expect(T![')']);
CSS_BOGUS_PSEUDO_CLASS
};

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

#[inline]
fn eat_or_recover_function_close_token(p: &mut CssParser, identifier: ParsedSyntax) -> bool {
if p.eat(T![')']) {
true
} else {
let start = identifier
.ok()
.map(|m| m.range(p).start())
.unwrap_or_else(|| p.cur_range().start());

if let Ok(marker) = ParseRecovery::new(CSS_BOGUS, SELECTOR_FUNCTION_RECOVERY_SET).recover(p)
{
p.error(expected_dir_parameter_identifier(
p,
TextRange::new(start, marker.range(p).end()),
));
}

p.expect(T![')']);

false
}
}
const DIR_PARAMETER_IDENTIFIER_SET: TokenSet<CssSyntaxKind> = token_set![LTR_KW, RTL_KW];
#[inline]
fn is_at_dir_parameter_identifier(p: &mut CssParser) -> bool {
p.at_ts(DIR_PARAMETER_IDENTIFIER_SET)
}

#[inline]
fn recover_invalid_dir_parameter_identifier(p: &mut CssParser) {
let start = p.cur_range().start();

match ParseRecovery::new(CSS_BOGUS, SELECTOR_FUNCTION_RECOVERY_SET).recover(p) {
Ok(marker) => {
p.error(expected_dir_parameter_identifier(
p,
TextRange::new(start, marker.range(p).end()),
));
}
Err(_) => {
p.error(expected_dir_parameter_identifier(p, p.cur_range()));
}
}
}

#[inline]
fn expected_dir_parameter_identifier(p: &CssParser, range: TextRange) -> ParseDiagnostic {
expected_any(&["ltr", "rtl"], range, p)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
:dir( {}
:dir() {}
:dir(ltr {}
:dir(invalid {}
:dir(.invalid) {}
:dir(.invalid {}
:dir(ltr .class {}
:dir(ltr .class) {}
:dir(invalid .class {}
:dir(invalid .class) {}
:dir(.invalid
Loading

0 comments on commit 98aeb72

Please sign in to comment.