From 0059cd9b5e6ba33cabb5e153bd03e2041effb0cd Mon Sep 17 00:00:00 2001 From: Denis Bezrukov <6227442+denbezrukov@users.noreply.github.com> Date: Mon, 14 Jul 2025 14:36:45 +0300 Subject: [PATCH] refactor(css_parser): flatten AnyCssDeclarationWithSemicolon (#6912) (#6879) --- .changeset/chilly-lions-rule.md | 18 + .../assist/source/use_sorted_properties.rs | 20 +- .../lint/correctness/no_invalid_grid_areas.rs | 1 - .../src/generated/node_factory.rs | 2 +- .../src/generated/syntax_factory.rs | 8 +- .../src/css/any/declaration.rs | 15 + .../src/css/any/declaration_or_at_rule.rs | 3 +- .../src/css/any/declaration_or_rule.rs | 3 +- .../src/css/any/declaration_with_semicolon.rs | 17 - crates/biome_css_formatter/src/css/any/mod.rs | 2 +- .../src/css/any/page_at_rule_item.rs | 3 +- crates/biome_css_formatter/src/generated.rs | 44 +- .../biome_css_syntax/src/generated/nodes.rs | 393 ++++++++++-------- crates/biome_css_syntax/src/stmt_ext.rs | 26 +- .../tests/specs/prettier/.DS_Store | Bin 0 -> 6148 bytes xtask/codegen/css.ungram | 25 +- 16 files changed, 323 insertions(+), 257 deletions(-) create mode 100644 .changeset/chilly-lions-rule.md create mode 100644 crates/biome_css_formatter/src/css/any/declaration.rs delete mode 100644 crates/biome_css_formatter/src/css/any/declaration_with_semicolon.rs create mode 100644 crates/biome_js_formatter/tests/specs/prettier/.DS_Store diff --git a/.changeset/chilly-lions-rule.md b/.changeset/chilly-lions-rule.md new file mode 100644 index 0000000000..bfef52d4be --- /dev/null +++ b/.changeset/chilly-lions-rule.md @@ -0,0 +1,18 @@ +--- +"@biomejs/biome": patch +--- + +Refactor: remove one level of indirection for CSS declarations with semicolon +Previously, accessing a declaration from a list required an extra step: + +```rust +item +.as_any_css_declaration_with_semicolon() +.as_css_declaration_with_semicolon() +``` + +Now, it can be done directly with: + +```rust +item.as_css_declaration_with_semicolon() +``` diff --git a/crates/biome_css_analyze/src/assist/source/use_sorted_properties.rs b/crates/biome_css_analyze/src/assist/source/use_sorted_properties.rs index 18d5016d85..9953d35f20 100644 --- a/crates/biome_css_analyze/src/assist/source/use_sorted_properties.rs +++ b/crates/biome_css_analyze/src/assist/source/use_sorted_properties.rs @@ -248,12 +248,8 @@ impl RecessOrderMember { AnyCssRule::CssNestedQualifiedRule(_) => NodeKindOrder::NestedRuleOrAtRule, AnyCssRule::CssQualifiedRule(_) => NodeKindOrder::UnknownKind, }, - AnyCssDeclarationOrRule::AnyCssDeclarationWithSemicolon(any_decl_with_semicolon) => { - let Some(decl_with_semicolon) = - any_decl_with_semicolon.as_css_declaration_with_semicolon() - else { - return NodeKindOrder::UnknownKind; - }; + AnyCssDeclarationOrRule::CssEmptyDeclaration(_) => NodeKindOrder::UnknownKind, + AnyCssDeclarationOrRule::CssDeclarationWithSemicolon(decl_with_semicolon) => { let Some(decl) = decl_with_semicolon.declaration().ok() else { return NodeKindOrder::UnknownKind; }; @@ -284,8 +280,7 @@ impl RecessOrderMember { pub fn property_index(&self) -> usize { let Some(prop_text) = &self .0 - .as_any_css_declaration_with_semicolon() - .and_then(|decl| decl.as_css_declaration_with_semicolon()) + .as_css_declaration_with_semicolon() .and_then(css_declaration_to_prop_text) else { return usize::MAX; @@ -300,8 +295,7 @@ impl RecessOrderMember { pub fn vendor_prefix_index(&self) -> usize { let Some(prop_text) = &self .0 - .as_any_css_declaration_with_semicolon() - .and_then(|decl| decl.as_css_declaration_with_semicolon()) + .as_css_declaration_with_semicolon() .and_then(css_declaration_to_prop_text) else { return usize::MAX; @@ -408,8 +402,7 @@ fn contains_shorthand_after_longhand(nodes: &[AnyCssDeclarationOrRule]) -> bool // Starting from the bottom, when we see a shorthand property, record the set of longhand properties that are no longer allowed to appear above it. for node in nodes.iter().rev() { let Some(prop_text) = &node - .as_any_css_declaration_with_semicolon() - .and_then(|decl| decl.as_css_declaration_with_semicolon()) + .as_css_declaration_with_semicolon() .and_then(css_declaration_to_prop_text) else { continue; @@ -442,8 +435,7 @@ fn contains_shorthand_after_longhand(nodes: &[AnyCssDeclarationOrRule]) -> bool fn contains_unknown_property(nodes: &[AnyCssDeclarationOrRule]) -> bool { for node in nodes.iter() { let Some(prop_text) = &node - .as_any_css_declaration_with_semicolon() - .and_then(|decl| decl.as_css_declaration_with_semicolon()) + .as_css_declaration_with_semicolon() .and_then(css_declaration_to_prop_text) else { continue; diff --git a/crates/biome_css_analyze/src/lint/correctness/no_invalid_grid_areas.rs b/crates/biome_css_analyze/src/lint/correctness/no_invalid_grid_areas.rs index e11e267d90..ec4e985192 100644 --- a/crates/biome_css_analyze/src/lint/correctness/no_invalid_grid_areas.rs +++ b/crates/biome_css_analyze/src/lint/correctness/no_invalid_grid_areas.rs @@ -91,7 +91,6 @@ impl Rule for NoInvalidGridAreas { .into_iter() .filter_map(|item| { let binding = item - .as_any_css_declaration_with_semicolon()? .as_css_declaration_with_semicolon()? .declaration() .ok()? diff --git a/crates/biome_css_factory/src/generated/node_factory.rs b/crates/biome_css_factory/src/generated/node_factory.rs index dd609332be..288e6be8fa 100644 --- a/crates/biome_css_factory/src/generated/node_factory.rs +++ b/crates/biome_css_factory/src/generated/node_factory.rs @@ -2419,7 +2419,7 @@ where } pub fn css_declaration_list(items: I) -> CssDeclarationList where - I: IntoIterator, + I: IntoIterator, I::IntoIter: ExactSizeIterator, { CssDeclarationList::unwrap_cast(SyntaxNode::new_detached( diff --git a/crates/biome_css_factory/src/generated/syntax_factory.rs b/crates/biome_css_factory/src/generated/syntax_factory.rs index bba0849cd6..b0c42819f6 100644 --- a/crates/biome_css_factory/src/generated/syntax_factory.rs +++ b/crates/biome_css_factory/src/generated/syntax_factory.rs @@ -4824,11 +4824,9 @@ impl SyntaxFactory for CssSyntaxFactory { CSS_CUSTOM_IDENTIFIER_LIST => { Self::make_node_list_syntax(kind, children, AnyCssCustomIdentifier::can_cast) } - CSS_DECLARATION_LIST => Self::make_node_list_syntax( - kind, - children, - AnyCssDeclarationWithSemicolon::can_cast, - ), + CSS_DECLARATION_LIST => { + Self::make_node_list_syntax(kind, children, AnyCssDeclaration::can_cast) + } CSS_DECLARATION_OR_AT_RULE_LIST => { Self::make_node_list_syntax(kind, children, AnyCssDeclarationOrAtRule::can_cast) } diff --git a/crates/biome_css_formatter/src/css/any/declaration.rs b/crates/biome_css_formatter/src/css/any/declaration.rs new file mode 100644 index 0000000000..5f793dbbc9 --- /dev/null +++ b/crates/biome_css_formatter/src/css/any/declaration.rs @@ -0,0 +1,15 @@ +//! This is a generated file. Don't modify it by hand! Run 'cargo codegen formatter' to re-generate the file. + +use crate::prelude::*; +use biome_css_syntax::AnyCssDeclaration; +#[derive(Debug, Clone, Default)] +pub(crate) struct FormatAnyCssDeclaration; +impl FormatRule for FormatAnyCssDeclaration { + type Context = CssFormatContext; + fn fmt(&self, node: &AnyCssDeclaration, f: &mut CssFormatter) -> FormatResult<()> { + match node { + AnyCssDeclaration::CssDeclarationWithSemicolon(node) => node.format().fmt(f), + AnyCssDeclaration::CssEmptyDeclaration(node) => node.format().fmt(f), + } + } +} diff --git a/crates/biome_css_formatter/src/css/any/declaration_or_at_rule.rs b/crates/biome_css_formatter/src/css/any/declaration_or_at_rule.rs index 19661ed4ee..bb558ec0d9 100644 --- a/crates/biome_css_formatter/src/css/any/declaration_or_at_rule.rs +++ b/crates/biome_css_formatter/src/css/any/declaration_or_at_rule.rs @@ -8,8 +8,9 @@ impl FormatRule for FormatAnyCssDeclarationOrAtRule { type Context = CssFormatContext; fn fmt(&self, node: &AnyCssDeclarationOrAtRule, f: &mut CssFormatter) -> FormatResult<()> { match node { - AnyCssDeclarationOrAtRule::AnyCssDeclarationWithSemicolon(node) => node.format().fmt(f), AnyCssDeclarationOrAtRule::CssAtRule(node) => node.format().fmt(f), + AnyCssDeclarationOrAtRule::CssDeclarationWithSemicolon(node) => node.format().fmt(f), + AnyCssDeclarationOrAtRule::CssEmptyDeclaration(node) => node.format().fmt(f), } } } 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 8395ff20d8..5be7e15e5a 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 @@ -8,9 +8,10 @@ impl FormatRule for FormatAnyCssDeclarationOrRule { type Context = CssFormatContext; fn fmt(&self, node: &AnyCssDeclarationOrRule, f: &mut CssFormatter) -> FormatResult<()> { match node { - AnyCssDeclarationOrRule::AnyCssDeclarationWithSemicolon(node) => node.format().fmt(f), 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/any/declaration_with_semicolon.rs b/crates/biome_css_formatter/src/css/any/declaration_with_semicolon.rs deleted file mode 100644 index 6806215670..0000000000 --- a/crates/biome_css_formatter/src/css/any/declaration_with_semicolon.rs +++ /dev/null @@ -1,17 +0,0 @@ -//! This is a generated file. Don't modify it by hand! Run 'cargo codegen formatter' to re-generate the file. - -use crate::prelude::*; -use biome_css_syntax::AnyCssDeclarationWithSemicolon; -#[derive(Debug, Clone, Default)] -pub(crate) struct FormatAnyCssDeclarationWithSemicolon; -impl FormatRule for FormatAnyCssDeclarationWithSemicolon { - type Context = CssFormatContext; - fn fmt(&self, node: &AnyCssDeclarationWithSemicolon, f: &mut CssFormatter) -> FormatResult<()> { - match node { - AnyCssDeclarationWithSemicolon::CssDeclarationWithSemicolon(node) => { - node.format().fmt(f) - } - AnyCssDeclarationWithSemicolon::CssEmptyDeclaration(node) => node.format().fmt(f), - } - } -} diff --git a/crates/biome_css_formatter/src/css/any/mod.rs b/crates/biome_css_formatter/src/css/any/mod.rs index e2a3a8f587..8e6232d2ae 100644 --- a/crates/biome_css_formatter/src/css/any/mod.rs +++ b/crates/biome_css_formatter/src/css/any/mod.rs @@ -14,13 +14,13 @@ pub(crate) mod container_style_in_parens; pub(crate) mod container_style_or_combinable_query; pub(crate) mod container_style_query; pub(crate) mod custom_identifier; +pub(crate) mod declaration; pub(crate) mod declaration_block; pub(crate) mod declaration_name; pub(crate) mod declaration_or_at_rule; pub(crate) mod declaration_or_at_rule_block; pub(crate) mod declaration_or_rule; pub(crate) mod declaration_or_rule_block; -pub(crate) mod declaration_with_semicolon; pub(crate) mod dimension; pub(crate) mod document_matcher; pub(crate) mod expression; diff --git a/crates/biome_css_formatter/src/css/any/page_at_rule_item.rs b/crates/biome_css_formatter/src/css/any/page_at_rule_item.rs index cff9cf890b..af1fdf7727 100644 --- a/crates/biome_css_formatter/src/css/any/page_at_rule_item.rs +++ b/crates/biome_css_formatter/src/css/any/page_at_rule_item.rs @@ -8,8 +8,9 @@ impl FormatRule for FormatAnyCssPageAtRuleItem { type Context = CssFormatContext; fn fmt(&self, node: &AnyCssPageAtRuleItem, f: &mut CssFormatter) -> FormatResult<()> { match node { - AnyCssPageAtRuleItem::AnyCssDeclarationWithSemicolon(node) => node.format().fmt(f), AnyCssPageAtRuleItem::CssAtRule(node) => node.format().fmt(f), + AnyCssPageAtRuleItem::CssDeclarationWithSemicolon(node) => node.format().fmt(f), + AnyCssPageAtRuleItem::CssEmptyDeclaration(node) => node.format().fmt(f), AnyCssPageAtRuleItem::CssMarginAtRule(node) => node.format().fmt(f), } } diff --git a/crates/biome_css_formatter/src/generated.rs b/crates/biome_css_formatter/src/generated.rs index 73ec638592..7f11520829 100644 --- a/crates/biome_css_formatter/src/generated.rs +++ b/crates/biome_css_formatter/src/generated.rs @@ -7502,6 +7502,31 @@ impl IntoFormat for biome_css_syntax::AnyCssCustomIdentifier { ) } } +impl AsFormat for biome_css_syntax::AnyCssDeclaration { + type Format<'a> = FormatRefWithRule< + 'a, + biome_css_syntax::AnyCssDeclaration, + crate::css::any::declaration::FormatAnyCssDeclaration, + >; + fn format(&self) -> Self::Format<'_> { + FormatRefWithRule::new( + self, + crate::css::any::declaration::FormatAnyCssDeclaration::default(), + ) + } +} +impl IntoFormat for biome_css_syntax::AnyCssDeclaration { + type Format = FormatOwnedWithRule< + biome_css_syntax::AnyCssDeclaration, + crate::css::any::declaration::FormatAnyCssDeclaration, + >; + fn into_format(self) -> Self::Format { + FormatOwnedWithRule::new( + self, + crate::css::any::declaration::FormatAnyCssDeclaration::default(), + ) + } +} impl AsFormat for biome_css_syntax::AnyCssDeclarationBlock { type Format<'a> = FormatRefWithRule< 'a, @@ -7648,25 +7673,6 @@ impl IntoFormat for biome_css_syntax::AnyCssDeclarationOrRuleB ) } } -impl AsFormat for biome_css_syntax::AnyCssDeclarationWithSemicolon { - type Format<'a> = FormatRefWithRule< - 'a, - biome_css_syntax::AnyCssDeclarationWithSemicolon, - crate::css::any::declaration_with_semicolon::FormatAnyCssDeclarationWithSemicolon, - >; - fn format(&self) -> Self::Format<'_> { - FormatRefWithRule :: new (self , crate :: css :: any :: declaration_with_semicolon :: FormatAnyCssDeclarationWithSemicolon :: default ()) - } -} -impl IntoFormat for biome_css_syntax::AnyCssDeclarationWithSemicolon { - type Format = FormatOwnedWithRule< - biome_css_syntax::AnyCssDeclarationWithSemicolon, - crate::css::any::declaration_with_semicolon::FormatAnyCssDeclarationWithSemicolon, - >; - fn into_format(self) -> Self::Format { - FormatOwnedWithRule :: new (self , crate :: css :: any :: declaration_with_semicolon :: FormatAnyCssDeclarationWithSemicolon :: default ()) - } -} impl AsFormat for biome_css_syntax::AnyCssDimension { type Format<'a> = FormatRefWithRule< 'a, diff --git a/crates/biome_css_syntax/src/generated/nodes.rs b/crates/biome_css_syntax/src/generated/nodes.rs index 52fb45a0a6..f8e0ed9f06 100644 --- a/crates/biome_css_syntax/src/generated/nodes.rs +++ b/crates/biome_css_syntax/src/generated/nodes.rs @@ -7256,6 +7256,25 @@ impl AnyCssCustomIdentifier { } } #[derive(Clone, PartialEq, Eq, Hash, Serialize)] +pub enum AnyCssDeclaration { + CssDeclarationWithSemicolon(CssDeclarationWithSemicolon), + CssEmptyDeclaration(CssEmptyDeclaration), +} +impl AnyCssDeclaration { + pub fn as_css_declaration_with_semicolon(&self) -> Option<&CssDeclarationWithSemicolon> { + match &self { + Self::CssDeclarationWithSemicolon(item) => Some(item), + _ => None, + } + } + pub fn as_css_empty_declaration(&self) -> Option<&CssEmptyDeclaration> { + match &self { + Self::CssEmptyDeclaration(item) => Some(item), + _ => None, + } + } +} +#[derive(Clone, PartialEq, Eq, Hash, Serialize)] pub enum AnyCssDeclarationBlock { CssBogusBlock(CssBogusBlock), CssDeclarationBlock(CssDeclarationBlock), @@ -7295,22 +7314,29 @@ impl AnyCssDeclarationName { } #[derive(Clone, PartialEq, Eq, Hash, Serialize)] pub enum AnyCssDeclarationOrAtRule { - AnyCssDeclarationWithSemicolon(AnyCssDeclarationWithSemicolon), CssAtRule(CssAtRule), + CssDeclarationWithSemicolon(CssDeclarationWithSemicolon), + CssEmptyDeclaration(CssEmptyDeclaration), } impl AnyCssDeclarationOrAtRule { - pub fn as_any_css_declaration_with_semicolon(&self) -> Option<&AnyCssDeclarationWithSemicolon> { - match &self { - Self::AnyCssDeclarationWithSemicolon(item) => Some(item), - _ => None, - } - } pub fn as_css_at_rule(&self) -> Option<&CssAtRule> { match &self { Self::CssAtRule(item) => Some(item), _ => None, } } + pub fn as_css_declaration_with_semicolon(&self) -> Option<&CssDeclarationWithSemicolon> { + match &self { + Self::CssDeclarationWithSemicolon(item) => Some(item), + _ => None, + } + } + pub fn as_css_empty_declaration(&self) -> Option<&CssEmptyDeclaration> { + match &self { + Self::CssEmptyDeclaration(item) => Some(item), + _ => None, + } + } } #[derive(Clone, PartialEq, Eq, Hash, Serialize)] pub enum AnyCssDeclarationOrAtRuleBlock { @@ -7333,18 +7359,13 @@ impl AnyCssDeclarationOrAtRuleBlock { } #[derive(Clone, PartialEq, Eq, Hash, Serialize)] pub enum AnyCssDeclarationOrRule { - AnyCssDeclarationWithSemicolon(AnyCssDeclarationWithSemicolon), AnyCssRule(AnyCssRule), CssBogus(CssBogus), + CssDeclarationWithSemicolon(CssDeclarationWithSemicolon), + CssEmptyDeclaration(CssEmptyDeclaration), CssMetavariable(CssMetavariable), } impl AnyCssDeclarationOrRule { - pub fn as_any_css_declaration_with_semicolon(&self) -> Option<&AnyCssDeclarationWithSemicolon> { - match &self { - Self::AnyCssDeclarationWithSemicolon(item) => Some(item), - _ => None, - } - } pub fn as_any_css_rule(&self) -> Option<&AnyCssRule> { match &self { Self::AnyCssRule(item) => Some(item), @@ -7357,6 +7378,18 @@ impl AnyCssDeclarationOrRule { _ => None, } } + pub fn as_css_declaration_with_semicolon(&self) -> Option<&CssDeclarationWithSemicolon> { + match &self { + Self::CssDeclarationWithSemicolon(item) => Some(item), + _ => None, + } + } + pub fn as_css_empty_declaration(&self) -> Option<&CssEmptyDeclaration> { + match &self { + Self::CssEmptyDeclaration(item) => Some(item), + _ => None, + } + } pub fn as_css_metavariable(&self) -> Option<&CssMetavariable> { match &self { Self::CssMetavariable(item) => Some(item), @@ -7384,25 +7417,6 @@ impl AnyCssDeclarationOrRuleBlock { } } #[derive(Clone, PartialEq, Eq, Hash, Serialize)] -pub enum AnyCssDeclarationWithSemicolon { - CssDeclarationWithSemicolon(CssDeclarationWithSemicolon), - CssEmptyDeclaration(CssEmptyDeclaration), -} -impl AnyCssDeclarationWithSemicolon { - pub fn as_css_declaration_with_semicolon(&self) -> Option<&CssDeclarationWithSemicolon> { - match &self { - Self::CssDeclarationWithSemicolon(item) => Some(item), - _ => None, - } - } - pub fn as_css_empty_declaration(&self) -> Option<&CssEmptyDeclaration> { - match &self { - Self::CssEmptyDeclaration(item) => Some(item), - _ => None, - } - } -} -#[derive(Clone, PartialEq, Eq, Hash, Serialize)] pub enum AnyCssDimension { CssPercentage(CssPercentage), CssRegularDimension(CssRegularDimension), @@ -8022,23 +8036,30 @@ impl AnyCssPageAtRuleBlock { } #[derive(Clone, PartialEq, Eq, Hash, Serialize)] pub enum AnyCssPageAtRuleItem { - AnyCssDeclarationWithSemicolon(AnyCssDeclarationWithSemicolon), CssAtRule(CssAtRule), + CssDeclarationWithSemicolon(CssDeclarationWithSemicolon), + CssEmptyDeclaration(CssEmptyDeclaration), CssMarginAtRule(CssMarginAtRule), } impl AnyCssPageAtRuleItem { - pub fn as_any_css_declaration_with_semicolon(&self) -> Option<&AnyCssDeclarationWithSemicolon> { - match &self { - Self::AnyCssDeclarationWithSemicolon(item) => Some(item), - _ => None, - } - } pub fn as_css_at_rule(&self) -> Option<&CssAtRule> { match &self { Self::CssAtRule(item) => Some(item), _ => None, } } + pub fn as_css_declaration_with_semicolon(&self) -> Option<&CssDeclarationWithSemicolon> { + match &self { + Self::CssDeclarationWithSemicolon(item) => Some(item), + _ => None, + } + } + pub fn as_css_empty_declaration(&self) -> Option<&CssEmptyDeclaration> { + match &self { + Self::CssEmptyDeclaration(item) => Some(item), + _ => None, + } + } pub fn as_css_margin_at_rule(&self) -> Option<&CssMarginAtRule> { match &self { Self::CssMarginAtRule(item) => Some(item), @@ -18404,6 +18425,68 @@ impl From for SyntaxElement { node.into() } } +impl From for AnyCssDeclaration { + fn from(node: CssDeclarationWithSemicolon) -> Self { + Self::CssDeclarationWithSemicolon(node) + } +} +impl From for AnyCssDeclaration { + fn from(node: CssEmptyDeclaration) -> Self { + Self::CssEmptyDeclaration(node) + } +} +impl AstNode for AnyCssDeclaration { + type Language = Language; + const KIND_SET: SyntaxKindSet = + CssDeclarationWithSemicolon::KIND_SET.union(CssEmptyDeclaration::KIND_SET); + fn can_cast(kind: SyntaxKind) -> bool { + matches!(kind, CSS_DECLARATION_WITH_SEMICOLON | CSS_EMPTY_DECLARATION) + } + fn cast(syntax: SyntaxNode) -> Option { + let res = match syntax.kind() { + CSS_DECLARATION_WITH_SEMICOLON => { + Self::CssDeclarationWithSemicolon(CssDeclarationWithSemicolon { syntax }) + } + CSS_EMPTY_DECLARATION => Self::CssEmptyDeclaration(CssEmptyDeclaration { syntax }), + _ => return None, + }; + Some(res) + } + fn syntax(&self) -> &SyntaxNode { + match self { + Self::CssDeclarationWithSemicolon(it) => &it.syntax, + Self::CssEmptyDeclaration(it) => &it.syntax, + } + } + fn into_syntax(self) -> SyntaxNode { + match self { + Self::CssDeclarationWithSemicolon(it) => it.syntax, + Self::CssEmptyDeclaration(it) => it.syntax, + } + } +} +impl std::fmt::Debug for AnyCssDeclaration { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::CssDeclarationWithSemicolon(it) => std::fmt::Debug::fmt(it, f), + Self::CssEmptyDeclaration(it) => std::fmt::Debug::fmt(it, f), + } + } +} +impl From for SyntaxNode { + fn from(n: AnyCssDeclaration) -> Self { + match n { + AnyCssDeclaration::CssDeclarationWithSemicolon(it) => it.into(), + AnyCssDeclaration::CssEmptyDeclaration(it) => it.into(), + } + } +} +impl From for SyntaxElement { + fn from(n: AnyCssDeclaration) -> Self { + let node: SyntaxNode = n.into(); + node.into() + } +} impl From for AnyCssDeclarationBlock { fn from(node: CssBogusBlock) -> Self { Self::CssBogusBlock(node) @@ -18529,59 +18612,68 @@ impl From for AnyCssDeclarationOrAtRule { Self::CssAtRule(node) } } +impl From for AnyCssDeclarationOrAtRule { + fn from(node: CssDeclarationWithSemicolon) -> Self { + Self::CssDeclarationWithSemicolon(node) + } +} +impl From for AnyCssDeclarationOrAtRule { + fn from(node: CssEmptyDeclaration) -> Self { + Self::CssEmptyDeclaration(node) + } +} impl AstNode for AnyCssDeclarationOrAtRule { type Language = Language; - const KIND_SET: SyntaxKindSet = - AnyCssDeclarationWithSemicolon::KIND_SET.union(CssAtRule::KIND_SET); + const KIND_SET: SyntaxKindSet = CssAtRule::KIND_SET + .union(CssDeclarationWithSemicolon::KIND_SET) + .union(CssEmptyDeclaration::KIND_SET); fn can_cast(kind: SyntaxKind) -> bool { - match kind { - CSS_AT_RULE => true, - k if AnyCssDeclarationWithSemicolon::can_cast(k) => true, - _ => false, - } + matches!( + kind, + CSS_AT_RULE | CSS_DECLARATION_WITH_SEMICOLON | CSS_EMPTY_DECLARATION + ) } fn cast(syntax: SyntaxNode) -> Option { let res = match syntax.kind() { CSS_AT_RULE => Self::CssAtRule(CssAtRule { syntax }), - _ => { - if let Some(any_css_declaration_with_semicolon) = - AnyCssDeclarationWithSemicolon::cast(syntax) - { - return Some(Self::AnyCssDeclarationWithSemicolon( - any_css_declaration_with_semicolon, - )); - } - return None; + CSS_DECLARATION_WITH_SEMICOLON => { + Self::CssDeclarationWithSemicolon(CssDeclarationWithSemicolon { syntax }) } + CSS_EMPTY_DECLARATION => Self::CssEmptyDeclaration(CssEmptyDeclaration { syntax }), + _ => return None, }; Some(res) } fn syntax(&self) -> &SyntaxNode { match self { Self::CssAtRule(it) => &it.syntax, - Self::AnyCssDeclarationWithSemicolon(it) => it.syntax(), + Self::CssDeclarationWithSemicolon(it) => &it.syntax, + Self::CssEmptyDeclaration(it) => &it.syntax, } } fn into_syntax(self) -> SyntaxNode { match self { Self::CssAtRule(it) => it.syntax, - Self::AnyCssDeclarationWithSemicolon(it) => it.into_syntax(), + Self::CssDeclarationWithSemicolon(it) => it.syntax, + Self::CssEmptyDeclaration(it) => it.syntax, } } } impl std::fmt::Debug for AnyCssDeclarationOrAtRule { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - Self::AnyCssDeclarationWithSemicolon(it) => std::fmt::Debug::fmt(it, f), Self::CssAtRule(it) => std::fmt::Debug::fmt(it, f), + Self::CssDeclarationWithSemicolon(it) => std::fmt::Debug::fmt(it, f), + Self::CssEmptyDeclaration(it) => std::fmt::Debug::fmt(it, f), } } } impl From for SyntaxNode { fn from(n: AnyCssDeclarationOrAtRule) -> Self { match n { - AnyCssDeclarationOrAtRule::AnyCssDeclarationWithSemicolon(it) => it.into(), AnyCssDeclarationOrAtRule::CssAtRule(it) => it.into(), + AnyCssDeclarationOrAtRule::CssDeclarationWithSemicolon(it) => it.into(), + AnyCssDeclarationOrAtRule::CssEmptyDeclaration(it) => it.into(), } } } @@ -18658,6 +18750,16 @@ impl From for AnyCssDeclarationOrRule { Self::CssBogus(node) } } +impl From for AnyCssDeclarationOrRule { + fn from(node: CssDeclarationWithSemicolon) -> Self { + Self::CssDeclarationWithSemicolon(node) + } +} +impl From for AnyCssDeclarationOrRule { + fn from(node: CssEmptyDeclaration) -> Self { + Self::CssEmptyDeclaration(node) + } +} impl From for AnyCssDeclarationOrRule { fn from(node: CssMetavariable) -> Self { Self::CssMetavariable(node) @@ -18665,14 +18767,17 @@ impl From for AnyCssDeclarationOrRule { } impl AstNode for AnyCssDeclarationOrRule { type Language = Language; - const KIND_SET: SyntaxKindSet = AnyCssDeclarationWithSemicolon::KIND_SET - .union(AnyCssRule::KIND_SET) + 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_METAVARIABLE => true, - k if AnyCssDeclarationWithSemicolon::can_cast(k) => true, + CSS_BOGUS + | CSS_DECLARATION_WITH_SEMICOLON + | CSS_EMPTY_DECLARATION + | CSS_METAVARIABLE => true, k if AnyCssRule::can_cast(k) => true, _ => false, } @@ -18680,16 +18785,12 @@ impl AstNode for AnyCssDeclarationOrRule { fn cast(syntax: SyntaxNode) -> Option { let res = match syntax.kind() { CSS_BOGUS => Self::CssBogus(CssBogus { syntax }), + CSS_DECLARATION_WITH_SEMICOLON => { + Self::CssDeclarationWithSemicolon(CssDeclarationWithSemicolon { syntax }) + } + CSS_EMPTY_DECLARATION => Self::CssEmptyDeclaration(CssEmptyDeclaration { syntax }), CSS_METAVARIABLE => Self::CssMetavariable(CssMetavariable { syntax }), _ => { - let syntax = match AnyCssDeclarationWithSemicolon::try_cast(syntax) { - Ok(any_css_declaration_with_semicolon) => { - return Some(Self::AnyCssDeclarationWithSemicolon( - any_css_declaration_with_semicolon, - )); - } - Err(syntax) => syntax, - }; if let Some(any_css_rule) = AnyCssRule::cast(syntax) { return Some(Self::AnyCssRule(any_css_rule)); } @@ -18701,16 +18802,18 @@ impl AstNode for AnyCssDeclarationOrRule { fn syntax(&self) -> &SyntaxNode { match self { Self::CssBogus(it) => &it.syntax, + Self::CssDeclarationWithSemicolon(it) => &it.syntax, + Self::CssEmptyDeclaration(it) => &it.syntax, Self::CssMetavariable(it) => &it.syntax, - Self::AnyCssDeclarationWithSemicolon(it) => it.syntax(), Self::AnyCssRule(it) => it.syntax(), } } fn into_syntax(self) -> SyntaxNode { match self { Self::CssBogus(it) => it.syntax, + Self::CssDeclarationWithSemicolon(it) => it.syntax, + Self::CssEmptyDeclaration(it) => it.syntax, Self::CssMetavariable(it) => it.syntax, - Self::AnyCssDeclarationWithSemicolon(it) => it.into_syntax(), Self::AnyCssRule(it) => it.into_syntax(), } } @@ -18718,9 +18821,10 @@ impl AstNode for AnyCssDeclarationOrRule { impl std::fmt::Debug for AnyCssDeclarationOrRule { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - Self::AnyCssDeclarationWithSemicolon(it) => std::fmt::Debug::fmt(it, f), Self::AnyCssRule(it) => std::fmt::Debug::fmt(it, f), Self::CssBogus(it) => std::fmt::Debug::fmt(it, f), + Self::CssDeclarationWithSemicolon(it) => std::fmt::Debug::fmt(it, f), + Self::CssEmptyDeclaration(it) => std::fmt::Debug::fmt(it, f), Self::CssMetavariable(it) => std::fmt::Debug::fmt(it, f), } } @@ -18728,9 +18832,10 @@ impl std::fmt::Debug for AnyCssDeclarationOrRule { impl From for SyntaxNode { fn from(n: AnyCssDeclarationOrRule) -> Self { match n { - AnyCssDeclarationOrRule::AnyCssDeclarationWithSemicolon(it) => it.into(), 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(), } } @@ -18803,68 +18908,6 @@ impl From for SyntaxElement { node.into() } } -impl From for AnyCssDeclarationWithSemicolon { - fn from(node: CssDeclarationWithSemicolon) -> Self { - Self::CssDeclarationWithSemicolon(node) - } -} -impl From for AnyCssDeclarationWithSemicolon { - fn from(node: CssEmptyDeclaration) -> Self { - Self::CssEmptyDeclaration(node) - } -} -impl AstNode for AnyCssDeclarationWithSemicolon { - type Language = Language; - const KIND_SET: SyntaxKindSet = - CssDeclarationWithSemicolon::KIND_SET.union(CssEmptyDeclaration::KIND_SET); - fn can_cast(kind: SyntaxKind) -> bool { - matches!(kind, CSS_DECLARATION_WITH_SEMICOLON | CSS_EMPTY_DECLARATION) - } - fn cast(syntax: SyntaxNode) -> Option { - let res = match syntax.kind() { - CSS_DECLARATION_WITH_SEMICOLON => { - Self::CssDeclarationWithSemicolon(CssDeclarationWithSemicolon { syntax }) - } - CSS_EMPTY_DECLARATION => Self::CssEmptyDeclaration(CssEmptyDeclaration { syntax }), - _ => return None, - }; - Some(res) - } - fn syntax(&self) -> &SyntaxNode { - match self { - Self::CssDeclarationWithSemicolon(it) => &it.syntax, - Self::CssEmptyDeclaration(it) => &it.syntax, - } - } - fn into_syntax(self) -> SyntaxNode { - match self { - Self::CssDeclarationWithSemicolon(it) => it.syntax, - Self::CssEmptyDeclaration(it) => it.syntax, - } - } -} -impl std::fmt::Debug for AnyCssDeclarationWithSemicolon { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::CssDeclarationWithSemicolon(it) => std::fmt::Debug::fmt(it, f), - Self::CssEmptyDeclaration(it) => std::fmt::Debug::fmt(it, f), - } - } -} -impl From for SyntaxNode { - fn from(n: AnyCssDeclarationWithSemicolon) -> Self { - match n { - AnyCssDeclarationWithSemicolon::CssDeclarationWithSemicolon(it) => it.into(), - AnyCssDeclarationWithSemicolon::CssEmptyDeclaration(it) => it.into(), - } - } -} -impl From for SyntaxElement { - fn from(n: AnyCssDeclarationWithSemicolon) -> Self { - let node: SyntaxNode = n.into(); - node.into() - } -} impl From for AnyCssDimension { fn from(node: CssPercentage) -> Self { Self::CssPercentage(node) @@ -20782,6 +20825,16 @@ impl From for AnyCssPageAtRuleItem { Self::CssAtRule(node) } } +impl From for AnyCssPageAtRuleItem { + fn from(node: CssDeclarationWithSemicolon) -> Self { + Self::CssDeclarationWithSemicolon(node) + } +} +impl From for AnyCssPageAtRuleItem { + fn from(node: CssEmptyDeclaration) -> Self { + Self::CssEmptyDeclaration(node) + } +} impl From for AnyCssPageAtRuleItem { fn from(node: CssMarginAtRule) -> Self { Self::CssMarginAtRule(node) @@ -20789,53 +20842,54 @@ impl From for AnyCssPageAtRuleItem { } impl AstNode for AnyCssPageAtRuleItem { type Language = Language; - const KIND_SET: SyntaxKindSet = AnyCssDeclarationWithSemicolon::KIND_SET - .union(CssAtRule::KIND_SET) + const KIND_SET: SyntaxKindSet = CssAtRule::KIND_SET + .union(CssDeclarationWithSemicolon::KIND_SET) + .union(CssEmptyDeclaration::KIND_SET) .union(CssMarginAtRule::KIND_SET); fn can_cast(kind: SyntaxKind) -> bool { - match kind { - CSS_AT_RULE | CSS_MARGIN_AT_RULE => true, - k if AnyCssDeclarationWithSemicolon::can_cast(k) => true, - _ => false, - } + matches!( + kind, + CSS_AT_RULE + | CSS_DECLARATION_WITH_SEMICOLON + | CSS_EMPTY_DECLARATION + | CSS_MARGIN_AT_RULE + ) } fn cast(syntax: SyntaxNode) -> Option { let res = match syntax.kind() { CSS_AT_RULE => Self::CssAtRule(CssAtRule { syntax }), - CSS_MARGIN_AT_RULE => Self::CssMarginAtRule(CssMarginAtRule { syntax }), - _ => { - if let Some(any_css_declaration_with_semicolon) = - AnyCssDeclarationWithSemicolon::cast(syntax) - { - return Some(Self::AnyCssDeclarationWithSemicolon( - any_css_declaration_with_semicolon, - )); - } - return None; + CSS_DECLARATION_WITH_SEMICOLON => { + Self::CssDeclarationWithSemicolon(CssDeclarationWithSemicolon { syntax }) } + CSS_EMPTY_DECLARATION => Self::CssEmptyDeclaration(CssEmptyDeclaration { syntax }), + CSS_MARGIN_AT_RULE => Self::CssMarginAtRule(CssMarginAtRule { syntax }), + _ => return None, }; Some(res) } fn syntax(&self) -> &SyntaxNode { match self { Self::CssAtRule(it) => &it.syntax, + Self::CssDeclarationWithSemicolon(it) => &it.syntax, + Self::CssEmptyDeclaration(it) => &it.syntax, Self::CssMarginAtRule(it) => &it.syntax, - Self::AnyCssDeclarationWithSemicolon(it) => it.syntax(), } } fn into_syntax(self) -> SyntaxNode { match self { Self::CssAtRule(it) => it.syntax, + Self::CssDeclarationWithSemicolon(it) => it.syntax, + Self::CssEmptyDeclaration(it) => it.syntax, Self::CssMarginAtRule(it) => it.syntax, - Self::AnyCssDeclarationWithSemicolon(it) => it.into_syntax(), } } } impl std::fmt::Debug for AnyCssPageAtRuleItem { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - Self::AnyCssDeclarationWithSemicolon(it) => std::fmt::Debug::fmt(it, f), Self::CssAtRule(it) => std::fmt::Debug::fmt(it, f), + Self::CssDeclarationWithSemicolon(it) => std::fmt::Debug::fmt(it, f), + Self::CssEmptyDeclaration(it) => std::fmt::Debug::fmt(it, f), Self::CssMarginAtRule(it) => std::fmt::Debug::fmt(it, f), } } @@ -20843,8 +20897,9 @@ impl std::fmt::Debug for AnyCssPageAtRuleItem { impl From for SyntaxNode { fn from(n: AnyCssPageAtRuleItem) -> Self { match n { - AnyCssPageAtRuleItem::AnyCssDeclarationWithSemicolon(it) => it.into(), AnyCssPageAtRuleItem::CssAtRule(it) => it.into(), + AnyCssPageAtRuleItem::CssDeclarationWithSemicolon(it) => it.into(), + AnyCssPageAtRuleItem::CssEmptyDeclaration(it) => it.into(), AnyCssPageAtRuleItem::CssMarginAtRule(it) => it.into(), } } @@ -23380,6 +23435,11 @@ impl std::fmt::Display for AnyCssCustomIdentifier { std::fmt::Display::fmt(self.syntax(), f) } } +impl std::fmt::Display for AnyCssDeclaration { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} impl std::fmt::Display for AnyCssDeclarationBlock { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) @@ -23410,11 +23470,6 @@ impl std::fmt::Display for AnyCssDeclarationOrRuleBlock { std::fmt::Display::fmt(self.syntax(), f) } } -impl std::fmt::Display for AnyCssDeclarationWithSemicolon { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - std::fmt::Display::fmt(self.syntax(), f) - } -} impl std::fmt::Display for AnyCssDimension { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) @@ -26477,7 +26532,7 @@ impl Serialize for CssDeclarationList { } impl AstNodeList for CssDeclarationList { type Language = Language; - type Node = AnyCssDeclarationWithSemicolon; + type Node = AnyCssDeclaration; fn syntax_list(&self) -> &SyntaxList { &self.syntax_list } @@ -26492,15 +26547,15 @@ impl Debug for CssDeclarationList { } } impl IntoIterator for &CssDeclarationList { - type Item = AnyCssDeclarationWithSemicolon; - type IntoIter = AstNodeListIterator; + type Item = AnyCssDeclaration; + type IntoIter = AstNodeListIterator; fn into_iter(self) -> Self::IntoIter { self.iter() } } impl IntoIterator for CssDeclarationList { - type Item = AnyCssDeclarationWithSemicolon; - type IntoIter = AstNodeListIterator; + type Item = AnyCssDeclaration; + type IntoIter = AstNodeListIterator; fn into_iter(self) -> Self::IntoIter { self.iter() } diff --git a/crates/biome_css_syntax/src/stmt_ext.rs b/crates/biome_css_syntax/src/stmt_ext.rs index b1d2714aec..4e0f669f60 100644 --- a/crates/biome_css_syntax/src/stmt_ext.rs +++ b/crates/biome_css_syntax/src/stmt_ext.rs @@ -1,8 +1,8 @@ +use crate::CssSyntaxToken; use crate::generated::{ CssDeclarationBlock, CssDeclarationOrAtRuleBlock, CssDeclarationOrRuleBlock, CssFontFeatureValuesBlock, CssKeyframesBlock, CssPageAtRuleBlock, CssRuleBlock, }; -use crate::{AnyCssDeclarationOrAtRule, AnyCssDeclarationOrRule, CssSyntaxToken}; use biome_rowan::{AstNodeList, SyntaxResult, declare_node_union}; declare_node_union! { @@ -60,21 +60,15 @@ impl CssBlockLike { Self::CssDeclarationBlock(block) => block .declarations() .iter() - .all(|decl| decl.as_css_declaration_with_semicolon().is_none()), - Self::CssDeclarationOrAtRuleBlock(block) => block.items().iter().all(|item| { - matches!( - item, - AnyCssDeclarationOrAtRule::AnyCssDeclarationWithSemicolon(decl) - if decl.as_css_declaration_with_semicolon().is_none() - ) - }), - Self::CssDeclarationOrRuleBlock(block) => block.items().iter().all(|item| { - matches!( - item, - AnyCssDeclarationOrRule::AnyCssDeclarationWithSemicolon(decl) - if decl.as_css_declaration_with_semicolon().is_none() - ) - }), + .all(|decl| decl.as_css_empty_declaration().is_some()), + Self::CssDeclarationOrAtRuleBlock(block) => block + .items() + .iter() + .all(|item| item.as_css_empty_declaration().is_some()), + Self::CssDeclarationOrRuleBlock(block) => block + .items() + .iter() + .all(|item| item.as_css_empty_declaration().is_some()), _ => false, } } diff --git a/crates/biome_js_formatter/tests/specs/prettier/.DS_Store b/crates/biome_js_formatter/tests/specs/prettier/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..3fbb3d6d8f511dc8838da6a7d8b0ba4f8e4b1689 GIT binary patch literal 6148 zcmeHKJx;?=4E75}3a~_CVS>zTy+Mcq7w830TSP&L645fSpd5jfGq5l+AU3!R3*3Ou z&!$R9BQ}I8Tk`uHKl}ZBk>Z$$TzWkn5=BH5LK%ZC3@e1^SvwM_hbJAcF)546`*xom zJJII&j|}j;3+R&O`T214zpSad`=T%z!)$F)|~;= zY?g3G&_-jx7%&Ew4Dk0MhB79KwV?lWVDK#fZ~(U#*by7RSTRwo1u+A0k_wbmr!9t) zbogV9OB8ECNhhbxhtr*%b|}v8j`d?7PA(C&(HJlW+6-*SW5o6UxWE5zC)txRU<|Ak z18$IwvJ_X++S<7s*V-6*3T5HAT5y>H!N5IoygE8=@ G4158t3{SNH literal 0 HcmV?d00001 diff --git a/xtask/codegen/css.ungram b/xtask/codegen/css.ungram index d2f3036b07..16cd820d39 100644 --- a/xtask/codegen/css.ungram +++ b/xtask/codegen/css.ungram @@ -421,7 +421,7 @@ CssPseudoElementFunctionCustomIdentifier = '(' ident: CssCustomIdentifier ')' - + // ::part(active button) {} // ^^^^^^^^^^^^^^^^^^^ CssPseudoElementFunction = @@ -429,7 +429,7 @@ CssPseudoElementFunction = '(' items: CssPseudoElementFunctionParameterList ')' - + CssPseudoElementFunctionParameterList = CssIdentifier* ///////////// @@ -450,7 +450,8 @@ CssDeclarationOrRuleList = AnyCssDeclarationOrRule* AnyCssDeclarationOrRule = AnyCssRule - | AnyCssDeclarationWithSemicolon + | CssDeclarationWithSemicolon + | CssEmptyDeclaration | CssBogus | CssMetavariable @@ -468,8 +469,9 @@ CssDeclarationOrAtRuleBlock = CssDeclarationOrAtRuleList = AnyCssDeclarationOrAtRule* AnyCssDeclarationOrAtRule = - AnyCssDeclarationWithSemicolon - | CssAtRule + CssDeclarationWithSemicolon + | CssEmptyDeclaration + | CssAtRule // When nested in this way, the contents of a nested group rule's block are parsed as rather than : // https://drafts.csswg.org/css-nesting-1/#conditionals @@ -478,10 +480,6 @@ AnyCssConditionalBlock = | CssRuleBlock | CssBogusBlock -AnyCssDeclarationWithSemicolon = - CssDeclarationWithSemicolon - | CssEmptyDeclaration - // @page :left { background: red; @media (500px <= width <= 500px) { } } // ^^^^^^^^^^^^^^^^ // NOTE: ';' is optional for the last declaration in a block. For declarations not at the end, the parser will raise an error if ';' is missing. @@ -501,7 +499,11 @@ CssDeclarationBlock = declarations: CssDeclarationList '}' -CssDeclarationList = AnyCssDeclarationWithSemicolon* +CssDeclarationList = AnyCssDeclaration* + +AnyCssDeclaration = + CssDeclarationWithSemicolon + | CssEmptyDeclaration AnyCssRuleBlock = CssRuleBlock @@ -1232,7 +1234,8 @@ CssPageAtRuleBlock = CssPageAtRuleItemList = AnyCssPageAtRuleItem* AnyCssPageAtRuleItem = - AnyCssDeclarationWithSemicolon + CssDeclarationWithSemicolon + | CssEmptyDeclaration | CssAtRule | CssMarginAtRule