From ca64b687115805c6fd9cb2788ba0cb1aca34653d Mon Sep 17 00:00:00 2001 From: Zheyu Zhang Date: Tue, 2 Jul 2024 10:42:27 +0800 Subject: [PATCH 01/17] add grammar --- .../src/generated/node_factory.rs | 6 ++ .../src/generated/syntax_factory.rs | 19 +++++ crates/biome_css_syntax/src/generated/kind.rs | 2 + .../biome_css_syntax/src/generated/macros.rs | 4 + .../biome_css_syntax/src/generated/nodes.rs | 82 +++++++++++++++++++ .../src/generated/nodes_mut.rs | 8 ++ xtask/codegen/css.ungram | 2 + xtask/codegen/src/css_kinds_src.rs | 3 + 8 files changed, 126 insertions(+) diff --git a/crates/biome_css_factory/src/generated/node_factory.rs b/crates/biome_css_factory/src/generated/node_factory.rs index 2f59f5aa43cc..429a5759eeba 100644 --- a/crates/biome_css_factory/src/generated/node_factory.rs +++ b/crates/biome_css_factory/src/generated/node_factory.rs @@ -737,6 +737,12 @@ pub fn css_generic_property( ], )) } +pub fn css_grit_metavariable(value_token: SyntaxToken) -> CssGritMetavariable { + CssGritMetavariable::unwrap_cast(SyntaxNode::new_detached( + CssSyntaxKind::CSS_GRIT_METAVARIABLE, + [Some(SyntaxElement::Token(value_token))], + )) +} pub fn css_id_selector(hash_token: SyntaxToken, name: CssCustomIdentifier) -> CssIdSelector { CssIdSelector::unwrap_cast(SyntaxNode::new_detached( CssSyntaxKind::CSS_ID_SELECTOR, diff --git a/crates/biome_css_factory/src/generated/syntax_factory.rs b/crates/biome_css_factory/src/generated/syntax_factory.rs index 824bc7ab209d..5c450656446b 100644 --- a/crates/biome_css_factory/src/generated/syntax_factory.rs +++ b/crates/biome_css_factory/src/generated/syntax_factory.rs @@ -1492,6 +1492,25 @@ impl SyntaxFactory for CssSyntaxFactory { } slots.into_node(CSS_GENERIC_PROPERTY, children) } + CSS_GRIT_METAVARIABLE => { + let mut elements = (&children).into_iter(); + let mut slots: RawNodeSlots<1usize> = RawNodeSlots::default(); + let mut current_element = elements.next(); + if let Some(element) = ¤t_element { + if element.kind() == GRIT_METAVARIABLE { + slots.mark_present(); + current_element = elements.next(); + } + } + slots.next_slot(); + if current_element.is_some() { + return RawSyntaxNode::new( + CSS_GRIT_METAVARIABLE.to_bogus(), + children.into_iter().map(Some), + ); + } + slots.into_node(CSS_GRIT_METAVARIABLE, children) + } CSS_ID_SELECTOR => { let mut elements = (&children).into_iter(); let mut slots: RawNodeSlots<2usize> = RawNodeSlots::default(); diff --git a/crates/biome_css_syntax/src/generated/kind.rs b/crates/biome_css_syntax/src/generated/kind.rs index 0fe4c268908d..5a0443256199 100644 --- a/crates/biome_css_syntax/src/generated/kind.rs +++ b/crates/biome_css_syntax/src/generated/kind.rs @@ -249,6 +249,7 @@ pub enum CssSyntaxKind { WHITESPACE, COMMENT, MULTILINE_COMMENT, + GRIT_METAVARIABLE, CSS_ROOT, CSS_RULE_LIST, CSS_QUALIFIED_RULE, @@ -476,6 +477,7 @@ pub enum CssSyntaxKind { CSS_BOGUS_CUSTOM_IDENTIFIER, CSS_BOGUS_KEYFRAMES_NAME, CSS_BOGUS_UNICODE_RANGE_VALUE, + CSS_GRIT_METAVARIABLE, #[doc(hidden)] __LAST, } diff --git a/crates/biome_css_syntax/src/generated/macros.rs b/crates/biome_css_syntax/src/generated/macros.rs index e423b98c8524..f4691472cd51 100644 --- a/crates/biome_css_syntax/src/generated/macros.rs +++ b/crates/biome_css_syntax/src/generated/macros.rs @@ -217,6 +217,10 @@ macro_rules! map_syntax_node { let $pattern = unsafe { $crate::CssGenericProperty::new_unchecked(node) }; $body } + $crate::CssSyntaxKind::CSS_GRIT_METAVARIABLE => { + let $pattern = unsafe { $crate::CssGritMetavariable::new_unchecked(node) }; + $body + } $crate::CssSyntaxKind::CSS_ID_SELECTOR => { let $pattern = unsafe { $crate::CssIdSelector::new_unchecked(node) }; $body diff --git a/crates/biome_css_syntax/src/generated/nodes.rs b/crates/biome_css_syntax/src/generated/nodes.rs index e6e5f42da355..d39515dfe69f 100644 --- a/crates/biome_css_syntax/src/generated/nodes.rs +++ b/crates/biome_css_syntax/src/generated/nodes.rs @@ -2100,6 +2100,42 @@ pub struct CssGenericPropertyFields { pub value: CssGenericComponentValueList, } #[derive(Clone, PartialEq, Eq, Hash)] +pub struct CssGritMetavariable { + pub(crate) syntax: SyntaxNode, +} +impl CssGritMetavariable { + #[doc = r" Create an AstNode from a SyntaxNode without checking its kind"] + #[doc = r""] + #[doc = r" # Safety"] + #[doc = r" This function must be guarded with a call to [AstNode::can_cast]"] + #[doc = r" or a match on [SyntaxNode::kind]"] + #[inline] + pub const unsafe fn new_unchecked(syntax: SyntaxNode) -> Self { + Self { syntax } + } + pub fn as_fields(&self) -> CssGritMetavariableFields { + CssGritMetavariableFields { + value_token: self.value_token(), + } + } + pub fn value_token(&self) -> SyntaxResult { + support::required_token(&self.syntax, 0usize) + } +} +#[cfg(feature = "serde")] +impl Serialize for CssGritMetavariable { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + self.as_fields().serialize(serializer) + } +} +#[cfg_attr(feature = "serde", derive(Serialize))] +pub struct CssGritMetavariableFields { + pub value_token: SyntaxResult, +} +#[derive(Clone, PartialEq, Eq, Hash)] pub struct CssIdSelector { pub(crate) syntax: SyntaxNode, } @@ -10943,6 +10979,47 @@ impl From for SyntaxElement { n.syntax.into() } } +impl AstNode for CssGritMetavariable { + type Language = Language; + const KIND_SET: SyntaxKindSet = + SyntaxKindSet::from_raw(RawSyntaxKind(CSS_GRIT_METAVARIABLE as u16)); + fn can_cast(kind: SyntaxKind) -> bool { + kind == CSS_GRIT_METAVARIABLE + } + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } + fn into_syntax(self) -> SyntaxNode { + self.syntax + } +} +impl std::fmt::Debug for CssGritMetavariable { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("CssGritMetavariable") + .field( + "value_token", + &support::DebugSyntaxResult(self.value_token()), + ) + .finish() + } +} +impl From for SyntaxNode { + fn from(n: CssGritMetavariable) -> SyntaxNode { + n.syntax + } +} +impl From for SyntaxElement { + fn from(n: CssGritMetavariable) -> SyntaxElement { + n.syntax.into() + } +} impl AstNode for CssIdSelector { type Language = Language; const KIND_SET: SyntaxKindSet = @@ -22517,6 +22594,11 @@ impl std::fmt::Display for CssGenericProperty { std::fmt::Display::fmt(self.syntax(), f) } } +impl std::fmt::Display for CssGritMetavariable { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} impl std::fmt::Display for CssIdSelector { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) diff --git a/crates/biome_css_syntax/src/generated/nodes_mut.rs b/crates/biome_css_syntax/src/generated/nodes_mut.rs index 6637fad8e52c..557e7e6861d3 100644 --- a/crates/biome_css_syntax/src/generated/nodes_mut.rs +++ b/crates/biome_css_syntax/src/generated/nodes_mut.rs @@ -841,6 +841,14 @@ impl CssGenericProperty { ) } } +impl CssGritMetavariable { + pub fn with_value_token(self, element: SyntaxToken) -> Self { + Self::unwrap_cast( + self.syntax + .splice_slots(0usize..=0usize, once(Some(element.into()))), + ) + } +} impl CssIdSelector { pub fn with_hash_token(self, element: SyntaxToken) -> Self { Self::unwrap_cast( diff --git a/xtask/codegen/css.ungram b/xtask/codegen/css.ungram index 5ee5e5c59f88..dae382c51ece 100644 --- a/xtask/codegen/css.ungram +++ b/xtask/codegen/css.ungram @@ -1795,3 +1795,5 @@ CssNumber = value: 'css_number_literal' CssUnicodeCodepoint = value: 'css_unicode_codepoint_literal' CssUnicodeRangeWildcard = value: 'css_unicode_range_wildcard_literal' CssUrlValueRaw = value: 'css_url_value_raw_literal' + +CssGritMetavariable = value: 'grit_metavariable' \ No newline at end of file diff --git a/xtask/codegen/src/css_kinds_src.rs b/xtask/codegen/src/css_kinds_src.rs index 46bb9613682c..4d4e6eba82db 100644 --- a/xtask/codegen/src/css_kinds_src.rs +++ b/xtask/codegen/src/css_kinds_src.rs @@ -271,6 +271,7 @@ pub const CSS_KINDS_SRC: KindsSrc = KindsSrc { "WHITESPACE", "COMMENT", "MULTILINE_COMMENT", + "GRIT_METAVARIABLE", ], nodes: &[ "CSS_ROOT", @@ -505,5 +506,7 @@ pub const CSS_KINDS_SRC: KindsSrc = KindsSrc { "CSS_BOGUS_CUSTOM_IDENTIFIER", "CSS_BOGUS_KEYFRAMES_NAME", "CSS_BOGUS_UNICODE_RANGE_VALUE", + //Grit metavariable + "CSS_GRIT_METAVARIABLE", ], }; From 311de89d4e37a95197acac43708075f1de855f50 Mon Sep 17 00:00:00 2001 From: Zheyu Zhang Date: Tue, 2 Jul 2024 10:43:39 +0800 Subject: [PATCH 02/17] gen formatter --- .../src/css/auxiliary/grit_metavariable.rs | 10 +++++ .../src/css/auxiliary/mod.rs | 1 + crates/biome_css_formatter/src/generated.rs | 40 +++++++++++++++++++ 3 files changed, 51 insertions(+) create mode 100644 crates/biome_css_formatter/src/css/auxiliary/grit_metavariable.rs diff --git a/crates/biome_css_formatter/src/css/auxiliary/grit_metavariable.rs b/crates/biome_css_formatter/src/css/auxiliary/grit_metavariable.rs new file mode 100644 index 000000000000..317f977d8f22 --- /dev/null +++ b/crates/biome_css_formatter/src/css/auxiliary/grit_metavariable.rs @@ -0,0 +1,10 @@ +use crate::prelude::*; +use biome_css_syntax::CssGritMetavariable; +use biome_rowan::AstNode; +#[derive(Debug, Clone, Default)] +pub(crate) struct FormatCssGritMetavariable; +impl FormatNodeRule for FormatCssGritMetavariable { + fn fmt_fields(&self, node: &CssGritMetavariable, f: &mut CssFormatter) -> FormatResult<()> { + format_verbatim_node(node.syntax()).fmt(f) + } +} diff --git a/crates/biome_css_formatter/src/css/auxiliary/mod.rs b/crates/biome_css_formatter/src/css/auxiliary/mod.rs index b49df5ae5c91..3704c7f41593 100644 --- a/crates/biome_css_formatter/src/css/auxiliary/mod.rs +++ b/crates/biome_css_formatter/src/css/auxiliary/mod.rs @@ -29,6 +29,7 @@ pub(crate) mod font_feature_values_block; pub(crate) mod font_feature_values_item; pub(crate) mod function; pub(crate) mod generic_delimiter; +pub(crate) mod grit_metavariable; pub(crate) mod import_anonymous_layer; pub(crate) mod import_named_layer; pub(crate) mod import_supports; diff --git a/crates/biome_css_formatter/src/generated.rs b/crates/biome_css_formatter/src/generated.rs index 9d63bc1f38a3..558e590f1725 100644 --- a/crates/biome_css_formatter/src/generated.rs +++ b/crates/biome_css_formatter/src/generated.rs @@ -1747,6 +1747,46 @@ impl IntoFormat for biome_css_syntax::CssGenericProperty { ) } } +impl FormatRule + for crate::css::auxiliary::grit_metavariable::FormatCssGritMetavariable +{ + type Context = CssFormatContext; + #[inline(always)] + fn fmt( + &self, + node: &biome_css_syntax::CssGritMetavariable, + f: &mut CssFormatter, + ) -> FormatResult<()> { + FormatNodeRule::::fmt(self, node, f) + } +} +impl AsFormat for biome_css_syntax::CssGritMetavariable { + type Format<'a> = FormatRefWithRule< + 'a, + biome_css_syntax::CssGritMetavariable, + crate::css::auxiliary::grit_metavariable::FormatCssGritMetavariable, + >; + fn format(&self) -> Self::Format<'_> { + #![allow(clippy::default_constructed_unit_structs)] + FormatRefWithRule::new( + self, + crate::css::auxiliary::grit_metavariable::FormatCssGritMetavariable::default(), + ) + } +} +impl IntoFormat for biome_css_syntax::CssGritMetavariable { + type Format = FormatOwnedWithRule< + biome_css_syntax::CssGritMetavariable, + crate::css::auxiliary::grit_metavariable::FormatCssGritMetavariable, + >; + fn into_format(self) -> Self::Format { + #![allow(clippy::default_constructed_unit_structs)] + FormatOwnedWithRule::new( + self, + crate::css::auxiliary::grit_metavariable::FormatCssGritMetavariable::default(), + ) + } +} impl FormatRule for crate::css::selectors::id_selector::FormatCssIdSelector { From 9cb8a4fed941f6cdd310200fbe01717c5651b4b4 Mon Sep 17 00:00:00 2001 From: Zheyu Zhang Date: Tue, 2 Jul 2024 10:45:46 +0800 Subject: [PATCH 03/17] implement metavar formatting --- .../src/css/auxiliary/grit_metavariable.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/crates/biome_css_formatter/src/css/auxiliary/grit_metavariable.rs b/crates/biome_css_formatter/src/css/auxiliary/grit_metavariable.rs index 317f977d8f22..f4162fbd24ed 100644 --- a/crates/biome_css_formatter/src/css/auxiliary/grit_metavariable.rs +++ b/crates/biome_css_formatter/src/css/auxiliary/grit_metavariable.rs @@ -1,10 +1,12 @@ use crate::prelude::*; -use biome_css_syntax::CssGritMetavariable; -use biome_rowan::AstNode; +use biome_css_syntax::{CssGritMetavariable, CssGritMetavariableFields}; +use biome_formatter::write; + #[derive(Debug, Clone, Default)] pub(crate) struct FormatCssGritMetavariable; impl FormatNodeRule for FormatCssGritMetavariable { fn fmt_fields(&self, node: &CssGritMetavariable, f: &mut CssFormatter) -> FormatResult<()> { - format_verbatim_node(node.syntax()).fmt(f) + let CssGritMetavariableFields { value_token } = node.as_fields(); + write!(f, [value_token.format()]) } } From be54c82dc45e6e24cfd91eec52b676ccbeae9fc1 Mon Sep 17 00:00:00 2001 From: Zheyu Zhang Date: Tue, 2 Jul 2024 12:15:12 +0800 Subject: [PATCH 04/17] as any value --- .../biome_css_formatter/src/css/any/value.rs | 1 + crates/biome_css_parser/src/lexer/mod.rs | 25 +++++++++++++++++++ crates/biome_css_parser/src/parser.rs | 4 +++ crates/biome_css_parser/src/syntax/mod.rs | 22 +++++++++++++++- crates/biome_css_parser/tests/spec_test.rs | 6 ++--- .../biome_css_syntax/src/generated/nodes.rs | 21 ++++++++++++++++ xtask/codegen/css.ungram | 2 ++ 7 files changed, 76 insertions(+), 5 deletions(-) diff --git a/crates/biome_css_formatter/src/css/any/value.rs b/crates/biome_css_formatter/src/css/any/value.rs index ad9316306fd0..140630ba8dd8 100644 --- a/crates/biome_css_formatter/src/css/any/value.rs +++ b/crates/biome_css_formatter/src/css/any/value.rs @@ -14,6 +14,7 @@ impl FormatRule for FormatAnyCssValue { AnyCssValue::CssColor(node) => node.format().fmt(f), AnyCssValue::CssCustomIdentifier(node) => node.format().fmt(f), AnyCssValue::CssDashedIdentifier(node) => node.format().fmt(f), + AnyCssValue::CssGritMetavariable(node) => node.format().fmt(f), AnyCssValue::CssIdentifier(node) => node.format().fmt(f), AnyCssValue::CssNumber(node) => node.format().fmt(f), AnyCssValue::CssRatio(node) => node.format().fmt(f), diff --git a/crates/biome_css_parser/src/lexer/mod.rs b/crates/biome_css_parser/src/lexer/mod.rs index 1217828ddb89..5ceff4322b90 100644 --- a/crates/biome_css_parser/src/lexer/mod.rs +++ b/crates/biome_css_parser/src/lexer/mod.rs @@ -58,6 +58,7 @@ pub enum CssReLexContext { Regular, /// See [CssLexContext::UnicodeRange] UnicodeRange, + GritMetavariable, } /// An extremely fast, lookup table based, lossless CSS lexer @@ -1315,6 +1316,29 @@ impl<'src> CssLexer<'src> { _ => false, } } + + // $[a-zA-Z_][a-zA-Z0-9_]* + fn re_lex_grit_metavariable(&mut self, current_end: usize) -> CssSyntaxKind { + if self.current_kind == T![ident] { + if self.current_byte() == Some(b'$') { + if matches!(self.next_byte(), Some(b'a'..=b'z' | b'0'..=b'9' | b'_')) { + while let Some(chr) = self.current_byte() { + match chr { + b'a'..=b'z' | b'A'..=b'Z' | b'0'..=b'9' | b'_' => { + self.advance(1); + } + _ => break, + } + } + if current_end == self.position { + return GRIT_METAVARIABLE; + } + } + } + } + + self.current_kind + } } impl<'src> ReLexer<'src> for CssLexer<'src> { @@ -1326,6 +1350,7 @@ impl<'src> ReLexer<'src> for CssLexer<'src> { Some(current) => match context { CssReLexContext::Regular => self.consume_token(current), CssReLexContext::UnicodeRange => self.consume_unicode_range_token(current), + CssReLexContext::GritMetavariable => self.re_lex_grit_metavariable(old_position), }, None => EOF, }; diff --git a/crates/biome_css_parser/src/parser.rs b/crates/biome_css_parser/src/parser.rs index 8c48b98ee25e..080ee91c9989 100644 --- a/crates/biome_css_parser/src/parser.rs +++ b/crates/biome_css_parser/src/parser.rs @@ -48,6 +48,10 @@ impl CssParserOptions { pub fn is_css_modules_disabled(&self) -> bool { !self.css_modules } + + pub fn is_grit_metavariable_enabled(&self) -> bool { + true + } } impl<'source> CssParser<'source> { diff --git a/crates/biome_css_parser/src/syntax/mod.rs b/crates/biome_css_parser/src/syntax/mod.rs index dab4e9945035..c907ca7463b0 100644 --- a/crates/biome_css_parser/src/syntax/mod.rs +++ b/crates/biome_css_parser/src/syntax/mod.rs @@ -6,7 +6,7 @@ mod property; mod selector; mod value; -use crate::lexer::CssLexContext; +use crate::lexer::{CssLexContext, CssReLexContext}; use crate::parser::CssParser; use crate::syntax::at_rule::{is_at_at_rule, parse_at_rule}; use crate::syntax::block::parse_declaration_or_rule_list_block; @@ -248,6 +248,21 @@ fn parse_declaration_important(p: &mut CssParser) -> ParsedSyntax { Present(m.complete(p, CSS_DECLARATION_IMPORTANT)) } +#[inline] +fn is_at_grit_metavariable(p: &mut CssParser) -> bool { + p.at(GRIT_METAVARIABLE) +} + +#[inline] +fn parse_grit_metavariable(p: &mut CssParser) -> ParsedSyntax { + if !is_at_grit_metavariable(p) { + return Absent; + } + let m = p.start(); + p.bump(GRIT_METAVARIABLE); + Present(m.complete(p, CSS_GRIT_METAVARIABLE)) +} + #[inline] pub(crate) fn is_at_any_value(p: &mut CssParser) -> bool { is_at_any_function(p) @@ -263,6 +278,9 @@ pub(crate) fn is_at_any_value(p: &mut CssParser) -> bool { #[inline] pub(crate) fn parse_any_value(p: &mut CssParser) -> ParsedSyntax { + if p.options().is_grit_metavariable_enabled() { + p.re_lex(CssReLexContext::GritMetavariable); + } if is_at_any_function(p) { parse_any_function(p) } else if is_at_dashed_identifier(p) { @@ -283,6 +301,8 @@ pub(crate) fn parse_any_value(p: &mut CssParser) -> ParsedSyntax { parse_color(p) } else if is_at_bracketed_value(p) { parse_bracketed_value(p) + } else if is_at_grit_metavariable(p) { + parse_grit_metavariable(p) } else { Absent } diff --git a/crates/biome_css_parser/tests/spec_test.rs b/crates/biome_css_parser/tests/spec_test.rs index 0b0e8163fb63..ce5d37f1fa51 100644 --- a/crates/biome_css_parser/tests/spec_test.rs +++ b/crates/biome_css_parser/tests/spec_test.rs @@ -171,11 +171,9 @@ pub fn run(test_case: &str, _snapshot_name: &str, test_directory: &str, outcome_ #[test] pub fn quick_test() { let code = r#" -.test { -&&&&& { -color: red; +.foo { +color: $color } - } "#; let root = parse_css( diff --git a/crates/biome_css_syntax/src/generated/nodes.rs b/crates/biome_css_syntax/src/generated/nodes.rs index d39515dfe69f..b298a8f095da 100644 --- a/crates/biome_css_syntax/src/generated/nodes.rs +++ b/crates/biome_css_syntax/src/generated/nodes.rs @@ -8815,6 +8815,7 @@ pub enum AnyCssValue { CssColor(CssColor), CssCustomIdentifier(CssCustomIdentifier), CssDashedIdentifier(CssDashedIdentifier), + CssGritMetavariable(CssGritMetavariable), CssIdentifier(CssIdentifier), CssNumber(CssNumber), CssRatio(CssRatio), @@ -8858,6 +8859,12 @@ impl AnyCssValue { _ => None, } } + pub fn as_css_grit_metavariable(&self) -> Option<&CssGritMetavariable> { + match &self { + AnyCssValue::CssGritMetavariable(item) => Some(item), + _ => None, + } + } pub fn as_css_identifier(&self) -> Option<&CssIdentifier> { match &self { AnyCssValue::CssIdentifier(item) => Some(item), @@ -21532,6 +21539,11 @@ impl From for AnyCssValue { AnyCssValue::CssDashedIdentifier(node) } } +impl From for AnyCssValue { + fn from(node: CssGritMetavariable) -> AnyCssValue { + AnyCssValue::CssGritMetavariable(node) + } +} impl From for AnyCssValue { fn from(node: CssIdentifier) -> AnyCssValue { AnyCssValue::CssIdentifier(node) @@ -21565,6 +21577,7 @@ impl AstNode for AnyCssValue { .union(CssColor::KIND_SET) .union(CssCustomIdentifier::KIND_SET) .union(CssDashedIdentifier::KIND_SET) + .union(CssGritMetavariable::KIND_SET) .union(CssIdentifier::KIND_SET) .union(CssNumber::KIND_SET) .union(CssRatio::KIND_SET) @@ -21576,6 +21589,7 @@ impl AstNode for AnyCssValue { | CSS_COLOR | CSS_CUSTOM_IDENTIFIER | CSS_DASHED_IDENTIFIER + | CSS_GRIT_METAVARIABLE | CSS_IDENTIFIER | CSS_NUMBER | CSS_RATIO @@ -21596,6 +21610,9 @@ impl AstNode for AnyCssValue { CSS_DASHED_IDENTIFIER => { AnyCssValue::CssDashedIdentifier(CssDashedIdentifier { syntax }) } + CSS_GRIT_METAVARIABLE => { + AnyCssValue::CssGritMetavariable(CssGritMetavariable { syntax }) + } CSS_IDENTIFIER => AnyCssValue::CssIdentifier(CssIdentifier { syntax }), CSS_NUMBER => AnyCssValue::CssNumber(CssNumber { syntax }), CSS_RATIO => AnyCssValue::CssRatio(CssRatio { syntax }), @@ -21619,6 +21636,7 @@ impl AstNode for AnyCssValue { AnyCssValue::CssColor(it) => &it.syntax, AnyCssValue::CssCustomIdentifier(it) => &it.syntax, AnyCssValue::CssDashedIdentifier(it) => &it.syntax, + AnyCssValue::CssGritMetavariable(it) => &it.syntax, AnyCssValue::CssIdentifier(it) => &it.syntax, AnyCssValue::CssNumber(it) => &it.syntax, AnyCssValue::CssRatio(it) => &it.syntax, @@ -21634,6 +21652,7 @@ impl AstNode for AnyCssValue { AnyCssValue::CssColor(it) => it.syntax, AnyCssValue::CssCustomIdentifier(it) => it.syntax, AnyCssValue::CssDashedIdentifier(it) => it.syntax, + AnyCssValue::CssGritMetavariable(it) => it.syntax, AnyCssValue::CssIdentifier(it) => it.syntax, AnyCssValue::CssNumber(it) => it.syntax, AnyCssValue::CssRatio(it) => it.syntax, @@ -21653,6 +21672,7 @@ impl std::fmt::Debug for AnyCssValue { AnyCssValue::CssColor(it) => std::fmt::Debug::fmt(it, f), AnyCssValue::CssCustomIdentifier(it) => std::fmt::Debug::fmt(it, f), AnyCssValue::CssDashedIdentifier(it) => std::fmt::Debug::fmt(it, f), + AnyCssValue::CssGritMetavariable(it) => std::fmt::Debug::fmt(it, f), AnyCssValue::CssIdentifier(it) => std::fmt::Debug::fmt(it, f), AnyCssValue::CssNumber(it) => std::fmt::Debug::fmt(it, f), AnyCssValue::CssRatio(it) => std::fmt::Debug::fmt(it, f), @@ -21670,6 +21690,7 @@ impl From for SyntaxNode { AnyCssValue::CssColor(it) => it.into(), AnyCssValue::CssCustomIdentifier(it) => it.into(), AnyCssValue::CssDashedIdentifier(it) => it.into(), + AnyCssValue::CssGritMetavariable(it) => it.into(), AnyCssValue::CssIdentifier(it) => it.into(), AnyCssValue::CssNumber(it) => it.into(), AnyCssValue::CssRatio(it) => it.into(), diff --git a/xtask/codegen/css.ungram b/xtask/codegen/css.ungram index dae382c51ece..8d30bb182dda 100644 --- a/xtask/codegen/css.ungram +++ b/xtask/codegen/css.ungram @@ -1624,6 +1624,7 @@ AnyCssValue = | CssColor | CssBracketedValue | CssUnicodeRange + | CssGritMetavariable // https://drafts.csswg.org/css-syntax/#typedef-dimension-token @@ -1796,4 +1797,5 @@ CssUnicodeCodepoint = value: 'css_unicode_codepoint_literal' CssUnicodeRangeWildcard = value: 'css_unicode_range_wildcard_literal' CssUrlValueRaw = value: 'css_url_value_raw_literal' +// https://github.com/getgrit/gritql/blob/main/resources/language-metavariables/tree-sitter-css/grammar.js CssGritMetavariable = value: 'grit_metavariable' \ No newline at end of file From da35f9305fece4f341e841fc9b4f4943bd8ac9f3 Mon Sep 17 00:00:00 2001 From: Zheyu Zhang Date: Tue, 2 Jul 2024 12:40:23 +0800 Subject: [PATCH 05/17] as block item --- .../src/css/any/declaration_or_rule.rs | 1 + .../block/declaration_or_rule_list_block.rs | 16 ++++++++++--- crates/biome_css_parser/tests/spec_test.rs | 3 ++- .../biome_css_syntax/src/generated/nodes.rs | 24 +++++++++++++++++-- xtask/codegen/css.ungram | 1 + 5 files changed, 39 insertions(+), 6 deletions(-) diff --git a/crates/biome_css_formatter/src/css/any/declaration_or_rule.rs b/crates/biome_css_formatter/src/css/any/declaration_or_rule.rs index 1aa331f1f822..3a85ef75c0ab 100644 --- a/crates/biome_css_formatter/src/css/any/declaration_or_rule.rs +++ b/crates/biome_css_formatter/src/css/any/declaration_or_rule.rs @@ -11,6 +11,7 @@ impl FormatRule for FormatAnyCssDeclarationOrRule { AnyCssDeclarationOrRule::AnyCssRule(node) => node.format().fmt(f), AnyCssDeclarationOrRule::CssBogus(node) => node.format().fmt(f), AnyCssDeclarationOrRule::CssDeclarationWithSemicolon(node) => node.format().fmt(f), + AnyCssDeclarationOrRule::CssGritMetavariable(node) => node.format().fmt(f), } } } diff --git a/crates/biome_css_parser/src/syntax/block/declaration_or_rule_list_block.rs b/crates/biome_css_parser/src/syntax/block/declaration_or_rule_list_block.rs index b561bfdc7722..7a46256e5e15 100644 --- a/crates/biome_css_parser/src/syntax/block/declaration_or_rule_list_block.rs +++ b/crates/biome_css_parser/src/syntax/block/declaration_or_rule_list_block.rs @@ -1,10 +1,12 @@ +use crate::lexer::CssReLexContext; use crate::parser::CssParser; use crate::syntax::at_rule::{is_at_at_rule, parse_at_rule}; use crate::syntax::block::ParseBlockBody; use crate::syntax::parse_error::expected_any_declaration_or_at_rule; use crate::syntax::{ - is_at_declaration, is_at_nested_qualified_rule, parse_declaration_with_semicolon, - parse_nested_qualified_rule, try_parse, + is_at_declaration, is_at_grit_metavariable, is_at_identifier, is_at_nested_qualified_rule, + parse_declaration_with_semicolon, parse_grit_metavariable, parse_nested_qualified_rule, + try_parse, }; use biome_css_syntax::CssSyntaxKind::*; use biome_css_syntax::{CssSyntaxKind, T}; @@ -35,7 +37,10 @@ impl ParseBlockBody for DeclarationOrRuleListBlock { #[inline] fn is_at_declaration_or_rule_item(p: &mut CssParser) -> bool { - is_at_at_rule(p) || is_at_nested_qualified_rule(p) || is_at_declaration(p) + is_at_at_rule(p) + || is_at_nested_qualified_rule(p) + || is_at_declaration(p) + || is_at_identifier(p) } struct DeclarationOrRuleListParseRecovery; @@ -56,6 +61,9 @@ impl ParseNodeList for DeclarationOrRuleList { const LIST_KIND: Self::Kind = CSS_DECLARATION_OR_RULE_LIST; fn parse_element(&mut self, p: &mut Self::Parser<'_>) -> ParsedSyntax { + if p.options().is_grit_metavariable_enabled() { + p.re_lex(CssReLexContext::GritMetavariable); + } if is_at_at_rule(p) { parse_at_rule(p) } else if is_at_declaration(p) { @@ -124,6 +132,8 @@ impl ParseNodeList for DeclarationOrRuleList { parse_declaration_with_semicolon(p) } else if is_at_nested_qualified_rule(p) { parse_nested_qualified_rule(p) + } else if is_at_grit_metavariable(p) { + parse_grit_metavariable(p) } else { Absent } diff --git a/crates/biome_css_parser/tests/spec_test.rs b/crates/biome_css_parser/tests/spec_test.rs index ce5d37f1fa51..dd6ee65b256a 100644 --- a/crates/biome_css_parser/tests/spec_test.rs +++ b/crates/biome_css_parser/tests/spec_test.rs @@ -172,7 +172,8 @@ pub fn run(test_case: &str, _snapshot_name: &str, test_directory: &str, outcome_ pub fn quick_test() { let code = r#" .foo { -color: $color +color: red; +$fo-o } "#; diff --git a/crates/biome_css_syntax/src/generated/nodes.rs b/crates/biome_css_syntax/src/generated/nodes.rs index b298a8f095da..182f3cc57769 100644 --- a/crates/biome_css_syntax/src/generated/nodes.rs +++ b/crates/biome_css_syntax/src/generated/nodes.rs @@ -7331,6 +7331,7 @@ pub enum AnyCssDeclarationOrRule { AnyCssRule(AnyCssRule), CssBogus(CssBogus), CssDeclarationWithSemicolon(CssDeclarationWithSemicolon), + CssGritMetavariable(CssGritMetavariable), } impl AnyCssDeclarationOrRule { pub fn as_any_css_rule(&self) -> Option<&AnyCssRule> { @@ -7351,6 +7352,12 @@ impl AnyCssDeclarationOrRule { _ => None, } } + pub fn as_css_grit_metavariable(&self) -> Option<&CssGritMetavariable> { + match &self { + AnyCssDeclarationOrRule::CssGritMetavariable(item) => Some(item), + _ => None, + } + } } #[derive(Clone, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(Serialize))] @@ -17152,14 +17159,20 @@ impl From for AnyCssDeclarationOrRule { AnyCssDeclarationOrRule::CssDeclarationWithSemicolon(node) } } +impl From for AnyCssDeclarationOrRule { + fn from(node: CssGritMetavariable) -> AnyCssDeclarationOrRule { + AnyCssDeclarationOrRule::CssGritMetavariable(node) + } +} impl AstNode for AnyCssDeclarationOrRule { type Language = Language; const KIND_SET: SyntaxKindSet = AnyCssRule::KIND_SET .union(CssBogus::KIND_SET) - .union(CssDeclarationWithSemicolon::KIND_SET); + .union(CssDeclarationWithSemicolon::KIND_SET) + .union(CssGritMetavariable::KIND_SET); fn can_cast(kind: SyntaxKind) -> bool { match kind { - CSS_BOGUS | CSS_DECLARATION_WITH_SEMICOLON => true, + CSS_BOGUS | CSS_DECLARATION_WITH_SEMICOLON | CSS_GRIT_METAVARIABLE => true, k if AnyCssRule::can_cast(k) => true, _ => false, } @@ -17172,6 +17185,9 @@ impl AstNode for AnyCssDeclarationOrRule { syntax, }) } + CSS_GRIT_METAVARIABLE => { + AnyCssDeclarationOrRule::CssGritMetavariable(CssGritMetavariable { syntax }) + } _ => { if let Some(any_css_rule) = AnyCssRule::cast(syntax) { return Some(AnyCssDeclarationOrRule::AnyCssRule(any_css_rule)); @@ -17185,6 +17201,7 @@ impl AstNode for AnyCssDeclarationOrRule { match self { AnyCssDeclarationOrRule::CssBogus(it) => &it.syntax, AnyCssDeclarationOrRule::CssDeclarationWithSemicolon(it) => &it.syntax, + AnyCssDeclarationOrRule::CssGritMetavariable(it) => &it.syntax, AnyCssDeclarationOrRule::AnyCssRule(it) => it.syntax(), } } @@ -17192,6 +17209,7 @@ impl AstNode for AnyCssDeclarationOrRule { match self { AnyCssDeclarationOrRule::CssBogus(it) => it.syntax, AnyCssDeclarationOrRule::CssDeclarationWithSemicolon(it) => it.syntax, + AnyCssDeclarationOrRule::CssGritMetavariable(it) => it.syntax, AnyCssDeclarationOrRule::AnyCssRule(it) => it.into_syntax(), } } @@ -17202,6 +17220,7 @@ impl std::fmt::Debug for AnyCssDeclarationOrRule { AnyCssDeclarationOrRule::AnyCssRule(it) => std::fmt::Debug::fmt(it, f), AnyCssDeclarationOrRule::CssBogus(it) => std::fmt::Debug::fmt(it, f), AnyCssDeclarationOrRule::CssDeclarationWithSemicolon(it) => std::fmt::Debug::fmt(it, f), + AnyCssDeclarationOrRule::CssGritMetavariable(it) => std::fmt::Debug::fmt(it, f), } } } @@ -17211,6 +17230,7 @@ impl From for SyntaxNode { AnyCssDeclarationOrRule::AnyCssRule(it) => it.into(), AnyCssDeclarationOrRule::CssBogus(it) => it.into(), AnyCssDeclarationOrRule::CssDeclarationWithSemicolon(it) => it.into(), + AnyCssDeclarationOrRule::CssGritMetavariable(it) => it.into(), } } } diff --git a/xtask/codegen/css.ungram b/xtask/codegen/css.ungram index 8d30bb182dda..a2b3bee5682c 100644 --- a/xtask/codegen/css.ungram +++ b/xtask/codegen/css.ungram @@ -441,6 +441,7 @@ AnyCssDeclarationOrRule = AnyCssRule | CssDeclarationWithSemicolon | CssBogus + | CssGritMetavariable // @page :left { background: red; @media (500px <= width <= 500px) { } } // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ From b5de569626e67f5572f5a2bfeb983af21704d5dd Mon Sep 17 00:00:00 2001 From: Zheyu Zhang Date: Tue, 2 Jul 2024 12:54:03 +0800 Subject: [PATCH 06/17] as any media query --- .../src/css/any/media_query.rs | 1 + .../src/syntax/at_rule/media.rs | 11 +++++++++- crates/biome_css_parser/tests/spec_test.rs | 3 +-- .../biome_css_syntax/src/generated/nodes.rs | 22 ++++++++++++++++++- xtask/codegen/css.ungram | 1 + 5 files changed, 34 insertions(+), 4 deletions(-) diff --git a/crates/biome_css_formatter/src/css/any/media_query.rs b/crates/biome_css_formatter/src/css/any/media_query.rs index d7df4bc85a2e..c564c266fdda 100644 --- a/crates/biome_css_formatter/src/css/any/media_query.rs +++ b/crates/biome_css_formatter/src/css/any/media_query.rs @@ -10,6 +10,7 @@ impl FormatRule for FormatAnyCssMediaQuery { match node { AnyCssMediaQuery::AnyCssMediaTypeQuery(node) => node.format().fmt(f), AnyCssMediaQuery::CssBogusMediaQuery(node) => node.format().fmt(f), + AnyCssMediaQuery::CssGritMetavariable(node) => node.format().fmt(f), AnyCssMediaQuery::CssMediaConditionQuery(node) => node.format().fmt(f), } } diff --git a/crates/biome_css_parser/src/syntax/at_rule/media.rs b/crates/biome_css_parser/src/syntax/at_rule/media.rs index dc6803ea564e..a54b39791165 100644 --- a/crates/biome_css_parser/src/syntax/at_rule/media.rs +++ b/crates/biome_css_parser/src/syntax/at_rule/media.rs @@ -1,8 +1,12 @@ use super::parse_error::expected_media_query; +use crate::lexer::CssReLexContext; use crate::parser::CssParser; use crate::syntax::at_rule::feature::parse_any_query_feature; use crate::syntax::block::parse_conditional_block; -use crate::syntax::{is_at_identifier, is_nth_at_identifier, parse_regular_identifier}; +use crate::syntax::{ + is_at_grit_metavariable, is_at_identifier, is_nth_at_identifier, parse_grit_metavariable, + parse_regular_identifier, +}; use biome_css_syntax::CssSyntaxKind::*; use biome_css_syntax::{CssSyntaxKind, T}; use biome_parser::parse_lists::ParseSeparatedList; @@ -75,8 +79,13 @@ impl ParseSeparatedList for MediaQueryList { #[inline] fn parse_any_media_query(p: &mut CssParser) -> ParsedSyntax { + if p.options().is_grit_metavariable_enabled() { + p.re_lex(CssReLexContext::GritMetavariable); + } if is_at_media_type_query(p) { parse_any_media_type_query(p) + } else if is_at_grit_metavariable(p) { + parse_grit_metavariable(p) } else { let m = p.start(); parse_any_media_condition(p).ok(); // TODO handle error diff --git a/crates/biome_css_parser/tests/spec_test.rs b/crates/biome_css_parser/tests/spec_test.rs index dd6ee65b256a..4b6f5b175fa5 100644 --- a/crates/biome_css_parser/tests/spec_test.rs +++ b/crates/biome_css_parser/tests/spec_test.rs @@ -172,8 +172,7 @@ pub fn run(test_case: &str, _snapshot_name: &str, test_directory: &str, outcome_ pub fn quick_test() { let code = r#" .foo { -color: red; -$fo-o +@media $foo {} } "#; diff --git a/crates/biome_css_syntax/src/generated/nodes.rs b/crates/biome_css_syntax/src/generated/nodes.rs index 182f3cc57769..8d25413e2e6d 100644 --- a/crates/biome_css_syntax/src/generated/nodes.rs +++ b/crates/biome_css_syntax/src/generated/nodes.rs @@ -7889,6 +7889,7 @@ impl AnyCssMediaOrCombinableCondition { pub enum AnyCssMediaQuery { AnyCssMediaTypeQuery(AnyCssMediaTypeQuery), CssBogusMediaQuery(CssBogusMediaQuery), + CssGritMetavariable(CssGritMetavariable), CssMediaConditionQuery(CssMediaConditionQuery), } impl AnyCssMediaQuery { @@ -7904,6 +7905,12 @@ impl AnyCssMediaQuery { _ => None, } } + pub fn as_css_grit_metavariable(&self) -> Option<&CssGritMetavariable> { + match &self { + AnyCssMediaQuery::CssGritMetavariable(item) => Some(item), + _ => None, + } + } pub fn as_css_media_condition_query(&self) -> Option<&CssMediaConditionQuery> { match &self { AnyCssMediaQuery::CssMediaConditionQuery(item) => Some(item), @@ -18896,6 +18903,11 @@ impl From for AnyCssMediaQuery { AnyCssMediaQuery::CssBogusMediaQuery(node) } } +impl From for AnyCssMediaQuery { + fn from(node: CssGritMetavariable) -> AnyCssMediaQuery { + AnyCssMediaQuery::CssGritMetavariable(node) + } +} impl From for AnyCssMediaQuery { fn from(node: CssMediaConditionQuery) -> AnyCssMediaQuery { AnyCssMediaQuery::CssMediaConditionQuery(node) @@ -18905,10 +18917,11 @@ impl AstNode for AnyCssMediaQuery { type Language = Language; const KIND_SET: SyntaxKindSet = AnyCssMediaTypeQuery::KIND_SET .union(CssBogusMediaQuery::KIND_SET) + .union(CssGritMetavariable::KIND_SET) .union(CssMediaConditionQuery::KIND_SET); fn can_cast(kind: SyntaxKind) -> bool { match kind { - CSS_BOGUS_MEDIA_QUERY | CSS_MEDIA_CONDITION_QUERY => true, + CSS_BOGUS_MEDIA_QUERY | CSS_GRIT_METAVARIABLE | CSS_MEDIA_CONDITION_QUERY => true, k if AnyCssMediaTypeQuery::can_cast(k) => true, _ => false, } @@ -18918,6 +18931,9 @@ impl AstNode for AnyCssMediaQuery { CSS_BOGUS_MEDIA_QUERY => { AnyCssMediaQuery::CssBogusMediaQuery(CssBogusMediaQuery { syntax }) } + CSS_GRIT_METAVARIABLE => { + AnyCssMediaQuery::CssGritMetavariable(CssGritMetavariable { syntax }) + } CSS_MEDIA_CONDITION_QUERY => { AnyCssMediaQuery::CssMediaConditionQuery(CssMediaConditionQuery { syntax }) } @@ -18935,6 +18951,7 @@ impl AstNode for AnyCssMediaQuery { fn syntax(&self) -> &SyntaxNode { match self { AnyCssMediaQuery::CssBogusMediaQuery(it) => &it.syntax, + AnyCssMediaQuery::CssGritMetavariable(it) => &it.syntax, AnyCssMediaQuery::CssMediaConditionQuery(it) => &it.syntax, AnyCssMediaQuery::AnyCssMediaTypeQuery(it) => it.syntax(), } @@ -18942,6 +18959,7 @@ impl AstNode for AnyCssMediaQuery { fn into_syntax(self) -> SyntaxNode { match self { AnyCssMediaQuery::CssBogusMediaQuery(it) => it.syntax, + AnyCssMediaQuery::CssGritMetavariable(it) => it.syntax, AnyCssMediaQuery::CssMediaConditionQuery(it) => it.syntax, AnyCssMediaQuery::AnyCssMediaTypeQuery(it) => it.into_syntax(), } @@ -18952,6 +18970,7 @@ impl std::fmt::Debug for AnyCssMediaQuery { match self { AnyCssMediaQuery::AnyCssMediaTypeQuery(it) => std::fmt::Debug::fmt(it, f), AnyCssMediaQuery::CssBogusMediaQuery(it) => std::fmt::Debug::fmt(it, f), + AnyCssMediaQuery::CssGritMetavariable(it) => std::fmt::Debug::fmt(it, f), AnyCssMediaQuery::CssMediaConditionQuery(it) => std::fmt::Debug::fmt(it, f), } } @@ -18961,6 +18980,7 @@ impl From for SyntaxNode { match n { AnyCssMediaQuery::AnyCssMediaTypeQuery(it) => it.into(), AnyCssMediaQuery::CssBogusMediaQuery(it) => it.into(), + AnyCssMediaQuery::CssGritMetavariable(it) => it.into(), AnyCssMediaQuery::CssMediaConditionQuery(it) => it.into(), } } diff --git a/xtask/codegen/css.ungram b/xtask/codegen/css.ungram index a2b3bee5682c..6c4459a24eff 100644 --- a/xtask/codegen/css.ungram +++ b/xtask/codegen/css.ungram @@ -1010,6 +1010,7 @@ AnyCssMediaQuery = CssMediaConditionQuery | AnyCssMediaTypeQuery | CssBogusMediaQuery + | CssGritMetavariable // @media screen, (width > 500px), print {} // ^^^^^^^^^^^^^^^ From 564ed026e7149d2933a630a6a607c021adbf580b Mon Sep 17 00:00:00 2001 From: Zheyu Zhang Date: Tue, 2 Jul 2024 13:04:04 +0800 Subject: [PATCH 07/17] as any selector --- .../src/css/any/selector.rs | 1 + .../src/syntax/selector/mod.rs | 24 ++++++++++++----- crates/biome_css_parser/tests/spec_test.rs | 4 +-- .../biome_css_syntax/src/generated/nodes.rs | 27 +++++++++++++++++-- xtask/codegen/css.ungram | 1 + 5 files changed, 45 insertions(+), 12 deletions(-) diff --git a/crates/biome_css_formatter/src/css/any/selector.rs b/crates/biome_css_formatter/src/css/any/selector.rs index b2284c224f3b..20dff64f7de2 100644 --- a/crates/biome_css_formatter/src/css/any/selector.rs +++ b/crates/biome_css_formatter/src/css/any/selector.rs @@ -11,6 +11,7 @@ impl FormatRule for FormatAnyCssSelector { AnyCssSelector::CssBogusSelector(node) => node.format().fmt(f), AnyCssSelector::CssComplexSelector(node) => node.format().fmt(f), AnyCssSelector::CssCompoundSelector(node) => node.format().fmt(f), + AnyCssSelector::CssGritMetavariable(node) => node.format().fmt(f), } } } diff --git a/crates/biome_css_parser/src/syntax/selector/mod.rs b/crates/biome_css_parser/src/syntax/selector/mod.rs index 0340791521bd..385fe578572e 100644 --- a/crates/biome_css_parser/src/syntax/selector/mod.rs +++ b/crates/biome_css_parser/src/syntax/selector/mod.rs @@ -4,7 +4,7 @@ mod pseudo_class; mod pseudo_element; pub(crate) mod relative_selector; -use crate::lexer::CssLexContext; +use crate::lexer::{CssLexContext, CssReLexContext}; use crate::parser::CssParser; use crate::syntax::parse_error::{ expected_any_sub_selector, expected_compound_selector, expected_identifier, expected_selector, @@ -28,6 +28,8 @@ use biome_parser::prelude::ParsedSyntax; use biome_parser::prelude::ParsedSyntax::{Absent, Present}; use biome_parser::{token_set, CompletedMarker, Parser, ParserProgress, TokenSet}; +use super::{is_at_grit_metavariable, parse_grit_metavariable}; + /// Determines the lexical context for parsing CSS selectors. /// /// This function is applied when lexing CSS selectors. It decides whether the @@ -197,7 +199,7 @@ impl ParseRecovery for SelectorListParseRecovery { /// the elements to which a set of CSS rules apply. #[inline] pub(crate) fn is_nth_at_selector(p: &mut CssParser, n: usize) -> bool { - is_nth_at_compound_selector(p, n) + is_nth_at_compound_selector(p, n) || is_nth_at_identifier(p, n) } /// Parses a CSS selector. @@ -214,11 +216,19 @@ pub(crate) fn parse_selector(p: &mut CssParser) -> ParsedSyntax { return Absent; } - // In CSS, we have compound selectors and complex selectors. - // Compound selectors are simple, unseparated chains of selectors, - // while complex selectors are compound selectors separated by combinators. - // After parsing the compound selector, it then checks if this compound selector is a part of a complex selector. - parse_compound_selector(p).and_then(|selector| parse_complex_selector(p, selector)) + if p.options().is_grit_metavariable_enabled() { + p.re_lex(CssReLexContext::GritMetavariable); + } + + if is_at_grit_metavariable(p) { + parse_grit_metavariable(p) + } else { + // In CSS, we have compound selectors and complex selectors. + // Compound selectors are simple, unseparated chains of selectors, + // while complex selectors are compound selectors separated by combinators. + // After parsing the compound selector, it then checks if this compound selector is a part of a complex selector. + parse_compound_selector(p).and_then(|selector| parse_complex_selector(p, selector)) + } } const COMPLEX_SELECTOR_COMBINATOR_SET: TokenSet = diff --git a/crates/biome_css_parser/tests/spec_test.rs b/crates/biome_css_parser/tests/spec_test.rs index 4b6f5b175fa5..d99abd2cb992 100644 --- a/crates/biome_css_parser/tests/spec_test.rs +++ b/crates/biome_css_parser/tests/spec_test.rs @@ -171,9 +171,7 @@ pub fn run(test_case: &str, _snapshot_name: &str, test_directory: &str, outcome_ #[test] pub fn quick_test() { let code = r#" -.foo { -@media $foo {} -} +$foo {} "#; let root = parse_css( diff --git a/crates/biome_css_syntax/src/generated/nodes.rs b/crates/biome_css_syntax/src/generated/nodes.rs index 8d25413e2e6d..8c6f3e42b8c8 100644 --- a/crates/biome_css_syntax/src/generated/nodes.rs +++ b/crates/biome_css_syntax/src/generated/nodes.rs @@ -8508,6 +8508,7 @@ pub enum AnyCssSelector { CssBogusSelector(CssBogusSelector), CssComplexSelector(CssComplexSelector), CssCompoundSelector(CssCompoundSelector), + CssGritMetavariable(CssGritMetavariable), } impl AnyCssSelector { pub fn as_css_bogus_selector(&self) -> Option<&CssBogusSelector> { @@ -8528,6 +8529,12 @@ impl AnyCssSelector { _ => None, } } + pub fn as_css_grit_metavariable(&self) -> Option<&CssGritMetavariable> { + match &self { + AnyCssSelector::CssGritMetavariable(item) => Some(item), + _ => None, + } + } } #[derive(Clone, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(Serialize))] @@ -20654,15 +20661,24 @@ impl From for AnyCssSelector { AnyCssSelector::CssCompoundSelector(node) } } +impl From for AnyCssSelector { + fn from(node: CssGritMetavariable) -> AnyCssSelector { + AnyCssSelector::CssGritMetavariable(node) + } +} impl AstNode for AnyCssSelector { type Language = Language; const KIND_SET: SyntaxKindSet = CssBogusSelector::KIND_SET .union(CssComplexSelector::KIND_SET) - .union(CssCompoundSelector::KIND_SET); + .union(CssCompoundSelector::KIND_SET) + .union(CssGritMetavariable::KIND_SET); fn can_cast(kind: SyntaxKind) -> bool { matches!( kind, - CSS_BOGUS_SELECTOR | CSS_COMPLEX_SELECTOR | CSS_COMPOUND_SELECTOR + CSS_BOGUS_SELECTOR + | CSS_COMPLEX_SELECTOR + | CSS_COMPOUND_SELECTOR + | CSS_GRIT_METAVARIABLE ) } fn cast(syntax: SyntaxNode) -> Option { @@ -20674,6 +20690,9 @@ impl AstNode for AnyCssSelector { CSS_COMPOUND_SELECTOR => { AnyCssSelector::CssCompoundSelector(CssCompoundSelector { syntax }) } + CSS_GRIT_METAVARIABLE => { + AnyCssSelector::CssGritMetavariable(CssGritMetavariable { syntax }) + } _ => return None, }; Some(res) @@ -20683,6 +20702,7 @@ impl AstNode for AnyCssSelector { AnyCssSelector::CssBogusSelector(it) => &it.syntax, AnyCssSelector::CssComplexSelector(it) => &it.syntax, AnyCssSelector::CssCompoundSelector(it) => &it.syntax, + AnyCssSelector::CssGritMetavariable(it) => &it.syntax, } } fn into_syntax(self) -> SyntaxNode { @@ -20690,6 +20710,7 @@ impl AstNode for AnyCssSelector { AnyCssSelector::CssBogusSelector(it) => it.syntax, AnyCssSelector::CssComplexSelector(it) => it.syntax, AnyCssSelector::CssCompoundSelector(it) => it.syntax, + AnyCssSelector::CssGritMetavariable(it) => it.syntax, } } } @@ -20699,6 +20720,7 @@ impl std::fmt::Debug for AnyCssSelector { AnyCssSelector::CssBogusSelector(it) => std::fmt::Debug::fmt(it, f), AnyCssSelector::CssComplexSelector(it) => std::fmt::Debug::fmt(it, f), AnyCssSelector::CssCompoundSelector(it) => std::fmt::Debug::fmt(it, f), + AnyCssSelector::CssGritMetavariable(it) => std::fmt::Debug::fmt(it, f), } } } @@ -20708,6 +20730,7 @@ impl From for SyntaxNode { AnyCssSelector::CssBogusSelector(it) => it.into(), AnyCssSelector::CssComplexSelector(it) => it.into(), AnyCssSelector::CssCompoundSelector(it) => it.into(), + AnyCssSelector::CssGritMetavariable(it) => it.into(), } } } diff --git a/xtask/codegen/css.ungram b/xtask/codegen/css.ungram index 6c4459a24eff..800f38570d4e 100644 --- a/xtask/codegen/css.ungram +++ b/xtask/codegen/css.ungram @@ -96,6 +96,7 @@ AnyCssSelector = CssComplexSelector | CssCompoundSelector | CssBogusSelector + | CssGritMetavariable // div a {} // ^^^^^ From 0416c8ad4cb2cd0045fbaddda4b872890766a20b Mon Sep 17 00:00:00 2001 From: Zheyu Zhang Date: Tue, 2 Jul 2024 13:14:13 +0800 Subject: [PATCH 08/17] add option --- crates/biome_css_parser/src/parser.rs | 13 ++++++++++++- crates/biome_service/src/file_handlers/css.rs | 1 + 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/crates/biome_css_parser/src/parser.rs b/crates/biome_css_parser/src/parser.rs index 080ee91c9989..9bb1f5e88f6a 100644 --- a/crates/biome_css_parser/src/parser.rs +++ b/crates/biome_css_parser/src/parser.rs @@ -29,6 +29,10 @@ pub struct CssParserOptions { /// Enables parsing of CSS Modules specific features. /// Defaults to `false`. pub css_modules: bool, + + /// Enables parsing of Grit metavariables. + /// Defaults to `false`. + pub grit_metavariable: bool, } impl CssParserOptions { @@ -44,13 +48,20 @@ impl CssParserOptions { self } + /// Enables parsing of Grit metavariables. + pub fn allow_grit_metavariable(mut self) -> Self { + self.grit_metavariable = true; + self + } + /// Checks if parsing of CSS Modules features is disabled. pub fn is_css_modules_disabled(&self) -> bool { !self.css_modules } + /// Checks if parsing of Grit metavariables is enabled. pub fn is_grit_metavariable_enabled(&self) -> bool { - true + self.grit_metavariable } } diff --git a/crates/biome_service/src/file_handlers/css.rs b/crates/biome_service/src/file_handlers/css.rs index 8c97c0bb76f4..b7734e04b673 100644 --- a/crates/biome_service/src/file_handlers/css.rs +++ b/crates/biome_service/src/file_handlers/css.rs @@ -220,6 +220,7 @@ fn parse( css_modules: settings .and_then(|s| s.languages.css.parser.css_modules) .unwrap_or_default(), + grit_metavariable: false, }; if let Some(settings) = settings { options = settings From 46ec9fff4bca341a0431c1bf5f1bcb2b2abe6413 Mon Sep 17 00:00:00 2001 From: Zheyu Zhang Date: Tue, 2 Jul 2024 13:19:58 +0800 Subject: [PATCH 09/17] add tests --- .../ok/grit_metavariable/metavar.css | 13 + .../ok/grit_metavariable/metavar.css.snap | 231 ++++++++++++++++++ crates/biome_css_parser/tests/spec_test.rs | 8 +- 3 files changed, 250 insertions(+), 2 deletions(-) create mode 100644 crates/biome_css_parser/tests/css_test_suite/ok/grit_metavariable/metavar.css create mode 100644 crates/biome_css_parser/tests/css_test_suite/ok/grit_metavariable/metavar.css.snap diff --git a/crates/biome_css_parser/tests/css_test_suite/ok/grit_metavariable/metavar.css b/crates/biome_css_parser/tests/css_test_suite/ok/grit_metavariable/metavar.css new file mode 100644 index 000000000000..3d3df999fdd9 --- /dev/null +++ b/crates/biome_css_parser/tests/css_test_suite/ok/grit_metavariable/metavar.css @@ -0,0 +1,13 @@ +.foo { + color: $color; +} + +.foo { + $bar +} + +.foo { + @media $baz {} +} + +$qux {} \ No newline at end of file diff --git a/crates/biome_css_parser/tests/css_test_suite/ok/grit_metavariable/metavar.css.snap b/crates/biome_css_parser/tests/css_test_suite/ok/grit_metavariable/metavar.css.snap new file mode 100644 index 000000000000..eecb84d803e5 --- /dev/null +++ b/crates/biome_css_parser/tests/css_test_suite/ok/grit_metavariable/metavar.css.snap @@ -0,0 +1,231 @@ +--- +source: crates/biome_css_parser/tests/spec_test.rs +expression: snapshot +--- +## Input + +```css +.foo { + color: $color; +} + +.foo { + $bar +} + +.foo { + @media $baz {} +} + +$qux {} +``` + + +## AST + +``` +CssRoot { + bom_token: missing (optional), + rules: CssRuleList [ + CssQualifiedRule { + prelude: CssSelectorList [ + CssCompoundSelector { + nesting_selector_token: missing (optional), + simple_selector: missing (optional), + sub_selectors: CssSubSelectorList [ + CssClassSelector { + dot_token: DOT@0..1 "." [] [], + name: CssCustomIdentifier { + value_token: IDENT@1..5 "foo" [] [Whitespace(" ")], + }, + }, + ], + }, + ], + block: CssDeclarationOrRuleBlock { + l_curly_token: L_CURLY@5..6 "{" [] [], + items: CssDeclarationOrRuleList [ + CssDeclarationWithSemicolon { + declaration: CssDeclaration { + property: CssGenericProperty { + name: CssIdentifier { + value_token: IDENT@6..16 "color" [Newline("\n"), Whitespace(" ")] [], + }, + colon_token: COLON@16..18 ":" [] [Whitespace(" ")], + value: CssGenericComponentValueList [ + CssGritMetavariable { + value_token: GRIT_METAVARIABLE@18..24 "$color" [] [], + }, + ], + }, + important: missing (optional), + }, + semicolon_token: SEMICOLON@24..25 ";" [] [], + }, + ], + r_curly_token: R_CURLY@25..27 "}" [Newline("\n")] [], + }, + }, + CssQualifiedRule { + prelude: CssSelectorList [ + CssCompoundSelector { + nesting_selector_token: missing (optional), + simple_selector: missing (optional), + sub_selectors: CssSubSelectorList [ + CssClassSelector { + dot_token: DOT@27..30 "." [Newline("\n"), Newline("\n")] [], + name: CssCustomIdentifier { + value_token: IDENT@30..34 "foo" [] [Whitespace(" ")], + }, + }, + ], + }, + ], + block: CssDeclarationOrRuleBlock { + l_curly_token: L_CURLY@34..35 "{" [] [], + items: CssDeclarationOrRuleList [ + CssGritMetavariable { + value_token: GRIT_METAVARIABLE@35..44 "$bar" [Newline("\n"), Whitespace(" ")] [], + }, + ], + r_curly_token: R_CURLY@44..46 "}" [Newline("\n")] [], + }, + }, + CssQualifiedRule { + prelude: CssSelectorList [ + CssCompoundSelector { + nesting_selector_token: missing (optional), + simple_selector: missing (optional), + sub_selectors: CssSubSelectorList [ + CssClassSelector { + dot_token: DOT@46..49 "." [Newline("\n"), Newline("\n")] [], + name: CssCustomIdentifier { + value_token: IDENT@49..53 "foo" [] [Whitespace(" ")], + }, + }, + ], + }, + ], + block: CssDeclarationOrRuleBlock { + l_curly_token: L_CURLY@53..54 "{" [] [], + items: CssDeclarationOrRuleList [ + CssAtRule { + at_token: AT@54..60 "@" [Newline("\n"), Whitespace(" ")] [], + rule: CssMediaAtRule { + media_token: MEDIA_KW@60..66 "media" [] [Whitespace(" ")], + queries: CssMediaQueryList [ + CssGritMetavariable { + value_token: GRIT_METAVARIABLE@66..71 "$baz" [] [Whitespace(" ")], + }, + ], + block: CssDeclarationOrRuleBlock { + l_curly_token: L_CURLY@71..72 "{" [] [], + items: CssDeclarationOrRuleList [], + r_curly_token: R_CURLY@72..73 "}" [] [], + }, + }, + }, + ], + r_curly_token: R_CURLY@73..75 "}" [Newline("\n")] [], + }, + }, + CssQualifiedRule { + prelude: CssSelectorList [ + CssGritMetavariable { + value_token: GRIT_METAVARIABLE@75..82 "$qux" [Newline("\n"), Newline("\n")] [Whitespace(" ")], + }, + ], + block: CssDeclarationOrRuleBlock { + l_curly_token: L_CURLY@82..83 "{" [] [], + items: CssDeclarationOrRuleList [], + r_curly_token: R_CURLY@83..84 "}" [] [], + }, + }, + ], + eof_token: EOF@84..84 "" [] [], +} +``` + +## CST + +``` +0: CSS_ROOT@0..84 + 0: (empty) + 1: CSS_RULE_LIST@0..84 + 0: CSS_QUALIFIED_RULE@0..27 + 0: CSS_SELECTOR_LIST@0..5 + 0: CSS_COMPOUND_SELECTOR@0..5 + 0: (empty) + 1: (empty) + 2: CSS_SUB_SELECTOR_LIST@0..5 + 0: CSS_CLASS_SELECTOR@0..5 + 0: DOT@0..1 "." [] [] + 1: CSS_CUSTOM_IDENTIFIER@1..5 + 0: IDENT@1..5 "foo" [] [Whitespace(" ")] + 1: CSS_DECLARATION_OR_RULE_BLOCK@5..27 + 0: L_CURLY@5..6 "{" [] [] + 1: CSS_DECLARATION_OR_RULE_LIST@6..25 + 0: CSS_DECLARATION_WITH_SEMICOLON@6..25 + 0: CSS_DECLARATION@6..24 + 0: CSS_GENERIC_PROPERTY@6..24 + 0: CSS_IDENTIFIER@6..16 + 0: IDENT@6..16 "color" [Newline("\n"), Whitespace(" ")] [] + 1: COLON@16..18 ":" [] [Whitespace(" ")] + 2: CSS_GENERIC_COMPONENT_VALUE_LIST@18..24 + 0: CSS_GRIT_METAVARIABLE@18..24 + 0: GRIT_METAVARIABLE@18..24 "$color" [] [] + 1: (empty) + 1: SEMICOLON@24..25 ";" [] [] + 2: R_CURLY@25..27 "}" [Newline("\n")] [] + 1: CSS_QUALIFIED_RULE@27..46 + 0: CSS_SELECTOR_LIST@27..34 + 0: CSS_COMPOUND_SELECTOR@27..34 + 0: (empty) + 1: (empty) + 2: CSS_SUB_SELECTOR_LIST@27..34 + 0: CSS_CLASS_SELECTOR@27..34 + 0: DOT@27..30 "." [Newline("\n"), Newline("\n")] [] + 1: CSS_CUSTOM_IDENTIFIER@30..34 + 0: IDENT@30..34 "foo" [] [Whitespace(" ")] + 1: CSS_DECLARATION_OR_RULE_BLOCK@34..46 + 0: L_CURLY@34..35 "{" [] [] + 1: CSS_DECLARATION_OR_RULE_LIST@35..44 + 0: CSS_GRIT_METAVARIABLE@35..44 + 0: GRIT_METAVARIABLE@35..44 "$bar" [Newline("\n"), Whitespace(" ")] [] + 2: R_CURLY@44..46 "}" [Newline("\n")] [] + 2: CSS_QUALIFIED_RULE@46..75 + 0: CSS_SELECTOR_LIST@46..53 + 0: CSS_COMPOUND_SELECTOR@46..53 + 0: (empty) + 1: (empty) + 2: CSS_SUB_SELECTOR_LIST@46..53 + 0: CSS_CLASS_SELECTOR@46..53 + 0: DOT@46..49 "." [Newline("\n"), Newline("\n")] [] + 1: CSS_CUSTOM_IDENTIFIER@49..53 + 0: IDENT@49..53 "foo" [] [Whitespace(" ")] + 1: CSS_DECLARATION_OR_RULE_BLOCK@53..75 + 0: L_CURLY@53..54 "{" [] [] + 1: CSS_DECLARATION_OR_RULE_LIST@54..73 + 0: CSS_AT_RULE@54..73 + 0: AT@54..60 "@" [Newline("\n"), Whitespace(" ")] [] + 1: CSS_MEDIA_AT_RULE@60..73 + 0: MEDIA_KW@60..66 "media" [] [Whitespace(" ")] + 1: CSS_MEDIA_QUERY_LIST@66..71 + 0: CSS_GRIT_METAVARIABLE@66..71 + 0: GRIT_METAVARIABLE@66..71 "$baz" [] [Whitespace(" ")] + 2: CSS_DECLARATION_OR_RULE_BLOCK@71..73 + 0: L_CURLY@71..72 "{" [] [] + 1: CSS_DECLARATION_OR_RULE_LIST@72..72 + 2: R_CURLY@72..73 "}" [] [] + 2: R_CURLY@73..75 "}" [Newline("\n")] [] + 3: CSS_QUALIFIED_RULE@75..84 + 0: CSS_SELECTOR_LIST@75..82 + 0: CSS_GRIT_METAVARIABLE@75..82 + 0: GRIT_METAVARIABLE@75..82 "$qux" [Newline("\n"), Newline("\n")] [Whitespace(" ")] + 1: CSS_DECLARATION_OR_RULE_BLOCK@82..84 + 0: L_CURLY@82..83 "{" [] [] + 1: CSS_DECLARATION_OR_RULE_LIST@83..83 + 2: R_CURLY@83..84 "}" [] [] + 2: EOF@84..84 "" [] [] + +``` diff --git a/crates/biome_css_parser/tests/spec_test.rs b/crates/biome_css_parser/tests/spec_test.rs index d99abd2cb992..c0327ea865d8 100644 --- a/crates/biome_css_parser/tests/spec_test.rs +++ b/crates/biome_css_parser/tests/spec_test.rs @@ -40,7 +40,10 @@ pub fn run(test_case: &str, _snapshot_name: &str, test_directory: &str, outcome_ let content = fs::read_to_string(test_case_path) .expect("Expected test path to be a readable file in UTF8 encoding"); - let mut options = CssParserOptions::default(); + let mut options = CssParserOptions::default() + // it is an internal option that cannot be configured via options.json + // TODO: find a way to make it configurable + .allow_grit_metavariable(); let options_path = Path::new(test_directory).join("options.json"); @@ -178,7 +181,8 @@ $foo {} code, CssParserOptions::default() .allow_wrong_line_comments() - .allow_css_modules(), + .allow_css_modules() + .allow_grit_metavariable(), ); let syntax = root.syntax(); dbg!(&syntax, root.diagnostics(), root.has_errors()); From 20d5380f5055e0c7be5fec8904d0a8fae389137c Mon Sep 17 00:00:00 2001 From: Zheyu Zhang Date: Tue, 2 Jul 2024 14:16:03 +0800 Subject: [PATCH 10/17] clippy --- crates/biome_css_parser/src/lexer/mod.rs | 25 ++++++++++++------------ xtask/codegen/src/css_kinds_src.rs | 2 +- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/crates/biome_css_parser/src/lexer/mod.rs b/crates/biome_css_parser/src/lexer/mod.rs index 5ceff4322b90..90176f84793a 100644 --- a/crates/biome_css_parser/src/lexer/mod.rs +++ b/crates/biome_css_parser/src/lexer/mod.rs @@ -1319,22 +1319,21 @@ impl<'src> CssLexer<'src> { // $[a-zA-Z_][a-zA-Z0-9_]* fn re_lex_grit_metavariable(&mut self, current_end: usize) -> CssSyntaxKind { - if self.current_kind == T![ident] { - if self.current_byte() == Some(b'$') { - if matches!(self.next_byte(), Some(b'a'..=b'z' | b'0'..=b'9' | b'_')) { - while let Some(chr) = self.current_byte() { - match chr { - b'a'..=b'z' | b'A'..=b'Z' | b'0'..=b'9' | b'_' => { - self.advance(1); - } - _ => break, - } - } - if current_end == self.position { - return GRIT_METAVARIABLE; + if self.current_kind == T![ident] + && self.current_byte() == Some(b'$') + && matches!(self.next_byte(), Some(b'a'..=b'z' | b'A'..=b'Z' | b'_')) + { + while let Some(chr) = self.current_byte() { + match chr { + b'a'..=b'z' | b'A'..=b'Z' | b'0'..=b'9' | b'_' => { + self.advance(1); } + _ => break, } } + if current_end == self.position { + return GRIT_METAVARIABLE; + } } self.current_kind diff --git a/xtask/codegen/src/css_kinds_src.rs b/xtask/codegen/src/css_kinds_src.rs index 4d4e6eba82db..8693d82de21f 100644 --- a/xtask/codegen/src/css_kinds_src.rs +++ b/xtask/codegen/src/css_kinds_src.rs @@ -506,7 +506,7 @@ pub const CSS_KINDS_SRC: KindsSrc = KindsSrc { "CSS_BOGUS_CUSTOM_IDENTIFIER", "CSS_BOGUS_KEYFRAMES_NAME", "CSS_BOGUS_UNICODE_RANGE_VALUE", - //Grit metavariable + // Grit metavariable "CSS_GRIT_METAVARIABLE", ], }; From fc7e3d5430bfc8541a91390df748198cb928576d Mon Sep 17 00:00:00 2001 From: Zheyu Zhang Date: Wed, 3 Jul 2024 10:29:44 +0800 Subject: [PATCH 11/17] add comments --- crates/biome_css_parser/src/lexer/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/biome_css_parser/src/lexer/mod.rs b/crates/biome_css_parser/src/lexer/mod.rs index 90176f84793a..34eab97f3818 100644 --- a/crates/biome_css_parser/src/lexer/mod.rs +++ b/crates/biome_css_parser/src/lexer/mod.rs @@ -58,6 +58,7 @@ pub enum CssReLexContext { Regular, /// See [CssLexContext::UnicodeRange] UnicodeRange, + /// Re-lexes an identifier as a Grit metavariable if it exactly matches the RegExp `$[a-zA-Z_][a-zA-Z0-9_]*`. GritMetavariable, } @@ -1317,7 +1318,6 @@ impl<'src> CssLexer<'src> { } } - // $[a-zA-Z_][a-zA-Z0-9_]* fn re_lex_grit_metavariable(&mut self, current_end: usize) -> CssSyntaxKind { if self.current_kind == T![ident] && self.current_byte() == Some(b'$') From 74e00d4f05e38ba731d344ab819d08cc2b5064fb Mon Sep 17 00:00:00 2001 From: Zheyu Zhang Date: Fri, 5 Jul 2024 10:47:20 +0800 Subject: [PATCH 12/17] =?UTF-8?q?$=20=3D>=20=CE=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/biome_css_parser/src/lexer/mod.rs | 30 ++-- .../ok/grit_metavariable/metavar.css | 8 +- .../ok/grit_metavariable/metavar.css.snap | 168 +++++++++--------- crates/biome_css_parser/tests/spec_test.rs | 2 +- 4 files changed, 106 insertions(+), 102 deletions(-) diff --git a/crates/biome_css_parser/src/lexer/mod.rs b/crates/biome_css_parser/src/lexer/mod.rs index 34eab97f3818..10549bc49dd8 100644 --- a/crates/biome_css_parser/src/lexer/mod.rs +++ b/crates/biome_css_parser/src/lexer/mod.rs @@ -58,7 +58,7 @@ pub enum CssReLexContext { Regular, /// See [CssLexContext::UnicodeRange] UnicodeRange, - /// Re-lexes an identifier as a Grit metavariable if it exactly matches the RegExp `$[a-zA-Z_][a-zA-Z0-9_]*`. + /// Re-lexes an identifier as a Grit metavariable if it exactly matches the RegExp `μ[a-zA-Z_][a-zA-Z0-9_]*`. GritMetavariable, } @@ -1319,21 +1319,25 @@ impl<'src> CssLexer<'src> { } fn re_lex_grit_metavariable(&mut self, current_end: usize) -> CssSyntaxKind { - if self.current_kind == T![ident] - && self.current_byte() == Some(b'$') - && matches!(self.next_byte(), Some(b'a'..=b'z' | b'A'..=b'Z' | b'_')) - { - while let Some(chr) = self.current_byte() { - match chr { - b'a'..=b'z' | b'A'..=b'Z' | b'0'..=b'9' | b'_' => { - self.advance(1); + if self.current_kind == T![ident] { + let current_char = self.current_char_unchecked(); + if current_char == 'μ' { + self.advance(current_char.len_utf8()); + if matches!(self.current_byte(), Some(b'a'..=b'z' | b'A'..=b'Z' | b'_')) { + self.advance(1); + while let Some(chr) = self.current_byte() { + match chr { + b'a'..=b'z' | b'A'..=b'Z' | b'0'..=b'9' | b'_' => { + self.advance(1); + } + _ => break, + } + } + if current_end == self.position { + return GRIT_METAVARIABLE; } - _ => break, } } - if current_end == self.position { - return GRIT_METAVARIABLE; - } } self.current_kind diff --git a/crates/biome_css_parser/tests/css_test_suite/ok/grit_metavariable/metavar.css b/crates/biome_css_parser/tests/css_test_suite/ok/grit_metavariable/metavar.css index 3d3df999fdd9..54100f7e7f9a 100644 --- a/crates/biome_css_parser/tests/css_test_suite/ok/grit_metavariable/metavar.css +++ b/crates/biome_css_parser/tests/css_test_suite/ok/grit_metavariable/metavar.css @@ -1,13 +1,13 @@ .foo { - color: $color; + color: μcolor; } .foo { - $bar + μbar } .foo { - @media $baz {} + @media μbaz {} } -$qux {} \ No newline at end of file +μqux {} \ No newline at end of file diff --git a/crates/biome_css_parser/tests/css_test_suite/ok/grit_metavariable/metavar.css.snap b/crates/biome_css_parser/tests/css_test_suite/ok/grit_metavariable/metavar.css.snap index eecb84d803e5..12d301fc5fae 100644 --- a/crates/biome_css_parser/tests/css_test_suite/ok/grit_metavariable/metavar.css.snap +++ b/crates/biome_css_parser/tests/css_test_suite/ok/grit_metavariable/metavar.css.snap @@ -6,18 +6,18 @@ expression: snapshot ```css .foo { - color: $color; + color: μcolor; } .foo { - $bar + μbar } .foo { - @media $baz {} + @media μbaz {} } -$qux {} +μqux {} ``` @@ -54,16 +54,16 @@ CssRoot { colon_token: COLON@16..18 ":" [] [Whitespace(" ")], value: CssGenericComponentValueList [ CssGritMetavariable { - value_token: GRIT_METAVARIABLE@18..24 "$color" [] [], + value_token: GRIT_METAVARIABLE@18..25 "μcolor" [] [], }, ], }, important: missing (optional), }, - semicolon_token: SEMICOLON@24..25 ";" [] [], + semicolon_token: SEMICOLON@25..26 ";" [] [], }, ], - r_curly_token: R_CURLY@25..27 "}" [Newline("\n")] [], + r_curly_token: R_CURLY@26..28 "}" [Newline("\n")] [], }, }, CssQualifiedRule { @@ -73,22 +73,22 @@ CssRoot { simple_selector: missing (optional), sub_selectors: CssSubSelectorList [ CssClassSelector { - dot_token: DOT@27..30 "." [Newline("\n"), Newline("\n")] [], + dot_token: DOT@28..31 "." [Newline("\n"), Newline("\n")] [], name: CssCustomIdentifier { - value_token: IDENT@30..34 "foo" [] [Whitespace(" ")], + value_token: IDENT@31..35 "foo" [] [Whitespace(" ")], }, }, ], }, ], block: CssDeclarationOrRuleBlock { - l_curly_token: L_CURLY@34..35 "{" [] [], + l_curly_token: L_CURLY@35..36 "{" [] [], items: CssDeclarationOrRuleList [ CssGritMetavariable { - value_token: GRIT_METAVARIABLE@35..44 "$bar" [Newline("\n"), Whitespace(" ")] [], + value_token: GRIT_METAVARIABLE@36..46 "μbar" [Newline("\n"), Whitespace(" ")] [], }, ], - r_curly_token: R_CURLY@44..46 "}" [Newline("\n")] [], + r_curly_token: R_CURLY@46..48 "}" [Newline("\n")] [], }, }, CssQualifiedRule { @@ -98,61 +98,61 @@ CssRoot { simple_selector: missing (optional), sub_selectors: CssSubSelectorList [ CssClassSelector { - dot_token: DOT@46..49 "." [Newline("\n"), Newline("\n")] [], + dot_token: DOT@48..51 "." [Newline("\n"), Newline("\n")] [], name: CssCustomIdentifier { - value_token: IDENT@49..53 "foo" [] [Whitespace(" ")], + value_token: IDENT@51..55 "foo" [] [Whitespace(" ")], }, }, ], }, ], block: CssDeclarationOrRuleBlock { - l_curly_token: L_CURLY@53..54 "{" [] [], + l_curly_token: L_CURLY@55..56 "{" [] [], items: CssDeclarationOrRuleList [ CssAtRule { - at_token: AT@54..60 "@" [Newline("\n"), Whitespace(" ")] [], + at_token: AT@56..62 "@" [Newline("\n"), Whitespace(" ")] [], rule: CssMediaAtRule { - media_token: MEDIA_KW@60..66 "media" [] [Whitespace(" ")], + media_token: MEDIA_KW@62..68 "media" [] [Whitespace(" ")], queries: CssMediaQueryList [ CssGritMetavariable { - value_token: GRIT_METAVARIABLE@66..71 "$baz" [] [Whitespace(" ")], + value_token: GRIT_METAVARIABLE@68..74 "μbaz" [] [Whitespace(" ")], }, ], block: CssDeclarationOrRuleBlock { - l_curly_token: L_CURLY@71..72 "{" [] [], + l_curly_token: L_CURLY@74..75 "{" [] [], items: CssDeclarationOrRuleList [], - r_curly_token: R_CURLY@72..73 "}" [] [], + r_curly_token: R_CURLY@75..76 "}" [] [], }, }, }, ], - r_curly_token: R_CURLY@73..75 "}" [Newline("\n")] [], + r_curly_token: R_CURLY@76..78 "}" [Newline("\n")] [], }, }, CssQualifiedRule { prelude: CssSelectorList [ CssGritMetavariable { - value_token: GRIT_METAVARIABLE@75..82 "$qux" [Newline("\n"), Newline("\n")] [Whitespace(" ")], + value_token: GRIT_METAVARIABLE@78..86 "μqux" [Newline("\n"), Newline("\n")] [Whitespace(" ")], }, ], block: CssDeclarationOrRuleBlock { - l_curly_token: L_CURLY@82..83 "{" [] [], + l_curly_token: L_CURLY@86..87 "{" [] [], items: CssDeclarationOrRuleList [], - r_curly_token: R_CURLY@83..84 "}" [] [], + r_curly_token: R_CURLY@87..88 "}" [] [], }, }, ], - eof_token: EOF@84..84 "" [] [], + eof_token: EOF@88..88 "" [] [], } ``` ## CST ``` -0: CSS_ROOT@0..84 +0: CSS_ROOT@0..88 0: (empty) - 1: CSS_RULE_LIST@0..84 - 0: CSS_QUALIFIED_RULE@0..27 + 1: CSS_RULE_LIST@0..88 + 0: CSS_QUALIFIED_RULE@0..28 0: CSS_SELECTOR_LIST@0..5 0: CSS_COMPOUND_SELECTOR@0..5 0: (empty) @@ -162,70 +162,70 @@ CssRoot { 0: DOT@0..1 "." [] [] 1: CSS_CUSTOM_IDENTIFIER@1..5 0: IDENT@1..5 "foo" [] [Whitespace(" ")] - 1: CSS_DECLARATION_OR_RULE_BLOCK@5..27 + 1: CSS_DECLARATION_OR_RULE_BLOCK@5..28 0: L_CURLY@5..6 "{" [] [] - 1: CSS_DECLARATION_OR_RULE_LIST@6..25 - 0: CSS_DECLARATION_WITH_SEMICOLON@6..25 - 0: CSS_DECLARATION@6..24 - 0: CSS_GENERIC_PROPERTY@6..24 + 1: CSS_DECLARATION_OR_RULE_LIST@6..26 + 0: CSS_DECLARATION_WITH_SEMICOLON@6..26 + 0: CSS_DECLARATION@6..25 + 0: CSS_GENERIC_PROPERTY@6..25 0: CSS_IDENTIFIER@6..16 0: IDENT@6..16 "color" [Newline("\n"), Whitespace(" ")] [] 1: COLON@16..18 ":" [] [Whitespace(" ")] - 2: CSS_GENERIC_COMPONENT_VALUE_LIST@18..24 - 0: CSS_GRIT_METAVARIABLE@18..24 - 0: GRIT_METAVARIABLE@18..24 "$color" [] [] + 2: CSS_GENERIC_COMPONENT_VALUE_LIST@18..25 + 0: CSS_GRIT_METAVARIABLE@18..25 + 0: GRIT_METAVARIABLE@18..25 "μcolor" [] [] 1: (empty) - 1: SEMICOLON@24..25 ";" [] [] - 2: R_CURLY@25..27 "}" [Newline("\n")] [] - 1: CSS_QUALIFIED_RULE@27..46 - 0: CSS_SELECTOR_LIST@27..34 - 0: CSS_COMPOUND_SELECTOR@27..34 + 1: SEMICOLON@25..26 ";" [] [] + 2: R_CURLY@26..28 "}" [Newline("\n")] [] + 1: CSS_QUALIFIED_RULE@28..48 + 0: CSS_SELECTOR_LIST@28..35 + 0: CSS_COMPOUND_SELECTOR@28..35 0: (empty) 1: (empty) - 2: CSS_SUB_SELECTOR_LIST@27..34 - 0: CSS_CLASS_SELECTOR@27..34 - 0: DOT@27..30 "." [Newline("\n"), Newline("\n")] [] - 1: CSS_CUSTOM_IDENTIFIER@30..34 - 0: IDENT@30..34 "foo" [] [Whitespace(" ")] - 1: CSS_DECLARATION_OR_RULE_BLOCK@34..46 - 0: L_CURLY@34..35 "{" [] [] - 1: CSS_DECLARATION_OR_RULE_LIST@35..44 - 0: CSS_GRIT_METAVARIABLE@35..44 - 0: GRIT_METAVARIABLE@35..44 "$bar" [Newline("\n"), Whitespace(" ")] [] - 2: R_CURLY@44..46 "}" [Newline("\n")] [] - 2: CSS_QUALIFIED_RULE@46..75 - 0: CSS_SELECTOR_LIST@46..53 - 0: CSS_COMPOUND_SELECTOR@46..53 + 2: CSS_SUB_SELECTOR_LIST@28..35 + 0: CSS_CLASS_SELECTOR@28..35 + 0: DOT@28..31 "." [Newline("\n"), Newline("\n")] [] + 1: CSS_CUSTOM_IDENTIFIER@31..35 + 0: IDENT@31..35 "foo" [] [Whitespace(" ")] + 1: CSS_DECLARATION_OR_RULE_BLOCK@35..48 + 0: L_CURLY@35..36 "{" [] [] + 1: CSS_DECLARATION_OR_RULE_LIST@36..46 + 0: CSS_GRIT_METAVARIABLE@36..46 + 0: GRIT_METAVARIABLE@36..46 "μbar" [Newline("\n"), Whitespace(" ")] [] + 2: R_CURLY@46..48 "}" [Newline("\n")] [] + 2: CSS_QUALIFIED_RULE@48..78 + 0: CSS_SELECTOR_LIST@48..55 + 0: CSS_COMPOUND_SELECTOR@48..55 0: (empty) 1: (empty) - 2: CSS_SUB_SELECTOR_LIST@46..53 - 0: CSS_CLASS_SELECTOR@46..53 - 0: DOT@46..49 "." [Newline("\n"), Newline("\n")] [] - 1: CSS_CUSTOM_IDENTIFIER@49..53 - 0: IDENT@49..53 "foo" [] [Whitespace(" ")] - 1: CSS_DECLARATION_OR_RULE_BLOCK@53..75 - 0: L_CURLY@53..54 "{" [] [] - 1: CSS_DECLARATION_OR_RULE_LIST@54..73 - 0: CSS_AT_RULE@54..73 - 0: AT@54..60 "@" [Newline("\n"), Whitespace(" ")] [] - 1: CSS_MEDIA_AT_RULE@60..73 - 0: MEDIA_KW@60..66 "media" [] [Whitespace(" ")] - 1: CSS_MEDIA_QUERY_LIST@66..71 - 0: CSS_GRIT_METAVARIABLE@66..71 - 0: GRIT_METAVARIABLE@66..71 "$baz" [] [Whitespace(" ")] - 2: CSS_DECLARATION_OR_RULE_BLOCK@71..73 - 0: L_CURLY@71..72 "{" [] [] - 1: CSS_DECLARATION_OR_RULE_LIST@72..72 - 2: R_CURLY@72..73 "}" [] [] - 2: R_CURLY@73..75 "}" [Newline("\n")] [] - 3: CSS_QUALIFIED_RULE@75..84 - 0: CSS_SELECTOR_LIST@75..82 - 0: CSS_GRIT_METAVARIABLE@75..82 - 0: GRIT_METAVARIABLE@75..82 "$qux" [Newline("\n"), Newline("\n")] [Whitespace(" ")] - 1: CSS_DECLARATION_OR_RULE_BLOCK@82..84 - 0: L_CURLY@82..83 "{" [] [] - 1: CSS_DECLARATION_OR_RULE_LIST@83..83 - 2: R_CURLY@83..84 "}" [] [] - 2: EOF@84..84 "" [] [] + 2: CSS_SUB_SELECTOR_LIST@48..55 + 0: CSS_CLASS_SELECTOR@48..55 + 0: DOT@48..51 "." [Newline("\n"), Newline("\n")] [] + 1: CSS_CUSTOM_IDENTIFIER@51..55 + 0: IDENT@51..55 "foo" [] [Whitespace(" ")] + 1: CSS_DECLARATION_OR_RULE_BLOCK@55..78 + 0: L_CURLY@55..56 "{" [] [] + 1: CSS_DECLARATION_OR_RULE_LIST@56..76 + 0: CSS_AT_RULE@56..76 + 0: AT@56..62 "@" [Newline("\n"), Whitespace(" ")] [] + 1: CSS_MEDIA_AT_RULE@62..76 + 0: MEDIA_KW@62..68 "media" [] [Whitespace(" ")] + 1: CSS_MEDIA_QUERY_LIST@68..74 + 0: CSS_GRIT_METAVARIABLE@68..74 + 0: GRIT_METAVARIABLE@68..74 "μbaz" [] [Whitespace(" ")] + 2: CSS_DECLARATION_OR_RULE_BLOCK@74..76 + 0: L_CURLY@74..75 "{" [] [] + 1: CSS_DECLARATION_OR_RULE_LIST@75..75 + 2: R_CURLY@75..76 "}" [] [] + 2: R_CURLY@76..78 "}" [Newline("\n")] [] + 3: CSS_QUALIFIED_RULE@78..88 + 0: CSS_SELECTOR_LIST@78..86 + 0: CSS_GRIT_METAVARIABLE@78..86 + 0: GRIT_METAVARIABLE@78..86 "μqux" [Newline("\n"), Newline("\n")] [Whitespace(" ")] + 1: CSS_DECLARATION_OR_RULE_BLOCK@86..88 + 0: L_CURLY@86..87 "{" [] [] + 1: CSS_DECLARATION_OR_RULE_LIST@87..87 + 2: R_CURLY@87..88 "}" [] [] + 2: EOF@88..88 "" [] [] ``` diff --git a/crates/biome_css_parser/tests/spec_test.rs b/crates/biome_css_parser/tests/spec_test.rs index c0327ea865d8..8cd38df21492 100644 --- a/crates/biome_css_parser/tests/spec_test.rs +++ b/crates/biome_css_parser/tests/spec_test.rs @@ -174,7 +174,7 @@ pub fn run(test_case: &str, _snapshot_name: &str, test_directory: &str, outcome_ #[test] pub fn quick_test() { let code = r#" -$foo {} +μfoo {} "#; let root = parse_css( From dcc180a061173e22838eba2f5b02c7346fbbdd00 Mon Sep 17 00:00:00 2001 From: Zheyu Zhang Date: Mon, 8 Jul 2024 01:34:51 +0800 Subject: [PATCH 13/17] =?UTF-8?q?support=20=CE=BC...?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/biome_css_parser/src/lexer/mod.rs | 31 +++++++++++++------ .../block/declaration_or_rule_list_block.rs | 7 ++--- .../src/syntax/selector/mod.rs | 2 +- .../ok/grit_metavariable/metavar.css | 4 ++- .../ok/grit_metavariable/metavar.css.snap | 30 +++++++++++++++--- crates/biome_css_parser/tests/spec_test.rs | 2 +- 6 files changed, 54 insertions(+), 22 deletions(-) diff --git a/crates/biome_css_parser/src/lexer/mod.rs b/crates/biome_css_parser/src/lexer/mod.rs index 10549bc49dd8..1a734c965117 100644 --- a/crates/biome_css_parser/src/lexer/mod.rs +++ b/crates/biome_css_parser/src/lexer/mod.rs @@ -58,7 +58,7 @@ pub enum CssReLexContext { Regular, /// See [CssLexContext::UnicodeRange] UnicodeRange, - /// Re-lexes an identifier as a Grit metavariable if it exactly matches the RegExp `μ[a-zA-Z_][a-zA-Z0-9_]*`. + /// Re-lexes an identifier as a Grit metavariable if it exactly matches `μ...` or the RegExp `μ[a-zA-Z_][a-zA-Z0-9_]*`. GritMetavariable, } @@ -1323,19 +1323,30 @@ impl<'src> CssLexer<'src> { let current_char = self.current_char_unchecked(); if current_char == 'μ' { self.advance(current_char.len_utf8()); - if matches!(self.current_byte(), Some(b'a'..=b'z' | b'A'..=b'Z' | b'_')) { - self.advance(1); - while let Some(chr) = self.current_byte() { - match chr { - b'a'..=b'z' | b'A'..=b'Z' | b'0'..=b'9' | b'_' => { - self.advance(1); + match self.current_byte() { + // μ[a-zA-Z_][a-zA-Z0-9_]* + Some(b'a'..=b'z' | b'A'..=b'Z' | b'_') => { + self.advance(1); + while let Some(chr) = self.current_byte() { + match chr { + b'a'..=b'z' | b'A'..=b'Z' | b'0'..=b'9' | b'_' => { + self.advance(1); + } + _ => break, } - _ => break, + } + if current_end == self.position { + return GRIT_METAVARIABLE; } } - if current_end == self.position { - return GRIT_METAVARIABLE; + // μ... + Some(b'.') => { + if self.byte_at(1) == Some(b'.') && self.byte_at(2) == Some(b'.') { + self.advance(3); + return GRIT_METAVARIABLE; + } } + _ => {} } } } diff --git a/crates/biome_css_parser/src/syntax/block/declaration_or_rule_list_block.rs b/crates/biome_css_parser/src/syntax/block/declaration_or_rule_list_block.rs index 7a46256e5e15..cbb3046beb66 100644 --- a/crates/biome_css_parser/src/syntax/block/declaration_or_rule_list_block.rs +++ b/crates/biome_css_parser/src/syntax/block/declaration_or_rule_list_block.rs @@ -4,7 +4,7 @@ use crate::syntax::at_rule::{is_at_at_rule, parse_at_rule}; use crate::syntax::block::ParseBlockBody; use crate::syntax::parse_error::expected_any_declaration_or_at_rule; use crate::syntax::{ - is_at_declaration, is_at_grit_metavariable, is_at_identifier, is_at_nested_qualified_rule, + is_at_declaration, is_at_grit_metavariable, is_at_nested_qualified_rule, parse_declaration_with_semicolon, parse_grit_metavariable, parse_nested_qualified_rule, try_parse, }; @@ -37,10 +37,7 @@ impl ParseBlockBody for DeclarationOrRuleListBlock { #[inline] fn is_at_declaration_or_rule_item(p: &mut CssParser) -> bool { - is_at_at_rule(p) - || is_at_nested_qualified_rule(p) - || is_at_declaration(p) - || is_at_identifier(p) + is_at_at_rule(p) || is_at_nested_qualified_rule(p) || is_at_declaration(p) } struct DeclarationOrRuleListParseRecovery; diff --git a/crates/biome_css_parser/src/syntax/selector/mod.rs b/crates/biome_css_parser/src/syntax/selector/mod.rs index 385fe578572e..8166a633c7b9 100644 --- a/crates/biome_css_parser/src/syntax/selector/mod.rs +++ b/crates/biome_css_parser/src/syntax/selector/mod.rs @@ -199,7 +199,7 @@ impl ParseRecovery for SelectorListParseRecovery { /// the elements to which a set of CSS rules apply. #[inline] pub(crate) fn is_nth_at_selector(p: &mut CssParser, n: usize) -> bool { - is_nth_at_compound_selector(p, n) || is_nth_at_identifier(p, n) + is_nth_at_compound_selector(p, n) } /// Parses a CSS selector. diff --git a/crates/biome_css_parser/tests/css_test_suite/ok/grit_metavariable/metavar.css b/crates/biome_css_parser/tests/css_test_suite/ok/grit_metavariable/metavar.css index 54100f7e7f9a..49cf369ab780 100644 --- a/crates/biome_css_parser/tests/css_test_suite/ok/grit_metavariable/metavar.css +++ b/crates/biome_css_parser/tests/css_test_suite/ok/grit_metavariable/metavar.css @@ -10,4 +10,6 @@ @media μbaz {} } -μqux {} \ No newline at end of file +μqux {} + +μ... {} \ No newline at end of file diff --git a/crates/biome_css_parser/tests/css_test_suite/ok/grit_metavariable/metavar.css.snap b/crates/biome_css_parser/tests/css_test_suite/ok/grit_metavariable/metavar.css.snap index 12d301fc5fae..eb1b609d8898 100644 --- a/crates/biome_css_parser/tests/css_test_suite/ok/grit_metavariable/metavar.css.snap +++ b/crates/biome_css_parser/tests/css_test_suite/ok/grit_metavariable/metavar.css.snap @@ -18,6 +18,8 @@ expression: snapshot } μqux {} + +μ... {} ``` @@ -141,17 +143,29 @@ CssRoot { r_curly_token: R_CURLY@87..88 "}" [] [], }, }, + CssQualifiedRule { + prelude: CssSelectorList [ + CssGritMetavariable { + value_token: GRIT_METAVARIABLE@88..96 "μ..." [Newline("\n"), Newline("\n")] [Whitespace(" ")], + }, + ], + block: CssDeclarationOrRuleBlock { + l_curly_token: L_CURLY@96..97 "{" [] [], + items: CssDeclarationOrRuleList [], + r_curly_token: R_CURLY@97..98 "}" [] [], + }, + }, ], - eof_token: EOF@88..88 "" [] [], + eof_token: EOF@98..98 "" [] [], } ``` ## CST ``` -0: CSS_ROOT@0..88 +0: CSS_ROOT@0..98 0: (empty) - 1: CSS_RULE_LIST@0..88 + 1: CSS_RULE_LIST@0..98 0: CSS_QUALIFIED_RULE@0..28 0: CSS_SELECTOR_LIST@0..5 0: CSS_COMPOUND_SELECTOR@0..5 @@ -226,6 +240,14 @@ CssRoot { 0: L_CURLY@86..87 "{" [] [] 1: CSS_DECLARATION_OR_RULE_LIST@87..87 2: R_CURLY@87..88 "}" [] [] - 2: EOF@88..88 "" [] [] + 4: CSS_QUALIFIED_RULE@88..98 + 0: CSS_SELECTOR_LIST@88..96 + 0: CSS_GRIT_METAVARIABLE@88..96 + 0: GRIT_METAVARIABLE@88..96 "μ..." [Newline("\n"), Newline("\n")] [Whitespace(" ")] + 1: CSS_DECLARATION_OR_RULE_BLOCK@96..98 + 0: L_CURLY@96..97 "{" [] [] + 1: CSS_DECLARATION_OR_RULE_LIST@97..97 + 2: R_CURLY@97..98 "}" [] [] + 2: EOF@98..98 "" [] [] ``` diff --git a/crates/biome_css_parser/tests/spec_test.rs b/crates/biome_css_parser/tests/spec_test.rs index 8cd38df21492..10f9413b4ae8 100644 --- a/crates/biome_css_parser/tests/spec_test.rs +++ b/crates/biome_css_parser/tests/spec_test.rs @@ -174,7 +174,7 @@ pub fn run(test_case: &str, _snapshot_name: &str, test_directory: &str, outcome_ #[test] pub fn quick_test() { let code = r#" -μfoo {} +μ... {} "#; let root = parse_css( From 0ca8bd941a52e778a9b44e5140f7d852cfebaf8b Mon Sep 17 00:00:00 2001 From: Zheyu Zhang Date: Mon, 8 Jul 2024 14:38:14 +0800 Subject: [PATCH 14/17] move logic to the lexer --- crates/biome_css_parser/src/lexer/mod.rs | 80 ++++++++++++------- .../src/syntax/at_rule/media.rs | 4 - .../block/declaration_or_rule_list_block.rs | 9 +-- crates/biome_css_parser/src/syntax/mod.rs | 11 ++- .../src/syntax/selector/mod.rs | 13 +-- 5 files changed, 64 insertions(+), 53 deletions(-) diff --git a/crates/biome_css_parser/src/lexer/mod.rs b/crates/biome_css_parser/src/lexer/mod.rs index 1a734c965117..613ba6ba270b 100644 --- a/crates/biome_css_parser/src/lexer/mod.rs +++ b/crates/biome_css_parser/src/lexer/mod.rs @@ -58,8 +58,6 @@ pub enum CssReLexContext { Regular, /// See [CssLexContext::UnicodeRange] UnicodeRange, - /// Re-lexes an identifier as a Grit metavariable if it exactly matches `μ...` or the RegExp `μ[a-zA-Z_][a-zA-Z0-9_]*`. - GritMetavariable, } /// An extremely fast, lookup table based, lossless CSS lexer @@ -325,6 +323,11 @@ impl<'src> CssLexer<'src> { self.advance(1); self.consume_byte(T!["$="]) } + UNI if self.options.is_grit_metavariable_enabled() + && self.is_grit_metavariable_start() => + { + self.consume_grit_metavariable() + } IDT | UNI | BSL if self.is_ident_start() => self.consume_identifier(), MUL => self.consume_mul(), @@ -1318,40 +1321,56 @@ impl<'src> CssLexer<'src> { } } - fn re_lex_grit_metavariable(&mut self, current_end: usize) -> CssSyntaxKind { - if self.current_kind == T![ident] { - let current_char = self.current_char_unchecked(); - if current_char == 'μ' { - self.advance(current_char.len_utf8()); - match self.current_byte() { - // μ[a-zA-Z_][a-zA-Z0-9_]* - Some(b'a'..=b'z' | b'A'..=b'Z' | b'_') => { + /// Check if the lexer starts a grit metavariable + fn is_grit_metavariable_start(&mut self) -> bool { + let current_char = self.current_char_unchecked(); + if current_char == 'μ' { + let current_char_lenth = current_char.len_utf8(); + // μ[a-zA-Z_][a-zA-Z0-9_]* + if matches!( + self.byte_at(current_char_lenth), + Some(b'a'..=b'z' | b'A'..=b'Z' | b'_') + ) { + return true; + } + + // μ... + if self.byte_at(current_char_lenth) == Some(b'.') + && self.byte_at(current_char_lenth + 1) == Some(b'.') + && self.byte_at(current_char_lenth + 2) == Some(b'.') + { + return true; + } + } + false + } + + /// Consume a grit metavariable(μ[a-zA-Z_][a-zA-Z0-9_]*|μ...) + /// https://github.com/getgrit/gritql/blob/8f3f077d078ccaf0618510bba904a06309c2435e/resources/language-metavariables/tree-sitter-css/grammar.js#L388 + fn consume_grit_metavariable(&mut self) -> CssSyntaxKind { + debug_assert!(self.is_grit_metavariable_start()); + + // SAFETY: We know the current character is μ. + let current_char = self.current_char_unchecked(); + self.advance(current_char.len_utf8()); + + if self.current_byte() == Some(b'.') { + // SAFETY: We know that the current token is μ... + self.advance(3); + } else { + // μ[a-zA-Z_][a-zA-Z0-9_]* + self.advance(1); + while let Some(chr) = self.current_byte() { + match chr { + b'a'..=b'z' | b'A'..=b'Z' | b'0'..=b'9' | b'_' => { self.advance(1); - while let Some(chr) = self.current_byte() { - match chr { - b'a'..=b'z' | b'A'..=b'Z' | b'0'..=b'9' | b'_' => { - self.advance(1); - } - _ => break, - } - } - if current_end == self.position { - return GRIT_METAVARIABLE; - } } - // μ... - Some(b'.') => { - if self.byte_at(1) == Some(b'.') && self.byte_at(2) == Some(b'.') { - self.advance(3); - return GRIT_METAVARIABLE; - } - } - _ => {} + _ => break, } } } - self.current_kind + GRIT_METAVARIABLE } } @@ -1364,7 +1383,6 @@ impl<'src> ReLexer<'src> for CssLexer<'src> { Some(current) => match context { CssReLexContext::Regular => self.consume_token(current), CssReLexContext::UnicodeRange => self.consume_unicode_range_token(current), - CssReLexContext::GritMetavariable => self.re_lex_grit_metavariable(old_position), }, None => EOF, }; diff --git a/crates/biome_css_parser/src/syntax/at_rule/media.rs b/crates/biome_css_parser/src/syntax/at_rule/media.rs index a54b39791165..590d2091a2f0 100644 --- a/crates/biome_css_parser/src/syntax/at_rule/media.rs +++ b/crates/biome_css_parser/src/syntax/at_rule/media.rs @@ -1,5 +1,4 @@ use super::parse_error::expected_media_query; -use crate::lexer::CssReLexContext; use crate::parser::CssParser; use crate::syntax::at_rule::feature::parse_any_query_feature; use crate::syntax::block::parse_conditional_block; @@ -79,9 +78,6 @@ impl ParseSeparatedList for MediaQueryList { #[inline] fn parse_any_media_query(p: &mut CssParser) -> ParsedSyntax { - if p.options().is_grit_metavariable_enabled() { - p.re_lex(CssReLexContext::GritMetavariable); - } if is_at_media_type_query(p) { parse_any_media_type_query(p) } else if is_at_grit_metavariable(p) { diff --git a/crates/biome_css_parser/src/syntax/block/declaration_or_rule_list_block.rs b/crates/biome_css_parser/src/syntax/block/declaration_or_rule_list_block.rs index cbb3046beb66..f17a61066f3a 100644 --- a/crates/biome_css_parser/src/syntax/block/declaration_or_rule_list_block.rs +++ b/crates/biome_css_parser/src/syntax/block/declaration_or_rule_list_block.rs @@ -1,4 +1,3 @@ -use crate::lexer::CssReLexContext; use crate::parser::CssParser; use crate::syntax::at_rule::{is_at_at_rule, parse_at_rule}; use crate::syntax::block::ParseBlockBody; @@ -37,7 +36,10 @@ impl ParseBlockBody for DeclarationOrRuleListBlock { #[inline] fn is_at_declaration_or_rule_item(p: &mut CssParser) -> bool { - is_at_at_rule(p) || is_at_nested_qualified_rule(p) || is_at_declaration(p) + is_at_at_rule(p) + || is_at_nested_qualified_rule(p) + || is_at_declaration(p) + || is_at_grit_metavariable(p) } struct DeclarationOrRuleListParseRecovery; @@ -58,9 +60,6 @@ impl ParseNodeList for DeclarationOrRuleList { const LIST_KIND: Self::Kind = CSS_DECLARATION_OR_RULE_LIST; fn parse_element(&mut self, p: &mut Self::Parser<'_>) -> ParsedSyntax { - if p.options().is_grit_metavariable_enabled() { - p.re_lex(CssReLexContext::GritMetavariable); - } if is_at_at_rule(p) { parse_at_rule(p) } else if is_at_declaration(p) { diff --git a/crates/biome_css_parser/src/syntax/mod.rs b/crates/biome_css_parser/src/syntax/mod.rs index c907ca7463b0..0776b36f1f6b 100644 --- a/crates/biome_css_parser/src/syntax/mod.rs +++ b/crates/biome_css_parser/src/syntax/mod.rs @@ -6,7 +6,7 @@ mod property; mod selector; mod value; -use crate::lexer::{CssLexContext, CssReLexContext}; +use crate::lexer::CssLexContext; use crate::parser::CssParser; use crate::syntax::at_rule::{is_at_at_rule, parse_at_rule}; use crate::syntax::block::parse_declaration_or_rule_list_block; @@ -253,6 +253,11 @@ fn is_at_grit_metavariable(p: &mut CssParser) -> bool { p.at(GRIT_METAVARIABLE) } +#[inline] +fn is_nth_at_grit_metavariable(p: &mut CssParser, n: usize) -> bool { + p.nth_at(n, GRIT_METAVARIABLE) +} + #[inline] fn parse_grit_metavariable(p: &mut CssParser) -> ParsedSyntax { if !is_at_grit_metavariable(p) { @@ -274,13 +279,11 @@ pub(crate) fn is_at_any_value(p: &mut CssParser) -> bool { || is_at_ratio(p) || is_at_color(p) || is_at_bracketed_value(p) + || is_at_grit_metavariable(p) } #[inline] pub(crate) fn parse_any_value(p: &mut CssParser) -> ParsedSyntax { - if p.options().is_grit_metavariable_enabled() { - p.re_lex(CssReLexContext::GritMetavariable); - } if is_at_any_function(p) { parse_any_function(p) } else if is_at_dashed_identifier(p) { diff --git a/crates/biome_css_parser/src/syntax/selector/mod.rs b/crates/biome_css_parser/src/syntax/selector/mod.rs index 8166a633c7b9..9a161d4a5adb 100644 --- a/crates/biome_css_parser/src/syntax/selector/mod.rs +++ b/crates/biome_css_parser/src/syntax/selector/mod.rs @@ -4,7 +4,7 @@ mod pseudo_class; mod pseudo_element; pub(crate) mod relative_selector; -use crate::lexer::{CssLexContext, CssReLexContext}; +use crate::lexer::CssLexContext; use crate::parser::CssParser; use crate::syntax::parse_error::{ expected_any_sub_selector, expected_compound_selector, expected_identifier, expected_selector, @@ -28,7 +28,7 @@ use biome_parser::prelude::ParsedSyntax; use biome_parser::prelude::ParsedSyntax::{Absent, Present}; use biome_parser::{token_set, CompletedMarker, Parser, ParserProgress, TokenSet}; -use super::{is_at_grit_metavariable, parse_grit_metavariable}; +use super::{is_nth_at_grit_metavariable, parse_grit_metavariable}; /// Determines the lexical context for parsing CSS selectors. /// @@ -199,7 +199,7 @@ impl ParseRecovery for SelectorListParseRecovery { /// the elements to which a set of CSS rules apply. #[inline] pub(crate) fn is_nth_at_selector(p: &mut CssParser, n: usize) -> bool { - is_nth_at_compound_selector(p, n) + is_nth_at_compound_selector(p, n) || is_nth_at_grit_metavariable(p, n) } /// Parses a CSS selector. @@ -215,12 +215,7 @@ pub(crate) fn parse_selector(p: &mut CssParser) -> ParsedSyntax { if !is_nth_at_selector(p, 0) { return Absent; } - - if p.options().is_grit_metavariable_enabled() { - p.re_lex(CssReLexContext::GritMetavariable); - } - - if is_at_grit_metavariable(p) { + if is_nth_at_grit_metavariable(p, 0) { parse_grit_metavariable(p) } else { // In CSS, we have compound selectors and complex selectors. From 0dc8f1a92ff157e0373bf5e168180e27ff928171 Mon Sep 17 00:00:00 2001 From: Zheyu Zhang Date: Mon, 8 Jul 2024 16:23:35 +0800 Subject: [PATCH 15/17] update snapshot --- .../ok/grit_metavariable/metavar.css.snap | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/biome_css_parser/tests/css_test_suite/ok/grit_metavariable/metavar.css.snap b/crates/biome_css_parser/tests/css_test_suite/ok/grit_metavariable/metavar.css.snap index eb1b609d8898..fb430265fee9 100644 --- a/crates/biome_css_parser/tests/css_test_suite/ok/grit_metavariable/metavar.css.snap +++ b/crates/biome_css_parser/tests/css_test_suite/ok/grit_metavariable/metavar.css.snap @@ -32,7 +32,7 @@ CssRoot { CssQualifiedRule { prelude: CssSelectorList [ CssCompoundSelector { - nesting_selector_token: missing (optional), + nesting_selectors: CssNestedSelectorList [], simple_selector: missing (optional), sub_selectors: CssSubSelectorList [ CssClassSelector { @@ -71,7 +71,7 @@ CssRoot { CssQualifiedRule { prelude: CssSelectorList [ CssCompoundSelector { - nesting_selector_token: missing (optional), + nesting_selectors: CssNestedSelectorList [], simple_selector: missing (optional), sub_selectors: CssSubSelectorList [ CssClassSelector { @@ -96,7 +96,7 @@ CssRoot { CssQualifiedRule { prelude: CssSelectorList [ CssCompoundSelector { - nesting_selector_token: missing (optional), + nesting_selectors: CssNestedSelectorList [], simple_selector: missing (optional), sub_selectors: CssSubSelectorList [ CssClassSelector { @@ -169,7 +169,7 @@ CssRoot { 0: CSS_QUALIFIED_RULE@0..28 0: CSS_SELECTOR_LIST@0..5 0: CSS_COMPOUND_SELECTOR@0..5 - 0: (empty) + 0: CSS_NESTED_SELECTOR_LIST@0..0 1: (empty) 2: CSS_SUB_SELECTOR_LIST@0..5 0: CSS_CLASS_SELECTOR@0..5 @@ -194,7 +194,7 @@ CssRoot { 1: CSS_QUALIFIED_RULE@28..48 0: CSS_SELECTOR_LIST@28..35 0: CSS_COMPOUND_SELECTOR@28..35 - 0: (empty) + 0: CSS_NESTED_SELECTOR_LIST@28..28 1: (empty) 2: CSS_SUB_SELECTOR_LIST@28..35 0: CSS_CLASS_SELECTOR@28..35 @@ -210,7 +210,7 @@ CssRoot { 2: CSS_QUALIFIED_RULE@48..78 0: CSS_SELECTOR_LIST@48..55 0: CSS_COMPOUND_SELECTOR@48..55 - 0: (empty) + 0: CSS_NESTED_SELECTOR_LIST@48..48 1: (empty) 2: CSS_SUB_SELECTOR_LIST@48..55 0: CSS_CLASS_SELECTOR@48..55 From 9f6adab91a085ecdbbfc77019911dfbf7222c6c1 Mon Sep 17 00:00:00 2001 From: Zheyu Zhang Date: Tue, 9 Jul 2024 15:17:33 +0800 Subject: [PATCH 16/17] typo --- crates/biome_css_parser/src/lexer/mod.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/crates/biome_css_parser/src/lexer/mod.rs b/crates/biome_css_parser/src/lexer/mod.rs index 613ba6ba270b..a047cb450da9 100644 --- a/crates/biome_css_parser/src/lexer/mod.rs +++ b/crates/biome_css_parser/src/lexer/mod.rs @@ -1325,19 +1325,19 @@ impl<'src> CssLexer<'src> { fn is_grit_metavariable_start(&mut self) -> bool { let current_char = self.current_char_unchecked(); if current_char == 'μ' { - let current_char_lenth = current_char.len_utf8(); + let current_char_length = current_char.len_utf8(); // μ[a-zA-Z_][a-zA-Z0-9_]* if matches!( - self.byte_at(current_char_lenth), + self.byte_at(current_char_length), Some(b'a'..=b'z' | b'A'..=b'Z' | b'_') ) { return true; } // μ... - if self.byte_at(current_char_lenth) == Some(b'.') - && self.byte_at(current_char_lenth + 1) == Some(b'.') - && self.byte_at(current_char_lenth + 2) == Some(b'.') + if self.byte_at(current_char_length) == Some(b'.') + && self.byte_at(current_char_length + 1) == Some(b'.') + && self.byte_at(current_char_length + 2) == Some(b'.') { return true; } From 89ee4bb4fcb541d7a3ef848d87ef561b7cc84605 Mon Sep 17 00:00:00 2001 From: Zheyu Zhang Date: Tue, 9 Jul 2024 15:41:42 +0800 Subject: [PATCH 17/17] typo --- crates/biome_css_parser/src/parser.rs | 2 +- crates/biome_css_parser/tests/spec_test.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/biome_css_parser/src/parser.rs b/crates/biome_css_parser/src/parser.rs index 9bb1f5e88f6a..7f5bca1393d6 100644 --- a/crates/biome_css_parser/src/parser.rs +++ b/crates/biome_css_parser/src/parser.rs @@ -49,7 +49,7 @@ impl CssParserOptions { } /// Enables parsing of Grit metavariables. - pub fn allow_grit_metavariable(mut self) -> Self { + pub fn allow_grit_metavariables(mut self) -> Self { self.grit_metavariable = true; self } diff --git a/crates/biome_css_parser/tests/spec_test.rs b/crates/biome_css_parser/tests/spec_test.rs index 10f9413b4ae8..8b88ac43f042 100644 --- a/crates/biome_css_parser/tests/spec_test.rs +++ b/crates/biome_css_parser/tests/spec_test.rs @@ -43,7 +43,7 @@ pub fn run(test_case: &str, _snapshot_name: &str, test_directory: &str, outcome_ let mut options = CssParserOptions::default() // it is an internal option that cannot be configured via options.json // TODO: find a way to make it configurable - .allow_grit_metavariable(); + .allow_grit_metavariables(); let options_path = Path::new(test_directory).join("options.json"); @@ -182,7 +182,7 @@ pub fn quick_test() { CssParserOptions::default() .allow_wrong_line_comments() .allow_css_modules() - .allow_grit_metavariable(), + .allow_grit_metavariables(), ); let syntax = root.syntax(); dbg!(&syntax, root.diagnostics(), root.has_errors());