From 49fd6b1c47cf8d6054eade88e47d89e9e98944ca Mon Sep 17 00:00:00 2001 From: Denis Bezrukov <6227442+denbezrukov@users.noreply.github.com> Date: Fri, 27 Feb 2026 14:07:13 +0200 Subject: [PATCH 1/4] feat(css): add support for `@container scroll-state` at-rule and associated queries --- .../src/generated/node_factory.rs | 70 ++ .../src/generated/syntax_factory.rs | 165 +++ .../src/css/any/container_query_in_parens.rs | 3 + ...ainer_scroll_state_and_combinable_query.rs | 25 + .../any/container_scroll_state_in_parens.rs | 21 + ...tainer_scroll_state_or_combinable_query.rs | 25 + .../css/any/container_scroll_state_query.rs | 30 + crates/biome_css_formatter/src/css/any/mod.rs | 4 + .../container_scroll_state_and_query.rs | 29 + .../container_scroll_state_in_parens.rs | 27 + .../container_scroll_state_not_query.rs | 16 + .../container_scroll_state_or_query.rs | 29 + .../container_scroll_state_query_in_parens.rs | 35 + .../src/css/auxiliary/mod.rs | 5 + crates/biome_css_formatter/src/generated.rs | 143 +++ .../scss/at-rule/container-scroll-state.scss | 17 + .../at-rule/container-scroll-state.scss.snap | 68 ++ .../src/syntax/at_rule/container/error.rs | 29 + .../src/syntax/at_rule/container/mod.rs | 177 +++- ...at_rule_container_and_query_error.css.snap | 9 +- .../at_rule_container_error.css.snap | 2 + .../at_rule_container_or_query_error.css.snap | 11 +- .../scss/at-rule/container-scroll-state.scss | 5 + .../at-rule/container-scroll-state.scss.snap | 295 ++++++ .../ok/at_rule/at_rule_container.css.snap | 73 +- .../scss/at-rule/container-scroll-state.scss | 9 + .../at-rule/container-scroll-state.scss.snap | 352 +++++++ crates/biome_css_syntax/src/generated/kind.rs | 5 + .../biome_css_syntax/src/generated/macros.rs | 26 + .../biome_css_syntax/src/generated/nodes.rs | 988 ++++++++++++++++++ .../src/generated/nodes_mut.rs | 100 ++ .../css_target_language/generated_mappings.rs | 17 + xtask/codegen/css.ungram | 61 ++ xtask/codegen/src/css_kinds_src.rs | 5 + 34 files changed, 2827 insertions(+), 49 deletions(-) create mode 100644 crates/biome_css_formatter/src/css/any/container_scroll_state_and_combinable_query.rs create mode 100644 crates/biome_css_formatter/src/css/any/container_scroll_state_in_parens.rs create mode 100644 crates/biome_css_formatter/src/css/any/container_scroll_state_or_combinable_query.rs create mode 100644 crates/biome_css_formatter/src/css/any/container_scroll_state_query.rs create mode 100644 crates/biome_css_formatter/src/css/auxiliary/container_scroll_state_and_query.rs create mode 100644 crates/biome_css_formatter/src/css/auxiliary/container_scroll_state_in_parens.rs create mode 100644 crates/biome_css_formatter/src/css/auxiliary/container_scroll_state_not_query.rs create mode 100644 crates/biome_css_formatter/src/css/auxiliary/container_scroll_state_or_query.rs create mode 100644 crates/biome_css_formatter/src/css/auxiliary/container_scroll_state_query_in_parens.rs create mode 100644 crates/biome_css_formatter/tests/specs/css/scss/at-rule/container-scroll-state.scss create mode 100644 crates/biome_css_formatter/tests/specs/css/scss/at-rule/container-scroll-state.scss.snap create mode 100644 crates/biome_css_parser/tests/css_test_suite/error/scss/at-rule/container-scroll-state.scss create mode 100644 crates/biome_css_parser/tests/css_test_suite/error/scss/at-rule/container-scroll-state.scss.snap create mode 100644 crates/biome_css_parser/tests/css_test_suite/ok/scss/at-rule/container-scroll-state.scss create mode 100644 crates/biome_css_parser/tests/css_test_suite/ok/scss/at-rule/container-scroll-state.scss.snap diff --git a/crates/biome_css_factory/src/generated/node_factory.rs b/crates/biome_css_factory/src/generated/node_factory.rs index 8c3c87b2ed5a..af854b8a06ea 100644 --- a/crates/biome_css_factory/src/generated/node_factory.rs +++ b/crates/biome_css_factory/src/generated/node_factory.rs @@ -482,6 +482,76 @@ pub fn css_container_query_in_parens( ], )) } +pub fn css_container_scroll_state_and_query( + left: CssContainerScrollStateInParens, + and_token: SyntaxToken, + right: AnyCssContainerScrollStateAndCombinableQuery, +) -> CssContainerScrollStateAndQuery { + CssContainerScrollStateAndQuery::unwrap_cast(SyntaxNode::new_detached( + CssSyntaxKind::CSS_CONTAINER_SCROLL_STATE_AND_QUERY, + [ + Some(SyntaxElement::Node(left.into_syntax())), + Some(SyntaxElement::Token(and_token)), + Some(SyntaxElement::Node(right.into_syntax())), + ], + )) +} +pub fn css_container_scroll_state_in_parens( + l_paren_token: SyntaxToken, + query: AnyCssContainerScrollStateInParens, + r_paren_token: SyntaxToken, +) -> CssContainerScrollStateInParens { + CssContainerScrollStateInParens::unwrap_cast(SyntaxNode::new_detached( + CssSyntaxKind::CSS_CONTAINER_SCROLL_STATE_IN_PARENS, + [ + Some(SyntaxElement::Token(l_paren_token)), + Some(SyntaxElement::Node(query.into_syntax())), + Some(SyntaxElement::Token(r_paren_token)), + ], + )) +} +pub fn css_container_scroll_state_not_query( + not_token: SyntaxToken, + query: CssContainerScrollStateInParens, +) -> CssContainerScrollStateNotQuery { + CssContainerScrollStateNotQuery::unwrap_cast(SyntaxNode::new_detached( + CssSyntaxKind::CSS_CONTAINER_SCROLL_STATE_NOT_QUERY, + [ + Some(SyntaxElement::Token(not_token)), + Some(SyntaxElement::Node(query.into_syntax())), + ], + )) +} +pub fn css_container_scroll_state_or_query( + left: CssContainerScrollStateInParens, + or_token: SyntaxToken, + right: AnyCssContainerScrollStateOrCombinableQuery, +) -> CssContainerScrollStateOrQuery { + CssContainerScrollStateOrQuery::unwrap_cast(SyntaxNode::new_detached( + CssSyntaxKind::CSS_CONTAINER_SCROLL_STATE_OR_QUERY, + [ + Some(SyntaxElement::Node(left.into_syntax())), + Some(SyntaxElement::Token(or_token)), + Some(SyntaxElement::Node(right.into_syntax())), + ], + )) +} +pub fn css_container_scroll_state_query_in_parens( + name: CssIdentifier, + l_paren_token: SyntaxToken, + query: AnyCssContainerScrollStateQuery, + r_paren_token: SyntaxToken, +) -> CssContainerScrollStateQueryInParens { + CssContainerScrollStateQueryInParens::unwrap_cast(SyntaxNode::new_detached( + CssSyntaxKind::CSS_CONTAINER_SCROLL_STATE_QUERY_IN_PARENS, + [ + Some(SyntaxElement::Node(name.into_syntax())), + Some(SyntaxElement::Token(l_paren_token)), + Some(SyntaxElement::Node(query.into_syntax())), + Some(SyntaxElement::Token(r_paren_token)), + ], + )) +} pub fn css_container_size_feature_in_parens( l_paren_token: SyntaxToken, feature: AnyCssQueryFeature, diff --git a/crates/biome_css_factory/src/generated/syntax_factory.rs b/crates/biome_css_factory/src/generated/syntax_factory.rs index 299210457397..187df272cced 100644 --- a/crates/biome_css_factory/src/generated/syntax_factory.rs +++ b/crates/biome_css_factory/src/generated/syntax_factory.rs @@ -878,6 +878,171 @@ impl SyntaxFactory for CssSyntaxFactory { } slots.into_node(CSS_CONTAINER_QUERY_IN_PARENS, children) } + CSS_CONTAINER_SCROLL_STATE_AND_QUERY => { + let mut elements = (&children).into_iter(); + let mut slots: RawNodeSlots<3usize> = RawNodeSlots::default(); + let mut current_element = elements.next(); + if let Some(element) = ¤t_element + && CssContainerScrollStateInParens::can_cast(element.kind()) + { + slots.mark_present(); + current_element = elements.next(); + } + slots.next_slot(); + if let Some(element) = ¤t_element + && element.kind() == T![and] + { + slots.mark_present(); + current_element = elements.next(); + } + slots.next_slot(); + if let Some(element) = ¤t_element + && AnyCssContainerScrollStateAndCombinableQuery::can_cast(element.kind()) + { + slots.mark_present(); + current_element = elements.next(); + } + slots.next_slot(); + if current_element.is_some() { + return RawSyntaxNode::new( + CSS_CONTAINER_SCROLL_STATE_AND_QUERY.to_bogus(), + children.into_iter().map(Some), + ); + } + slots.into_node(CSS_CONTAINER_SCROLL_STATE_AND_QUERY, children) + } + CSS_CONTAINER_SCROLL_STATE_IN_PARENS => { + let mut elements = (&children).into_iter(); + let mut slots: RawNodeSlots<3usize> = RawNodeSlots::default(); + let mut current_element = elements.next(); + if let Some(element) = ¤t_element + && element.kind() == T!['('] + { + slots.mark_present(); + current_element = elements.next(); + } + slots.next_slot(); + if let Some(element) = ¤t_element + && AnyCssContainerScrollStateInParens::can_cast(element.kind()) + { + slots.mark_present(); + current_element = elements.next(); + } + slots.next_slot(); + if let Some(element) = ¤t_element + && element.kind() == T![')'] + { + slots.mark_present(); + current_element = elements.next(); + } + slots.next_slot(); + if current_element.is_some() { + return RawSyntaxNode::new( + CSS_CONTAINER_SCROLL_STATE_IN_PARENS.to_bogus(), + children.into_iter().map(Some), + ); + } + slots.into_node(CSS_CONTAINER_SCROLL_STATE_IN_PARENS, children) + } + CSS_CONTAINER_SCROLL_STATE_NOT_QUERY => { + let mut elements = (&children).into_iter(); + let mut slots: RawNodeSlots<2usize> = RawNodeSlots::default(); + let mut current_element = elements.next(); + if let Some(element) = ¤t_element + && element.kind() == T![not] + { + slots.mark_present(); + current_element = elements.next(); + } + slots.next_slot(); + if let Some(element) = ¤t_element + && CssContainerScrollStateInParens::can_cast(element.kind()) + { + slots.mark_present(); + current_element = elements.next(); + } + slots.next_slot(); + if current_element.is_some() { + return RawSyntaxNode::new( + CSS_CONTAINER_SCROLL_STATE_NOT_QUERY.to_bogus(), + children.into_iter().map(Some), + ); + } + slots.into_node(CSS_CONTAINER_SCROLL_STATE_NOT_QUERY, children) + } + CSS_CONTAINER_SCROLL_STATE_OR_QUERY => { + let mut elements = (&children).into_iter(); + let mut slots: RawNodeSlots<3usize> = RawNodeSlots::default(); + let mut current_element = elements.next(); + if let Some(element) = ¤t_element + && CssContainerScrollStateInParens::can_cast(element.kind()) + { + slots.mark_present(); + current_element = elements.next(); + } + slots.next_slot(); + if let Some(element) = ¤t_element + && element.kind() == T![or] + { + slots.mark_present(); + current_element = elements.next(); + } + slots.next_slot(); + if let Some(element) = ¤t_element + && AnyCssContainerScrollStateOrCombinableQuery::can_cast(element.kind()) + { + slots.mark_present(); + current_element = elements.next(); + } + slots.next_slot(); + if current_element.is_some() { + return RawSyntaxNode::new( + CSS_CONTAINER_SCROLL_STATE_OR_QUERY.to_bogus(), + children.into_iter().map(Some), + ); + } + slots.into_node(CSS_CONTAINER_SCROLL_STATE_OR_QUERY, children) + } + CSS_CONTAINER_SCROLL_STATE_QUERY_IN_PARENS => { + let mut elements = (&children).into_iter(); + let mut slots: RawNodeSlots<4usize> = RawNodeSlots::default(); + let mut current_element = elements.next(); + if let Some(element) = ¤t_element + && CssIdentifier::can_cast(element.kind()) + { + slots.mark_present(); + current_element = elements.next(); + } + slots.next_slot(); + if let Some(element) = ¤t_element + && element.kind() == T!['('] + { + slots.mark_present(); + current_element = elements.next(); + } + slots.next_slot(); + if let Some(element) = ¤t_element + && AnyCssContainerScrollStateQuery::can_cast(element.kind()) + { + slots.mark_present(); + current_element = elements.next(); + } + slots.next_slot(); + if let Some(element) = ¤t_element + && element.kind() == T![')'] + { + slots.mark_present(); + current_element = elements.next(); + } + slots.next_slot(); + if current_element.is_some() { + return RawSyntaxNode::new( + CSS_CONTAINER_SCROLL_STATE_QUERY_IN_PARENS.to_bogus(), + children.into_iter().map(Some), + ); + } + slots.into_node(CSS_CONTAINER_SCROLL_STATE_QUERY_IN_PARENS, children) + } CSS_CONTAINER_SIZE_FEATURE_IN_PARENS => { let mut elements = (&children).into_iter(); let mut slots: RawNodeSlots<3usize> = RawNodeSlots::default(); diff --git a/crates/biome_css_formatter/src/css/any/container_query_in_parens.rs b/crates/biome_css_formatter/src/css/any/container_query_in_parens.rs index 87b972a57f8e..8efbc85a6059 100644 --- a/crates/biome_css_formatter/src/css/any/container_query_in_parens.rs +++ b/crates/biome_css_formatter/src/css/any/container_query_in_parens.rs @@ -10,6 +10,9 @@ impl FormatRule for FormatAnyCssContainerQueryInPa match node { AnyCssContainerQueryInParens::AnyCssValue(node) => node.format().fmt(f), AnyCssContainerQueryInParens::CssContainerQueryInParens(node) => node.format().fmt(f), + AnyCssContainerQueryInParens::CssContainerScrollStateQueryInParens(node) => { + node.format().fmt(f) + } AnyCssContainerQueryInParens::CssContainerSizeFeatureInParens(node) => { node.format().fmt(f) } diff --git a/crates/biome_css_formatter/src/css/any/container_scroll_state_and_combinable_query.rs b/crates/biome_css_formatter/src/css/any/container_scroll_state_and_combinable_query.rs new file mode 100644 index 000000000000..5cc4dcbe18dc --- /dev/null +++ b/crates/biome_css_formatter/src/css/any/container_scroll_state_and_combinable_query.rs @@ -0,0 +1,25 @@ +//! 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::AnyCssContainerScrollStateAndCombinableQuery; +#[derive(Debug, Clone, Default)] +pub(crate) struct FormatAnyCssContainerScrollStateAndCombinableQuery; +impl FormatRule + for FormatAnyCssContainerScrollStateAndCombinableQuery +{ + type Context = CssFormatContext; + fn fmt( + &self, + node: &AnyCssContainerScrollStateAndCombinableQuery, + f: &mut CssFormatter, + ) -> FormatResult<()> { + match node { + AnyCssContainerScrollStateAndCombinableQuery::CssContainerScrollStateAndQuery(node) => { + node.format().fmt(f) + } + AnyCssContainerScrollStateAndCombinableQuery::CssContainerScrollStateInParens(node) => { + node.format().fmt(f) + } + } + } +} diff --git a/crates/biome_css_formatter/src/css/any/container_scroll_state_in_parens.rs b/crates/biome_css_formatter/src/css/any/container_scroll_state_in_parens.rs new file mode 100644 index 000000000000..03f598c87fd3 --- /dev/null +++ b/crates/biome_css_formatter/src/css/any/container_scroll_state_in_parens.rs @@ -0,0 +1,21 @@ +//! 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::AnyCssContainerScrollStateInParens; +#[derive(Debug, Clone, Default)] +pub(crate) struct FormatAnyCssContainerScrollStateInParens; +impl FormatRule for FormatAnyCssContainerScrollStateInParens { + type Context = CssFormatContext; + fn fmt( + &self, + node: &AnyCssContainerScrollStateInParens, + f: &mut CssFormatter, + ) -> FormatResult<()> { + match node { + AnyCssContainerScrollStateInParens::AnyCssContainerScrollStateQuery(node) => { + node.format().fmt(f) + } + AnyCssContainerScrollStateInParens::AnyCssValue(node) => node.format().fmt(f), + } + } +} diff --git a/crates/biome_css_formatter/src/css/any/container_scroll_state_or_combinable_query.rs b/crates/biome_css_formatter/src/css/any/container_scroll_state_or_combinable_query.rs new file mode 100644 index 000000000000..32cb3ddd46d1 --- /dev/null +++ b/crates/biome_css_formatter/src/css/any/container_scroll_state_or_combinable_query.rs @@ -0,0 +1,25 @@ +//! 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::AnyCssContainerScrollStateOrCombinableQuery; +#[derive(Debug, Clone, Default)] +pub(crate) struct FormatAnyCssContainerScrollStateOrCombinableQuery; +impl FormatRule + for FormatAnyCssContainerScrollStateOrCombinableQuery +{ + type Context = CssFormatContext; + fn fmt( + &self, + node: &AnyCssContainerScrollStateOrCombinableQuery, + f: &mut CssFormatter, + ) -> FormatResult<()> { + match node { + AnyCssContainerScrollStateOrCombinableQuery::CssContainerScrollStateInParens(node) => { + node.format().fmt(f) + } + AnyCssContainerScrollStateOrCombinableQuery::CssContainerScrollStateOrQuery(node) => { + node.format().fmt(f) + } + } + } +} diff --git a/crates/biome_css_formatter/src/css/any/container_scroll_state_query.rs b/crates/biome_css_formatter/src/css/any/container_scroll_state_query.rs new file mode 100644 index 000000000000..406d65201dc5 --- /dev/null +++ b/crates/biome_css_formatter/src/css/any/container_scroll_state_query.rs @@ -0,0 +1,30 @@ +//! 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::AnyCssContainerScrollStateQuery; +#[derive(Debug, Clone, Default)] +pub(crate) struct FormatAnyCssContainerScrollStateQuery; +impl FormatRule for FormatAnyCssContainerScrollStateQuery { + type Context = CssFormatContext; + fn fmt( + &self, + node: &AnyCssContainerScrollStateQuery, + f: &mut CssFormatter, + ) -> FormatResult<()> { + match node { + AnyCssContainerScrollStateQuery::AnyCssQueryFeature(node) => node.format().fmt(f), + AnyCssContainerScrollStateQuery::CssContainerScrollStateAndQuery(node) => { + node.format().fmt(f) + } + AnyCssContainerScrollStateQuery::CssContainerScrollStateInParens(node) => { + node.format().fmt(f) + } + AnyCssContainerScrollStateQuery::CssContainerScrollStateNotQuery(node) => { + node.format().fmt(f) + } + AnyCssContainerScrollStateQuery::CssContainerScrollStateOrQuery(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 0ed14d030401..3a8ff9f170ec 100644 --- a/crates/biome_css_formatter/src/css/any/mod.rs +++ b/crates/biome_css_formatter/src/css/any/mod.rs @@ -14,6 +14,10 @@ pub(crate) mod container_and_combinable_query; pub(crate) mod container_or_combinable_query; pub(crate) mod container_query; pub(crate) mod container_query_in_parens; +pub(crate) mod container_scroll_state_and_combinable_query; +pub(crate) mod container_scroll_state_in_parens; +pub(crate) mod container_scroll_state_or_combinable_query; +pub(crate) mod container_scroll_state_query; pub(crate) mod container_style_and_combinable_query; pub(crate) mod container_style_in_parens; pub(crate) mod container_style_or_combinable_query; diff --git a/crates/biome_css_formatter/src/css/auxiliary/container_scroll_state_and_query.rs b/crates/biome_css_formatter/src/css/auxiliary/container_scroll_state_and_query.rs new file mode 100644 index 000000000000..e870c075cb9a --- /dev/null +++ b/crates/biome_css_formatter/src/css/auxiliary/container_scroll_state_and_query.rs @@ -0,0 +1,29 @@ +use crate::prelude::*; +use biome_css_syntax::{CssContainerScrollStateAndQuery, CssContainerScrollStateAndQueryFields}; +use biome_formatter::write; +#[derive(Debug, Clone, Default)] +pub(crate) struct FormatCssContainerScrollStateAndQuery; +impl FormatNodeRule for FormatCssContainerScrollStateAndQuery { + fn fmt_fields( + &self, + node: &CssContainerScrollStateAndQuery, + f: &mut CssFormatter, + ) -> FormatResult<()> { + let CssContainerScrollStateAndQueryFields { + left, + and_token, + right, + } = node.as_fields(); + + write!( + f, + [ + left.format(), + space(), + and_token.format(), + space(), + right.format() + ] + ) + } +} diff --git a/crates/biome_css_formatter/src/css/auxiliary/container_scroll_state_in_parens.rs b/crates/biome_css_formatter/src/css/auxiliary/container_scroll_state_in_parens.rs new file mode 100644 index 000000000000..28df5241eb94 --- /dev/null +++ b/crates/biome_css_formatter/src/css/auxiliary/container_scroll_state_in_parens.rs @@ -0,0 +1,27 @@ +use crate::prelude::*; +use biome_css_syntax::{CssContainerScrollStateInParens, CssContainerScrollStateInParensFields}; +use biome_formatter::{format_args, write}; +#[derive(Debug, Clone, Default)] +pub(crate) struct FormatCssContainerScrollStateInParens; +impl FormatNodeRule for FormatCssContainerScrollStateInParens { + fn fmt_fields( + &self, + node: &CssContainerScrollStateInParens, + f: &mut CssFormatter, + ) -> FormatResult<()> { + let CssContainerScrollStateInParensFields { + l_paren_token, + query, + r_paren_token, + } = node.as_fields(); + + write!( + f, + [group(&format_args![ + l_paren_token.format(), + &soft_block_indent(&query.format()), + r_paren_token.format() + ])] + ) + } +} diff --git a/crates/biome_css_formatter/src/css/auxiliary/container_scroll_state_not_query.rs b/crates/biome_css_formatter/src/css/auxiliary/container_scroll_state_not_query.rs new file mode 100644 index 000000000000..7bcc47c0a391 --- /dev/null +++ b/crates/biome_css_formatter/src/css/auxiliary/container_scroll_state_not_query.rs @@ -0,0 +1,16 @@ +use crate::prelude::*; +use biome_css_syntax::{CssContainerScrollStateNotQuery, CssContainerScrollStateNotQueryFields}; +use biome_formatter::write; +#[derive(Debug, Clone, Default)] +pub(crate) struct FormatCssContainerScrollStateNotQuery; +impl FormatNodeRule for FormatCssContainerScrollStateNotQuery { + fn fmt_fields( + &self, + node: &CssContainerScrollStateNotQuery, + f: &mut CssFormatter, + ) -> FormatResult<()> { + let CssContainerScrollStateNotQueryFields { not_token, query } = node.as_fields(); + + write!(f, [not_token.format(), space(), query.format()]) + } +} diff --git a/crates/biome_css_formatter/src/css/auxiliary/container_scroll_state_or_query.rs b/crates/biome_css_formatter/src/css/auxiliary/container_scroll_state_or_query.rs new file mode 100644 index 000000000000..5e109392c265 --- /dev/null +++ b/crates/biome_css_formatter/src/css/auxiliary/container_scroll_state_or_query.rs @@ -0,0 +1,29 @@ +use crate::prelude::*; +use biome_css_syntax::{CssContainerScrollStateOrQuery, CssContainerScrollStateOrQueryFields}; +use biome_formatter::write; +#[derive(Debug, Clone, Default)] +pub(crate) struct FormatCssContainerScrollStateOrQuery; +impl FormatNodeRule for FormatCssContainerScrollStateOrQuery { + fn fmt_fields( + &self, + node: &CssContainerScrollStateOrQuery, + f: &mut CssFormatter, + ) -> FormatResult<()> { + let CssContainerScrollStateOrQueryFields { + left, + or_token, + right, + } = node.as_fields(); + + write!( + f, + [ + left.format(), + space(), + or_token.format(), + space(), + right.format() + ] + ) + } +} diff --git a/crates/biome_css_formatter/src/css/auxiliary/container_scroll_state_query_in_parens.rs b/crates/biome_css_formatter/src/css/auxiliary/container_scroll_state_query_in_parens.rs new file mode 100644 index 000000000000..9e0a4b3e4d63 --- /dev/null +++ b/crates/biome_css_formatter/src/css/auxiliary/container_scroll_state_query_in_parens.rs @@ -0,0 +1,35 @@ +use crate::prelude::*; +use biome_css_syntax::{ + CssContainerScrollStateQueryInParens, CssContainerScrollStateQueryInParensFields, +}; +use biome_formatter::{format_args, write}; +#[derive(Debug, Clone, Default)] +pub(crate) struct FormatCssContainerScrollStateQueryInParens; +impl FormatNodeRule + for FormatCssContainerScrollStateQueryInParens +{ + fn fmt_fields( + &self, + node: &CssContainerScrollStateQueryInParens, + f: &mut CssFormatter, + ) -> FormatResult<()> { + let CssContainerScrollStateQueryInParensFields { + name, + l_paren_token, + query, + r_paren_token, + } = node.as_fields(); + + write!( + f, + [ + name.format(), + group(&format_args![ + l_paren_token.format(), + soft_block_indent(&query.format()), + r_paren_token.format() + ]) + ] + ) + } +} diff --git a/crates/biome_css_formatter/src/css/auxiliary/mod.rs b/crates/biome_css_formatter/src/css/auxiliary/mod.rs index e954a8ac3a78..3832b06fb108 100644 --- a/crates/biome_css_formatter/src/css/auxiliary/mod.rs +++ b/crates/biome_css_formatter/src/css/auxiliary/mod.rs @@ -17,6 +17,11 @@ pub(crate) mod container_at_rule_declarator; pub(crate) mod container_not_query; pub(crate) mod container_or_query; pub(crate) mod container_query_in_parens; +pub(crate) mod container_scroll_state_and_query; +pub(crate) mod container_scroll_state_in_parens; +pub(crate) mod container_scroll_state_not_query; +pub(crate) mod container_scroll_state_or_query; +pub(crate) mod container_scroll_state_query_in_parens; pub(crate) mod container_size_feature_in_parens; pub(crate) mod container_style_and_query; pub(crate) mod container_style_in_parens; diff --git a/crates/biome_css_formatter/src/generated.rs b/crates/biome_css_formatter/src/generated.rs index 97796303e7e1..ac8cc5f1a2d0 100644 --- a/crates/biome_css_formatter/src/generated.rs +++ b/crates/biome_css_formatter/src/generated.rs @@ -970,6 +970,83 @@ impl IntoFormat for biome_css_syntax::CssContainerQueryInParen FormatOwnedWithRule :: new (self , crate :: css :: auxiliary :: container_query_in_parens :: FormatCssContainerQueryInParens :: default ()) } } +impl FormatRule < biome_css_syntax :: CssContainerScrollStateAndQuery > for crate :: css :: auxiliary :: container_scroll_state_and_query :: FormatCssContainerScrollStateAndQuery { type Context = CssFormatContext ; # [inline (always)] fn fmt (& self , node : & biome_css_syntax :: CssContainerScrollStateAndQuery , f : & mut CssFormatter) -> FormatResult < () > { FormatNodeRule :: < biome_css_syntax :: CssContainerScrollStateAndQuery > :: fmt (self , node , f) } } +impl AsFormat for biome_css_syntax::CssContainerScrollStateAndQuery { + type Format < 'a > = FormatRefWithRule < 'a , biome_css_syntax :: CssContainerScrollStateAndQuery , crate :: css :: auxiliary :: container_scroll_state_and_query :: FormatCssContainerScrollStateAndQuery > ; + fn format(&self) -> Self::Format<'_> { + FormatRefWithRule :: new (self , crate :: css :: auxiliary :: container_scroll_state_and_query :: FormatCssContainerScrollStateAndQuery :: default ()) + } +} +impl IntoFormat for biome_css_syntax::CssContainerScrollStateAndQuery { + type Format = FormatOwnedWithRule < biome_css_syntax :: CssContainerScrollStateAndQuery , crate :: css :: auxiliary :: container_scroll_state_and_query :: FormatCssContainerScrollStateAndQuery > ; + fn into_format(self) -> Self::Format { + FormatOwnedWithRule :: new (self , crate :: css :: auxiliary :: container_scroll_state_and_query :: FormatCssContainerScrollStateAndQuery :: default ()) + } +} +impl FormatRule < biome_css_syntax :: CssContainerScrollStateInParens > for crate :: css :: auxiliary :: container_scroll_state_in_parens :: FormatCssContainerScrollStateInParens { type Context = CssFormatContext ; # [inline (always)] fn fmt (& self , node : & biome_css_syntax :: CssContainerScrollStateInParens , f : & mut CssFormatter) -> FormatResult < () > { FormatNodeRule :: < biome_css_syntax :: CssContainerScrollStateInParens > :: fmt (self , node , f) } } +impl AsFormat for biome_css_syntax::CssContainerScrollStateInParens { + type Format < 'a > = FormatRefWithRule < 'a , biome_css_syntax :: CssContainerScrollStateInParens , crate :: css :: auxiliary :: container_scroll_state_in_parens :: FormatCssContainerScrollStateInParens > ; + fn format(&self) -> Self::Format<'_> { + FormatRefWithRule :: new (self , crate :: css :: auxiliary :: container_scroll_state_in_parens :: FormatCssContainerScrollStateInParens :: default ()) + } +} +impl IntoFormat for biome_css_syntax::CssContainerScrollStateInParens { + type Format = FormatOwnedWithRule < biome_css_syntax :: CssContainerScrollStateInParens , crate :: css :: auxiliary :: container_scroll_state_in_parens :: FormatCssContainerScrollStateInParens > ; + fn into_format(self) -> Self::Format { + FormatOwnedWithRule :: new (self , crate :: css :: auxiliary :: container_scroll_state_in_parens :: FormatCssContainerScrollStateInParens :: default ()) + } +} +impl FormatRule < biome_css_syntax :: CssContainerScrollStateNotQuery > for crate :: css :: auxiliary :: container_scroll_state_not_query :: FormatCssContainerScrollStateNotQuery { type Context = CssFormatContext ; # [inline (always)] fn fmt (& self , node : & biome_css_syntax :: CssContainerScrollStateNotQuery , f : & mut CssFormatter) -> FormatResult < () > { FormatNodeRule :: < biome_css_syntax :: CssContainerScrollStateNotQuery > :: fmt (self , node , f) } } +impl AsFormat for biome_css_syntax::CssContainerScrollStateNotQuery { + type Format < 'a > = FormatRefWithRule < 'a , biome_css_syntax :: CssContainerScrollStateNotQuery , crate :: css :: auxiliary :: container_scroll_state_not_query :: FormatCssContainerScrollStateNotQuery > ; + fn format(&self) -> Self::Format<'_> { + FormatRefWithRule :: new (self , crate :: css :: auxiliary :: container_scroll_state_not_query :: FormatCssContainerScrollStateNotQuery :: default ()) + } +} +impl IntoFormat for biome_css_syntax::CssContainerScrollStateNotQuery { + type Format = FormatOwnedWithRule < biome_css_syntax :: CssContainerScrollStateNotQuery , crate :: css :: auxiliary :: container_scroll_state_not_query :: FormatCssContainerScrollStateNotQuery > ; + fn into_format(self) -> Self::Format { + FormatOwnedWithRule :: new (self , crate :: css :: auxiliary :: container_scroll_state_not_query :: FormatCssContainerScrollStateNotQuery :: default ()) + } +} +impl FormatRule + for crate::css::auxiliary::container_scroll_state_or_query::FormatCssContainerScrollStateOrQuery +{ + type Context = CssFormatContext; + #[inline(always)] + fn fmt( + &self, + node: &biome_css_syntax::CssContainerScrollStateOrQuery, + f: &mut CssFormatter, + ) -> FormatResult<()> { + FormatNodeRule::::fmt(self, node, f) + } +} +impl AsFormat for biome_css_syntax::CssContainerScrollStateOrQuery { + type Format < 'a > = FormatRefWithRule < 'a , biome_css_syntax :: CssContainerScrollStateOrQuery , crate :: css :: auxiliary :: container_scroll_state_or_query :: FormatCssContainerScrollStateOrQuery > ; + fn format(&self) -> Self::Format<'_> { + FormatRefWithRule :: new (self , crate :: css :: auxiliary :: container_scroll_state_or_query :: FormatCssContainerScrollStateOrQuery :: default ()) + } +} +impl IntoFormat for biome_css_syntax::CssContainerScrollStateOrQuery { + type Format = FormatOwnedWithRule < biome_css_syntax :: CssContainerScrollStateOrQuery , crate :: css :: auxiliary :: container_scroll_state_or_query :: FormatCssContainerScrollStateOrQuery > ; + fn into_format(self) -> Self::Format { + FormatOwnedWithRule :: new (self , crate :: css :: auxiliary :: container_scroll_state_or_query :: FormatCssContainerScrollStateOrQuery :: default ()) + } +} +impl FormatRule < biome_css_syntax :: CssContainerScrollStateQueryInParens > for crate :: css :: auxiliary :: container_scroll_state_query_in_parens :: FormatCssContainerScrollStateQueryInParens { type Context = CssFormatContext ; # [inline (always)] fn fmt (& self , node : & biome_css_syntax :: CssContainerScrollStateQueryInParens , f : & mut CssFormatter) -> FormatResult < () > { FormatNodeRule :: < biome_css_syntax :: CssContainerScrollStateQueryInParens > :: fmt (self , node , f) } } +impl AsFormat for biome_css_syntax::CssContainerScrollStateQueryInParens { + type Format < 'a > = FormatRefWithRule < 'a , biome_css_syntax :: CssContainerScrollStateQueryInParens , crate :: css :: auxiliary :: container_scroll_state_query_in_parens :: FormatCssContainerScrollStateQueryInParens > ; + fn format(&self) -> Self::Format<'_> { + FormatRefWithRule :: new (self , crate :: css :: auxiliary :: container_scroll_state_query_in_parens :: FormatCssContainerScrollStateQueryInParens :: default ()) + } +} +impl IntoFormat for biome_css_syntax::CssContainerScrollStateQueryInParens { + type Format = FormatOwnedWithRule < biome_css_syntax :: CssContainerScrollStateQueryInParens , crate :: css :: auxiliary :: container_scroll_state_query_in_parens :: FormatCssContainerScrollStateQueryInParens > ; + fn into_format(self) -> Self::Format { + FormatOwnedWithRule :: new (self , crate :: css :: auxiliary :: container_scroll_state_query_in_parens :: FormatCssContainerScrollStateQueryInParens :: default ()) + } +} impl FormatRule < biome_css_syntax :: CssContainerSizeFeatureInParens > for crate :: css :: auxiliary :: container_size_feature_in_parens :: FormatCssContainerSizeFeatureInParens { type Context = CssFormatContext ; # [inline (always)] fn fmt (& self , node : & biome_css_syntax :: CssContainerSizeFeatureInParens , f : & mut CssFormatter) -> FormatResult < () > { FormatNodeRule :: < biome_css_syntax :: CssContainerSizeFeatureInParens > :: fmt (self , node , f) } } impl AsFormat for biome_css_syntax::CssContainerSizeFeatureInParens { type Format < 'a > = FormatRefWithRule < 'a , biome_css_syntax :: CssContainerSizeFeatureInParens , crate :: css :: auxiliary :: container_size_feature_in_parens :: FormatCssContainerSizeFeatureInParens > ; @@ -10699,6 +10776,72 @@ impl IntoFormat for biome_css_syntax::AnyCssContainerQueryInPa ) } } +impl AsFormat for biome_css_syntax::AnyCssContainerScrollStateAndCombinableQuery { + type Format < 'a > = FormatRefWithRule < 'a , biome_css_syntax :: AnyCssContainerScrollStateAndCombinableQuery , crate :: css :: any :: container_scroll_state_and_combinable_query :: FormatAnyCssContainerScrollStateAndCombinableQuery > ; + fn format(&self) -> Self::Format<'_> { + FormatRefWithRule :: new (self , crate :: css :: any :: container_scroll_state_and_combinable_query :: FormatAnyCssContainerScrollStateAndCombinableQuery :: default ()) + } +} +impl IntoFormat + for biome_css_syntax::AnyCssContainerScrollStateAndCombinableQuery +{ + type Format = FormatOwnedWithRule < biome_css_syntax :: AnyCssContainerScrollStateAndCombinableQuery , crate :: css :: any :: container_scroll_state_and_combinable_query :: FormatAnyCssContainerScrollStateAndCombinableQuery > ; + fn into_format(self) -> Self::Format { + FormatOwnedWithRule :: new (self , crate :: css :: any :: container_scroll_state_and_combinable_query :: FormatAnyCssContainerScrollStateAndCombinableQuery :: default ()) + } +} +impl AsFormat for biome_css_syntax::AnyCssContainerScrollStateInParens { + type Format<'a> = FormatRefWithRule< + 'a, + biome_css_syntax::AnyCssContainerScrollStateInParens, + crate::css::any::container_scroll_state_in_parens::FormatAnyCssContainerScrollStateInParens, + >; + fn format(&self) -> Self::Format<'_> { + FormatRefWithRule :: new (self , crate :: css :: any :: container_scroll_state_in_parens :: FormatAnyCssContainerScrollStateInParens :: default ()) + } +} +impl IntoFormat for biome_css_syntax::AnyCssContainerScrollStateInParens { + type Format = FormatOwnedWithRule< + biome_css_syntax::AnyCssContainerScrollStateInParens, + crate::css::any::container_scroll_state_in_parens::FormatAnyCssContainerScrollStateInParens, + >; + fn into_format(self) -> Self::Format { + FormatOwnedWithRule :: new (self , crate :: css :: any :: container_scroll_state_in_parens :: FormatAnyCssContainerScrollStateInParens :: default ()) + } +} +impl AsFormat for biome_css_syntax::AnyCssContainerScrollStateOrCombinableQuery { + type Format < 'a > = FormatRefWithRule < 'a , biome_css_syntax :: AnyCssContainerScrollStateOrCombinableQuery , crate :: css :: any :: container_scroll_state_or_combinable_query :: FormatAnyCssContainerScrollStateOrCombinableQuery > ; + fn format(&self) -> Self::Format<'_> { + FormatRefWithRule :: new (self , crate :: css :: any :: container_scroll_state_or_combinable_query :: FormatAnyCssContainerScrollStateOrCombinableQuery :: default ()) + } +} +impl IntoFormat + for biome_css_syntax::AnyCssContainerScrollStateOrCombinableQuery +{ + type Format = FormatOwnedWithRule < biome_css_syntax :: AnyCssContainerScrollStateOrCombinableQuery , crate :: css :: any :: container_scroll_state_or_combinable_query :: FormatAnyCssContainerScrollStateOrCombinableQuery > ; + fn into_format(self) -> Self::Format { + FormatOwnedWithRule :: new (self , crate :: css :: any :: container_scroll_state_or_combinable_query :: FormatAnyCssContainerScrollStateOrCombinableQuery :: default ()) + } +} +impl AsFormat for biome_css_syntax::AnyCssContainerScrollStateQuery { + type Format<'a> = FormatRefWithRule< + 'a, + biome_css_syntax::AnyCssContainerScrollStateQuery, + crate::css::any::container_scroll_state_query::FormatAnyCssContainerScrollStateQuery, + >; + fn format(&self) -> Self::Format<'_> { + FormatRefWithRule :: new (self , crate :: css :: any :: container_scroll_state_query :: FormatAnyCssContainerScrollStateQuery :: default ()) + } +} +impl IntoFormat for biome_css_syntax::AnyCssContainerScrollStateQuery { + type Format = FormatOwnedWithRule< + biome_css_syntax::AnyCssContainerScrollStateQuery, + crate::css::any::container_scroll_state_query::FormatAnyCssContainerScrollStateQuery, + >; + fn into_format(self) -> Self::Format { + FormatOwnedWithRule :: new (self , crate :: css :: any :: container_scroll_state_query :: FormatAnyCssContainerScrollStateQuery :: default ()) + } +} impl AsFormat for biome_css_syntax::AnyCssContainerStyleAndCombinableQuery { type Format < 'a > = FormatRefWithRule < 'a , biome_css_syntax :: AnyCssContainerStyleAndCombinableQuery , crate :: css :: any :: container_style_and_combinable_query :: FormatAnyCssContainerStyleAndCombinableQuery > ; fn format(&self) -> Self::Format<'_> { diff --git a/crates/biome_css_formatter/tests/specs/css/scss/at-rule/container-scroll-state.scss b/crates/biome_css_formatter/tests/specs/css/scss/at-rule/container-scroll-state.scss new file mode 100644 index 000000000000..360cf87141a5 --- /dev/null +++ b/crates/biome_css_formatter/tests/specs/css/scss/at-rule/container-scroll-state.scss @@ -0,0 +1,17 @@ +@container scroll-state( scrolled: bottom ) { } + +@container scroll-state( not ( stuck ) ) { } + +@container scroll-state( ( stuck ) and ( scrolled: bottom ) ) { } + +@container scroll-state( ( stuck ) or ( scrolled: bottom ) ) { } + +@container main-layout scroll-state( not (( stuck ) and ( scrolled: bottom )) ) { } + +@container scroll-state( ( stuck ) and ( scrolled: bottom ) and ( snapped: y ) ) { } + +@container scroll-state( + ( stuck ) + or + ( snapped: x ) +) { } diff --git a/crates/biome_css_formatter/tests/specs/css/scss/at-rule/container-scroll-state.scss.snap b/crates/biome_css_formatter/tests/specs/css/scss/at-rule/container-scroll-state.scss.snap new file mode 100644 index 000000000000..069ff0dc7181 --- /dev/null +++ b/crates/biome_css_formatter/tests/specs/css/scss/at-rule/container-scroll-state.scss.snap @@ -0,0 +1,68 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +assertion_line: 212 +info: css/scss/at-rule/container-scroll-state.scss +--- + +# Input + +```scss +@container scroll-state( scrolled: bottom ) { } + +@container scroll-state( not ( stuck ) ) { } + +@container scroll-state( ( stuck ) and ( scrolled: bottom ) ) { } + +@container scroll-state( ( stuck ) or ( scrolled: bottom ) ) { } + +@container main-layout scroll-state( not (( stuck ) and ( scrolled: bottom )) ) { } + +@container scroll-state( ( stuck ) and ( scrolled: bottom ) and ( snapped: y ) ) { } + +@container scroll-state( + ( stuck ) + or + ( snapped: x ) +) { } + +``` + + +============================= + +# Outputs + +## Output 1 + +----- +Indent style: Tab +Indent width: 2 +Line ending: LF +Line width: 80 +Quote style: Double Quotes +Trailing newline: true +----- + +```scss +@container scroll-state(scrolled: bottom) { +} + +@container scroll-state(not (stuck)) { +} + +@container scroll-state((stuck) and (scrolled: bottom)) { +} + +@container scroll-state((stuck) or (scrolled: bottom)) { +} + +@container main-layout scroll-state(not ((stuck) and (scrolled: bottom))) { +} + +@container scroll-state((stuck) and (scrolled: bottom) and (snapped: y)) { +} + +@container scroll-state((stuck) or (snapped: x)) { +} + +``` diff --git a/crates/biome_css_parser/src/syntax/at_rule/container/error.rs b/crates/biome_css_parser/src/syntax/at_rule/container/error.rs index 2547634e6acf..706a78edf2fb 100644 --- a/crates/biome_css_parser/src/syntax/at_rule/container/error.rs +++ b/crates/biome_css_parser/src/syntax/at_rule/container/error.rs @@ -23,6 +23,7 @@ pub(crate) fn expected_any_container_query_in_parens( &[ "( )", "( )", + "scroll-state( )", "style( )", ], range, @@ -30,6 +31,34 @@ pub(crate) fn expected_any_container_query_in_parens( .into_diagnostic(p) } +pub(crate) fn expected_any_container_scroll_state_query( + p: &CssParser, + range: TextRange, +) -> ParseDiagnostic { + expect_one_of( + &[ + "not ", + "", + "", + " and ", + " or ", + ], + range, + ) + .into_diagnostic(p) +} + +pub(crate) fn expected_any_container_scroll_state_in_parens( + p: &CssParser, + range: TextRange, +) -> ParseDiagnostic { + expect_one_of( + &["( )", "( )"], + range, + ) + .into_diagnostic(p) +} + pub(crate) fn expected_any_container_style_query( p: &CssParser, range: TextRange, diff --git a/crates/biome_css_parser/src/syntax/at_rule/container/mod.rs b/crates/biome_css_parser/src/syntax/at_rule/container/mod.rs index 6ef43601de10..01f96fd1208d 100644 --- a/crates/biome_css_parser/src/syntax/at_rule/container/mod.rs +++ b/crates/biome_css_parser/src/syntax/at_rule/container/mod.rs @@ -3,6 +3,7 @@ pub(crate) mod error; use crate::lexer::CssLexContext; use crate::parser::CssParser; use crate::syntax::at_rule::container::error::{ + expected_any_container_scroll_state_in_parens, expected_any_container_scroll_state_query, expected_any_container_style_in_parens, expected_any_container_style_query, }; use crate::syntax::at_rule::error::{ @@ -13,7 +14,7 @@ use crate::syntax::block::parse_conditional_block; use crate::syntax::parse_error::expected_non_css_wide_keyword_identifier; use crate::syntax::value::function::is_nth_at_function; use crate::syntax::{ - is_at_declaration, parse_any_value, parse_custom_identifier, parse_declaration, + is_at_declaration, parse_custom_identifier, parse_declaration, parse_regular_identifier, }; use biome_css_syntax::CssSyntaxKind::*; use biome_css_syntax::{CssSyntaxKind, T}; @@ -213,7 +214,179 @@ fn parse_container_scroll_state_query(p: &mut CssParser) -> ParsedSyntax { return Absent; } - parse_any_value(p) + let m = p.start(); + + parse_regular_identifier(p).ok(); + p.bump(T!['(']); + parse_any_container_scroll_state_query(p) + .or_recover( + p, + &AnyInParensParseRecovery, + expected_any_container_scroll_state_query, + ) + .ok(); + p.expect(T![')']); + + Present(m.complete(p, CSS_CONTAINER_SCROLL_STATE_QUERY_IN_PARENS)) +} + +#[inline] +fn parse_any_container_scroll_state_query(p: &mut CssParser) -> ParsedSyntax { + if is_at_container_scroll_state_not_query(p) { + return parse_container_scroll_state_not_query(p); + } + + if is_at_container_scroll_state_in_parens(p) { + return parse_container_scroll_state_in_parens(p).map(|lhs| match p.cur() { + T![and] => parse_container_scroll_state_combinable_and_query(p, lhs), + T![or] => parse_container_scroll_state_combinable_or_query(p, lhs), + _ => lhs, + }); + } + + parse_any_query_feature(p) +} + +#[inline] +fn is_at_container_scroll_state_not_query(p: &mut CssParser) -> bool { + p.at(T![not]) +} + +#[inline] +fn parse_container_scroll_state_not_query(p: &mut CssParser) -> ParsedSyntax { + if !is_at_container_scroll_state_not_query(p) { + return Absent; + } + + let m = p.start(); + + p.bump(T![not]); + + parse_container_scroll_state_in_parens(p) + .or_recover( + p, + &AnyInParensParseRecovery, + expected_any_container_scroll_state_in_parens, + ) + .ok(); + + Present(m.complete(p, CSS_CONTAINER_SCROLL_STATE_NOT_QUERY)) +} + +#[inline] +fn parse_container_scroll_state_combinable_and_query( + p: &mut CssParser, + lhs: CompletedMarker, +) -> CompletedMarker { + if !p.at(T![and]) { + return lhs; + } + + let m = lhs.precede(p); + p.bump(T![and]); + + let recovery_result = parse_container_scroll_state_in_parens(p) + .or_recover( + p, + &AnyContainerScrollStateQueryInParensChainParseRecovery::new(T![and]), + expected_any_container_scroll_state_in_parens, + ) + .map(|rhs| parse_container_scroll_state_combinable_and_query(p, rhs)); + + if recovery_result.is_err() && p.at(T![and]) { + // If we're here, it seems that we have + // @container scroll-state((stuck) and and and ... + // parse_container_scroll_state_in_parens failed to parse, + // but the parser is already at a recovered position. + let m = p.start(); + let rhs = m.complete(p, CSS_BOGUS); + parse_container_scroll_state_combinable_and_query(p, rhs); + } + + m.complete(p, CSS_CONTAINER_SCROLL_STATE_AND_QUERY) +} + +#[inline] +fn parse_container_scroll_state_combinable_or_query( + p: &mut CssParser, + lhs: CompletedMarker, +) -> CompletedMarker { + if !p.at(T![or]) { + return lhs; + } + + let m = lhs.precede(p); + p.bump(T![or]); + + let recovery_result = parse_container_scroll_state_in_parens(p) + .or_recover( + p, + &AnyContainerScrollStateQueryInParensChainParseRecovery::new(T![or]), + expected_any_container_scroll_state_in_parens, + ) + .map(|rhs| parse_container_scroll_state_combinable_or_query(p, rhs)); + + if recovery_result.is_err() && p.at(T![or]) { + // If we're here, it seems that we have + // @container scroll-state((stuck) or or or ... + // parse_container_scroll_state_in_parens failed to parse, + // but the parser is already at a recovered position. + let m = p.start(); + let rhs = m.complete(p, CSS_BOGUS); + parse_container_scroll_state_combinable_or_query(p, rhs); + } + + m.complete(p, CSS_CONTAINER_SCROLL_STATE_OR_QUERY) +} + +struct AnyContainerScrollStateQueryInParensChainParseRecovery { + chain_kind: CssSyntaxKind, +} + +impl AnyContainerScrollStateQueryInParensChainParseRecovery { + fn new(chain_kind: CssSyntaxKind) -> Self { + Self { chain_kind } + } +} + +impl ParseRecovery for AnyContainerScrollStateQueryInParensChainParseRecovery { + type Kind = CssSyntaxKind; + type Parser<'source> = CssParser<'source>; + const RECOVERED_KIND: Self::Kind = CSS_BOGUS; + + fn is_at_recovered(&self, p: &mut Self::Parser<'_>) -> bool { + p.at(T!['(']) + || p.at(T![')']) + || p.at(T!['{']) + || p.at(self.chain_kind) + || p.has_preceding_line_break() + } +} + +#[inline] +fn is_at_container_scroll_state_in_parens(p: &mut CssParser) -> bool { + p.at(T!['(']) +} + +#[inline] +fn parse_container_scroll_state_in_parens(p: &mut CssParser) -> ParsedSyntax { + if !is_at_container_scroll_state_in_parens(p) { + return Absent; + } + + let m = p.start(); + p.bump(T!['(']); + + parse_any_container_scroll_state_query(p) + .or_recover( + p, + &AnyInParensParseRecovery, + expected_any_container_scroll_state_query, + ) + .ok(); + + p.expect(T![')']); + Present(m.complete(p, CSS_CONTAINER_SCROLL_STATE_IN_PARENS)) } /// Parses a negated container query using the `not(...)` syntax. diff --git a/crates/biome_css_parser/tests/css_test_suite/error/at_rule/at_rule_container/at_rule_container_and_query_error.css.snap b/crates/biome_css_parser/tests/css_test_suite/error/at_rule/at_rule_container/at_rule_container_and_query_error.css.snap index 54b1218fde3a..211d83cea5a1 100644 --- a/crates/biome_css_parser/tests/css_test_suite/error/at_rule/at_rule_container/at_rule_container_and_query_error.css.snap +++ b/crates/biome_css_parser/tests/css_test_suite/error/at_rule/at_rule_container/at_rule_container_and_query_error.css.snap @@ -1,6 +1,6 @@ --- source: crates/biome_css_parser/tests/spec_test.rs -assertion_line: 179 +assertion_line: 208 expression: snapshot --- @@ -657,6 +657,7 @@ at_rule_container_and_query_error.css:2:32 parse ━━━━━━━━━━ - ( ) - ( ) + - scroll-state( ) - style( ) at_rule_container_and_query_error.css:7:1 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ @@ -674,6 +675,7 @@ at_rule_container_and_query_error.css:7:1 parse ━━━━━━━━━━ - ( ) - ( ) + - scroll-state( ) - style( ) at_rule_container_and_query_error.css:10:32 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ @@ -690,6 +692,7 @@ at_rule_container_and_query_error.css:10:32 parse ━━━━━━━━━━ - ( ) - ( ) + - scroll-state( ) - style( ) at_rule_container_and_query_error.css:14:53 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ @@ -706,6 +709,7 @@ at_rule_container_and_query_error.css:14:53 parse ━━━━━━━━━━ - ( ) - ( ) + - scroll-state( ) - style( ) at_rule_container_and_query_error.css:18:32 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ @@ -722,6 +726,7 @@ at_rule_container_and_query_error.css:18:32 parse ━━━━━━━━━━ - ( ) - ( ) + - scroll-state( ) - style( ) at_rule_container_and_query_error.css:22:32 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ @@ -738,6 +743,7 @@ at_rule_container_and_query_error.css:22:32 parse ━━━━━━━━━━ - ( ) - ( ) + - scroll-state( ) - style( ) at_rule_container_and_query_error.css:26:32 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ @@ -754,6 +760,7 @@ at_rule_container_and_query_error.css:26:32 parse ━━━━━━━━━━ - ( ) - ( ) + - scroll-state( ) - style( ) ``` diff --git a/crates/biome_css_parser/tests/css_test_suite/error/at_rule/at_rule_container/at_rule_container_error.css.snap b/crates/biome_css_parser/tests/css_test_suite/error/at_rule/at_rule_container/at_rule_container_error.css.snap index 2b6cf0d44d57..9ee84345811d 100644 --- a/crates/biome_css_parser/tests/css_test_suite/error/at_rule/at_rule_container/at_rule_container_error.css.snap +++ b/crates/biome_css_parser/tests/css_test_suite/error/at_rule/at_rule_container/at_rule_container_error.css.snap @@ -1,5 +1,6 @@ --- source: crates/biome_css_parser/tests/spec_test.rs +assertion_line: 208 expression: snapshot --- @@ -963,6 +964,7 @@ at_rule_container_error.css:16:41 parse ━━━━━━━━━━━━━ - ( ) - ( ) + - scroll-state( ) - style( ) at_rule_container_error.css:18:34 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ diff --git a/crates/biome_css_parser/tests/css_test_suite/error/at_rule/at_rule_container/at_rule_container_or_query_error.css.snap b/crates/biome_css_parser/tests/css_test_suite/error/at_rule/at_rule_container/at_rule_container_or_query_error.css.snap index bc601d063270..2134896deffa 100644 --- a/crates/biome_css_parser/tests/css_test_suite/error/at_rule/at_rule_container/at_rule_container_or_query_error.css.snap +++ b/crates/biome_css_parser/tests/css_test_suite/error/at_rule/at_rule_container/at_rule_container_or_query_error.css.snap @@ -1,6 +1,6 @@ --- source: crates/biome_css_parser/tests/spec_test.rs -assertion_line: 179 +assertion_line: 208 expression: snapshot --- @@ -756,6 +756,7 @@ at_rule_container_or_query_error.css:3:31 parse ━━━━━━━━━━ - ( ) - ( ) + - scroll-state( ) - style( ) at_rule_container_or_query_error.css:8:1 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ @@ -773,6 +774,7 @@ at_rule_container_or_query_error.css:8:1 parse ━━━━━━━━━━━ - ( ) - ( ) + - scroll-state( ) - style( ) at_rule_container_or_query_error.css:11:31 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ @@ -789,6 +791,7 @@ at_rule_container_or_query_error.css:11:31 parse ━━━━━━━━━━ - ( ) - ( ) + - scroll-state( ) - style( ) at_rule_container_or_query_error.css:15:51 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ @@ -805,6 +808,7 @@ at_rule_container_or_query_error.css:15:51 parse ━━━━━━━━━━ - ( ) - ( ) + - scroll-state( ) - style( ) at_rule_container_or_query_error.css:19:31 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ @@ -821,6 +825,7 @@ at_rule_container_or_query_error.css:19:31 parse ━━━━━━━━━━ - ( ) - ( ) + - scroll-state( ) - style( ) at_rule_container_or_query_error.css:23:31 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ @@ -837,6 +842,7 @@ at_rule_container_or_query_error.css:23:31 parse ━━━━━━━━━━ - ( ) - ( ) + - scroll-state( ) - style( ) at_rule_container_or_query_error.css:27:31 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ @@ -853,6 +859,7 @@ at_rule_container_or_query_error.css:27:31 parse ━━━━━━━━━━ - ( ) - ( ) + - scroll-state( ) - style( ) at_rule_container_or_query_error.css:31:21 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ @@ -869,6 +876,7 @@ at_rule_container_or_query_error.css:31:21 parse ━━━━━━━━━━ - ( ) - ( ) + - scroll-state( ) - style( ) at_rule_container_or_query_error.css:34:21 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ @@ -886,6 +894,7 @@ at_rule_container_or_query_error.css:34:21 parse ━━━━━━━━━━ - ( ) - ( ) + - scroll-state( ) - style( ) at_rule_container_or_query_error.css:35:1 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ diff --git a/crates/biome_css_parser/tests/css_test_suite/error/scss/at-rule/container-scroll-state.scss b/crates/biome_css_parser/tests/css_test_suite/error/scss/at-rule/container-scroll-state.scss new file mode 100644 index 000000000000..d98aeb396136 --- /dev/null +++ b/crates/biome_css_parser/tests/css_test_suite/error/scss/at-rule/container-scroll-state.scss @@ -0,0 +1,5 @@ +@container scroll-state(scrolled: bottom and ) { } + +@container scroll-state(not stuck) { } + +@container scroll-state((stuck) or scrolled: bottom) { } diff --git a/crates/biome_css_parser/tests/css_test_suite/error/scss/at-rule/container-scroll-state.scss.snap b/crates/biome_css_parser/tests/css_test_suite/error/scss/at-rule/container-scroll-state.scss.snap new file mode 100644 index 000000000000..c64792201094 --- /dev/null +++ b/crates/biome_css_parser/tests/css_test_suite/error/scss/at-rule/container-scroll-state.scss.snap @@ -0,0 +1,295 @@ +--- +source: crates/biome_css_parser/tests/spec_test.rs +assertion_line: 208 +expression: snapshot +--- + +## Input + +```css +@container scroll-state(scrolled: bottom and ) { } + +@container scroll-state(not stuck) { } + +@container scroll-state((stuck) or scrolled: bottom) { } + +``` + + +## AST + +``` +CssRoot { + bom_token: missing (optional), + items: CssRootItemList [ + CssAtRule { + at_token: AT@0..1 "@" [] [], + rule: CssBogusAtRule { + items: [ + CssBogus { + items: [ + CONTAINER_KW@1..11 "container" [] [Whitespace(" ")], + CssBogus { + items: [ + CssContainerScrollStateQueryInParens { + name: CssIdentifier { + value_token: IDENT@11..23 "scroll-state" [] [], + }, + l_paren_token: L_PAREN@23..24 "(" [] [], + query: CssQueryFeaturePlain { + name: CssIdentifier { + value_token: IDENT@24..32 "scrolled" [] [], + }, + colon_token: COLON@32..34 ":" [] [Whitespace(" ")], + value: CssIdentifier { + value_token: IDENT@34..41 "bottom" [] [Whitespace(" ")], + }, + }, + r_paren_token: missing (required), + }, + AND_KW@41..45 "and" [] [Whitespace(" ")], + CssBogus { + items: [ + R_PAREN@45..47 ")" [] [Whitespace(" ")], + ], + }, + ], + }, + ], + }, + CssRuleBlock { + l_curly_token: L_CURLY@47..49 "{" [] [Whitespace(" ")], + rules: CssRuleList [], + r_curly_token: R_CURLY@49..50 "}" [] [], + }, + ], + }, + }, + CssAtRule { + at_token: AT@50..53 "@" [Newline("\n"), Newline("\n")] [], + rule: CssBogusAtRule { + items: [ + CssBogus { + items: [ + CONTAINER_KW@53..63 "container" [] [Whitespace(" ")], + CssBogus { + items: [ + CssIdentifier { + value_token: IDENT@63..75 "scroll-state" [] [], + }, + L_PAREN@75..76 "(" [] [], + CssBogus { + items: [ + NOT_KW@76..80 "not" [] [Whitespace(" ")], + CssBogus { + items: [ + IDENT@80..85 "stuck" [] [], + ], + }, + ], + }, + R_PAREN@85..87 ")" [] [Whitespace(" ")], + ], + }, + ], + }, + CssRuleBlock { + l_curly_token: L_CURLY@87..89 "{" [] [Whitespace(" ")], + rules: CssRuleList [], + r_curly_token: R_CURLY@89..90 "}" [] [], + }, + ], + }, + }, + CssAtRule { + at_token: AT@90..93 "@" [Newline("\n"), Newline("\n")] [], + rule: CssBogusAtRule { + items: [ + CssBogus { + items: [ + CONTAINER_KW@93..103 "container" [] [Whitespace(" ")], + CssBogus { + items: [ + CssIdentifier { + value_token: IDENT@103..115 "scroll-state" [] [], + }, + L_PAREN@115..116 "(" [] [], + CssBogus { + items: [ + CssContainerScrollStateInParens { + l_paren_token: L_PAREN@116..117 "(" [] [], + query: CssQueryFeatureBoolean { + name: CssIdentifier { + value_token: IDENT@117..122 "stuck" [] [], + }, + }, + r_paren_token: R_PAREN@122..124 ")" [] [Whitespace(" ")], + }, + OR_KW@124..127 "or" [] [Whitespace(" ")], + CssBogus { + items: [ + IDENT@127..135 "scrolled" [] [], + COLON@135..137 ":" [] [Whitespace(" ")], + IDENT@137..143 "bottom" [] [], + ], + }, + ], + }, + R_PAREN@143..145 ")" [] [Whitespace(" ")], + ], + }, + ], + }, + CssRuleBlock { + l_curly_token: L_CURLY@145..147 "{" [] [Whitespace(" ")], + rules: CssRuleList [], + r_curly_token: R_CURLY@147..148 "}" [] [], + }, + ], + }, + }, + ], + eof_token: EOF@148..149 "" [Newline("\n")] [], +} +``` + +## CST + +``` +0: CSS_ROOT@0..149 + 0: (empty) + 1: CSS_ROOT_ITEM_LIST@0..148 + 0: CSS_AT_RULE@0..50 + 0: AT@0..1 "@" [] [] + 1: CSS_BOGUS_AT_RULE@1..50 + 0: CSS_BOGUS@1..47 + 0: CONTAINER_KW@1..11 "container" [] [Whitespace(" ")] + 1: CSS_BOGUS@11..47 + 0: CSS_CONTAINER_SCROLL_STATE_QUERY_IN_PARENS@11..41 + 0: CSS_IDENTIFIER@11..23 + 0: IDENT@11..23 "scroll-state" [] [] + 1: L_PAREN@23..24 "(" [] [] + 2: CSS_QUERY_FEATURE_PLAIN@24..41 + 0: CSS_IDENTIFIER@24..32 + 0: IDENT@24..32 "scrolled" [] [] + 1: COLON@32..34 ":" [] [Whitespace(" ")] + 2: CSS_IDENTIFIER@34..41 + 0: IDENT@34..41 "bottom" [] [Whitespace(" ")] + 3: (empty) + 1: AND_KW@41..45 "and" [] [Whitespace(" ")] + 2: CSS_BOGUS@45..47 + 0: R_PAREN@45..47 ")" [] [Whitespace(" ")] + 1: CSS_RULE_BLOCK@47..50 + 0: L_CURLY@47..49 "{" [] [Whitespace(" ")] + 1: CSS_RULE_LIST@49..49 + 2: R_CURLY@49..50 "}" [] [] + 1: CSS_AT_RULE@50..90 + 0: AT@50..53 "@" [Newline("\n"), Newline("\n")] [] + 1: CSS_BOGUS_AT_RULE@53..90 + 0: CSS_BOGUS@53..87 + 0: CONTAINER_KW@53..63 "container" [] [Whitespace(" ")] + 1: CSS_BOGUS@63..87 + 0: CSS_IDENTIFIER@63..75 + 0: IDENT@63..75 "scroll-state" [] [] + 1: L_PAREN@75..76 "(" [] [] + 2: CSS_BOGUS@76..85 + 0: NOT_KW@76..80 "not" [] [Whitespace(" ")] + 1: CSS_BOGUS@80..85 + 0: IDENT@80..85 "stuck" [] [] + 3: R_PAREN@85..87 ")" [] [Whitespace(" ")] + 1: CSS_RULE_BLOCK@87..90 + 0: L_CURLY@87..89 "{" [] [Whitespace(" ")] + 1: CSS_RULE_LIST@89..89 + 2: R_CURLY@89..90 "}" [] [] + 2: CSS_AT_RULE@90..148 + 0: AT@90..93 "@" [Newline("\n"), Newline("\n")] [] + 1: CSS_BOGUS_AT_RULE@93..148 + 0: CSS_BOGUS@93..145 + 0: CONTAINER_KW@93..103 "container" [] [Whitespace(" ")] + 1: CSS_BOGUS@103..145 + 0: CSS_IDENTIFIER@103..115 + 0: IDENT@103..115 "scroll-state" [] [] + 1: L_PAREN@115..116 "(" [] [] + 2: CSS_BOGUS@116..143 + 0: CSS_CONTAINER_SCROLL_STATE_IN_PARENS@116..124 + 0: L_PAREN@116..117 "(" [] [] + 1: CSS_QUERY_FEATURE_BOOLEAN@117..122 + 0: CSS_IDENTIFIER@117..122 + 0: IDENT@117..122 "stuck" [] [] + 2: R_PAREN@122..124 ")" [] [Whitespace(" ")] + 1: OR_KW@124..127 "or" [] [Whitespace(" ")] + 2: CSS_BOGUS@127..143 + 0: IDENT@127..135 "scrolled" [] [] + 1: COLON@135..137 ":" [] [Whitespace(" ")] + 2: IDENT@137..143 "bottom" [] [] + 3: R_PAREN@143..145 ")" [] [Whitespace(" ")] + 1: CSS_RULE_BLOCK@145..148 + 0: L_CURLY@145..147 "{" [] [Whitespace(" ")] + 1: CSS_RULE_LIST@147..147 + 2: R_CURLY@147..148 "}" [] [] + 2: EOF@148..149 "" [Newline("\n")] [] + +``` + +## Diagnostics + +``` +container-scroll-state.scss:1:42 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × expected `)` but instead found `and` + + > 1 │ @container scroll-state(scrolled: bottom and ) { } + │ ^^^ + 2 │ + 3 │ @container scroll-state(not stuck) { } + + i Remove and + +container-scroll-state.scss:1:46 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × Unexpected value or character. + + > 1 │ @container scroll-state(scrolled: bottom and ) { } + │ ^ + 2 │ + 3 │ @container scroll-state(not stuck) { } + + i Expected one of: + + - ( ) + - ( ) + - scroll-state( ) + - style( ) + +container-scroll-state.scss:3:29 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × Unexpected value or character. + + 1 │ @container scroll-state(scrolled: bottom and ) { } + 2 │ + > 3 │ @container scroll-state(not stuck) { } + │ ^^^^^ + 4 │ + 5 │ @container scroll-state((stuck) or scrolled: bottom) { } + + i Expected one of: + + - ( ) + - ( ) + +container-scroll-state.scss:5:36 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × Unexpected value or character. + + 3 │ @container scroll-state(not stuck) { } + 4 │ + > 5 │ @container scroll-state((stuck) or scrolled: bottom) { } + │ ^^^^^^^^^^^^^^^^ + 6 │ + + i Expected one of: + + - ( ) + - ( ) + +``` diff --git a/crates/biome_css_parser/tests/css_test_suite/ok/at_rule/at_rule_container.css.snap b/crates/biome_css_parser/tests/css_test_suite/ok/at_rule/at_rule_container.css.snap index a741ae2b72a1..c56950042847 100644 --- a/crates/biome_css_parser/tests/css_test_suite/ok/at_rule/at_rule_container.css.snap +++ b/crates/biome_css_parser/tests/css_test_suite/ok/at_rule/at_rule_container.css.snap @@ -1,5 +1,6 @@ --- source: crates/biome_css_parser/tests/spec_test.rs +assertion_line: 208 expression: snapshot --- @@ -778,20 +779,16 @@ CssRoot { name: CssCustomIdentifier { value_token: IDENT@904..908 "not" [] [Whitespace(" ")], }, - query: CssFunction { + query: CssContainerScrollStateQueryInParens { name: CssIdentifier { value_token: IDENT@908..920 "scroll-state" [] [], }, l_paren_token: L_PAREN@920..921 "(" [] [], - items: CssParameterList [ - CssListOfComponentValuesExpression { - css_component_value_list: CssComponentValueList [ - CssIdentifier { - value_token: IDENT@921..926 "stuck" [] [], - }, - ], + query: CssQueryFeatureBoolean { + name: CssIdentifier { + value_token: IDENT@921..926 "stuck" [] [], }, - ], + }, r_paren_token: R_PAREN@926..928 ")" [] [Whitespace(" ")], }, }, @@ -812,20 +809,16 @@ CssRoot { }, query: CssContainerQueryInParens { l_paren_token: L_PAREN@949..950 "(" [] [], - query: CssFunction { + query: CssContainerScrollStateQueryInParens { name: CssIdentifier { value_token: IDENT@950..962 "scroll-state" [] [], }, l_paren_token: L_PAREN@962..963 "(" [] [], - items: CssParameterList [ - CssListOfComponentValuesExpression { - css_component_value_list: CssComponentValueList [ - CssIdentifier { - value_token: IDENT@963..968 "stuck" [] [], - }, - ], + query: CssQueryFeatureBoolean { + name: CssIdentifier { + value_token: IDENT@963..968 "stuck" [] [], }, - ], + }, r_paren_token: R_PAREN@968..969 ")" [] [], }, r_paren_token: R_PAREN@969..971 ")" [] [Whitespace(" ")], @@ -848,20 +841,16 @@ CssRoot { l_paren_token: L_PAREN@988..989 "(" [] [], query: CssContainerNotQuery { not_token: NOT_KW@989..993 "not" [] [Whitespace(" ")], - query: CssFunction { + query: CssContainerScrollStateQueryInParens { name: CssIdentifier { value_token: IDENT@993..1005 "scroll-state" [] [], }, l_paren_token: L_PAREN@1005..1006 "(" [] [], - items: CssParameterList [ - CssListOfComponentValuesExpression { - css_component_value_list: CssComponentValueList [ - CssIdentifier { - value_token: IDENT@1006..1011 "stuck" [] [], - }, - ], + query: CssQueryFeatureBoolean { + name: CssIdentifier { + value_token: IDENT@1006..1011 "stuck" [] [], }, - ], + }, r_paren_token: R_PAREN@1011..1012 ")" [] [], }, }, @@ -1385,15 +1374,13 @@ CssRoot { 0: CONTAINER_KW@894..904 "container" [] [Whitespace(" ")] 1: CSS_CUSTOM_IDENTIFIER@904..908 0: IDENT@904..908 "not" [] [Whitespace(" ")] - 2: CSS_FUNCTION@908..928 + 2: CSS_CONTAINER_SCROLL_STATE_QUERY_IN_PARENS@908..928 0: CSS_IDENTIFIER@908..920 0: IDENT@908..920 "scroll-state" [] [] 1: L_PAREN@920..921 "(" [] [] - 2: CSS_PARAMETER_LIST@921..926 - 0: CSS_LIST_OF_COMPONENT_VALUES_EXPRESSION@921..926 - 0: CSS_COMPONENT_VALUE_LIST@921..926 - 0: CSS_IDENTIFIER@921..926 - 0: IDENT@921..926 "stuck" [] [] + 2: CSS_QUERY_FEATURE_BOOLEAN@921..926 + 0: CSS_IDENTIFIER@921..926 + 0: IDENT@921..926 "stuck" [] [] 3: R_PAREN@926..928 ")" [] [Whitespace(" ")] 1: CSS_RULE_BLOCK@928..932 0: L_CURLY@928..931 "{" [] [Whitespace(" ")] @@ -1408,15 +1395,13 @@ CssRoot { 0: IDENT@945..949 "not" [] [Whitespace(" ")] 2: CSS_CONTAINER_QUERY_IN_PARENS@949..971 0: L_PAREN@949..950 "(" [] [] - 1: CSS_FUNCTION@950..969 + 1: CSS_CONTAINER_SCROLL_STATE_QUERY_IN_PARENS@950..969 0: CSS_IDENTIFIER@950..962 0: IDENT@950..962 "scroll-state" [] [] 1: L_PAREN@962..963 "(" [] [] - 2: CSS_PARAMETER_LIST@963..968 - 0: CSS_LIST_OF_COMPONENT_VALUES_EXPRESSION@963..968 - 0: CSS_COMPONENT_VALUE_LIST@963..968 - 0: CSS_IDENTIFIER@963..968 - 0: IDENT@963..968 "stuck" [] [] + 2: CSS_QUERY_FEATURE_BOOLEAN@963..968 + 0: CSS_IDENTIFIER@963..968 + 0: IDENT@963..968 "stuck" [] [] 3: R_PAREN@968..969 ")" [] [] 2: R_PAREN@969..971 ")" [] [Whitespace(" ")] 1: CSS_RULE_BLOCK@971..975 @@ -1433,15 +1418,13 @@ CssRoot { 0: L_PAREN@988..989 "(" [] [] 1: CSS_CONTAINER_NOT_QUERY@989..1012 0: NOT_KW@989..993 "not" [] [Whitespace(" ")] - 1: CSS_FUNCTION@993..1012 + 1: CSS_CONTAINER_SCROLL_STATE_QUERY_IN_PARENS@993..1012 0: CSS_IDENTIFIER@993..1005 0: IDENT@993..1005 "scroll-state" [] [] 1: L_PAREN@1005..1006 "(" [] [] - 2: CSS_PARAMETER_LIST@1006..1011 - 0: CSS_LIST_OF_COMPONENT_VALUES_EXPRESSION@1006..1011 - 0: CSS_COMPONENT_VALUE_LIST@1006..1011 - 0: CSS_IDENTIFIER@1006..1011 - 0: IDENT@1006..1011 "stuck" [] [] + 2: CSS_QUERY_FEATURE_BOOLEAN@1006..1011 + 0: CSS_IDENTIFIER@1006..1011 + 0: IDENT@1006..1011 "stuck" [] [] 3: R_PAREN@1011..1012 ")" [] [] 2: R_PAREN@1012..1014 ")" [] [Whitespace(" ")] 1: CSS_RULE_BLOCK@1014..1018 diff --git a/crates/biome_css_parser/tests/css_test_suite/ok/scss/at-rule/container-scroll-state.scss b/crates/biome_css_parser/tests/css_test_suite/ok/scss/at-rule/container-scroll-state.scss new file mode 100644 index 000000000000..768cfbe82e30 --- /dev/null +++ b/crates/biome_css_parser/tests/css_test_suite/ok/scss/at-rule/container-scroll-state.scss @@ -0,0 +1,9 @@ +@container scroll-state( scrolled: bottom ) { } + +@container not scroll-state(stuck) { } + +@container scroll-state(not (stuck)) { } + +@container scroll-state((stuck) and (scrolled: bottom)) { } + +@container scroll-state((stuck) or (scrolled: bottom)) { } diff --git a/crates/biome_css_parser/tests/css_test_suite/ok/scss/at-rule/container-scroll-state.scss.snap b/crates/biome_css_parser/tests/css_test_suite/ok/scss/at-rule/container-scroll-state.scss.snap new file mode 100644 index 000000000000..fadc93221b74 --- /dev/null +++ b/crates/biome_css_parser/tests/css_test_suite/ok/scss/at-rule/container-scroll-state.scss.snap @@ -0,0 +1,352 @@ +--- +source: crates/biome_css_parser/tests/spec_test.rs +assertion_line: 208 +expression: snapshot +--- + +## Input + +```css +@container scroll-state( scrolled: bottom ) { } + +@container not scroll-state(stuck) { } + +@container scroll-state(not (stuck)) { } + +@container scroll-state((stuck) and (scrolled: bottom)) { } + +@container scroll-state((stuck) or (scrolled: bottom)) { } + +``` + + +## AST + +``` +CssRoot { + bom_token: missing (optional), + items: CssRootItemList [ + CssAtRule { + at_token: AT@0..1 "@" [] [], + rule: CssContainerAtRule { + declarator: CssContainerAtRuleDeclarator { + container_token: CONTAINER_KW@1..13 "container" [] [Whitespace(" ")], + name: missing (optional), + query: CssContainerScrollStateQueryInParens { + name: CssIdentifier { + value_token: IDENT@13..25 "scroll-state" [] [], + }, + l_paren_token: L_PAREN@25..28 "(" [] [Whitespace(" ")], + query: CssQueryFeaturePlain { + name: CssIdentifier { + value_token: IDENT@28..36 "scrolled" [] [], + }, + colon_token: COLON@36..40 ":" [] [Whitespace(" ")], + value: CssIdentifier { + value_token: IDENT@40..48 "bottom" [] [Whitespace(" ")], + }, + }, + r_paren_token: R_PAREN@48..52 ")" [] [Whitespace(" ")], + }, + }, + block: CssRuleBlock { + l_curly_token: L_CURLY@52..55 "{" [] [Whitespace(" ")], + rules: CssRuleList [], + r_curly_token: R_CURLY@55..56 "}" [] [], + }, + }, + }, + CssAtRule { + at_token: AT@56..59 "@" [Newline("\n"), Newline("\n")] [], + rule: CssContainerAtRule { + declarator: CssContainerAtRuleDeclarator { + container_token: CONTAINER_KW@59..69 "container" [] [Whitespace(" ")], + name: CssCustomIdentifier { + value_token: IDENT@69..73 "not" [] [Whitespace(" ")], + }, + query: CssContainerScrollStateQueryInParens { + name: CssIdentifier { + value_token: IDENT@73..85 "scroll-state" [] [], + }, + l_paren_token: L_PAREN@85..86 "(" [] [], + query: CssQueryFeatureBoolean { + name: CssIdentifier { + value_token: IDENT@86..91 "stuck" [] [], + }, + }, + r_paren_token: R_PAREN@91..93 ")" [] [Whitespace(" ")], + }, + }, + block: CssRuleBlock { + l_curly_token: L_CURLY@93..96 "{" [] [Whitespace(" ")], + rules: CssRuleList [], + r_curly_token: R_CURLY@96..97 "}" [] [], + }, + }, + }, + CssAtRule { + at_token: AT@97..100 "@" [Newline("\n"), Newline("\n")] [], + rule: CssContainerAtRule { + declarator: CssContainerAtRuleDeclarator { + container_token: CONTAINER_KW@100..110 "container" [] [Whitespace(" ")], + name: missing (optional), + query: CssContainerScrollStateQueryInParens { + name: CssIdentifier { + value_token: IDENT@110..122 "scroll-state" [] [], + }, + l_paren_token: L_PAREN@122..123 "(" [] [], + query: CssContainerScrollStateNotQuery { + not_token: NOT_KW@123..127 "not" [] [Whitespace(" ")], + query: CssContainerScrollStateInParens { + l_paren_token: L_PAREN@127..128 "(" [] [], + query: CssQueryFeatureBoolean { + name: CssIdentifier { + value_token: IDENT@128..133 "stuck" [] [], + }, + }, + r_paren_token: R_PAREN@133..134 ")" [] [], + }, + }, + r_paren_token: R_PAREN@134..136 ")" [] [Whitespace(" ")], + }, + }, + block: CssRuleBlock { + l_curly_token: L_CURLY@136..138 "{" [] [Whitespace(" ")], + rules: CssRuleList [], + r_curly_token: R_CURLY@138..139 "}" [] [], + }, + }, + }, + CssAtRule { + at_token: AT@139..142 "@" [Newline("\n"), Newline("\n")] [], + rule: CssContainerAtRule { + declarator: CssContainerAtRuleDeclarator { + container_token: CONTAINER_KW@142..152 "container" [] [Whitespace(" ")], + name: missing (optional), + query: CssContainerScrollStateQueryInParens { + name: CssIdentifier { + value_token: IDENT@152..164 "scroll-state" [] [], + }, + l_paren_token: L_PAREN@164..165 "(" [] [], + query: CssContainerScrollStateAndQuery { + left: CssContainerScrollStateInParens { + l_paren_token: L_PAREN@165..166 "(" [] [], + query: CssQueryFeatureBoolean { + name: CssIdentifier { + value_token: IDENT@166..171 "stuck" [] [], + }, + }, + r_paren_token: R_PAREN@171..173 ")" [] [Whitespace(" ")], + }, + and_token: AND_KW@173..177 "and" [] [Whitespace(" ")], + right: CssContainerScrollStateInParens { + l_paren_token: L_PAREN@177..178 "(" [] [], + query: CssQueryFeaturePlain { + name: CssIdentifier { + value_token: IDENT@178..186 "scrolled" [] [], + }, + colon_token: COLON@186..188 ":" [] [Whitespace(" ")], + value: CssIdentifier { + value_token: IDENT@188..194 "bottom" [] [], + }, + }, + r_paren_token: R_PAREN@194..195 ")" [] [], + }, + }, + r_paren_token: R_PAREN@195..197 ")" [] [Whitespace(" ")], + }, + }, + block: CssRuleBlock { + l_curly_token: L_CURLY@197..199 "{" [] [Whitespace(" ")], + rules: CssRuleList [], + r_curly_token: R_CURLY@199..200 "}" [] [], + }, + }, + }, + CssAtRule { + at_token: AT@200..203 "@" [Newline("\n"), Newline("\n")] [], + rule: CssContainerAtRule { + declarator: CssContainerAtRuleDeclarator { + container_token: CONTAINER_KW@203..213 "container" [] [Whitespace(" ")], + name: missing (optional), + query: CssContainerScrollStateQueryInParens { + name: CssIdentifier { + value_token: IDENT@213..225 "scroll-state" [] [], + }, + l_paren_token: L_PAREN@225..226 "(" [] [], + query: CssContainerScrollStateOrQuery { + left: CssContainerScrollStateInParens { + l_paren_token: L_PAREN@226..227 "(" [] [], + query: CssQueryFeatureBoolean { + name: CssIdentifier { + value_token: IDENT@227..232 "stuck" [] [], + }, + }, + r_paren_token: R_PAREN@232..234 ")" [] [Whitespace(" ")], + }, + or_token: OR_KW@234..237 "or" [] [Whitespace(" ")], + right: CssContainerScrollStateInParens { + l_paren_token: L_PAREN@237..238 "(" [] [], + query: CssQueryFeaturePlain { + name: CssIdentifier { + value_token: IDENT@238..246 "scrolled" [] [], + }, + colon_token: COLON@246..248 ":" [] [Whitespace(" ")], + value: CssIdentifier { + value_token: IDENT@248..254 "bottom" [] [], + }, + }, + r_paren_token: R_PAREN@254..255 ")" [] [], + }, + }, + r_paren_token: R_PAREN@255..257 ")" [] [Whitespace(" ")], + }, + }, + block: CssRuleBlock { + l_curly_token: L_CURLY@257..259 "{" [] [Whitespace(" ")], + rules: CssRuleList [], + r_curly_token: R_CURLY@259..260 "}" [] [], + }, + }, + }, + ], + eof_token: EOF@260..261 "" [Newline("\n")] [], +} +``` + +## CST + +``` +0: CSS_ROOT@0..261 + 0: (empty) + 1: CSS_ROOT_ITEM_LIST@0..260 + 0: CSS_AT_RULE@0..56 + 0: AT@0..1 "@" [] [] + 1: CSS_CONTAINER_AT_RULE@1..56 + 0: CSS_CONTAINER_AT_RULE_DECLARATOR@1..52 + 0: CONTAINER_KW@1..13 "container" [] [Whitespace(" ")] + 1: (empty) + 2: CSS_CONTAINER_SCROLL_STATE_QUERY_IN_PARENS@13..52 + 0: CSS_IDENTIFIER@13..25 + 0: IDENT@13..25 "scroll-state" [] [] + 1: L_PAREN@25..28 "(" [] [Whitespace(" ")] + 2: CSS_QUERY_FEATURE_PLAIN@28..48 + 0: CSS_IDENTIFIER@28..36 + 0: IDENT@28..36 "scrolled" [] [] + 1: COLON@36..40 ":" [] [Whitespace(" ")] + 2: CSS_IDENTIFIER@40..48 + 0: IDENT@40..48 "bottom" [] [Whitespace(" ")] + 3: R_PAREN@48..52 ")" [] [Whitespace(" ")] + 1: CSS_RULE_BLOCK@52..56 + 0: L_CURLY@52..55 "{" [] [Whitespace(" ")] + 1: CSS_RULE_LIST@55..55 + 2: R_CURLY@55..56 "}" [] [] + 1: CSS_AT_RULE@56..97 + 0: AT@56..59 "@" [Newline("\n"), Newline("\n")] [] + 1: CSS_CONTAINER_AT_RULE@59..97 + 0: CSS_CONTAINER_AT_RULE_DECLARATOR@59..93 + 0: CONTAINER_KW@59..69 "container" [] [Whitespace(" ")] + 1: CSS_CUSTOM_IDENTIFIER@69..73 + 0: IDENT@69..73 "not" [] [Whitespace(" ")] + 2: CSS_CONTAINER_SCROLL_STATE_QUERY_IN_PARENS@73..93 + 0: CSS_IDENTIFIER@73..85 + 0: IDENT@73..85 "scroll-state" [] [] + 1: L_PAREN@85..86 "(" [] [] + 2: CSS_QUERY_FEATURE_BOOLEAN@86..91 + 0: CSS_IDENTIFIER@86..91 + 0: IDENT@86..91 "stuck" [] [] + 3: R_PAREN@91..93 ")" [] [Whitespace(" ")] + 1: CSS_RULE_BLOCK@93..97 + 0: L_CURLY@93..96 "{" [] [Whitespace(" ")] + 1: CSS_RULE_LIST@96..96 + 2: R_CURLY@96..97 "}" [] [] + 2: CSS_AT_RULE@97..139 + 0: AT@97..100 "@" [Newline("\n"), Newline("\n")] [] + 1: CSS_CONTAINER_AT_RULE@100..139 + 0: CSS_CONTAINER_AT_RULE_DECLARATOR@100..136 + 0: CONTAINER_KW@100..110 "container" [] [Whitespace(" ")] + 1: (empty) + 2: CSS_CONTAINER_SCROLL_STATE_QUERY_IN_PARENS@110..136 + 0: CSS_IDENTIFIER@110..122 + 0: IDENT@110..122 "scroll-state" [] [] + 1: L_PAREN@122..123 "(" [] [] + 2: CSS_CONTAINER_SCROLL_STATE_NOT_QUERY@123..134 + 0: NOT_KW@123..127 "not" [] [Whitespace(" ")] + 1: CSS_CONTAINER_SCROLL_STATE_IN_PARENS@127..134 + 0: L_PAREN@127..128 "(" [] [] + 1: CSS_QUERY_FEATURE_BOOLEAN@128..133 + 0: CSS_IDENTIFIER@128..133 + 0: IDENT@128..133 "stuck" [] [] + 2: R_PAREN@133..134 ")" [] [] + 3: R_PAREN@134..136 ")" [] [Whitespace(" ")] + 1: CSS_RULE_BLOCK@136..139 + 0: L_CURLY@136..138 "{" [] [Whitespace(" ")] + 1: CSS_RULE_LIST@138..138 + 2: R_CURLY@138..139 "}" [] [] + 3: CSS_AT_RULE@139..200 + 0: AT@139..142 "@" [Newline("\n"), Newline("\n")] [] + 1: CSS_CONTAINER_AT_RULE@142..200 + 0: CSS_CONTAINER_AT_RULE_DECLARATOR@142..197 + 0: CONTAINER_KW@142..152 "container" [] [Whitespace(" ")] + 1: (empty) + 2: CSS_CONTAINER_SCROLL_STATE_QUERY_IN_PARENS@152..197 + 0: CSS_IDENTIFIER@152..164 + 0: IDENT@152..164 "scroll-state" [] [] + 1: L_PAREN@164..165 "(" [] [] + 2: CSS_CONTAINER_SCROLL_STATE_AND_QUERY@165..195 + 0: CSS_CONTAINER_SCROLL_STATE_IN_PARENS@165..173 + 0: L_PAREN@165..166 "(" [] [] + 1: CSS_QUERY_FEATURE_BOOLEAN@166..171 + 0: CSS_IDENTIFIER@166..171 + 0: IDENT@166..171 "stuck" [] [] + 2: R_PAREN@171..173 ")" [] [Whitespace(" ")] + 1: AND_KW@173..177 "and" [] [Whitespace(" ")] + 2: CSS_CONTAINER_SCROLL_STATE_IN_PARENS@177..195 + 0: L_PAREN@177..178 "(" [] [] + 1: CSS_QUERY_FEATURE_PLAIN@178..194 + 0: CSS_IDENTIFIER@178..186 + 0: IDENT@178..186 "scrolled" [] [] + 1: COLON@186..188 ":" [] [Whitespace(" ")] + 2: CSS_IDENTIFIER@188..194 + 0: IDENT@188..194 "bottom" [] [] + 2: R_PAREN@194..195 ")" [] [] + 3: R_PAREN@195..197 ")" [] [Whitespace(" ")] + 1: CSS_RULE_BLOCK@197..200 + 0: L_CURLY@197..199 "{" [] [Whitespace(" ")] + 1: CSS_RULE_LIST@199..199 + 2: R_CURLY@199..200 "}" [] [] + 4: CSS_AT_RULE@200..260 + 0: AT@200..203 "@" [Newline("\n"), Newline("\n")] [] + 1: CSS_CONTAINER_AT_RULE@203..260 + 0: CSS_CONTAINER_AT_RULE_DECLARATOR@203..257 + 0: CONTAINER_KW@203..213 "container" [] [Whitespace(" ")] + 1: (empty) + 2: CSS_CONTAINER_SCROLL_STATE_QUERY_IN_PARENS@213..257 + 0: CSS_IDENTIFIER@213..225 + 0: IDENT@213..225 "scroll-state" [] [] + 1: L_PAREN@225..226 "(" [] [] + 2: CSS_CONTAINER_SCROLL_STATE_OR_QUERY@226..255 + 0: CSS_CONTAINER_SCROLL_STATE_IN_PARENS@226..234 + 0: L_PAREN@226..227 "(" [] [] + 1: CSS_QUERY_FEATURE_BOOLEAN@227..232 + 0: CSS_IDENTIFIER@227..232 + 0: IDENT@227..232 "stuck" [] [] + 2: R_PAREN@232..234 ")" [] [Whitespace(" ")] + 1: OR_KW@234..237 "or" [] [Whitespace(" ")] + 2: CSS_CONTAINER_SCROLL_STATE_IN_PARENS@237..255 + 0: L_PAREN@237..238 "(" [] [] + 1: CSS_QUERY_FEATURE_PLAIN@238..254 + 0: CSS_IDENTIFIER@238..246 + 0: IDENT@238..246 "scrolled" [] [] + 1: COLON@246..248 ":" [] [Whitespace(" ")] + 2: CSS_IDENTIFIER@248..254 + 0: IDENT@248..254 "bottom" [] [] + 2: R_PAREN@254..255 ")" [] [] + 3: R_PAREN@255..257 ")" [] [Whitespace(" ")] + 1: CSS_RULE_BLOCK@257..260 + 0: L_CURLY@257..259 "{" [] [Whitespace(" ")] + 1: CSS_RULE_LIST@259..259 + 2: R_CURLY@259..260 "}" [] [] + 2: EOF@260..261 "" [Newline("\n")] [] + +``` diff --git a/crates/biome_css_syntax/src/generated/kind.rs b/crates/biome_css_syntax/src/generated/kind.rs index e8b005b2ec00..da5d521ff038 100644 --- a/crates/biome_css_syntax/src/generated/kind.rs +++ b/crates/biome_css_syntax/src/generated/kind.rs @@ -436,6 +436,11 @@ pub enum CssSyntaxKind { CSS_CONTAINER_AND_QUERY, CSS_CONTAINER_OR_QUERY, CSS_CONTAINER_QUERY_IN_PARENS, + CSS_CONTAINER_SCROLL_STATE_QUERY_IN_PARENS, + CSS_CONTAINER_SCROLL_STATE_NOT_QUERY, + CSS_CONTAINER_SCROLL_STATE_AND_QUERY, + CSS_CONTAINER_SCROLL_STATE_OR_QUERY, + CSS_CONTAINER_SCROLL_STATE_IN_PARENS, CSS_CONTAINER_STYLE_QUERY_IN_PARENS, CSS_CONTAINER_SIZE_FEATURE_IN_PARENS, CSS_CONTAINER_STYLE_NOT_QUERY, diff --git a/crates/biome_css_syntax/src/generated/macros.rs b/crates/biome_css_syntax/src/generated/macros.rs index 705a5c1967ef..8a6d11be87a3 100644 --- a/crates/biome_css_syntax/src/generated/macros.rs +++ b/crates/biome_css_syntax/src/generated/macros.rs @@ -128,6 +128,32 @@ macro_rules! map_syntax_node { unsafe { $crate::CssContainerQueryInParens::new_unchecked(node) }; $body } + $crate::CssSyntaxKind::CSS_CONTAINER_SCROLL_STATE_AND_QUERY => { + let $pattern = + unsafe { $crate::CssContainerScrollStateAndQuery::new_unchecked(node) }; + $body + } + $crate::CssSyntaxKind::CSS_CONTAINER_SCROLL_STATE_IN_PARENS => { + let $pattern = + unsafe { $crate::CssContainerScrollStateInParens::new_unchecked(node) }; + $body + } + $crate::CssSyntaxKind::CSS_CONTAINER_SCROLL_STATE_NOT_QUERY => { + let $pattern = + unsafe { $crate::CssContainerScrollStateNotQuery::new_unchecked(node) }; + $body + } + $crate::CssSyntaxKind::CSS_CONTAINER_SCROLL_STATE_OR_QUERY => { + let $pattern = + unsafe { $crate::CssContainerScrollStateOrQuery::new_unchecked(node) }; + $body + } + $crate::CssSyntaxKind::CSS_CONTAINER_SCROLL_STATE_QUERY_IN_PARENS => { + let $pattern = unsafe { + $crate::CssContainerScrollStateQueryInParens::new_unchecked(node) + }; + $body + } $crate::CssSyntaxKind::CSS_CONTAINER_SIZE_FEATURE_IN_PARENS => { let $pattern = unsafe { $crate::CssContainerSizeFeatureInParens::new_unchecked(node) }; diff --git a/crates/biome_css_syntax/src/generated/nodes.rs b/crates/biome_css_syntax/src/generated/nodes.rs index 7d6581270585..54260f75a2c3 100644 --- a/crates/biome_css_syntax/src/generated/nodes.rs +++ b/crates/biome_css_syntax/src/generated/nodes.rs @@ -1183,6 +1183,231 @@ pub struct CssContainerQueryInParensFields { pub r_paren_token: SyntaxResult, } #[derive(Clone, PartialEq, Eq, Hash)] +pub struct CssContainerScrollStateAndQuery { + pub(crate) syntax: SyntaxNode, +} +impl CssContainerScrollStateAndQuery { + #[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) -> CssContainerScrollStateAndQueryFields { + CssContainerScrollStateAndQueryFields { + left: self.left(), + and_token: self.and_token(), + right: self.right(), + } + } + pub fn left(&self) -> SyntaxResult { + support::required_node(&self.syntax, 0usize) + } + pub fn and_token(&self) -> SyntaxResult { + support::required_token(&self.syntax, 1usize) + } + pub fn right(&self) -> SyntaxResult { + support::required_node(&self.syntax, 2usize) + } +} +impl Serialize for CssContainerScrollStateAndQuery { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + self.as_fields().serialize(serializer) + } +} +#[derive(Serialize)] +pub struct CssContainerScrollStateAndQueryFields { + pub left: SyntaxResult, + pub and_token: SyntaxResult, + pub right: SyntaxResult, +} +#[derive(Clone, PartialEq, Eq, Hash)] +pub struct CssContainerScrollStateInParens { + pub(crate) syntax: SyntaxNode, +} +impl CssContainerScrollStateInParens { + #[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) -> CssContainerScrollStateInParensFields { + CssContainerScrollStateInParensFields { + l_paren_token: self.l_paren_token(), + query: self.query(), + r_paren_token: self.r_paren_token(), + } + } + pub fn l_paren_token(&self) -> SyntaxResult { + support::required_token(&self.syntax, 0usize) + } + pub fn query(&self) -> SyntaxResult { + support::required_node(&self.syntax, 1usize) + } + pub fn r_paren_token(&self) -> SyntaxResult { + support::required_token(&self.syntax, 2usize) + } +} +impl Serialize for CssContainerScrollStateInParens { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + self.as_fields().serialize(serializer) + } +} +#[derive(Serialize)] +pub struct CssContainerScrollStateInParensFields { + pub l_paren_token: SyntaxResult, + pub query: SyntaxResult, + pub r_paren_token: SyntaxResult, +} +#[derive(Clone, PartialEq, Eq, Hash)] +pub struct CssContainerScrollStateNotQuery { + pub(crate) syntax: SyntaxNode, +} +impl CssContainerScrollStateNotQuery { + #[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) -> CssContainerScrollStateNotQueryFields { + CssContainerScrollStateNotQueryFields { + not_token: self.not_token(), + query: self.query(), + } + } + pub fn not_token(&self) -> SyntaxResult { + support::required_token(&self.syntax, 0usize) + } + pub fn query(&self) -> SyntaxResult { + support::required_node(&self.syntax, 1usize) + } +} +impl Serialize for CssContainerScrollStateNotQuery { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + self.as_fields().serialize(serializer) + } +} +#[derive(Serialize)] +pub struct CssContainerScrollStateNotQueryFields { + pub not_token: SyntaxResult, + pub query: SyntaxResult, +} +#[derive(Clone, PartialEq, Eq, Hash)] +pub struct CssContainerScrollStateOrQuery { + pub(crate) syntax: SyntaxNode, +} +impl CssContainerScrollStateOrQuery { + #[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) -> CssContainerScrollStateOrQueryFields { + CssContainerScrollStateOrQueryFields { + left: self.left(), + or_token: self.or_token(), + right: self.right(), + } + } + pub fn left(&self) -> SyntaxResult { + support::required_node(&self.syntax, 0usize) + } + pub fn or_token(&self) -> SyntaxResult { + support::required_token(&self.syntax, 1usize) + } + pub fn right(&self) -> SyntaxResult { + support::required_node(&self.syntax, 2usize) + } +} +impl Serialize for CssContainerScrollStateOrQuery { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + self.as_fields().serialize(serializer) + } +} +#[derive(Serialize)] +pub struct CssContainerScrollStateOrQueryFields { + pub left: SyntaxResult, + pub or_token: SyntaxResult, + pub right: SyntaxResult, +} +#[derive(Clone, PartialEq, Eq, Hash)] +pub struct CssContainerScrollStateQueryInParens { + pub(crate) syntax: SyntaxNode, +} +impl CssContainerScrollStateQueryInParens { + #[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) -> CssContainerScrollStateQueryInParensFields { + CssContainerScrollStateQueryInParensFields { + name: self.name(), + l_paren_token: self.l_paren_token(), + query: self.query(), + r_paren_token: self.r_paren_token(), + } + } + pub fn name(&self) -> SyntaxResult { + support::required_node(&self.syntax, 0usize) + } + pub fn l_paren_token(&self) -> SyntaxResult { + support::required_token(&self.syntax, 1usize) + } + pub fn query(&self) -> SyntaxResult { + support::required_node(&self.syntax, 2usize) + } + pub fn r_paren_token(&self) -> SyntaxResult { + support::required_token(&self.syntax, 3usize) + } +} +impl Serialize for CssContainerScrollStateQueryInParens { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + self.as_fields().serialize(serializer) + } +} +#[derive(Serialize)] +pub struct CssContainerScrollStateQueryInParensFields { + pub name: SyntaxResult, + pub l_paren_token: SyntaxResult, + pub query: SyntaxResult, + pub r_paren_token: SyntaxResult, +} +#[derive(Clone, PartialEq, Eq, Hash)] pub struct CssContainerSizeFeatureInParens { pub(crate) syntax: SyntaxNode, } @@ -10688,6 +10913,7 @@ impl AnyCssContainerQuery { pub enum AnyCssContainerQueryInParens { AnyCssValue(AnyCssValue), CssContainerQueryInParens(CssContainerQueryInParens), + CssContainerScrollStateQueryInParens(CssContainerScrollStateQueryInParens), CssContainerSizeFeatureInParens(CssContainerSizeFeatureInParens), CssContainerStyleQueryInParens(CssContainerStyleQueryInParens), } @@ -10704,6 +10930,14 @@ impl AnyCssContainerQueryInParens { _ => None, } } + pub fn as_css_container_scroll_state_query_in_parens( + &self, + ) -> Option<&CssContainerScrollStateQueryInParens> { + match &self { + Self::CssContainerScrollStateQueryInParens(item) => Some(item), + _ => None, + } + } pub fn as_css_container_size_feature_in_parens( &self, ) -> Option<&CssContainerSizeFeatureInParens> { @@ -10722,6 +10956,121 @@ impl AnyCssContainerQueryInParens { } } #[derive(Clone, PartialEq, Eq, Hash, Serialize)] +pub enum AnyCssContainerScrollStateAndCombinableQuery { + CssContainerScrollStateAndQuery(CssContainerScrollStateAndQuery), + CssContainerScrollStateInParens(CssContainerScrollStateInParens), +} +impl AnyCssContainerScrollStateAndCombinableQuery { + pub fn as_css_container_scroll_state_and_query( + &self, + ) -> Option<&CssContainerScrollStateAndQuery> { + match &self { + Self::CssContainerScrollStateAndQuery(item) => Some(item), + _ => None, + } + } + pub fn as_css_container_scroll_state_in_parens( + &self, + ) -> Option<&CssContainerScrollStateInParens> { + match &self { + Self::CssContainerScrollStateInParens(item) => Some(item), + _ => None, + } + } +} +#[derive(Clone, PartialEq, Eq, Hash, Serialize)] +pub enum AnyCssContainerScrollStateInParens { + AnyCssContainerScrollStateQuery(AnyCssContainerScrollStateQuery), + AnyCssValue(AnyCssValue), +} +impl AnyCssContainerScrollStateInParens { + pub fn as_any_css_container_scroll_state_query( + &self, + ) -> Option<&AnyCssContainerScrollStateQuery> { + match &self { + Self::AnyCssContainerScrollStateQuery(item) => Some(item), + _ => None, + } + } + pub fn as_any_css_value(&self) -> Option<&AnyCssValue> { + match &self { + Self::AnyCssValue(item) => Some(item), + _ => None, + } + } +} +#[derive(Clone, PartialEq, Eq, Hash, Serialize)] +pub enum AnyCssContainerScrollStateOrCombinableQuery { + CssContainerScrollStateInParens(CssContainerScrollStateInParens), + CssContainerScrollStateOrQuery(CssContainerScrollStateOrQuery), +} +impl AnyCssContainerScrollStateOrCombinableQuery { + pub fn as_css_container_scroll_state_in_parens( + &self, + ) -> Option<&CssContainerScrollStateInParens> { + match &self { + Self::CssContainerScrollStateInParens(item) => Some(item), + _ => None, + } + } + pub fn as_css_container_scroll_state_or_query( + &self, + ) -> Option<&CssContainerScrollStateOrQuery> { + match &self { + Self::CssContainerScrollStateOrQuery(item) => Some(item), + _ => None, + } + } +} +#[derive(Clone, PartialEq, Eq, Hash, Serialize)] +pub enum AnyCssContainerScrollStateQuery { + AnyCssQueryFeature(AnyCssQueryFeature), + CssContainerScrollStateAndQuery(CssContainerScrollStateAndQuery), + CssContainerScrollStateInParens(CssContainerScrollStateInParens), + CssContainerScrollStateNotQuery(CssContainerScrollStateNotQuery), + CssContainerScrollStateOrQuery(CssContainerScrollStateOrQuery), +} +impl AnyCssContainerScrollStateQuery { + pub fn as_any_css_query_feature(&self) -> Option<&AnyCssQueryFeature> { + match &self { + Self::AnyCssQueryFeature(item) => Some(item), + _ => None, + } + } + pub fn as_css_container_scroll_state_and_query( + &self, + ) -> Option<&CssContainerScrollStateAndQuery> { + match &self { + Self::CssContainerScrollStateAndQuery(item) => Some(item), + _ => None, + } + } + pub fn as_css_container_scroll_state_in_parens( + &self, + ) -> Option<&CssContainerScrollStateInParens> { + match &self { + Self::CssContainerScrollStateInParens(item) => Some(item), + _ => None, + } + } + pub fn as_css_container_scroll_state_not_query( + &self, + ) -> Option<&CssContainerScrollStateNotQuery> { + match &self { + Self::CssContainerScrollStateNotQuery(item) => Some(item), + _ => None, + } + } + pub fn as_css_container_scroll_state_or_query( + &self, + ) -> Option<&CssContainerScrollStateOrQuery> { + match &self { + Self::CssContainerScrollStateOrQuery(item) => Some(item), + _ => None, + } + } +} +#[derive(Clone, PartialEq, Eq, Hash, Serialize)] pub enum AnyCssContainerStyleAndCombinableQuery { CssContainerStyleAndQuery(CssContainerStyleAndQuery), CssContainerStyleInParens(CssContainerStyleInParens), @@ -14750,6 +15099,265 @@ impl From for SyntaxElement { n.syntax.into() } } +impl AstNode for CssContainerScrollStateAndQuery { + type Language = Language; + const KIND_SET: SyntaxKindSet = + SyntaxKindSet::from_raw(RawSyntaxKind(CSS_CONTAINER_SCROLL_STATE_AND_QUERY as u16)); + fn can_cast(kind: SyntaxKind) -> bool { + kind == CSS_CONTAINER_SCROLL_STATE_AND_QUERY + } + 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 CssContainerScrollStateAndQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + thread_local! { static DEPTH : std :: cell :: Cell < u8 > = const { std :: cell :: Cell :: new (0) } }; + let current_depth = DEPTH.get(); + let result = if current_depth < 16 { + DEPTH.set(current_depth + 1); + f.debug_struct("CssContainerScrollStateAndQuery") + .field("left", &support::DebugSyntaxResult(self.left())) + .field("and_token", &support::DebugSyntaxResult(self.and_token())) + .field("right", &support::DebugSyntaxResult(self.right())) + .finish() + } else { + f.debug_struct("CssContainerScrollStateAndQuery").finish() + }; + DEPTH.set(current_depth); + result + } +} +impl From for SyntaxNode { + fn from(n: CssContainerScrollStateAndQuery) -> Self { + n.syntax + } +} +impl From for SyntaxElement { + fn from(n: CssContainerScrollStateAndQuery) -> Self { + n.syntax.into() + } +} +impl AstNode for CssContainerScrollStateInParens { + type Language = Language; + const KIND_SET: SyntaxKindSet = + SyntaxKindSet::from_raw(RawSyntaxKind(CSS_CONTAINER_SCROLL_STATE_IN_PARENS as u16)); + fn can_cast(kind: SyntaxKind) -> bool { + kind == CSS_CONTAINER_SCROLL_STATE_IN_PARENS + } + 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 CssContainerScrollStateInParens { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + thread_local! { static DEPTH : std :: cell :: Cell < u8 > = const { std :: cell :: Cell :: new (0) } }; + let current_depth = DEPTH.get(); + let result = if current_depth < 16 { + DEPTH.set(current_depth + 1); + f.debug_struct("CssContainerScrollStateInParens") + .field( + "l_paren_token", + &support::DebugSyntaxResult(self.l_paren_token()), + ) + .field("query", &support::DebugSyntaxResult(self.query())) + .field( + "r_paren_token", + &support::DebugSyntaxResult(self.r_paren_token()), + ) + .finish() + } else { + f.debug_struct("CssContainerScrollStateInParens").finish() + }; + DEPTH.set(current_depth); + result + } +} +impl From for SyntaxNode { + fn from(n: CssContainerScrollStateInParens) -> Self { + n.syntax + } +} +impl From for SyntaxElement { + fn from(n: CssContainerScrollStateInParens) -> Self { + n.syntax.into() + } +} +impl AstNode for CssContainerScrollStateNotQuery { + type Language = Language; + const KIND_SET: SyntaxKindSet = + SyntaxKindSet::from_raw(RawSyntaxKind(CSS_CONTAINER_SCROLL_STATE_NOT_QUERY as u16)); + fn can_cast(kind: SyntaxKind) -> bool { + kind == CSS_CONTAINER_SCROLL_STATE_NOT_QUERY + } + 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 CssContainerScrollStateNotQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + thread_local! { static DEPTH : std :: cell :: Cell < u8 > = const { std :: cell :: Cell :: new (0) } }; + let current_depth = DEPTH.get(); + let result = if current_depth < 16 { + DEPTH.set(current_depth + 1); + f.debug_struct("CssContainerScrollStateNotQuery") + .field("not_token", &support::DebugSyntaxResult(self.not_token())) + .field("query", &support::DebugSyntaxResult(self.query())) + .finish() + } else { + f.debug_struct("CssContainerScrollStateNotQuery").finish() + }; + DEPTH.set(current_depth); + result + } +} +impl From for SyntaxNode { + fn from(n: CssContainerScrollStateNotQuery) -> Self { + n.syntax + } +} +impl From for SyntaxElement { + fn from(n: CssContainerScrollStateNotQuery) -> Self { + n.syntax.into() + } +} +impl AstNode for CssContainerScrollStateOrQuery { + type Language = Language; + const KIND_SET: SyntaxKindSet = + SyntaxKindSet::from_raw(RawSyntaxKind(CSS_CONTAINER_SCROLL_STATE_OR_QUERY as u16)); + fn can_cast(kind: SyntaxKind) -> bool { + kind == CSS_CONTAINER_SCROLL_STATE_OR_QUERY + } + 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 CssContainerScrollStateOrQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + thread_local! { static DEPTH : std :: cell :: Cell < u8 > = const { std :: cell :: Cell :: new (0) } }; + let current_depth = DEPTH.get(); + let result = if current_depth < 16 { + DEPTH.set(current_depth + 1); + f.debug_struct("CssContainerScrollStateOrQuery") + .field("left", &support::DebugSyntaxResult(self.left())) + .field("or_token", &support::DebugSyntaxResult(self.or_token())) + .field("right", &support::DebugSyntaxResult(self.right())) + .finish() + } else { + f.debug_struct("CssContainerScrollStateOrQuery").finish() + }; + DEPTH.set(current_depth); + result + } +} +impl From for SyntaxNode { + fn from(n: CssContainerScrollStateOrQuery) -> Self { + n.syntax + } +} +impl From for SyntaxElement { + fn from(n: CssContainerScrollStateOrQuery) -> Self { + n.syntax.into() + } +} +impl AstNode for CssContainerScrollStateQueryInParens { + type Language = Language; + const KIND_SET: SyntaxKindSet = SyntaxKindSet::from_raw(RawSyntaxKind( + CSS_CONTAINER_SCROLL_STATE_QUERY_IN_PARENS as u16, + )); + fn can_cast(kind: SyntaxKind) -> bool { + kind == CSS_CONTAINER_SCROLL_STATE_QUERY_IN_PARENS + } + 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 CssContainerScrollStateQueryInParens { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + thread_local! { static DEPTH : std :: cell :: Cell < u8 > = const { std :: cell :: Cell :: new (0) } }; + let current_depth = DEPTH.get(); + let result = if current_depth < 16 { + DEPTH.set(current_depth + 1); + f.debug_struct("CssContainerScrollStateQueryInParens") + .field("name", &support::DebugSyntaxResult(self.name())) + .field( + "l_paren_token", + &support::DebugSyntaxResult(self.l_paren_token()), + ) + .field("query", &support::DebugSyntaxResult(self.query())) + .field( + "r_paren_token", + &support::DebugSyntaxResult(self.r_paren_token()), + ) + .finish() + } else { + f.debug_struct("CssContainerScrollStateQueryInParens") + .finish() + }; + DEPTH.set(current_depth); + result + } +} +impl From for SyntaxNode { + fn from(n: CssContainerScrollStateQueryInParens) -> Self { + n.syntax + } +} +impl From for SyntaxElement { + fn from(n: CssContainerScrollStateQueryInParens) -> Self { + n.syntax.into() + } +} impl AstNode for CssContainerSizeFeatureInParens { type Language = Language; const KIND_SET: SyntaxKindSet = @@ -27025,6 +27633,11 @@ impl From for AnyCssContainerQueryInParens { Self::CssContainerQueryInParens(node) } } +impl From for AnyCssContainerQueryInParens { + fn from(node: CssContainerScrollStateQueryInParens) -> Self { + Self::CssContainerScrollStateQueryInParens(node) + } +} impl From for AnyCssContainerQueryInParens { fn from(node: CssContainerSizeFeatureInParens) -> Self { Self::CssContainerSizeFeatureInParens(node) @@ -27039,11 +27652,13 @@ impl AstNode for AnyCssContainerQueryInParens { type Language = Language; const KIND_SET: SyntaxKindSet = AnyCssValue::KIND_SET .union(CssContainerQueryInParens::KIND_SET) + .union(CssContainerScrollStateQueryInParens::KIND_SET) .union(CssContainerSizeFeatureInParens::KIND_SET) .union(CssContainerStyleQueryInParens::KIND_SET); fn can_cast(kind: SyntaxKind) -> bool { match kind { CSS_CONTAINER_QUERY_IN_PARENS + | CSS_CONTAINER_SCROLL_STATE_QUERY_IN_PARENS | CSS_CONTAINER_SIZE_FEATURE_IN_PARENS | CSS_CONTAINER_STYLE_QUERY_IN_PARENS => true, k if AnyCssValue::can_cast(k) => true, @@ -27055,6 +27670,11 @@ impl AstNode for AnyCssContainerQueryInParens { CSS_CONTAINER_QUERY_IN_PARENS => { Self::CssContainerQueryInParens(CssContainerQueryInParens { syntax }) } + CSS_CONTAINER_SCROLL_STATE_QUERY_IN_PARENS => { + Self::CssContainerScrollStateQueryInParens(CssContainerScrollStateQueryInParens { + syntax, + }) + } CSS_CONTAINER_SIZE_FEATURE_IN_PARENS => { Self::CssContainerSizeFeatureInParens(CssContainerSizeFeatureInParens { syntax }) } @@ -27073,6 +27693,7 @@ impl AstNode for AnyCssContainerQueryInParens { fn syntax(&self) -> &SyntaxNode { match self { Self::CssContainerQueryInParens(it) => it.syntax(), + Self::CssContainerScrollStateQueryInParens(it) => it.syntax(), Self::CssContainerSizeFeatureInParens(it) => it.syntax(), Self::CssContainerStyleQueryInParens(it) => it.syntax(), Self::AnyCssValue(it) => it.syntax(), @@ -27081,6 +27702,7 @@ impl AstNode for AnyCssContainerQueryInParens { fn into_syntax(self) -> SyntaxNode { match self { Self::CssContainerQueryInParens(it) => it.into_syntax(), + Self::CssContainerScrollStateQueryInParens(it) => it.into_syntax(), Self::CssContainerSizeFeatureInParens(it) => it.into_syntax(), Self::CssContainerStyleQueryInParens(it) => it.into_syntax(), Self::AnyCssValue(it) => it.into_syntax(), @@ -27092,6 +27714,7 @@ impl std::fmt::Debug for AnyCssContainerQueryInParens { match self { Self::AnyCssValue(it) => std::fmt::Debug::fmt(it, f), Self::CssContainerQueryInParens(it) => std::fmt::Debug::fmt(it, f), + Self::CssContainerScrollStateQueryInParens(it) => std::fmt::Debug::fmt(it, f), Self::CssContainerSizeFeatureInParens(it) => std::fmt::Debug::fmt(it, f), Self::CssContainerStyleQueryInParens(it) => std::fmt::Debug::fmt(it, f), } @@ -27102,6 +27725,9 @@ impl From for SyntaxNode { match n { AnyCssContainerQueryInParens::AnyCssValue(it) => it.into_syntax(), AnyCssContainerQueryInParens::CssContainerQueryInParens(it) => it.into_syntax(), + AnyCssContainerQueryInParens::CssContainerScrollStateQueryInParens(it) => { + it.into_syntax() + } AnyCssContainerQueryInParens::CssContainerSizeFeatureInParens(it) => it.into_syntax(), AnyCssContainerQueryInParens::CssContainerStyleQueryInParens(it) => it.into_syntax(), } @@ -27113,6 +27739,323 @@ impl From for SyntaxElement { node.into() } } +impl From for AnyCssContainerScrollStateAndCombinableQuery { + fn from(node: CssContainerScrollStateAndQuery) -> Self { + Self::CssContainerScrollStateAndQuery(node) + } +} +impl From for AnyCssContainerScrollStateAndCombinableQuery { + fn from(node: CssContainerScrollStateInParens) -> Self { + Self::CssContainerScrollStateInParens(node) + } +} +impl AstNode for AnyCssContainerScrollStateAndCombinableQuery { + type Language = Language; + const KIND_SET: SyntaxKindSet = + CssContainerScrollStateAndQuery::KIND_SET.union(CssContainerScrollStateInParens::KIND_SET); + fn can_cast(kind: SyntaxKind) -> bool { + matches!( + kind, + CSS_CONTAINER_SCROLL_STATE_AND_QUERY | CSS_CONTAINER_SCROLL_STATE_IN_PARENS + ) + } + fn cast(syntax: SyntaxNode) -> Option { + let res = match syntax.kind() { + CSS_CONTAINER_SCROLL_STATE_AND_QUERY => { + Self::CssContainerScrollStateAndQuery(CssContainerScrollStateAndQuery { syntax }) + } + CSS_CONTAINER_SCROLL_STATE_IN_PARENS => { + Self::CssContainerScrollStateInParens(CssContainerScrollStateInParens { syntax }) + } + _ => return None, + }; + Some(res) + } + fn syntax(&self) -> &SyntaxNode { + match self { + Self::CssContainerScrollStateAndQuery(it) => it.syntax(), + Self::CssContainerScrollStateInParens(it) => it.syntax(), + } + } + fn into_syntax(self) -> SyntaxNode { + match self { + Self::CssContainerScrollStateAndQuery(it) => it.into_syntax(), + Self::CssContainerScrollStateInParens(it) => it.into_syntax(), + } + } +} +impl std::fmt::Debug for AnyCssContainerScrollStateAndCombinableQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::CssContainerScrollStateAndQuery(it) => std::fmt::Debug::fmt(it, f), + Self::CssContainerScrollStateInParens(it) => std::fmt::Debug::fmt(it, f), + } + } +} +impl From for SyntaxNode { + fn from(n: AnyCssContainerScrollStateAndCombinableQuery) -> Self { + match n { + AnyCssContainerScrollStateAndCombinableQuery::CssContainerScrollStateAndQuery(it) => { + it.into_syntax() + } + AnyCssContainerScrollStateAndCombinableQuery::CssContainerScrollStateInParens(it) => { + it.into_syntax() + } + } + } +} +impl From for SyntaxElement { + fn from(n: AnyCssContainerScrollStateAndCombinableQuery) -> Self { + let node: SyntaxNode = n.into(); + node.into() + } +} +impl AstNode for AnyCssContainerScrollStateInParens { + type Language = Language; + const KIND_SET: SyntaxKindSet = + AnyCssContainerScrollStateQuery::KIND_SET.union(AnyCssValue::KIND_SET); + fn can_cast(kind: SyntaxKind) -> bool { + match kind { + k if AnyCssContainerScrollStateQuery::can_cast(k) => true, + k if AnyCssValue::can_cast(k) => true, + _ => false, + } + } + fn cast(syntax: SyntaxNode) -> Option { + let syntax = match AnyCssContainerScrollStateQuery::try_cast(syntax) { + Ok(any_css_container_scroll_state_query) => { + return Some(Self::AnyCssContainerScrollStateQuery( + any_css_container_scroll_state_query, + )); + } + Err(syntax) => syntax, + }; + if let Some(any_css_value) = AnyCssValue::cast(syntax) { + return Some(Self::AnyCssValue(any_css_value)); + } + None + } + fn syntax(&self) -> &SyntaxNode { + match self { + Self::AnyCssContainerScrollStateQuery(it) => it.syntax(), + Self::AnyCssValue(it) => it.syntax(), + } + } + fn into_syntax(self) -> SyntaxNode { + match self { + Self::AnyCssContainerScrollStateQuery(it) => it.into_syntax(), + Self::AnyCssValue(it) => it.into_syntax(), + } + } +} +impl std::fmt::Debug for AnyCssContainerScrollStateInParens { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::AnyCssContainerScrollStateQuery(it) => std::fmt::Debug::fmt(it, f), + Self::AnyCssValue(it) => std::fmt::Debug::fmt(it, f), + } + } +} +impl From for SyntaxNode { + fn from(n: AnyCssContainerScrollStateInParens) -> Self { + match n { + AnyCssContainerScrollStateInParens::AnyCssContainerScrollStateQuery(it) => { + it.into_syntax() + } + AnyCssContainerScrollStateInParens::AnyCssValue(it) => it.into_syntax(), + } + } +} +impl From for SyntaxElement { + fn from(n: AnyCssContainerScrollStateInParens) -> Self { + let node: SyntaxNode = n.into(); + node.into() + } +} +impl From for AnyCssContainerScrollStateOrCombinableQuery { + fn from(node: CssContainerScrollStateInParens) -> Self { + Self::CssContainerScrollStateInParens(node) + } +} +impl From for AnyCssContainerScrollStateOrCombinableQuery { + fn from(node: CssContainerScrollStateOrQuery) -> Self { + Self::CssContainerScrollStateOrQuery(node) + } +} +impl AstNode for AnyCssContainerScrollStateOrCombinableQuery { + type Language = Language; + const KIND_SET: SyntaxKindSet = + CssContainerScrollStateInParens::KIND_SET.union(CssContainerScrollStateOrQuery::KIND_SET); + fn can_cast(kind: SyntaxKind) -> bool { + matches!( + kind, + CSS_CONTAINER_SCROLL_STATE_IN_PARENS | CSS_CONTAINER_SCROLL_STATE_OR_QUERY + ) + } + fn cast(syntax: SyntaxNode) -> Option { + let res = match syntax.kind() { + CSS_CONTAINER_SCROLL_STATE_IN_PARENS => { + Self::CssContainerScrollStateInParens(CssContainerScrollStateInParens { syntax }) + } + CSS_CONTAINER_SCROLL_STATE_OR_QUERY => { + Self::CssContainerScrollStateOrQuery(CssContainerScrollStateOrQuery { syntax }) + } + _ => return None, + }; + Some(res) + } + fn syntax(&self) -> &SyntaxNode { + match self { + Self::CssContainerScrollStateInParens(it) => it.syntax(), + Self::CssContainerScrollStateOrQuery(it) => it.syntax(), + } + } + fn into_syntax(self) -> SyntaxNode { + match self { + Self::CssContainerScrollStateInParens(it) => it.into_syntax(), + Self::CssContainerScrollStateOrQuery(it) => it.into_syntax(), + } + } +} +impl std::fmt::Debug for AnyCssContainerScrollStateOrCombinableQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::CssContainerScrollStateInParens(it) => std::fmt::Debug::fmt(it, f), + Self::CssContainerScrollStateOrQuery(it) => std::fmt::Debug::fmt(it, f), + } + } +} +impl From for SyntaxNode { + fn from(n: AnyCssContainerScrollStateOrCombinableQuery) -> Self { + match n { + AnyCssContainerScrollStateOrCombinableQuery::CssContainerScrollStateInParens(it) => { + it.into_syntax() + } + AnyCssContainerScrollStateOrCombinableQuery::CssContainerScrollStateOrQuery(it) => { + it.into_syntax() + } + } + } +} +impl From for SyntaxElement { + fn from(n: AnyCssContainerScrollStateOrCombinableQuery) -> Self { + let node: SyntaxNode = n.into(); + node.into() + } +} +impl From for AnyCssContainerScrollStateQuery { + fn from(node: CssContainerScrollStateAndQuery) -> Self { + Self::CssContainerScrollStateAndQuery(node) + } +} +impl From for AnyCssContainerScrollStateQuery { + fn from(node: CssContainerScrollStateInParens) -> Self { + Self::CssContainerScrollStateInParens(node) + } +} +impl From for AnyCssContainerScrollStateQuery { + fn from(node: CssContainerScrollStateNotQuery) -> Self { + Self::CssContainerScrollStateNotQuery(node) + } +} +impl From for AnyCssContainerScrollStateQuery { + fn from(node: CssContainerScrollStateOrQuery) -> Self { + Self::CssContainerScrollStateOrQuery(node) + } +} +impl AstNode for AnyCssContainerScrollStateQuery { + type Language = Language; + const KIND_SET: SyntaxKindSet = AnyCssQueryFeature::KIND_SET + .union(CssContainerScrollStateAndQuery::KIND_SET) + .union(CssContainerScrollStateInParens::KIND_SET) + .union(CssContainerScrollStateNotQuery::KIND_SET) + .union(CssContainerScrollStateOrQuery::KIND_SET); + fn can_cast(kind: SyntaxKind) -> bool { + match kind { + CSS_CONTAINER_SCROLL_STATE_AND_QUERY + | CSS_CONTAINER_SCROLL_STATE_IN_PARENS + | CSS_CONTAINER_SCROLL_STATE_NOT_QUERY + | CSS_CONTAINER_SCROLL_STATE_OR_QUERY => true, + k if AnyCssQueryFeature::can_cast(k) => true, + _ => false, + } + } + fn cast(syntax: SyntaxNode) -> Option { + let res = match syntax.kind() { + CSS_CONTAINER_SCROLL_STATE_AND_QUERY => { + Self::CssContainerScrollStateAndQuery(CssContainerScrollStateAndQuery { syntax }) + } + CSS_CONTAINER_SCROLL_STATE_IN_PARENS => { + Self::CssContainerScrollStateInParens(CssContainerScrollStateInParens { syntax }) + } + CSS_CONTAINER_SCROLL_STATE_NOT_QUERY => { + Self::CssContainerScrollStateNotQuery(CssContainerScrollStateNotQuery { syntax }) + } + CSS_CONTAINER_SCROLL_STATE_OR_QUERY => { + Self::CssContainerScrollStateOrQuery(CssContainerScrollStateOrQuery { syntax }) + } + _ => { + if let Some(any_css_query_feature) = AnyCssQueryFeature::cast(syntax) { + return Some(Self::AnyCssQueryFeature(any_css_query_feature)); + } + return None; + } + }; + Some(res) + } + fn syntax(&self) -> &SyntaxNode { + match self { + Self::CssContainerScrollStateAndQuery(it) => it.syntax(), + Self::CssContainerScrollStateInParens(it) => it.syntax(), + Self::CssContainerScrollStateNotQuery(it) => it.syntax(), + Self::CssContainerScrollStateOrQuery(it) => it.syntax(), + Self::AnyCssQueryFeature(it) => it.syntax(), + } + } + fn into_syntax(self) -> SyntaxNode { + match self { + Self::CssContainerScrollStateAndQuery(it) => it.into_syntax(), + Self::CssContainerScrollStateInParens(it) => it.into_syntax(), + Self::CssContainerScrollStateNotQuery(it) => it.into_syntax(), + Self::CssContainerScrollStateOrQuery(it) => it.into_syntax(), + Self::AnyCssQueryFeature(it) => it.into_syntax(), + } + } +} +impl std::fmt::Debug for AnyCssContainerScrollStateQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::AnyCssQueryFeature(it) => std::fmt::Debug::fmt(it, f), + Self::CssContainerScrollStateAndQuery(it) => std::fmt::Debug::fmt(it, f), + Self::CssContainerScrollStateInParens(it) => std::fmt::Debug::fmt(it, f), + Self::CssContainerScrollStateNotQuery(it) => std::fmt::Debug::fmt(it, f), + Self::CssContainerScrollStateOrQuery(it) => std::fmt::Debug::fmt(it, f), + } + } +} +impl From for SyntaxNode { + fn from(n: AnyCssContainerScrollStateQuery) -> Self { + match n { + AnyCssContainerScrollStateQuery::AnyCssQueryFeature(it) => it.into_syntax(), + AnyCssContainerScrollStateQuery::CssContainerScrollStateAndQuery(it) => { + it.into_syntax() + } + AnyCssContainerScrollStateQuery::CssContainerScrollStateInParens(it) => { + it.into_syntax() + } + AnyCssContainerScrollStateQuery::CssContainerScrollStateNotQuery(it) => { + it.into_syntax() + } + AnyCssContainerScrollStateQuery::CssContainerScrollStateOrQuery(it) => it.into_syntax(), + } + } +} +impl From for SyntaxElement { + fn from(n: AnyCssContainerScrollStateQuery) -> Self { + let node: SyntaxNode = n.into(); + node.into() + } +} impl From for AnyCssContainerStyleAndCombinableQuery { fn from(node: CssContainerStyleAndQuery) -> Self { Self::CssContainerStyleAndQuery(node) @@ -34734,6 +35677,26 @@ impl std::fmt::Display for AnyCssContainerQueryInParens { std::fmt::Display::fmt(self.syntax(), f) } } +impl std::fmt::Display for AnyCssContainerScrollStateAndCombinableQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} +impl std::fmt::Display for AnyCssContainerScrollStateInParens { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} +impl std::fmt::Display for AnyCssContainerScrollStateOrCombinableQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} +impl std::fmt::Display for AnyCssContainerScrollStateQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} impl std::fmt::Display for AnyCssContainerStyleAndCombinableQuery { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) @@ -35349,6 +36312,31 @@ impl std::fmt::Display for CssContainerQueryInParens { std::fmt::Display::fmt(self.syntax(), f) } } +impl std::fmt::Display for CssContainerScrollStateAndQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} +impl std::fmt::Display for CssContainerScrollStateInParens { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} +impl std::fmt::Display for CssContainerScrollStateNotQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} +impl std::fmt::Display for CssContainerScrollStateOrQuery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} +impl std::fmt::Display for CssContainerScrollStateQueryInParens { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} impl std::fmt::Display for CssContainerSizeFeatureInParens { 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 be53ca1f3560..8d0dee1e0340 100644 --- a/crates/biome_css_syntax/src/generated/nodes_mut.rs +++ b/crates/biome_css_syntax/src/generated/nodes_mut.rs @@ -483,6 +483,106 @@ impl CssContainerQueryInParens { ) } } +impl CssContainerScrollStateAndQuery { + pub fn with_left(self, element: CssContainerScrollStateInParens) -> Self { + Self::unwrap_cast( + self.syntax + .splice_slots(0usize..=0usize, once(Some(element.into_syntax().into()))), + ) + } + pub fn with_and_token(self, element: SyntaxToken) -> Self { + Self::unwrap_cast( + self.syntax + .splice_slots(1usize..=1usize, once(Some(element.into()))), + ) + } + pub fn with_right(self, element: AnyCssContainerScrollStateAndCombinableQuery) -> Self { + Self::unwrap_cast( + self.syntax + .splice_slots(2usize..=2usize, once(Some(element.into_syntax().into()))), + ) + } +} +impl CssContainerScrollStateInParens { + pub fn with_l_paren_token(self, element: SyntaxToken) -> Self { + Self::unwrap_cast( + self.syntax + .splice_slots(0usize..=0usize, once(Some(element.into()))), + ) + } + pub fn with_query(self, element: AnyCssContainerScrollStateInParens) -> Self { + Self::unwrap_cast( + self.syntax + .splice_slots(1usize..=1usize, once(Some(element.into_syntax().into()))), + ) + } + pub fn with_r_paren_token(self, element: SyntaxToken) -> Self { + Self::unwrap_cast( + self.syntax + .splice_slots(2usize..=2usize, once(Some(element.into()))), + ) + } +} +impl CssContainerScrollStateNotQuery { + pub fn with_not_token(self, element: SyntaxToken) -> Self { + Self::unwrap_cast( + self.syntax + .splice_slots(0usize..=0usize, once(Some(element.into()))), + ) + } + pub fn with_query(self, element: CssContainerScrollStateInParens) -> Self { + Self::unwrap_cast( + self.syntax + .splice_slots(1usize..=1usize, once(Some(element.into_syntax().into()))), + ) + } +} +impl CssContainerScrollStateOrQuery { + pub fn with_left(self, element: CssContainerScrollStateInParens) -> Self { + Self::unwrap_cast( + self.syntax + .splice_slots(0usize..=0usize, once(Some(element.into_syntax().into()))), + ) + } + pub fn with_or_token(self, element: SyntaxToken) -> Self { + Self::unwrap_cast( + self.syntax + .splice_slots(1usize..=1usize, once(Some(element.into()))), + ) + } + pub fn with_right(self, element: AnyCssContainerScrollStateOrCombinableQuery) -> Self { + Self::unwrap_cast( + self.syntax + .splice_slots(2usize..=2usize, once(Some(element.into_syntax().into()))), + ) + } +} +impl CssContainerScrollStateQueryInParens { + pub fn with_name(self, element: CssIdentifier) -> Self { + Self::unwrap_cast( + self.syntax + .splice_slots(0usize..=0usize, once(Some(element.into_syntax().into()))), + ) + } + pub fn with_l_paren_token(self, element: SyntaxToken) -> Self { + Self::unwrap_cast( + self.syntax + .splice_slots(1usize..=1usize, once(Some(element.into()))), + ) + } + pub fn with_query(self, element: AnyCssContainerScrollStateQuery) -> Self { + Self::unwrap_cast( + self.syntax + .splice_slots(2usize..=2usize, once(Some(element.into_syntax().into()))), + ) + } + pub fn with_r_paren_token(self, element: SyntaxToken) -> Self { + Self::unwrap_cast( + self.syntax + .splice_slots(3usize..=3usize, once(Some(element.into()))), + ) + } +} impl CssContainerSizeFeatureInParens { pub fn with_l_paren_token(self, element: SyntaxToken) -> Self { Self::unwrap_cast( diff --git a/crates/biome_grit_patterns/src/grit_target_language/css_target_language/generated_mappings.rs b/crates/biome_grit_patterns/src/grit_target_language/css_target_language/generated_mappings.rs index 58c14a8806d4..1bc21fdd797f 100644 --- a/crates/biome_grit_patterns/src/grit_target_language/css_target_language/generated_mappings.rs +++ b/crates/biome_grit_patterns/src/grit_target_language/css_target_language/generated_mappings.rs @@ -40,6 +40,23 @@ pub fn kind_by_name(node_name: &str) -> Option { "CssContainerNotQuery" => lang::CssContainerNotQuery::KIND_SET.iter().next(), "CssContainerOrQuery" => lang::CssContainerOrQuery::KIND_SET.iter().next(), "CssContainerQueryInParens" => lang::CssContainerQueryInParens::KIND_SET.iter().next(), + "CssContainerScrollStateAndQuery" => lang::CssContainerScrollStateAndQuery::KIND_SET + .iter() + .next(), + "CssContainerScrollStateInParens" => lang::CssContainerScrollStateInParens::KIND_SET + .iter() + .next(), + "CssContainerScrollStateNotQuery" => lang::CssContainerScrollStateNotQuery::KIND_SET + .iter() + .next(), + "CssContainerScrollStateOrQuery" => { + lang::CssContainerScrollStateOrQuery::KIND_SET.iter().next() + } + "CssContainerScrollStateQueryInParens" => { + lang::CssContainerScrollStateQueryInParens::KIND_SET + .iter() + .next() + } "CssContainerSizeFeatureInParens" => lang::CssContainerSizeFeatureInParens::KIND_SET .iter() .next(), diff --git a/xtask/codegen/css.ungram b/xtask/codegen/css.ungram index 25f342daa90e..98b0c189086d 100644 --- a/xtask/codegen/css.ungram +++ b/xtask/codegen/css.ungram @@ -949,6 +949,7 @@ AnyCssContainerOrCombinableQuery = AnyCssContainerQueryInParens = CssContainerQueryInParens | CssContainerSizeFeatureInParens + | CssContainerScrollStateQueryInParens | CssContainerStyleQueryInParens | AnyCssValue // general-enclosed @@ -968,6 +969,66 @@ CssContainerSizeFeatureInParens = feature: AnyCssQueryFeature ')' +// scroll-state( ) +// Spec: https://drafts.csswg.org/css-conditional-5/#container-rule +// @container scroll-state(scrolled: bottom) {} +// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +CssContainerScrollStateQueryInParens = + name: CssIdentifier + '(' + query: AnyCssContainerScrollStateQuery + ')' + +// +AnyCssContainerScrollStateQuery = + CssContainerScrollStateNotQuery + | CssContainerScrollStateAndQuery + | CssContainerScrollStateOrQuery + | CssContainerScrollStateInParens + | AnyCssQueryFeature + +// @container scroll-state(not (stuck)) {} +// ^^^^^^^^^^^ +CssContainerScrollStateNotQuery = + 'not' + query: CssContainerScrollStateInParens + +// @container scroll-state((stuck) and (scrolled: bottom)) {} +// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +CssContainerScrollStateAndQuery = + left: CssContainerScrollStateInParens + 'and' + right: AnyCssContainerScrollStateAndCombinableQuery + +AnyCssContainerScrollStateAndCombinableQuery = + CssContainerScrollStateAndQuery + | CssContainerScrollStateInParens + +// @container scroll-state((stuck) or (scrolled: bottom)) {} +// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +CssContainerScrollStateOrQuery = + left: CssContainerScrollStateInParens + 'or' + right: AnyCssContainerScrollStateOrCombinableQuery + +AnyCssContainerScrollStateOrCombinableQuery = + CssContainerScrollStateOrQuery + | CssContainerScrollStateInParens + +// +// @container scroll-state((stuck)) {} +// ^^^^^^^ +CssContainerScrollStateInParens = + '(' + query: AnyCssContainerScrollStateInParens + ')' + +// ( ) +// ( ) +AnyCssContainerScrollStateInParens = + AnyCssContainerScrollStateQuery + | AnyCssValue + // style( ) // @container style(--accent-color: blue) {} // ^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/xtask/codegen/src/css_kinds_src.rs b/xtask/codegen/src/css_kinds_src.rs index 127153542047..0b5d5c6630f3 100644 --- a/xtask/codegen/src/css_kinds_src.rs +++ b/xtask/codegen/src/css_kinds_src.rs @@ -466,6 +466,11 @@ pub const CSS_KINDS_SRC: KindsSrc = KindsSrc { "CSS_CONTAINER_AND_QUERY", "CSS_CONTAINER_OR_QUERY", "CSS_CONTAINER_QUERY_IN_PARENS", + "CSS_CONTAINER_SCROLL_STATE_QUERY_IN_PARENS", + "CSS_CONTAINER_SCROLL_STATE_NOT_QUERY", + "CSS_CONTAINER_SCROLL_STATE_AND_QUERY", + "CSS_CONTAINER_SCROLL_STATE_OR_QUERY", + "CSS_CONTAINER_SCROLL_STATE_IN_PARENS", "CSS_CONTAINER_STYLE_QUERY_IN_PARENS", "CSS_CONTAINER_SIZE_FEATURE_IN_PARENS", "CSS_CONTAINER_STYLE_NOT_QUERY", From b0d15ede7d9b80b9b72cf4ed9b8f33c30d918937 Mon Sep 17 00:00:00 2001 From: Denis Bezrukov <6227442+denbezrukov@users.noreply.github.com> Date: Sat, 28 Feb 2026 12:21:40 +0200 Subject: [PATCH 2/4] test(css): update snapshots for `@container scroll-state` formatting adjustments --- .../tests/specs/css/atrule/container.css.snap | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/crates/biome_css_formatter/tests/specs/css/atrule/container.css.snap b/crates/biome_css_formatter/tests/specs/css/atrule/container.css.snap index 38cd1af5782e..1963c0dd158f 100644 --- a/crates/biome_css_formatter/tests/specs/css/atrule/container.css.snap +++ b/crates/biome_css_formatter/tests/specs/css/atrule/container.css.snap @@ -1,5 +1,6 @@ --- source: crates/biome_formatter_test/src/snapshot_builder.rs +assertion_line: 212 info: css/atrule/container.css --- @@ -159,15 +160,19 @@ Trailing newline: true @container (150px <= WIDTH) { } -@container scroll-state(scrolled: bottom) { } +@container scroll-state(scrolled: bottom) { +} -@container scroll-state(stuck: top) { } +@container scroll-state(stuck: top) { +} @container not scroll-state(stuck) { } -@container card scroll-state(scrolled: bottom) { } +@container card scroll-state(scrolled: bottom) { +} -@container scroll-state( scrolled: bottom ) { } +@container scroll-state(scrolled: bottom) { +} ``` From 7e52dddfc44a16ba34ce6d4f90bcfe5da4a428e5 Mon Sep 17 00:00:00 2001 From: Denis Bezrukov <6227442+denbezrukov@users.noreply.github.com> Date: Sat, 28 Feb 2026 22:43:23 +0200 Subject: [PATCH 3/4] add changeset --- .changeset/fix-container-scroll-state-scss.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 .changeset/fix-container-scroll-state-scss.md diff --git a/.changeset/fix-container-scroll-state-scss.md b/.changeset/fix-container-scroll-state-scss.md new file mode 100644 index 000000000000..6aaa773dcef2 --- /dev/null +++ b/.changeset/fix-container-scroll-state-scss.md @@ -0,0 +1,14 @@ +--- +"@biomejs/biome": patch +--- + +Fixed [#9253](https://github.com/biomejs/biome/issues/9253): parsing of `@container scroll-state(...)` queries. + +```css +@container scroll-state(scrolled: bottom) { } +@container scroll-state(stuck) { } +@container scroll-state(not (stuck)) { } +@container scroll-state((stuck) and (scrolled: bottom)) { } +@container scroll-state((stuck) or (snapped: x)) { } +@container main-layout scroll-state(not ((stuck) and (scrolled: bottom))) { } +``` From 6e3154b62be55315a43d27220c68f0f1ba82e41d Mon Sep 17 00:00:00 2001 From: Denis Bezrukov <6227442+denbezrukov@users.noreply.github.com> Date: Sat, 28 Feb 2026 23:06:26 +0200 Subject: [PATCH 4/4] add changeset --- ...ner-scroll-state-scss.md => fix-container-scroll-state-css.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .changeset/{fix-container-scroll-state-scss.md => fix-container-scroll-state-css.md} (100%) diff --git a/.changeset/fix-container-scroll-state-scss.md b/.changeset/fix-container-scroll-state-css.md similarity index 100% rename from .changeset/fix-container-scroll-state-scss.md rename to .changeset/fix-container-scroll-state-css.md