From 78f72316382ecb9708b1cb7cb079a749ee68f701 Mon Sep 17 00:00:00 2001 From: zoomdong <1344492820@qq.com> Date: Tue, 22 Oct 2024 11:35:58 +0800 Subject: [PATCH] feat(css_parser): recognize multiple semicolons after declaretion --- CHANGELOG.md | 11 + .../src/generated/node_factory.rs | 6 + .../src/generated/syntax_factory.rs | 19 + .../src/css/any/declaration_or_rule.rs | 1 + .../src/css/auxiliary/empty_declaration.rs | 10 + .../src/css/auxiliary/mod.rs | 1 + crates/biome_css_formatter/src/generated.rs | 40 ++ .../css/comments/custom-properties.css.snap | 24 +- .../prettier/css/comments/selectors.css.snap | 20 - .../tests/specs/prettier/css/url/url.css.snap | 20 - .../css/variables/apply-rule.css.snap | 40 -- .../block/declaration_or_rule_list_block.rs | 13 +- crates/biome_css_parser/src/syntax/mod.rs | 16 + .../css_test_suite/ok/declaration_list.css | 9 + .../ok/declaration_list.css.snap | 537 ++++++++++++------ crates/biome_css_parser/tests/spec_test.rs | 4 +- crates/biome_css_syntax/src/generated/kind.rs | 1 + .../biome_css_syntax/src/generated/macros.rs | 4 + .../biome_css_syntax/src/generated/nodes.rs | 106 +++- .../src/generated/nodes_mut.rs | 8 + xtask/codegen/css.ungram | 4 + xtask/codegen/src/css_kinds_src.rs | 1 + 22 files changed, 610 insertions(+), 285 deletions(-) create mode 100644 crates/biome_css_formatter/src/css/auxiliary/empty_declaration.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index ad3b4736bbd7..961e59370ddc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -88,6 +88,17 @@ our [guidelines for writing a good changelog entry](https://github.com/biomejs/b Contributed by @fireairforce +- Fix [#3836](https://github.com/biomejs/biome/issues/3836), css parser allow multiple semicolons after a declaration, the following example will now parsed correctly: + + ```css + .foo { + color: red;; + } + ``` + + Contributed by @fireairforce + + ## v1.9.4 (2024-10-17) ### Analyzer diff --git a/crates/biome_css_factory/src/generated/node_factory.rs b/crates/biome_css_factory/src/generated/node_factory.rs index c40ae44dbdc3..9bdbce70f3e8 100644 --- a/crates/biome_css_factory/src/generated/node_factory.rs +++ b/crates/biome_css_factory/src/generated/node_factory.rs @@ -627,6 +627,12 @@ pub fn css_document_custom_matcher( ], )) } +pub fn css_empty_declaration(semicolon_token: SyntaxToken) -> CssEmptyDeclaration { + CssEmptyDeclaration::unwrap_cast(SyntaxNode::new_detached( + CssSyntaxKind::CSS_EMPTY_DECLARATION, + [Some(SyntaxElement::Token(semicolon_token))], + )) +} pub fn css_font_face_at_rule( font_face_token: SyntaxToken, block: AnyCssDeclarationBlock, diff --git a/crates/biome_css_factory/src/generated/syntax_factory.rs b/crates/biome_css_factory/src/generated/syntax_factory.rs index 89cd7e03c30b..8c27279ccea4 100644 --- a/crates/biome_css_factory/src/generated/syntax_factory.rs +++ b/crates/biome_css_factory/src/generated/syntax_factory.rs @@ -1214,6 +1214,25 @@ impl SyntaxFactory for CssSyntaxFactory { } slots.into_node(CSS_DOCUMENT_CUSTOM_MATCHER, children) } + CSS_EMPTY_DECLARATION => { + 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() == T ! [;] { + slots.mark_present(); + current_element = elements.next(); + } + } + slots.next_slot(); + if current_element.is_some() { + return RawSyntaxNode::new( + CSS_EMPTY_DECLARATION.to_bogus(), + children.into_iter().map(Some), + ); + } + slots.into_node(CSS_EMPTY_DECLARATION, children) + } CSS_FONT_FACE_AT_RULE => { let mut elements = (&children).into_iter(); let mut slots: RawNodeSlots<2usize> = RawNodeSlots::default(); 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 a4619fa981f7..5be7e15e5ae2 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::CssEmptyDeclaration(node) => node.format().fmt(f), AnyCssDeclarationOrRule::CssMetavariable(node) => node.format().fmt(f), } } diff --git a/crates/biome_css_formatter/src/css/auxiliary/empty_declaration.rs b/crates/biome_css_formatter/src/css/auxiliary/empty_declaration.rs new file mode 100644 index 000000000000..6e646172f19d --- /dev/null +++ b/crates/biome_css_formatter/src/css/auxiliary/empty_declaration.rs @@ -0,0 +1,10 @@ +use crate::prelude::*; +use biome_css_syntax::CssEmptyDeclaration; +use biome_rowan::AstNode; +#[derive(Debug, Clone, Default)] +pub(crate) struct FormatCssEmptyDeclaration; +impl FormatNodeRule for FormatCssEmptyDeclaration { + fn fmt_fields(&self, node: &CssEmptyDeclaration, 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 fd73d3561f17..13937fc908d2 100644 --- a/crates/biome_css_formatter/src/css/auxiliary/mod.rs +++ b/crates/biome_css_formatter/src/css/auxiliary/mod.rs @@ -24,6 +24,7 @@ pub(crate) mod declaration_or_at_rule_block; pub(crate) mod declaration_or_rule_block; pub(crate) mod declaration_with_semicolon; pub(crate) mod document_custom_matcher; +pub(crate) mod empty_declaration; pub(crate) mod font_family_name; pub(crate) mod font_feature_values_block; pub(crate) mod font_feature_values_item; diff --git a/crates/biome_css_formatter/src/generated.rs b/crates/biome_css_formatter/src/generated.rs index f18c7d0861b3..76b3680d597b 100644 --- a/crates/biome_css_formatter/src/generated.rs +++ b/crates/biome_css_formatter/src/generated.rs @@ -1415,6 +1415,46 @@ impl IntoFormat for biome_css_syntax::CssDocumentCustomMatcher ) } } +impl FormatRule + for crate::css::auxiliary::empty_declaration::FormatCssEmptyDeclaration +{ + type Context = CssFormatContext; + #[inline(always)] + fn fmt( + &self, + node: &biome_css_syntax::CssEmptyDeclaration, + f: &mut CssFormatter, + ) -> FormatResult<()> { + FormatNodeRule::::fmt(self, node, f) + } +} +impl AsFormat for biome_css_syntax::CssEmptyDeclaration { + type Format<'a> = FormatRefWithRule< + 'a, + biome_css_syntax::CssEmptyDeclaration, + crate::css::auxiliary::empty_declaration::FormatCssEmptyDeclaration, + >; + fn format(&self) -> Self::Format<'_> { + #![allow(clippy::default_constructed_unit_structs)] + FormatRefWithRule::new( + self, + crate::css::auxiliary::empty_declaration::FormatCssEmptyDeclaration::default(), + ) + } +} +impl IntoFormat for biome_css_syntax::CssEmptyDeclaration { + type Format = FormatOwnedWithRule< + biome_css_syntax::CssEmptyDeclaration, + crate::css::auxiliary::empty_declaration::FormatCssEmptyDeclaration, + >; + fn into_format(self) -> Self::Format { + #![allow(clippy::default_constructed_unit_structs)] + FormatOwnedWithRule::new( + self, + crate::css::auxiliary::empty_declaration::FormatCssEmptyDeclaration::default(), + ) + } +} impl FormatRule for crate::css::statements::font_face_at_rule::FormatCssFontFaceAtRule { diff --git a/crates/biome_css_formatter/tests/specs/prettier/css/comments/custom-properties.css.snap b/crates/biome_css_formatter/tests/specs/prettier/css/comments/custom-properties.css.snap index 3a4a4d341ace..f1f7b4248d8d 100644 --- a/crates/biome_css_formatter/tests/specs/prettier/css/comments/custom-properties.css.snap +++ b/crates/biome_css_formatter/tests/specs/prettier/css/comments/custom-properties.css.snap @@ -27,7 +27,7 @@ font-size: 12px; ```diff --- Prettier +++ Biome -@@ -1,13 +1,14 @@ +@@ -1,13 +1,15 @@ /* comment 1 */ :root { /* comment 2 */ @@ -42,6 +42,7 @@ font-size: 12px; - }; + } + ; ++ /* comment 10 */ /* comment 10 */ } /* comment 11 */ @@ -62,6 +63,7 @@ font-size: 12px; } ; /* comment 10 */ + /* comment 10 */ } /* comment 11 */ ``` @@ -127,25 +129,5 @@ custom-properties.css:4:12 parse ━━━━━━━━━━━━━━━ - past - future -custom-properties.css:10:4 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - × Expected a declaration, or an at rule but instead found ';'. - - 8 │ font-size: 12px; - 9 │ /* comment 9 */ - > 10 │ }; - │ ^ - 11 │ /* comment 10 */ - 12 │ } - - i Expected a declaration, or an at rule here. - - 8 │ font-size: 12px; - 9 │ /* comment 9 */ - > 10 │ }; - │ ^ - 11 │ /* comment 10 */ - 12 │ } - ``` diff --git a/crates/biome_css_formatter/tests/specs/prettier/css/comments/selectors.css.snap b/crates/biome_css_formatter/tests/specs/prettier/css/comments/selectors.css.snap index 4fc67ba10979..77d150e5d60a 100644 --- a/crates/biome_css_formatter/tests/specs/prettier/css/comments/selectors.css.snap +++ b/crates/biome_css_formatter/tests/specs/prettier/css/comments/selectors.css.snap @@ -854,26 +854,6 @@ selectors.css:152:75 parse ━━━━━━━━━━━━━━━━━ - past - future -selectors.css:156:6 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - × Expected a declaration, or an at rule but instead found ';'. - - 154 │ align-items: center; - 155 │ justify-content: center; - > 156 │ }; - │ ^ - 157 │ } - 158 │ - - i Expected a declaration, or an at rule here. - - 154 │ align-items: center; - 155 │ justify-content: center; - > 156 │ }; - │ ^ - 157 │ } - 158 │ - ``` diff --git a/crates/biome_css_formatter/tests/specs/prettier/css/url/url.css.snap b/crates/biome_css_formatter/tests/specs/prettier/css/url/url.css.snap index ea933ab459c5..16a387cbfcd7 100644 --- a/crates/biome_css_formatter/tests/specs/prettier/css/url/url.css.snap +++ b/crates/biome_css_formatter/tests/specs/prettier/css/url/url.css.snap @@ -165,26 +165,6 @@ url.css:3:33 parse ━━━━━━━━━━━━━━━━━━━━ - custom property - function -url.css:30:3 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - × Expected a declaration, or an at rule but instead found ';'. - - 28 │ no-repeat url(foo.woff2?foo=rgb\(255,255,0\)) - 29 │ no-repeat url(RobotoFlex-VariableFont_GRAD,XTRA,YOPQ,YTAS,YTDE,YTFI,YTLC,YTUC,opsz,slnt,wdth,wght.ttf); - > 30 │ ; - │ ^ - 31 │ } - 32 │ - - i Expected a declaration, or an at rule here. - - 28 │ no-repeat url(foo.woff2?foo=rgb\(255,255,0\)) - 29 │ no-repeat url(RobotoFlex-VariableFont_GRAD,XTRA,YOPQ,YTAS,YTDE,YTFI,YTLC,YTUC,opsz,slnt,wdth,wght.ttf); - > 30 │ ; - │ ^ - 31 │ } - 32 │ - ``` diff --git a/crates/biome_css_formatter/tests/specs/prettier/css/variables/apply-rule.css.snap b/crates/biome_css_formatter/tests/specs/prettier/css/variables/apply-rule.css.snap index 710ae4f8d5b2..6d554b9b363e 100644 --- a/crates/biome_css_formatter/tests/specs/prettier/css/variables/apply-rule.css.snap +++ b/crates/biome_css_formatter/tests/specs/prettier/css/variables/apply-rule.css.snap @@ -268,26 +268,6 @@ apply-rule.css:9:26 parse ━━━━━━━━━━━━━━━━━━ - past - future -apply-rule.css:11:4 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - × Expected a declaration, or an at rule but instead found ';'. - - 9 │ --toolbar-title-theme: { - 10 │ color: green; - > 11 │ }; - │ ^ - 12 │ } - 13 │ - - i Expected a declaration, or an at rule here. - - 9 │ --toolbar-title-theme: { - 10 │ color: green; - > 11 │ }; - │ ^ - 12 │ } - 13 │ - apply-rule.css:15:19 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ × Unexpected value or character. @@ -521,25 +501,5 @@ apply-rule.css:27:36 parse ━━━━━━━━━━━━━━━━━ - past - future -apply-rule.css:29:4 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - × Expected a declaration, or an at rule but instead found ';'. - - 27 │ --another-one-like-a-apply-rule: { - 28 │ color:red; - > 29 │ }; - │ ^ - 30 │ } - 31 │ - - i Expected a declaration, or an at rule here. - - 27 │ --another-one-like-a-apply-rule: { - 28 │ color:red; - > 29 │ }; - │ ^ - 30 │ } - 31 │ - ``` 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 7daf282d74f6..d17e63bf9404 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 @@ -3,15 +3,16 @@ 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_metavariable, is_at_nested_qualified_rule, - parse_declaration_with_semicolon, parse_metavariable, parse_nested_qualified_rule, try_parse, + is_at_declaration, is_at_declaration_semion, is_at_metavariable, is_at_nested_qualified_rule, + parse_declaration_with_semicolon, parse_empty_declaration, parse_metavariable, + parse_nested_qualified_rule, try_parse, }; use biome_css_syntax::CssSyntaxKind::*; use biome_css_syntax::{CssSyntaxKind, T}; use biome_parser::parse_lists::ParseNodeList; use biome_parser::parse_recovery::{ParseRecovery, RecoveryResult}; -use biome_parser::parsed_syntax::ParsedSyntax; -use biome_parser::parsed_syntax::ParsedSyntax::Absent; +use biome_parser::prelude::ParsedSyntax; +use biome_parser::prelude::ParsedSyntax::Absent; use biome_parser::{CompletedMarker, Parser}; #[inline] @@ -89,8 +90,6 @@ impl ParseNodeList for DeclarationOrRuleList { if matches!(p.last(), Some(T![;])) || p.at(T!['}']) { Ok(declaration) } else { - // If neither condition is met, return an error to indicate parsing failure. - // And rewind the parser to the start of the declaration. Err(()) } }); @@ -129,6 +128,8 @@ impl ParseNodeList for DeclarationOrRuleList { parse_nested_qualified_rule(p) } else if is_at_metavariable(p) { parse_metavariable(p) + } else if is_at_declaration_semion(p) { + parse_empty_declaration(p) } else { Absent } diff --git a/crates/biome_css_parser/src/syntax/mod.rs b/crates/biome_css_parser/src/syntax/mod.rs index 578b420be045..0f00ad8273c1 100644 --- a/crates/biome_css_parser/src/syntax/mod.rs +++ b/crates/biome_css_parser/src/syntax/mod.rs @@ -232,11 +232,27 @@ pub(crate) fn parse_declaration_with_semicolon(p: &mut CssParser) -> ParsedSynta Present(m.complete(p, CSS_DECLARATION_WITH_SEMICOLON)) } +#[inline] +pub(crate) fn parse_empty_declaration(p: &mut CssParser) -> ParsedSyntax { + if p.at(T![;]) { + let m = p.start(); + p.bump_any(); // bump ; + m.complete(p, CSS_EMPTY_DECLARATION).into() + } else { + Absent + } +} + #[inline] fn is_at_declaration_important(p: &mut CssParser) -> bool { p.at(T![!]) && p.nth_at(1, T![important]) } +#[inline] +pub(crate) fn is_at_declaration_semion(p: &mut CssParser) -> bool { + p.at(T![;]) && (p.nth_at(1, T![;]) || p.nth_at(1, T!['}'])) +} + #[inline] fn parse_declaration_important(p: &mut CssParser) -> ParsedSyntax { if !is_at_declaration_important(p) { diff --git a/crates/biome_css_parser/tests/css_test_suite/ok/declaration_list.css b/crates/biome_css_parser/tests/css_test_suite/ok/declaration_list.css index 15c63e768934..de2725a523a9 100644 --- a/crates/biome_css_parser/tests/css_test_suite/ok/declaration_list.css +++ b/crates/biome_css_parser/tests/css_test_suite/ok/declaration_list.css @@ -31,6 +31,15 @@ a { prop1: --custom; } +a { + prop1: 1px; + prop2: 2px;;; +} + +a { + prop1: 1px;;; +} + div { flex: 1 1 auto !important; } diff --git a/crates/biome_css_parser/tests/css_test_suite/ok/declaration_list.css.snap b/crates/biome_css_parser/tests/css_test_suite/ok/declaration_list.css.snap index a80cf58cd73f..338baed19722 100644 --- a/crates/biome_css_parser/tests/css_test_suite/ok/declaration_list.css.snap +++ b/crates/biome_css_parser/tests/css_test_suite/ok/declaration_list.css.snap @@ -38,6 +38,15 @@ a { prop1: --custom; } +a { + prop1: 1px; + prop2: 2px;;; +} + +a { + prop1: 1px;;; +} + div { flex: 1 1 auto !important; } @@ -461,43 +470,149 @@ CssRoot { simple_selector: CssTypeSelector { namespace: missing (optional), ident: CssIdentifier { - value_token: IDENT@216..222 "div" [Newline("\n"), Newline("\n")] [Whitespace(" ")], + value_token: IDENT@216..220 "a" [Newline("\n"), Newline("\n")] [Whitespace(" ")], }, }, sub_selectors: CssSubSelectorList [], }, ], block: CssDeclarationOrRuleBlock { - l_curly_token: L_CURLY@222..223 "{" [] [], + l_curly_token: L_CURLY@220..221 "{" [] [], items: CssDeclarationOrRuleList [ CssDeclarationWithSemicolon { declaration: CssDeclaration { property: CssGenericProperty { name: CssIdentifier { - value_token: IDENT@223..229 "flex" [Newline("\n"), Whitespace("\t")] [], + value_token: IDENT@221..231 "prop1" [Newline("\n"), Whitespace(" ")] [], }, - colon_token: COLON@229..231 ":" [] [Whitespace(" ")], + colon_token: COLON@231..233 ":" [] [Whitespace(" ")], + value: CssGenericComponentValueList [ + CssRegularDimension { + value_token: CSS_NUMBER_LITERAL@233..234 "1" [] [], + unit_token: IDENT@234..236 "px" [] [], + }, + ], + }, + important: missing (optional), + }, + semicolon_token: SEMICOLON@236..237 ";" [] [], + }, + CssDeclarationWithSemicolon { + declaration: CssDeclaration { + property: CssGenericProperty { + name: CssIdentifier { + value_token: IDENT@237..247 "prop2" [Newline("\n"), Whitespace(" ")] [], + }, + colon_token: COLON@247..249 ":" [] [Whitespace(" ")], + value: CssGenericComponentValueList [ + CssRegularDimension { + value_token: CSS_NUMBER_LITERAL@249..250 "2" [] [], + unit_token: IDENT@250..252 "px" [] [], + }, + ], + }, + important: missing (optional), + }, + semicolon_token: SEMICOLON@252..253 ";" [] [], + }, + CssEmptyDeclaration { + semicolon_token: SEMICOLON@253..254 ";" [] [], + }, + CssEmptyDeclaration { + semicolon_token: SEMICOLON@254..255 ";" [] [], + }, + ], + r_curly_token: R_CURLY@255..257 "}" [Newline("\n")] [], + }, + }, + CssQualifiedRule { + prelude: CssSelectorList [ + CssCompoundSelector { + nesting_selectors: CssNestedSelectorList [], + simple_selector: CssTypeSelector { + namespace: missing (optional), + ident: CssIdentifier { + value_token: IDENT@257..261 "a" [Newline("\n"), Newline("\n")] [Whitespace(" ")], + }, + }, + sub_selectors: CssSubSelectorList [], + }, + ], + block: CssDeclarationOrRuleBlock { + l_curly_token: L_CURLY@261..262 "{" [] [], + items: CssDeclarationOrRuleList [ + CssDeclarationWithSemicolon { + declaration: CssDeclaration { + property: CssGenericProperty { + name: CssIdentifier { + value_token: IDENT@262..272 "prop1" [Newline("\n"), Whitespace(" ")] [], + }, + colon_token: COLON@272..274 ":" [] [Whitespace(" ")], + value: CssGenericComponentValueList [ + CssRegularDimension { + value_token: CSS_NUMBER_LITERAL@274..275 "1" [] [], + unit_token: IDENT@275..277 "px" [] [], + }, + ], + }, + important: missing (optional), + }, + semicolon_token: SEMICOLON@277..278 ";" [] [], + }, + CssEmptyDeclaration { + semicolon_token: SEMICOLON@278..279 ";" [] [], + }, + CssEmptyDeclaration { + semicolon_token: SEMICOLON@279..280 ";" [] [], + }, + ], + r_curly_token: R_CURLY@280..282 "}" [Newline("\n")] [], + }, + }, + CssQualifiedRule { + prelude: CssSelectorList [ + CssCompoundSelector { + nesting_selectors: CssNestedSelectorList [], + simple_selector: CssTypeSelector { + namespace: missing (optional), + ident: CssIdentifier { + value_token: IDENT@282..288 "div" [Newline("\n"), Newline("\n")] [Whitespace(" ")], + }, + }, + sub_selectors: CssSubSelectorList [], + }, + ], + block: CssDeclarationOrRuleBlock { + l_curly_token: L_CURLY@288..289 "{" [] [], + items: CssDeclarationOrRuleList [ + CssDeclarationWithSemicolon { + declaration: CssDeclaration { + property: CssGenericProperty { + name: CssIdentifier { + value_token: IDENT@289..295 "flex" [Newline("\n"), Whitespace("\t")] [], + }, + colon_token: COLON@295..297 ":" [] [Whitespace(" ")], value: CssGenericComponentValueList [ CssNumber { - value_token: CSS_NUMBER_LITERAL@231..233 "1" [] [Whitespace(" ")], + value_token: CSS_NUMBER_LITERAL@297..299 "1" [] [Whitespace(" ")], }, CssNumber { - value_token: CSS_NUMBER_LITERAL@233..235 "1" [] [Whitespace(" ")], + value_token: CSS_NUMBER_LITERAL@299..301 "1" [] [Whitespace(" ")], }, CssIdentifier { - value_token: IDENT@235..240 "auto" [] [Whitespace(" ")], + value_token: IDENT@301..306 "auto" [] [Whitespace(" ")], }, ], }, important: CssDeclarationImportant { - excl_token: BANG@240..241 "!" [] [], - important_token: IMPORTANT_KW@241..250 "important" [] [], + excl_token: BANG@306..307 "!" [] [], + important_token: IMPORTANT_KW@307..316 "important" [] [], }, }, - semicolon_token: SEMICOLON@250..251 ";" [] [], + semicolon_token: SEMICOLON@316..317 ";" [] [], }, ], - r_curly_token: R_CURLY@251..253 "}" [Newline("\n")] [], + r_curly_token: R_CURLY@317..319 "}" [Newline("\n")] [], }, }, CssQualifiedRule { @@ -507,52 +622,52 @@ CssRoot { simple_selector: CssTypeSelector { namespace: missing (optional), ident: CssIdentifier { - value_token: IDENT@253..259 "div" [Newline("\n"), Newline("\n")] [Whitespace(" ")], + value_token: IDENT@319..325 "div" [Newline("\n"), Newline("\n")] [Whitespace(" ")], }, }, sub_selectors: CssSubSelectorList [], }, ], block: CssDeclarationOrRuleBlock { - l_curly_token: L_CURLY@259..260 "{" [] [], + l_curly_token: L_CURLY@325..326 "{" [] [], items: CssDeclarationOrRuleList [ CssDeclarationWithSemicolon { declaration: CssDeclaration { property: CssGenericProperty { name: CssIdentifier { - value_token: IDENT@260..287 "border-bottom-left-radius" [Newline("\n"), Whitespace("\t")] [], + value_token: IDENT@326..353 "border-bottom-left-radius" [Newline("\n"), Whitespace("\t")] [], }, - colon_token: COLON@287..289 ":" [] [Whitespace(" ")], + colon_token: COLON@353..355 ":" [] [Whitespace(" ")], value: CssGenericComponentValueList [ CssFunction { name: CssIdentifier { - value_token: IDENT@289..292 "var" [] [], + value_token: IDENT@355..358 "var" [] [], }, - l_paren_token: L_PAREN@292..293 "(" [] [], + l_paren_token: L_PAREN@358..359 "(" [] [], items: CssParameterList [ CssParameter { any_css_expression: CssListOfComponentValuesExpression { css_component_value_list: CssComponentValueList [ CssDashedIdentifier { - value_token: IDENT@293..315 "--bs-border-radius-xxl" [] [], + value_token: IDENT@359..381 "--bs-border-radius-xxl" [] [], }, ], }, }, ], - r_paren_token: R_PAREN@315..317 ")" [] [Whitespace(" ")], + r_paren_token: R_PAREN@381..383 ")" [] [Whitespace(" ")], }, ], }, important: CssDeclarationImportant { - excl_token: BANG@317..318 "!" [] [], - important_token: IMPORTANT_KW@318..327 "important" [] [], + excl_token: BANG@383..384 "!" [] [], + important_token: IMPORTANT_KW@384..393 "important" [] [], }, }, - semicolon_token: SEMICOLON@327..328 ";" [] [], + semicolon_token: SEMICOLON@393..394 ";" [] [], }, ], - r_curly_token: R_CURLY@328..330 "}" [Newline("\n")] [], + r_curly_token: R_CURLY@394..396 "}" [Newline("\n")] [], }, }, CssQualifiedRule { @@ -562,110 +677,110 @@ CssRoot { simple_selector: CssTypeSelector { namespace: missing (optional), ident: CssIdentifier { - value_token: IDENT@330..336 "div" [Newline("\n"), Newline("\n")] [Whitespace(" ")], + value_token: IDENT@396..402 "div" [Newline("\n"), Newline("\n")] [Whitespace(" ")], }, }, sub_selectors: CssSubSelectorList [], }, ], block: CssDeclarationOrRuleBlock { - l_curly_token: L_CURLY@336..337 "{" [] [], + l_curly_token: L_CURLY@402..403 "{" [] [], items: CssDeclarationOrRuleList [ CssDeclarationWithSemicolon { declaration: CssDeclaration { property: CssGenericProperty { name: CssDashedIdentifier { - value_token: IDENT@337..364 "--bs-btn-focus-shadow-rgb" [Newline("\n"), Whitespace("\t")] [], + value_token: IDENT@403..430 "--bs-btn-focus-shadow-rgb" [Newline("\n"), Whitespace("\t")] [], }, - colon_token: COLON@364..366 ":" [] [Whitespace(" ")], + colon_token: COLON@430..432 ":" [] [Whitespace(" ")], value: CssGenericComponentValueList [ CssNumber { - value_token: CSS_NUMBER_LITERAL@366..368 "33" [] [], + value_token: CSS_NUMBER_LITERAL@432..434 "33" [] [], }, CssGenericDelimiter { - value: COMMA@368..370 "," [] [Whitespace(" ")], + value: COMMA@434..436 "," [] [Whitespace(" ")], }, CssNumber { - value_token: CSS_NUMBER_LITERAL@370..372 "37" [] [], + value_token: CSS_NUMBER_LITERAL@436..438 "37" [] [], }, CssGenericDelimiter { - value: COMMA@372..374 "," [] [Whitespace(" ")], + value: COMMA@438..440 "," [] [Whitespace(" ")], }, CssNumber { - value_token: CSS_NUMBER_LITERAL@374..376 "41" [] [], + value_token: CSS_NUMBER_LITERAL@440..442 "41" [] [], }, ], }, important: missing (optional), }, - semicolon_token: SEMICOLON@376..377 ";" [] [], + semicolon_token: SEMICOLON@442..443 ";" [] [], }, CssDeclarationWithSemicolon { declaration: CssDeclaration { property: CssGenericProperty { name: CssIdentifier { - value_token: IDENT@377..389 "background" [Newline("\n"), Whitespace("\t")] [], + value_token: IDENT@443..455 "background" [Newline("\n"), Whitespace("\t")] [], }, - colon_token: COLON@389..391 ":" [] [Whitespace(" ")], + colon_token: COLON@455..457 ":" [] [Whitespace(" ")], value: CssGenericComponentValueList [ CssIdentifier { - value_token: IDENT@391..403 "transparent" [] [Whitespace(" ")], + value_token: IDENT@457..469 "transparent" [] [Whitespace(" ")], }, CssFunction { name: CssIdentifier { - value_token: IDENT@403..406 "var" [] [], + value_token: IDENT@469..472 "var" [] [], }, - l_paren_token: L_PAREN@406..407 "(" [] [], + l_paren_token: L_PAREN@472..473 "(" [] [], items: CssParameterList [ CssParameter { any_css_expression: CssListOfComponentValuesExpression { css_component_value_list: CssComponentValueList [ CssDashedIdentifier { - value_token: IDENT@407..424 "--bs-btn-close-bg" [] [], + value_token: IDENT@473..490 "--bs-btn-close-bg" [] [], }, ], }, }, ], - r_paren_token: R_PAREN@424..426 ")" [] [Whitespace(" ")], + r_paren_token: R_PAREN@490..492 ")" [] [Whitespace(" ")], }, CssIdentifier { - value_token: IDENT@426..432 "center" [] [], + value_token: IDENT@492..498 "center" [] [], }, CssGenericDelimiter { - value: SLASH@432..433 "/" [] [], + value: SLASH@498..499 "/" [] [], }, CssRegularDimension { - value_token: CSS_NUMBER_LITERAL@433..434 "1" [] [], - unit_token: IDENT@434..437 "em" [] [Whitespace(" ")], + value_token: CSS_NUMBER_LITERAL@499..500 "1" [] [], + unit_token: IDENT@500..503 "em" [] [Whitespace(" ")], }, CssIdentifier { - value_token: IDENT@437..442 "auto" [] [Whitespace(" ")], + value_token: IDENT@503..508 "auto" [] [Whitespace(" ")], }, CssIdentifier { - value_token: IDENT@442..451 "no-repeat" [] [], + value_token: IDENT@508..517 "no-repeat" [] [], }, ], }, important: missing (optional), }, - semicolon_token: SEMICOLON@451..452 ";" [] [], + semicolon_token: SEMICOLON@517..518 ";" [] [], }, ], - r_curly_token: R_CURLY@452..455 "}" [Newline("\n"), Newline("\n")] [], + r_curly_token: R_CURLY@518..521 "}" [Newline("\n"), Newline("\n")] [], }, }, ], - eof_token: EOF@455..456 "" [Newline("\n")] [], + eof_token: EOF@521..522 "" [Newline("\n")] [], } ``` ## CST ``` -0: CSS_ROOT@0..456 +0: CSS_ROOT@0..522 0: (empty) - 1: CSS_RULE_LIST@0..455 + 1: CSS_RULE_LIST@0..521 0: CSS_QUALIFIED_RULE@0..41 0: CSS_SELECTOR_LIST@0..2 0: CSS_COMPOUND_SELECTOR@0..2 @@ -919,136 +1034,206 @@ CssRoot { 1: (empty) 1: SEMICOLON@213..214 ";" [] [] 2: R_CURLY@214..216 "}" [Newline("\n")] [] - 8: CSS_QUALIFIED_RULE@216..253 - 0: CSS_SELECTOR_LIST@216..222 - 0: CSS_COMPOUND_SELECTOR@216..222 + 8: CSS_QUALIFIED_RULE@216..257 + 0: CSS_SELECTOR_LIST@216..220 + 0: CSS_COMPOUND_SELECTOR@216..220 0: CSS_NESTED_SELECTOR_LIST@216..216 - 1: CSS_TYPE_SELECTOR@216..222 + 1: CSS_TYPE_SELECTOR@216..220 + 0: (empty) + 1: CSS_IDENTIFIER@216..220 + 0: IDENT@216..220 "a" [Newline("\n"), Newline("\n")] [Whitespace(" ")] + 2: CSS_SUB_SELECTOR_LIST@220..220 + 1: CSS_DECLARATION_OR_RULE_BLOCK@220..257 + 0: L_CURLY@220..221 "{" [] [] + 1: CSS_DECLARATION_OR_RULE_LIST@221..255 + 0: CSS_DECLARATION_WITH_SEMICOLON@221..237 + 0: CSS_DECLARATION@221..236 + 0: CSS_GENERIC_PROPERTY@221..236 + 0: CSS_IDENTIFIER@221..231 + 0: IDENT@221..231 "prop1" [Newline("\n"), Whitespace(" ")] [] + 1: COLON@231..233 ":" [] [Whitespace(" ")] + 2: CSS_GENERIC_COMPONENT_VALUE_LIST@233..236 + 0: CSS_REGULAR_DIMENSION@233..236 + 0: CSS_NUMBER_LITERAL@233..234 "1" [] [] + 1: IDENT@234..236 "px" [] [] + 1: (empty) + 1: SEMICOLON@236..237 ";" [] [] + 1: CSS_DECLARATION_WITH_SEMICOLON@237..253 + 0: CSS_DECLARATION@237..252 + 0: CSS_GENERIC_PROPERTY@237..252 + 0: CSS_IDENTIFIER@237..247 + 0: IDENT@237..247 "prop2" [Newline("\n"), Whitespace(" ")] [] + 1: COLON@247..249 ":" [] [Whitespace(" ")] + 2: CSS_GENERIC_COMPONENT_VALUE_LIST@249..252 + 0: CSS_REGULAR_DIMENSION@249..252 + 0: CSS_NUMBER_LITERAL@249..250 "2" [] [] + 1: IDENT@250..252 "px" [] [] + 1: (empty) + 1: SEMICOLON@252..253 ";" [] [] + 2: CSS_EMPTY_DECLARATION@253..254 + 0: SEMICOLON@253..254 ";" [] [] + 3: CSS_EMPTY_DECLARATION@254..255 + 0: SEMICOLON@254..255 ";" [] [] + 2: R_CURLY@255..257 "}" [Newline("\n")] [] + 9: CSS_QUALIFIED_RULE@257..282 + 0: CSS_SELECTOR_LIST@257..261 + 0: CSS_COMPOUND_SELECTOR@257..261 + 0: CSS_NESTED_SELECTOR_LIST@257..257 + 1: CSS_TYPE_SELECTOR@257..261 + 0: (empty) + 1: CSS_IDENTIFIER@257..261 + 0: IDENT@257..261 "a" [Newline("\n"), Newline("\n")] [Whitespace(" ")] + 2: CSS_SUB_SELECTOR_LIST@261..261 + 1: CSS_DECLARATION_OR_RULE_BLOCK@261..282 + 0: L_CURLY@261..262 "{" [] [] + 1: CSS_DECLARATION_OR_RULE_LIST@262..280 + 0: CSS_DECLARATION_WITH_SEMICOLON@262..278 + 0: CSS_DECLARATION@262..277 + 0: CSS_GENERIC_PROPERTY@262..277 + 0: CSS_IDENTIFIER@262..272 + 0: IDENT@262..272 "prop1" [Newline("\n"), Whitespace(" ")] [] + 1: COLON@272..274 ":" [] [Whitespace(" ")] + 2: CSS_GENERIC_COMPONENT_VALUE_LIST@274..277 + 0: CSS_REGULAR_DIMENSION@274..277 + 0: CSS_NUMBER_LITERAL@274..275 "1" [] [] + 1: IDENT@275..277 "px" [] [] + 1: (empty) + 1: SEMICOLON@277..278 ";" [] [] + 1: CSS_EMPTY_DECLARATION@278..279 + 0: SEMICOLON@278..279 ";" [] [] + 2: CSS_EMPTY_DECLARATION@279..280 + 0: SEMICOLON@279..280 ";" [] [] + 2: R_CURLY@280..282 "}" [Newline("\n")] [] + 10: CSS_QUALIFIED_RULE@282..319 + 0: CSS_SELECTOR_LIST@282..288 + 0: CSS_COMPOUND_SELECTOR@282..288 + 0: CSS_NESTED_SELECTOR_LIST@282..282 + 1: CSS_TYPE_SELECTOR@282..288 0: (empty) - 1: CSS_IDENTIFIER@216..222 - 0: IDENT@216..222 "div" [Newline("\n"), Newline("\n")] [Whitespace(" ")] - 2: CSS_SUB_SELECTOR_LIST@222..222 - 1: CSS_DECLARATION_OR_RULE_BLOCK@222..253 - 0: L_CURLY@222..223 "{" [] [] - 1: CSS_DECLARATION_OR_RULE_LIST@223..251 - 0: CSS_DECLARATION_WITH_SEMICOLON@223..251 - 0: CSS_DECLARATION@223..250 - 0: CSS_GENERIC_PROPERTY@223..240 - 0: CSS_IDENTIFIER@223..229 - 0: IDENT@223..229 "flex" [Newline("\n"), Whitespace("\t")] [] - 1: COLON@229..231 ":" [] [Whitespace(" ")] - 2: CSS_GENERIC_COMPONENT_VALUE_LIST@231..240 - 0: CSS_NUMBER@231..233 - 0: CSS_NUMBER_LITERAL@231..233 "1" [] [Whitespace(" ")] - 1: CSS_NUMBER@233..235 - 0: CSS_NUMBER_LITERAL@233..235 "1" [] [Whitespace(" ")] - 2: CSS_IDENTIFIER@235..240 - 0: IDENT@235..240 "auto" [] [Whitespace(" ")] - 1: CSS_DECLARATION_IMPORTANT@240..250 - 0: BANG@240..241 "!" [] [] - 1: IMPORTANT_KW@241..250 "important" [] [] - 1: SEMICOLON@250..251 ";" [] [] - 2: R_CURLY@251..253 "}" [Newline("\n")] [] - 9: CSS_QUALIFIED_RULE@253..330 - 0: CSS_SELECTOR_LIST@253..259 - 0: CSS_COMPOUND_SELECTOR@253..259 - 0: CSS_NESTED_SELECTOR_LIST@253..253 - 1: CSS_TYPE_SELECTOR@253..259 + 1: CSS_IDENTIFIER@282..288 + 0: IDENT@282..288 "div" [Newline("\n"), Newline("\n")] [Whitespace(" ")] + 2: CSS_SUB_SELECTOR_LIST@288..288 + 1: CSS_DECLARATION_OR_RULE_BLOCK@288..319 + 0: L_CURLY@288..289 "{" [] [] + 1: CSS_DECLARATION_OR_RULE_LIST@289..317 + 0: CSS_DECLARATION_WITH_SEMICOLON@289..317 + 0: CSS_DECLARATION@289..316 + 0: CSS_GENERIC_PROPERTY@289..306 + 0: CSS_IDENTIFIER@289..295 + 0: IDENT@289..295 "flex" [Newline("\n"), Whitespace("\t")] [] + 1: COLON@295..297 ":" [] [Whitespace(" ")] + 2: CSS_GENERIC_COMPONENT_VALUE_LIST@297..306 + 0: CSS_NUMBER@297..299 + 0: CSS_NUMBER_LITERAL@297..299 "1" [] [Whitespace(" ")] + 1: CSS_NUMBER@299..301 + 0: CSS_NUMBER_LITERAL@299..301 "1" [] [Whitespace(" ")] + 2: CSS_IDENTIFIER@301..306 + 0: IDENT@301..306 "auto" [] [Whitespace(" ")] + 1: CSS_DECLARATION_IMPORTANT@306..316 + 0: BANG@306..307 "!" [] [] + 1: IMPORTANT_KW@307..316 "important" [] [] + 1: SEMICOLON@316..317 ";" [] [] + 2: R_CURLY@317..319 "}" [Newline("\n")] [] + 11: CSS_QUALIFIED_RULE@319..396 + 0: CSS_SELECTOR_LIST@319..325 + 0: CSS_COMPOUND_SELECTOR@319..325 + 0: CSS_NESTED_SELECTOR_LIST@319..319 + 1: CSS_TYPE_SELECTOR@319..325 0: (empty) - 1: CSS_IDENTIFIER@253..259 - 0: IDENT@253..259 "div" [Newline("\n"), Newline("\n")] [Whitespace(" ")] - 2: CSS_SUB_SELECTOR_LIST@259..259 - 1: CSS_DECLARATION_OR_RULE_BLOCK@259..330 - 0: L_CURLY@259..260 "{" [] [] - 1: CSS_DECLARATION_OR_RULE_LIST@260..328 - 0: CSS_DECLARATION_WITH_SEMICOLON@260..328 - 0: CSS_DECLARATION@260..327 - 0: CSS_GENERIC_PROPERTY@260..317 - 0: CSS_IDENTIFIER@260..287 - 0: IDENT@260..287 "border-bottom-left-radius" [Newline("\n"), Whitespace("\t")] [] - 1: COLON@287..289 ":" [] [Whitespace(" ")] - 2: CSS_GENERIC_COMPONENT_VALUE_LIST@289..317 - 0: CSS_FUNCTION@289..317 - 0: CSS_IDENTIFIER@289..292 - 0: IDENT@289..292 "var" [] [] - 1: L_PAREN@292..293 "(" [] [] - 2: CSS_PARAMETER_LIST@293..315 - 0: CSS_PARAMETER@293..315 - 0: CSS_LIST_OF_COMPONENT_VALUES_EXPRESSION@293..315 - 0: CSS_COMPONENT_VALUE_LIST@293..315 - 0: CSS_DASHED_IDENTIFIER@293..315 - 0: IDENT@293..315 "--bs-border-radius-xxl" [] [] - 3: R_PAREN@315..317 ")" [] [Whitespace(" ")] - 1: CSS_DECLARATION_IMPORTANT@317..327 - 0: BANG@317..318 "!" [] [] - 1: IMPORTANT_KW@318..327 "important" [] [] - 1: SEMICOLON@327..328 ";" [] [] - 2: R_CURLY@328..330 "}" [Newline("\n")] [] - 10: CSS_QUALIFIED_RULE@330..455 - 0: CSS_SELECTOR_LIST@330..336 - 0: CSS_COMPOUND_SELECTOR@330..336 - 0: CSS_NESTED_SELECTOR_LIST@330..330 - 1: CSS_TYPE_SELECTOR@330..336 + 1: CSS_IDENTIFIER@319..325 + 0: IDENT@319..325 "div" [Newline("\n"), Newline("\n")] [Whitespace(" ")] + 2: CSS_SUB_SELECTOR_LIST@325..325 + 1: CSS_DECLARATION_OR_RULE_BLOCK@325..396 + 0: L_CURLY@325..326 "{" [] [] + 1: CSS_DECLARATION_OR_RULE_LIST@326..394 + 0: CSS_DECLARATION_WITH_SEMICOLON@326..394 + 0: CSS_DECLARATION@326..393 + 0: CSS_GENERIC_PROPERTY@326..383 + 0: CSS_IDENTIFIER@326..353 + 0: IDENT@326..353 "border-bottom-left-radius" [Newline("\n"), Whitespace("\t")] [] + 1: COLON@353..355 ":" [] [Whitespace(" ")] + 2: CSS_GENERIC_COMPONENT_VALUE_LIST@355..383 + 0: CSS_FUNCTION@355..383 + 0: CSS_IDENTIFIER@355..358 + 0: IDENT@355..358 "var" [] [] + 1: L_PAREN@358..359 "(" [] [] + 2: CSS_PARAMETER_LIST@359..381 + 0: CSS_PARAMETER@359..381 + 0: CSS_LIST_OF_COMPONENT_VALUES_EXPRESSION@359..381 + 0: CSS_COMPONENT_VALUE_LIST@359..381 + 0: CSS_DASHED_IDENTIFIER@359..381 + 0: IDENT@359..381 "--bs-border-radius-xxl" [] [] + 3: R_PAREN@381..383 ")" [] [Whitespace(" ")] + 1: CSS_DECLARATION_IMPORTANT@383..393 + 0: BANG@383..384 "!" [] [] + 1: IMPORTANT_KW@384..393 "important" [] [] + 1: SEMICOLON@393..394 ";" [] [] + 2: R_CURLY@394..396 "}" [Newline("\n")] [] + 12: CSS_QUALIFIED_RULE@396..521 + 0: CSS_SELECTOR_LIST@396..402 + 0: CSS_COMPOUND_SELECTOR@396..402 + 0: CSS_NESTED_SELECTOR_LIST@396..396 + 1: CSS_TYPE_SELECTOR@396..402 0: (empty) - 1: CSS_IDENTIFIER@330..336 - 0: IDENT@330..336 "div" [Newline("\n"), Newline("\n")] [Whitespace(" ")] - 2: CSS_SUB_SELECTOR_LIST@336..336 - 1: CSS_DECLARATION_OR_RULE_BLOCK@336..455 - 0: L_CURLY@336..337 "{" [] [] - 1: CSS_DECLARATION_OR_RULE_LIST@337..452 - 0: CSS_DECLARATION_WITH_SEMICOLON@337..377 - 0: CSS_DECLARATION@337..376 - 0: CSS_GENERIC_PROPERTY@337..376 - 0: CSS_DASHED_IDENTIFIER@337..364 - 0: IDENT@337..364 "--bs-btn-focus-shadow-rgb" [Newline("\n"), Whitespace("\t")] [] - 1: COLON@364..366 ":" [] [Whitespace(" ")] - 2: CSS_GENERIC_COMPONENT_VALUE_LIST@366..376 - 0: CSS_NUMBER@366..368 - 0: CSS_NUMBER_LITERAL@366..368 "33" [] [] - 1: CSS_GENERIC_DELIMITER@368..370 - 0: COMMA@368..370 "," [] [Whitespace(" ")] - 2: CSS_NUMBER@370..372 - 0: CSS_NUMBER_LITERAL@370..372 "37" [] [] - 3: CSS_GENERIC_DELIMITER@372..374 - 0: COMMA@372..374 "," [] [Whitespace(" ")] - 4: CSS_NUMBER@374..376 - 0: CSS_NUMBER_LITERAL@374..376 "41" [] [] + 1: CSS_IDENTIFIER@396..402 + 0: IDENT@396..402 "div" [Newline("\n"), Newline("\n")] [Whitespace(" ")] + 2: CSS_SUB_SELECTOR_LIST@402..402 + 1: CSS_DECLARATION_OR_RULE_BLOCK@402..521 + 0: L_CURLY@402..403 "{" [] [] + 1: CSS_DECLARATION_OR_RULE_LIST@403..518 + 0: CSS_DECLARATION_WITH_SEMICOLON@403..443 + 0: CSS_DECLARATION@403..442 + 0: CSS_GENERIC_PROPERTY@403..442 + 0: CSS_DASHED_IDENTIFIER@403..430 + 0: IDENT@403..430 "--bs-btn-focus-shadow-rgb" [Newline("\n"), Whitespace("\t")] [] + 1: COLON@430..432 ":" [] [Whitespace(" ")] + 2: CSS_GENERIC_COMPONENT_VALUE_LIST@432..442 + 0: CSS_NUMBER@432..434 + 0: CSS_NUMBER_LITERAL@432..434 "33" [] [] + 1: CSS_GENERIC_DELIMITER@434..436 + 0: COMMA@434..436 "," [] [Whitespace(" ")] + 2: CSS_NUMBER@436..438 + 0: CSS_NUMBER_LITERAL@436..438 "37" [] [] + 3: CSS_GENERIC_DELIMITER@438..440 + 0: COMMA@438..440 "," [] [Whitespace(" ")] + 4: CSS_NUMBER@440..442 + 0: CSS_NUMBER_LITERAL@440..442 "41" [] [] 1: (empty) - 1: SEMICOLON@376..377 ";" [] [] - 1: CSS_DECLARATION_WITH_SEMICOLON@377..452 - 0: CSS_DECLARATION@377..451 - 0: CSS_GENERIC_PROPERTY@377..451 - 0: CSS_IDENTIFIER@377..389 - 0: IDENT@377..389 "background" [Newline("\n"), Whitespace("\t")] [] - 1: COLON@389..391 ":" [] [Whitespace(" ")] - 2: CSS_GENERIC_COMPONENT_VALUE_LIST@391..451 - 0: CSS_IDENTIFIER@391..403 - 0: IDENT@391..403 "transparent" [] [Whitespace(" ")] - 1: CSS_FUNCTION@403..426 - 0: CSS_IDENTIFIER@403..406 - 0: IDENT@403..406 "var" [] [] - 1: L_PAREN@406..407 "(" [] [] - 2: CSS_PARAMETER_LIST@407..424 - 0: CSS_PARAMETER@407..424 - 0: CSS_LIST_OF_COMPONENT_VALUES_EXPRESSION@407..424 - 0: CSS_COMPONENT_VALUE_LIST@407..424 - 0: CSS_DASHED_IDENTIFIER@407..424 - 0: IDENT@407..424 "--bs-btn-close-bg" [] [] - 3: R_PAREN@424..426 ")" [] [Whitespace(" ")] - 2: CSS_IDENTIFIER@426..432 - 0: IDENT@426..432 "center" [] [] - 3: CSS_GENERIC_DELIMITER@432..433 - 0: SLASH@432..433 "/" [] [] - 4: CSS_REGULAR_DIMENSION@433..437 - 0: CSS_NUMBER_LITERAL@433..434 "1" [] [] - 1: IDENT@434..437 "em" [] [Whitespace(" ")] - 5: CSS_IDENTIFIER@437..442 - 0: IDENT@437..442 "auto" [] [Whitespace(" ")] - 6: CSS_IDENTIFIER@442..451 - 0: IDENT@442..451 "no-repeat" [] [] + 1: SEMICOLON@442..443 ";" [] [] + 1: CSS_DECLARATION_WITH_SEMICOLON@443..518 + 0: CSS_DECLARATION@443..517 + 0: CSS_GENERIC_PROPERTY@443..517 + 0: CSS_IDENTIFIER@443..455 + 0: IDENT@443..455 "background" [Newline("\n"), Whitespace("\t")] [] + 1: COLON@455..457 ":" [] [Whitespace(" ")] + 2: CSS_GENERIC_COMPONENT_VALUE_LIST@457..517 + 0: CSS_IDENTIFIER@457..469 + 0: IDENT@457..469 "transparent" [] [Whitespace(" ")] + 1: CSS_FUNCTION@469..492 + 0: CSS_IDENTIFIER@469..472 + 0: IDENT@469..472 "var" [] [] + 1: L_PAREN@472..473 "(" [] [] + 2: CSS_PARAMETER_LIST@473..490 + 0: CSS_PARAMETER@473..490 + 0: CSS_LIST_OF_COMPONENT_VALUES_EXPRESSION@473..490 + 0: CSS_COMPONENT_VALUE_LIST@473..490 + 0: CSS_DASHED_IDENTIFIER@473..490 + 0: IDENT@473..490 "--bs-btn-close-bg" [] [] + 3: R_PAREN@490..492 ")" [] [Whitespace(" ")] + 2: CSS_IDENTIFIER@492..498 + 0: IDENT@492..498 "center" [] [] + 3: CSS_GENERIC_DELIMITER@498..499 + 0: SLASH@498..499 "/" [] [] + 4: CSS_REGULAR_DIMENSION@499..503 + 0: CSS_NUMBER_LITERAL@499..500 "1" [] [] + 1: IDENT@500..503 "em" [] [Whitespace(" ")] + 5: CSS_IDENTIFIER@503..508 + 0: IDENT@503..508 "auto" [] [Whitespace(" ")] + 6: CSS_IDENTIFIER@508..517 + 0: IDENT@508..517 "no-repeat" [] [] 1: (empty) - 1: SEMICOLON@451..452 ";" [] [] - 2: R_CURLY@452..455 "}" [Newline("\n"), Newline("\n")] [] - 2: EOF@455..456 "" [Newline("\n")] [] + 1: SEMICOLON@517..518 ";" [] [] + 2: R_CURLY@518..521 "}" [Newline("\n"), Newline("\n")] [] + 2: EOF@521..522 "" [Newline("\n")] [] ``` diff --git a/crates/biome_css_parser/tests/spec_test.rs b/crates/biome_css_parser/tests/spec_test.rs index 0492518ef16c..87c15bcbccf2 100644 --- a/crates/biome_css_parser/tests/spec_test.rs +++ b/crates/biome_css_parser/tests/spec_test.rs @@ -174,7 +174,9 @@ pub fn run(test_case: &str, _snapshot_name: &str, test_directory: &str, outcome_ #[test] pub fn quick_test() { let code = r#" -µ... {} + .foo { + color: blue;; + } "#; let root = parse_css( diff --git a/crates/biome_css_syntax/src/generated/kind.rs b/crates/biome_css_syntax/src/generated/kind.rs index b78502c7052d..4ebc23ac9ae2 100644 --- a/crates/biome_css_syntax/src/generated/kind.rs +++ b/crates/biome_css_syntax/src/generated/kind.rs @@ -264,6 +264,7 @@ pub enum CssSyntaxKind { CSS_DECLARATION_OR_AT_RULE_LIST, CSS_DECLARATION_WITH_SEMICOLON, CSS_DECLARATION, + CSS_EMPTY_DECLARATION, CSS_IDENTIFIER, CSS_NUMBER, CSS_PARAMETER, diff --git a/crates/biome_css_syntax/src/generated/macros.rs b/crates/biome_css_syntax/src/generated/macros.rs index da71bfaa054c..f9d03f8f8f59 100644 --- a/crates/biome_css_syntax/src/generated/macros.rs +++ b/crates/biome_css_syntax/src/generated/macros.rs @@ -178,6 +178,10 @@ macro_rules! map_syntax_node { let $pattern = unsafe { $crate::CssDocumentCustomMatcher::new_unchecked(node) }; $body } + $crate::CssSyntaxKind::CSS_EMPTY_DECLARATION => { + let $pattern = unsafe { $crate::CssEmptyDeclaration::new_unchecked(node) }; + $body + } $crate::CssSyntaxKind::CSS_FONT_FACE_AT_RULE => { let $pattern = unsafe { $crate::CssFontFaceAtRule::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 6448f4163b95..b2df8240f512 100644 --- a/crates/biome_css_syntax/src/generated/nodes.rs +++ b/crates/biome_css_syntax/src/generated/nodes.rs @@ -1666,6 +1666,41 @@ pub struct CssDocumentCustomMatcherFields { pub r_paren_token: SyntaxResult, } #[derive(Clone, PartialEq, Eq, Hash)] +pub struct CssEmptyDeclaration { + pub(crate) syntax: SyntaxNode, +} +impl CssEmptyDeclaration { + #[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) -> CssEmptyDeclarationFields { + CssEmptyDeclarationFields { + semicolon_token: self.semicolon_token(), + } + } + pub fn semicolon_token(&self) -> SyntaxResult { + support::required_token(&self.syntax, 0usize) + } +} +impl Serialize for CssEmptyDeclaration { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + self.as_fields().serialize(serializer) + } +} +#[derive(Serialize)] +pub struct CssEmptyDeclarationFields { + pub semicolon_token: SyntaxResult, +} +#[derive(Clone, PartialEq, Eq, Hash)] pub struct CssFontFaceAtRule { pub(crate) syntax: SyntaxNode, } @@ -7155,6 +7190,7 @@ pub enum AnyCssDeclarationOrRule { AnyCssRule(AnyCssRule), CssBogus(CssBogus), CssDeclarationWithSemicolon(CssDeclarationWithSemicolon), + CssEmptyDeclaration(CssEmptyDeclaration), CssMetavariable(CssMetavariable), } impl AnyCssDeclarationOrRule { @@ -7176,6 +7212,12 @@ impl AnyCssDeclarationOrRule { _ => None, } } + pub fn as_css_empty_declaration(&self) -> Option<&CssEmptyDeclaration> { + match &self { + AnyCssDeclarationOrRule::CssEmptyDeclaration(item) => Some(item), + _ => None, + } + } pub fn as_css_metavariable(&self) -> Option<&CssMetavariable> { match &self { AnyCssDeclarationOrRule::CssMetavariable(item) => Some(item), @@ -10391,6 +10433,47 @@ impl From for SyntaxElement { n.syntax.into() } } +impl AstNode for CssEmptyDeclaration { + type Language = Language; + const KIND_SET: SyntaxKindSet = + SyntaxKindSet::from_raw(RawSyntaxKind(CSS_EMPTY_DECLARATION as u16)); + fn can_cast(kind: SyntaxKind) -> bool { + kind == CSS_EMPTY_DECLARATION + } + 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 CssEmptyDeclaration { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("CssEmptyDeclaration") + .field( + "semicolon_token", + &support::DebugSyntaxResult(self.semicolon_token()), + ) + .finish() + } +} +impl From for SyntaxNode { + fn from(n: CssEmptyDeclaration) -> SyntaxNode { + n.syntax + } +} +impl From for SyntaxElement { + fn from(n: CssEmptyDeclaration) -> SyntaxElement { + n.syntax.into() + } +} impl AstNode for CssFontFaceAtRule { type Language = Language; const KIND_SET: SyntaxKindSet = @@ -16937,6 +17020,11 @@ impl From for AnyCssDeclarationOrRule { AnyCssDeclarationOrRule::CssDeclarationWithSemicolon(node) } } +impl From for AnyCssDeclarationOrRule { + fn from(node: CssEmptyDeclaration) -> AnyCssDeclarationOrRule { + AnyCssDeclarationOrRule::CssEmptyDeclaration(node) + } +} impl From for AnyCssDeclarationOrRule { fn from(node: CssMetavariable) -> AnyCssDeclarationOrRule { AnyCssDeclarationOrRule::CssMetavariable(node) @@ -16947,10 +17035,14 @@ impl AstNode for AnyCssDeclarationOrRule { const KIND_SET: SyntaxKindSet = AnyCssRule::KIND_SET .union(CssBogus::KIND_SET) .union(CssDeclarationWithSemicolon::KIND_SET) + .union(CssEmptyDeclaration::KIND_SET) .union(CssMetavariable::KIND_SET); fn can_cast(kind: SyntaxKind) -> bool { match kind { - CSS_BOGUS | CSS_DECLARATION_WITH_SEMICOLON | CSS_METAVARIABLE => true, + CSS_BOGUS + | CSS_DECLARATION_WITH_SEMICOLON + | CSS_EMPTY_DECLARATION + | CSS_METAVARIABLE => true, k if AnyCssRule::can_cast(k) => true, _ => false, } @@ -16963,6 +17055,9 @@ impl AstNode for AnyCssDeclarationOrRule { syntax, }) } + CSS_EMPTY_DECLARATION => { + AnyCssDeclarationOrRule::CssEmptyDeclaration(CssEmptyDeclaration { syntax }) + } CSS_METAVARIABLE => { AnyCssDeclarationOrRule::CssMetavariable(CssMetavariable { syntax }) } @@ -16979,6 +17074,7 @@ impl AstNode for AnyCssDeclarationOrRule { match self { AnyCssDeclarationOrRule::CssBogus(it) => &it.syntax, AnyCssDeclarationOrRule::CssDeclarationWithSemicolon(it) => &it.syntax, + AnyCssDeclarationOrRule::CssEmptyDeclaration(it) => &it.syntax, AnyCssDeclarationOrRule::CssMetavariable(it) => &it.syntax, AnyCssDeclarationOrRule::AnyCssRule(it) => it.syntax(), } @@ -16987,6 +17083,7 @@ impl AstNode for AnyCssDeclarationOrRule { match self { AnyCssDeclarationOrRule::CssBogus(it) => it.syntax, AnyCssDeclarationOrRule::CssDeclarationWithSemicolon(it) => it.syntax, + AnyCssDeclarationOrRule::CssEmptyDeclaration(it) => it.syntax, AnyCssDeclarationOrRule::CssMetavariable(it) => it.syntax, AnyCssDeclarationOrRule::AnyCssRule(it) => it.into_syntax(), } @@ -16998,6 +17095,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::CssEmptyDeclaration(it) => std::fmt::Debug::fmt(it, f), AnyCssDeclarationOrRule::CssMetavariable(it) => std::fmt::Debug::fmt(it, f), } } @@ -17008,6 +17106,7 @@ impl From for SyntaxNode { AnyCssDeclarationOrRule::AnyCssRule(it) => it.into(), AnyCssDeclarationOrRule::CssBogus(it) => it.into(), AnyCssDeclarationOrRule::CssDeclarationWithSemicolon(it) => it.into(), + AnyCssDeclarationOrRule::CssEmptyDeclaration(it) => it.into(), AnyCssDeclarationOrRule::CssMetavariable(it) => it.into(), } } @@ -22394,6 +22493,11 @@ impl std::fmt::Display for CssDocumentCustomMatcher { std::fmt::Display::fmt(self.syntax(), f) } } +impl std::fmt::Display for CssEmptyDeclaration { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} impl std::fmt::Display for CssFontFaceAtRule { 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 795c38da835c..48bd165235f2 100644 --- a/crates/biome_css_syntax/src/generated/nodes_mut.rs +++ b/crates/biome_css_syntax/src/generated/nodes_mut.rs @@ -685,6 +685,14 @@ impl CssDocumentCustomMatcher { ) } } +impl CssEmptyDeclaration { + pub fn with_semicolon_token(self, element: SyntaxToken) -> Self { + Self::unwrap_cast( + self.syntax + .splice_slots(0usize..=0usize, once(Some(element.into()))), + ) + } +} impl CssFontFaceAtRule { pub fn with_font_face_token(self, element: SyntaxToken) -> Self { Self::unwrap_cast( diff --git a/xtask/codegen/css.ungram b/xtask/codegen/css.ungram index 218380074dde..d38bcc4cc517 100644 --- a/xtask/codegen/css.ungram +++ b/xtask/codegen/css.ungram @@ -443,6 +443,7 @@ AnyCssDeclarationOrRule = | CssDeclarationWithSemicolon | CssBogus | CssMetavariable + | CssEmptyDeclaration // @page :left { background: red; @media (500px <= width <= 500px) { } } // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -475,6 +476,9 @@ CssDeclarationWithSemicolon = declaration: CssDeclaration ';'? +CssEmptyDeclaration = + ';' + AnyCssDeclarationBlock = CssDeclarationBlock | CssBogusBlock diff --git a/xtask/codegen/src/css_kinds_src.rs b/xtask/codegen/src/css_kinds_src.rs index 4f10f6fd28de..7cffa42b7e6d 100644 --- a/xtask/codegen/src/css_kinds_src.rs +++ b/xtask/codegen/src/css_kinds_src.rs @@ -288,6 +288,7 @@ pub const CSS_KINDS_SRC: KindsSrc = KindsSrc { "CSS_DECLARATION_OR_AT_RULE_LIST", "CSS_DECLARATION_WITH_SEMICOLON", "CSS_DECLARATION", + "CSS_EMPTY_DECLARATION", "CSS_IDENTIFIER", "CSS_NUMBER", "CSS_PARAMETER",