diff --git a/crates/biome_css_analyze/src/lint/nursery/use_baseline.rs b/crates/biome_css_analyze/src/lint/nursery/use_baseline.rs index ad1576a73718..ba8f3199fe23 100644 --- a/crates/biome_css_analyze/src/lint/nursery/use_baseline.rs +++ b/crates/biome_css_analyze/src/lint/nursery/use_baseline.rs @@ -613,6 +613,7 @@ fn at_rule_name(rule: &AnyCssAtRule) -> Option<&'static str> { | AnyCssAtRule::TwThemeAtRule(_) | AnyCssAtRule::TwUtilityAtRule(_) | AnyCssAtRule::ScssContentAtRule(_) + | AnyCssAtRule::ScssAtRootAtRule(_) | AnyCssAtRule::ScssDebugAtRule(_) | AnyCssAtRule::ScssEachAtRule(_) | AnyCssAtRule::ScssErrorAtRule(_) diff --git a/crates/biome_css_factory/src/generated/node_factory.rs b/crates/biome_css_factory/src/generated/node_factory.rs index ca27feef48f8..009cd9d4e60f 100644 --- a/crates/biome_css_factory/src/generated/node_factory.rs +++ b/crates/biome_css_factory/src/generated/node_factory.rs @@ -3068,6 +3068,70 @@ pub fn scss_arbitrary_argument( ], )) } +pub fn scss_at_root_at_rule( + at_root_token: SyntaxToken, + block: CssDeclarationOrRuleBlock, +) -> ScssAtRootAtRuleBuilder { + ScssAtRootAtRuleBuilder { + at_root_token, + block, + query: None, + selector: None, + } +} +pub struct ScssAtRootAtRuleBuilder { + at_root_token: SyntaxToken, + block: CssDeclarationOrRuleBlock, + query: Option, + selector: Option, +} +impl ScssAtRootAtRuleBuilder { + pub fn with_query(mut self, query: ScssAtRootQuery) -> Self { + self.query = Some(query); + self + } + pub fn with_selector(mut self, selector: ScssAtRootSelector) -> Self { + self.selector = Some(selector); + self + } + pub fn build(self) -> ScssAtRootAtRule { + ScssAtRootAtRule::unwrap_cast(SyntaxNode::new_detached( + CssSyntaxKind::SCSS_AT_ROOT_AT_RULE, + [ + Some(SyntaxElement::Token(self.at_root_token)), + self.query + .map(|token| SyntaxElement::Node(token.into_syntax())), + self.selector + .map(|token| SyntaxElement::Node(token.into_syntax())), + Some(SyntaxElement::Node(self.block.into_syntax())), + ], + )) + } +} +pub fn scss_at_root_query( + l_paren_token: SyntaxToken, + modifier_token: SyntaxToken, + colon_token: SyntaxToken, + queries: ScssAtRootQueryList, + r_paren_token: SyntaxToken, +) -> ScssAtRootQuery { + ScssAtRootQuery::unwrap_cast(SyntaxNode::new_detached( + CssSyntaxKind::SCSS_AT_ROOT_QUERY, + [ + Some(SyntaxElement::Token(l_paren_token)), + Some(SyntaxElement::Token(modifier_token)), + Some(SyntaxElement::Token(colon_token)), + Some(SyntaxElement::Node(queries.into_syntax())), + Some(SyntaxElement::Token(r_paren_token)), + ], + )) +} +pub fn scss_at_root_selector(selector: CssSelectorList) -> ScssAtRootSelector { + ScssAtRootSelector::unwrap_cast(SyntaxNode::new_detached( + CssSyntaxKind::SCSS_AT_ROOT_SELECTOR, + [Some(SyntaxElement::Node(selector.into_syntax()))], + )) +} pub fn scss_binary_expression( left: AnyScssExpression, operator_token: SyntaxToken, @@ -4885,6 +4949,18 @@ where }), )) } +pub fn scss_at_root_query_list(items: I) -> ScssAtRootQueryList +where + I: IntoIterator, + I::IntoIter: ExactSizeIterator, +{ + ScssAtRootQueryList::unwrap_cast(SyntaxNode::new_detached( + CssSyntaxKind::SCSS_AT_ROOT_QUERY_LIST, + items + .into_iter() + .map(|item| Some(item.into_syntax().into())), + )) +} pub fn scss_each_binding_list(items: I, separators: S) -> ScssEachBindingList where I: IntoIterator, diff --git a/crates/biome_css_factory/src/generated/syntax_factory.rs b/crates/biome_css_factory/src/generated/syntax_factory.rs index 21d30c732933..e0017489f202 100644 --- a/crates/biome_css_factory/src/generated/syntax_factory.rs +++ b/crates/biome_css_factory/src/generated/syntax_factory.rs @@ -6330,6 +6330,112 @@ impl SyntaxFactory for CssSyntaxFactory { } slots.into_node(SCSS_ARBITRARY_ARGUMENT, children) } + SCSS_AT_ROOT_AT_RULE => { + 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 + && element.kind() == T![at_root] + { + slots.mark_present(); + current_element = elements.next(); + } + slots.next_slot(); + if let Some(element) = ¤t_element + && ScssAtRootQuery::can_cast(element.kind()) + { + slots.mark_present(); + current_element = elements.next(); + } + slots.next_slot(); + if let Some(element) = ¤t_element + && ScssAtRootSelector::can_cast(element.kind()) + { + slots.mark_present(); + current_element = elements.next(); + } + slots.next_slot(); + if let Some(element) = ¤t_element + && CssDeclarationOrRuleBlock::can_cast(element.kind()) + { + slots.mark_present(); + current_element = elements.next(); + } + slots.next_slot(); + if current_element.is_some() { + return RawSyntaxNode::new( + SCSS_AT_ROOT_AT_RULE.to_bogus(), + children.into_iter().map(Some), + ); + } + slots.into_node(SCSS_AT_ROOT_AT_RULE, children) + } + SCSS_AT_ROOT_QUERY => { + let mut elements = (&children).into_iter(); + let mut slots: RawNodeSlots<5usize> = 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 + && matches!(element.kind(), T![with] | T![without]) + { + 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 + && ScssAtRootQueryList::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( + SCSS_AT_ROOT_QUERY.to_bogus(), + children.into_iter().map(Some), + ); + } + slots.into_node(SCSS_AT_ROOT_QUERY, children) + } + SCSS_AT_ROOT_SELECTOR => { + let mut elements = (&children).into_iter(); + let mut slots: RawNodeSlots<1usize> = RawNodeSlots::default(); + let mut current_element = elements.next(); + if let Some(element) = ¤t_element + && CssSelectorList::can_cast(element.kind()) + { + slots.mark_present(); + current_element = elements.next(); + } + slots.next_slot(); + if current_element.is_some() { + return RawSyntaxNode::new( + SCSS_AT_ROOT_SELECTOR.to_bogus(), + children.into_iter().map(Some), + ); + } + slots.into_node(SCSS_AT_ROOT_SELECTOR, children) + } SCSS_BINARY_EXPRESSION => { let mut elements = (&children).into_iter(); let mut slots: RawNodeSlots<3usize> = RawNodeSlots::default(); @@ -8611,6 +8717,9 @@ impl SyntaxFactory for CssSyntaxFactory { T ! [,], false, ), + SCSS_AT_ROOT_QUERY_LIST => { + Self::make_node_list_syntax(kind, children, AnyCssCustomIdentifier::can_cast) + } SCSS_EACH_BINDING_LIST => Self::make_separated_list_syntax( kind, children, diff --git a/crates/biome_css_formatter/src/css/any/at_rule.rs b/crates/biome_css_formatter/src/css/any/at_rule.rs index 409393f2b49d..a286976b0930 100644 --- a/crates/biome_css_formatter/src/css/any/at_rule.rs +++ b/crates/biome_css_formatter/src/css/any/at_rule.rs @@ -33,6 +33,7 @@ impl FormatRule for FormatAnyCssAtRule { AnyCssAtRule::CssUnknownValueAtRule(node) => node.format().fmt(f), AnyCssAtRule::CssValueAtRule(node) => node.format().fmt(f), AnyCssAtRule::CssViewTransitionAtRule(node) => node.format().fmt(f), + AnyCssAtRule::ScssAtRootAtRule(node) => node.format().fmt(f), AnyCssAtRule::ScssContentAtRule(node) => node.format().fmt(f), AnyCssAtRule::ScssDebugAtRule(node) => node.format().fmt(f), AnyCssAtRule::ScssEachAtRule(node) => node.format().fmt(f), diff --git a/crates/biome_css_formatter/src/generated.rs b/crates/biome_css_formatter/src/generated.rs index 502470280168..acc4f6639f04 100644 --- a/crates/biome_css_formatter/src/generated.rs +++ b/crates/biome_css_formatter/src/generated.rs @@ -7115,6 +7115,120 @@ impl IntoFormat for biome_css_syntax::ScssArbitraryArgument { ) } } +impl FormatRule + for crate::scss::statements::at_root_at_rule::FormatScssAtRootAtRule +{ + type Context = CssFormatContext; + #[inline(always)] + fn fmt( + &self, + node: &biome_css_syntax::ScssAtRootAtRule, + f: &mut CssFormatter, + ) -> FormatResult<()> { + FormatNodeRule::::fmt(self, node, f) + } +} +impl AsFormat for biome_css_syntax::ScssAtRootAtRule { + type Format<'a> = FormatRefWithRule< + 'a, + biome_css_syntax::ScssAtRootAtRule, + crate::scss::statements::at_root_at_rule::FormatScssAtRootAtRule, + >; + fn format(&self) -> Self::Format<'_> { + FormatRefWithRule::new( + self, + crate::scss::statements::at_root_at_rule::FormatScssAtRootAtRule::default(), + ) + } +} +impl IntoFormat for biome_css_syntax::ScssAtRootAtRule { + type Format = FormatOwnedWithRule< + biome_css_syntax::ScssAtRootAtRule, + crate::scss::statements::at_root_at_rule::FormatScssAtRootAtRule, + >; + fn into_format(self) -> Self::Format { + FormatOwnedWithRule::new( + self, + crate::scss::statements::at_root_at_rule::FormatScssAtRootAtRule::default(), + ) + } +} +impl FormatRule + for crate::scss::auxiliary::at_root_query::FormatScssAtRootQuery +{ + type Context = CssFormatContext; + #[inline(always)] + fn fmt( + &self, + node: &biome_css_syntax::ScssAtRootQuery, + f: &mut CssFormatter, + ) -> FormatResult<()> { + FormatNodeRule::::fmt(self, node, f) + } +} +impl AsFormat for biome_css_syntax::ScssAtRootQuery { + type Format<'a> = FormatRefWithRule< + 'a, + biome_css_syntax::ScssAtRootQuery, + crate::scss::auxiliary::at_root_query::FormatScssAtRootQuery, + >; + fn format(&self) -> Self::Format<'_> { + FormatRefWithRule::new( + self, + crate::scss::auxiliary::at_root_query::FormatScssAtRootQuery::default(), + ) + } +} +impl IntoFormat for biome_css_syntax::ScssAtRootQuery { + type Format = FormatOwnedWithRule< + biome_css_syntax::ScssAtRootQuery, + crate::scss::auxiliary::at_root_query::FormatScssAtRootQuery, + >; + fn into_format(self) -> Self::Format { + FormatOwnedWithRule::new( + self, + crate::scss::auxiliary::at_root_query::FormatScssAtRootQuery::default(), + ) + } +} +impl FormatRule + for crate::scss::selectors::at_root_selector::FormatScssAtRootSelector +{ + type Context = CssFormatContext; + #[inline(always)] + fn fmt( + &self, + node: &biome_css_syntax::ScssAtRootSelector, + f: &mut CssFormatter, + ) -> FormatResult<()> { + FormatNodeRule::::fmt(self, node, f) + } +} +impl AsFormat for biome_css_syntax::ScssAtRootSelector { + type Format<'a> = FormatRefWithRule< + 'a, + biome_css_syntax::ScssAtRootSelector, + crate::scss::selectors::at_root_selector::FormatScssAtRootSelector, + >; + fn format(&self) -> Self::Format<'_> { + FormatRefWithRule::new( + self, + crate::scss::selectors::at_root_selector::FormatScssAtRootSelector::default(), + ) + } +} +impl IntoFormat for biome_css_syntax::ScssAtRootSelector { + type Format = FormatOwnedWithRule< + biome_css_syntax::ScssAtRootSelector, + crate::scss::selectors::at_root_selector::FormatScssAtRootSelector, + >; + fn into_format(self) -> Self::Format { + FormatOwnedWithRule::new( + self, + crate::scss::selectors::at_root_selector::FormatScssAtRootSelector::default(), + ) + } +} impl FormatRule for crate::scss::auxiliary::binary_expression::FormatScssBinaryExpression { @@ -10268,6 +10382,31 @@ impl IntoFormat for biome_css_syntax::CssValueAtRulePropertyLi FormatOwnedWithRule :: new (self , crate :: css :: lists :: value_at_rule_property_list :: FormatCssValueAtRulePropertyList :: default ()) } } +impl AsFormat for biome_css_syntax::ScssAtRootQueryList { + type Format<'a> = FormatRefWithRule< + 'a, + biome_css_syntax::ScssAtRootQueryList, + crate::scss::lists::at_root_query_list::FormatScssAtRootQueryList, + >; + fn format(&self) -> Self::Format<'_> { + FormatRefWithRule::new( + self, + crate::scss::lists::at_root_query_list::FormatScssAtRootQueryList::default(), + ) + } +} +impl IntoFormat for biome_css_syntax::ScssAtRootQueryList { + type Format = FormatOwnedWithRule< + biome_css_syntax::ScssAtRootQueryList, + crate::scss::lists::at_root_query_list::FormatScssAtRootQueryList, + >; + fn into_format(self) -> Self::Format { + FormatOwnedWithRule::new( + self, + crate::scss::lists::at_root_query_list::FormatScssAtRootQueryList::default(), + ) + } +} impl AsFormat for biome_css_syntax::ScssEachBindingList { type Format<'a> = FormatRefWithRule< 'a, diff --git a/crates/biome_css_formatter/src/scss/auxiliary/at_root_query.rs b/crates/biome_css_formatter/src/scss/auxiliary/at_root_query.rs new file mode 100644 index 000000000000..42a2fb3e1567 --- /dev/null +++ b/crates/biome_css_formatter/src/scss/auxiliary/at_root_query.rs @@ -0,0 +1,30 @@ +use crate::prelude::*; +use biome_css_syntax::{ScssAtRootQuery, ScssAtRootQueryFields}; +use biome_formatter::{format_args, write}; + +#[derive(Debug, Clone, Default)] +pub(crate) struct FormatScssAtRootQuery; + +impl FormatNodeRule for FormatScssAtRootQuery { + fn fmt_fields(&self, node: &ScssAtRootQuery, f: &mut CssFormatter) -> FormatResult<()> { + let ScssAtRootQueryFields { + l_paren_token, + modifier, + colon_token, + queries, + r_paren_token, + } = node.as_fields(); + + write!( + f, + [group(&format_args![ + l_paren_token.format(), + modifier.format(), + colon_token.format(), + space(), + group(&indent(&queries.format())), + r_paren_token.format() + ])] + ) + } +} diff --git a/crates/biome_css_formatter/src/scss/auxiliary/mod.rs b/crates/biome_css_formatter/src/scss/auxiliary/mod.rs index 5c4b8c4a2ee6..e734fe41b8cb 100644 --- a/crates/biome_css_formatter/src/scss/auxiliary/mod.rs +++ b/crates/biome_css_formatter/src/scss/auxiliary/mod.rs @@ -1,6 +1,7 @@ //! This is a generated file. Don't modify it by hand! Run 'cargo codegen formatter' to re-generate the file. pub(crate) mod arbitrary_argument; +pub(crate) mod at_root_query; pub(crate) mod binary_expression; pub(crate) mod declaration; pub(crate) mod else_clause; diff --git a/crates/biome_css_formatter/src/scss/lists/at_root_query_list.rs b/crates/biome_css_formatter/src/scss/lists/at_root_query_list.rs new file mode 100644 index 000000000000..ad7b72fff85e --- /dev/null +++ b/crates/biome_css_formatter/src/scss/lists/at_root_query_list.rs @@ -0,0 +1,15 @@ +use crate::prelude::*; +use biome_css_syntax::ScssAtRootQueryList; + +#[derive(Debug, Clone, Default)] +pub(crate) struct FormatScssAtRootQueryList; + +impl FormatRule for FormatScssAtRootQueryList { + type Context = CssFormatContext; + + fn fmt(&self, node: &ScssAtRootQueryList, f: &mut CssFormatter) -> FormatResult<()> { + f.join_with(&soft_line_break_or_space()) + .entries(node.iter().formatted()) + .finish() + } +} diff --git a/crates/biome_css_formatter/src/scss/lists/mod.rs b/crates/biome_css_formatter/src/scss/lists/mod.rs index 52e10fb2d8fb..7b6a7c88e72f 100644 --- a/crates/biome_css_formatter/src/scss/lists/mod.rs +++ b/crates/biome_css_formatter/src/scss/lists/mod.rs @@ -1,5 +1,6 @@ //! This is a generated file. Don't modify it by hand! Run 'cargo codegen formatter' to re-generate the file. +pub(crate) mod at_root_query_list; pub(crate) mod each_binding_list; pub(crate) mod expression_item_list; pub(crate) mod import_item_list; diff --git a/crates/biome_css_formatter/src/scss/selectors/at_root_selector.rs b/crates/biome_css_formatter/src/scss/selectors/at_root_selector.rs new file mode 100644 index 000000000000..25d0b06200b1 --- /dev/null +++ b/crates/biome_css_formatter/src/scss/selectors/at_root_selector.rs @@ -0,0 +1,12 @@ +use crate::prelude::*; +use biome_css_syntax::{ScssAtRootSelector, ScssAtRootSelectorFields}; + +#[derive(Debug, Clone, Default)] +pub(crate) struct FormatScssAtRootSelector; + +impl FormatNodeRule for FormatScssAtRootSelector { + fn fmt_fields(&self, node: &ScssAtRootSelector, f: &mut CssFormatter) -> FormatResult<()> { + let ScssAtRootSelectorFields { selector } = node.as_fields(); + selector.format().fmt(f) + } +} diff --git a/crates/biome_css_formatter/src/scss/selectors/mod.rs b/crates/biome_css_formatter/src/scss/selectors/mod.rs index 927620962bc4..dbdf077b7bbd 100644 --- a/crates/biome_css_formatter/src/scss/selectors/mod.rs +++ b/crates/biome_css_formatter/src/scss/selectors/mod.rs @@ -1,3 +1,4 @@ //! This is a generated file. Don't modify it by hand! Run 'cargo codegen formatter' to re-generate the file. +pub(crate) mod at_root_selector; pub(crate) mod placeholder_selector; diff --git a/crates/biome_css_formatter/src/scss/statements/at_root_at_rule.rs b/crates/biome_css_formatter/src/scss/statements/at_root_at_rule.rs new file mode 100644 index 000000000000..40f0584de174 --- /dev/null +++ b/crates/biome_css_formatter/src/scss/statements/at_root_at_rule.rs @@ -0,0 +1,29 @@ +use crate::prelude::*; +use biome_css_syntax::{ScssAtRootAtRule, ScssAtRootAtRuleFields}; +use biome_formatter::write; + +#[derive(Debug, Clone, Default)] +pub(crate) struct FormatScssAtRootAtRule; + +impl FormatNodeRule for FormatScssAtRootAtRule { + fn fmt_fields(&self, node: &ScssAtRootAtRule, f: &mut CssFormatter) -> FormatResult<()> { + let ScssAtRootAtRuleFields { + at_root_token, + query, + selector, + block, + } = node.as_fields(); + + write!(f, [at_root_token.format()])?; + + if let Some(query) = query { + write!(f, [space(), query.format()])?; + } + + if let Some(selector) = selector { + write!(f, [space(), group(&selector.format())])?; + } + + write!(f, [space(), block.format()]) + } +} diff --git a/crates/biome_css_formatter/src/scss/statements/mod.rs b/crates/biome_css_formatter/src/scss/statements/mod.rs index 51350e56d86d..fdf8c991ac14 100644 --- a/crates/biome_css_formatter/src/scss/statements/mod.rs +++ b/crates/biome_css_formatter/src/scss/statements/mod.rs @@ -1,5 +1,6 @@ //! This is a generated file. Don't modify it by hand! Run 'cargo codegen formatter' to re-generate the file. +pub(crate) mod at_root_at_rule; pub(crate) mod content_at_rule; pub(crate) mod debug_at_rule; pub(crate) mod each_at_rule; diff --git a/crates/biome_css_formatter/tests/specs/css/scss/at-rule/at-root.scss b/crates/biome_css_formatter/tests/specs/css/scss/at-rule/at-root.scss new file mode 100644 index 000000000000..9cb20c844997 --- /dev/null +++ b/crates/biome_css_formatter/tests/specs/css/scss/at-rule/at-root.scss @@ -0,0 +1,20 @@ +@at-root(without:media supports){ +.root-only{ +color:red; +} +} + +@at-root +.root-only, +.another-root +{ +color:blue; +} + +@mixin card{ +@at-root(with:rule){ +.card{ +display:block; +} +} +} diff --git a/crates/biome_css_formatter/tests/specs/css/scss/at-rule/at-root.scss.snap b/crates/biome_css_formatter/tests/specs/css/scss/at-rule/at-root.scss.snap new file mode 100644 index 000000000000..d0f2dfc8cdb4 --- /dev/null +++ b/crates/biome_css_formatter/tests/specs/css/scss/at-rule/at-root.scss.snap @@ -0,0 +1,67 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: css/scss/at-rule/at-root.scss +--- + +# Input + +```scss +@at-root(without:media supports){ +.root-only{ +color:red; +} +} + +@at-root +.root-only, +.another-root +{ +color:blue; +} + +@mixin card{ +@at-root(with:rule){ +.card{ +display:block; +} +} +} + +``` + + +============================= + +# Outputs + +## Output 1 + +----- +Indent style: Tab +Indent width: 2 +Line ending: LF +Line width: 80 +Quote style: Double Quotes +Trailing newline: true +----- + +```scss +@at-root (without: media supports) { + .root-only { + color: red; + } +} + +@at-root .root-only, .another-root { + color: blue; +} + +@mixin card { + @at-root (with: rule) { + .card { + display: block; + } + } +} + +``` diff --git a/crates/biome_css_parser/src/lexer/mod.rs b/crates/biome_css_parser/src/lexer/mod.rs index d8676bd1c37b..4b689a768dff 100644 --- a/crates/biome_css_parser/src/lexer/mod.rs +++ b/crates/biome_css_parser/src/lexer/mod.rs @@ -851,6 +851,7 @@ impl<'src> CssLexer<'src> { b"warn" => WARN_KW, b"error" => ERROR_KW, b"content" => CONTENT_KW, + b"at-root" => AT_ROOT_KW, b"extend" => EXTEND_KW, b"for" => FOR_KW, b"forward" => FORWARD_KW, @@ -859,6 +860,7 @@ impl<'src> CssLexer<'src> { b"mixin" => MIXIN_KW, b"optional" => OPTIONAL_KW, b"while" => WHILE_KW, + b"without" => WITHOUT_KW, b"show" => SHOW_KW, b"sass" => SASS_KW, b"style" => STYLE_KW, diff --git a/crates/biome_css_parser/src/syntax/at_rule/mod.rs b/crates/biome_css_parser/src/syntax/at_rule/mod.rs index 50f03715fa3a..93b75f2ce7d0 100644 --- a/crates/biome_css_parser/src/syntax/at_rule/mod.rs +++ b/crates/biome_css_parser/src/syntax/at_rule/mod.rs @@ -75,12 +75,12 @@ use crate::syntax::at_rule::view_transition::{ use crate::syntax::CssSyntaxFeatures; use crate::syntax::parse_error::{expected_any_at_rule, tailwind_disabled}; use crate::syntax::scss::{ - parse_bogus_scss_else_at_rule, parse_scss_content_at_rule, parse_scss_debug_at_rule, - parse_scss_each_at_rule, parse_scss_error_at_rule, parse_scss_extend_at_rule, - parse_scss_for_at_rule, parse_scss_forward_at_rule, parse_scss_function_at_rule, - parse_scss_if_at_rule, parse_scss_import_at_rule, parse_scss_include_at_rule, - parse_scss_mixin_at_rule, parse_scss_return_at_rule, parse_scss_use_at_rule, - parse_scss_warn_at_rule, parse_scss_while_at_rule, + parse_bogus_scss_else_at_rule, parse_scss_at_root_at_rule, parse_scss_content_at_rule, + parse_scss_debug_at_rule, parse_scss_each_at_rule, parse_scss_error_at_rule, + parse_scss_extend_at_rule, parse_scss_for_at_rule, parse_scss_forward_at_rule, + parse_scss_function_at_rule, parse_scss_if_at_rule, parse_scss_import_at_rule, + parse_scss_include_at_rule, parse_scss_mixin_at_rule, parse_scss_return_at_rule, + parse_scss_use_at_rule, parse_scss_warn_at_rule, parse_scss_while_at_rule, }; use biome_css_syntax::CssSyntaxKind::*; use biome_css_syntax::T; @@ -161,6 +161,9 @@ pub(crate) fn parse_any_at_rule(p: &mut CssParser) -> ParsedSyntax { T![extend] => CssSyntaxFeatures::Scss .parse_supported_syntax(p, parse_scss_extend_at_rule) .or_else(|| parse_unknown_at_rule(p)), + T![at_root] => CssSyntaxFeatures::Scss + .parse_supported_syntax(p, parse_scss_at_root_at_rule) + .or_else(|| parse_unknown_at_rule(p)), T![for] => CssSyntaxFeatures::Scss .parse_supported_syntax(p, parse_scss_for_at_rule) .or_else(|| parse_unknown_at_rule(p)), diff --git a/crates/biome_css_parser/src/syntax/scss/at_rule/at_root_at_rule.rs b/crates/biome_css_parser/src/syntax/scss/at_rule/at_root_at_rule.rs new file mode 100644 index 000000000000..2b78d32b9cb9 --- /dev/null +++ b/crates/biome_css_parser/src/syntax/scss/at_rule/at_root_at_rule.rs @@ -0,0 +1,166 @@ +use crate::lexer::CssLexContext; +use crate::parser::CssParser; +use crate::syntax::block::parse_declaration_or_rule_list_block; +use crate::syntax::parse_error::expected_identifier; +use crate::syntax::selector::SelectorList; +use crate::syntax::{is_at_identifier, parse_custom_identifier, parse_regular_identifier}; +use biome_css_syntax::CssSyntaxKind::{ + CSS_BOGUS_CUSTOM_IDENTIFIER, SCSS_AT_ROOT_AT_RULE, SCSS_AT_ROOT_QUERY, SCSS_AT_ROOT_QUERY_LIST, + SCSS_AT_ROOT_SELECTOR, +}; +use biome_css_syntax::{CssSyntaxKind, T}; +use biome_parser::diagnostic::expected_any; +use biome_parser::parse_lists::{ParseNodeList, ParseSeparatedList}; +use biome_parser::parse_recovery::{ParseRecovery, RecoveryResult}; +use biome_parser::prelude::ParsedSyntax::{Absent, Present}; +use biome_parser::prelude::*; + +/// Parses the SCSS `@at-root` at-rule. +/// +/// # Example +/// +/// ```scss +/// @at-root (without: media) { +/// .root-only { +/// color: red; +/// } +/// } +/// ``` +/// +/// Docs: https://sass-lang.com/documentation/at-rules/at-root/ +#[inline] +pub(crate) fn parse_scss_at_root_at_rule(p: &mut CssParser) -> ParsedSyntax { + if !is_at_scss_at_root_at_rule(p) { + return Absent; + } + + let m = p.start(); + + p.bump(T![at_root]); + let query = parse_scss_at_root_query(p); + + if query.is_present() || p.at(T!['{']) { + parse_declaration_or_rule_list_block(p); + } else { + parse_scss_at_root_selector(p); + parse_declaration_or_rule_list_block(p); + } + + Present(m.complete(p, SCSS_AT_ROOT_AT_RULE)) +} + +#[inline] +fn is_at_scss_at_root_at_rule(p: &mut CssParser) -> bool { + p.at(T![at_root]) +} + +/// Parses the optional `@at-root` query clause. +/// +/// # Example +/// +/// ```scss +/// @at-root (without: media supports) { +/// ^^^^^^^^^^^^^^^^^^^^^^^^^ +/// .root-only { +/// color: red; +/// } +/// } +/// ``` +/// +/// Docs: https://sass-lang.com/documentation/at-rules/at-root/#beyond-style-rules +#[inline] +fn parse_scss_at_root_query(p: &mut CssParser) -> ParsedSyntax { + if !is_at_scss_at_root_query(p) { + return Absent; + } + + let m = p.start(); + + p.bump(T!['(']); + parse_scss_at_root_query_modifier(p); + p.expect(T![:]); + + if p.at(T![')']) { + p.error(expected_identifier(p, p.cur_range())); + } + ScssAtRootQueryList.parse_list(p); + + p.expect(T![')']); + + Present(m.complete(p, SCSS_AT_ROOT_QUERY)) +} + +#[inline] +fn is_at_scss_at_root_query(p: &mut CssParser) -> bool { + p.at(T!['(']) +} + +#[inline] +fn parse_scss_at_root_query_modifier(p: &mut CssParser) { + if p.at(T![with]) { + p.bump(T![with]); + } else if p.at(T![without]) { + p.bump(T![without]); + } else { + p.error(expected_any(&["with", "without"], p.cur_range(), p)); + // Consume a stray identifier so recovery can continue at the `:` token. + parse_regular_identifier(p).ok(); + } +} + +/// Parses the selector shorthand used by `@at-root { ... }`. +/// +/// # Example +/// +/// ```scss +/// @at-root .root-only, .another-root { +/// ^^^^^^^^^^^^^^^^^^^^^^^^^ +/// color: red; +/// } +/// ``` +/// +/// Docs: https://sass-lang.com/documentation/at-rules/at-root/ +#[inline] +fn parse_scss_at_root_selector(p: &mut CssParser) -> CompletedMarker { + let m = p.start(); + + SelectorList::default().parse_list(p); + + m.complete(p, SCSS_AT_ROOT_SELECTOR) +} + +struct ScssAtRootQueryList; + +impl ParseNodeList for ScssAtRootQueryList { + type Kind = CssSyntaxKind; + type Parser<'source> = CssParser<'source>; + const LIST_KIND: Self::Kind = SCSS_AT_ROOT_QUERY_LIST; + + fn parse_element(&mut self, p: &mut Self::Parser<'_>) -> ParsedSyntax { + parse_custom_identifier(p, CssLexContext::Regular) + } + + fn is_at_list_end(&self, p: &mut Self::Parser<'_>) -> bool { + p.at(T![')']) + } + + fn recover( + &mut self, + p: &mut Self::Parser<'_>, + parsed_element: ParsedSyntax, + ) -> RecoveryResult { + parsed_element.or_recover(p, &ScssAtRootQueryListParseRecovery, expected_identifier) + } +} + +struct ScssAtRootQueryListParseRecovery; + +impl ParseRecovery for ScssAtRootQueryListParseRecovery { + type Kind = CssSyntaxKind; + type Parser<'source> = CssParser<'source>; + const RECOVERED_KIND: Self::Kind = CSS_BOGUS_CUSTOM_IDENTIFIER; + + fn is_at_recovered(&self, p: &mut Self::Parser<'_>) -> bool { + p.at(T![')']) || is_at_identifier(p) + } +} diff --git a/crates/biome_css_parser/src/syntax/scss/at_rule/mod.rs b/crates/biome_css_parser/src/syntax/scss/at_rule/mod.rs index 781bb171f3f3..2e18be212881 100644 --- a/crates/biome_css_parser/src/syntax/scss/at_rule/mod.rs +++ b/crates/biome_css_parser/src/syntax/scss/at_rule/mod.rs @@ -1,3 +1,4 @@ +mod at_root_at_rule; mod content_at_rule; mod debug; mod each_at_rule; @@ -25,6 +26,7 @@ use biome_parser::prelude::ParsedSyntax::{Absent, Present}; use biome_parser::prelude::*; use biome_parser::{TokenSet, token_set}; +pub(crate) use at_root_at_rule::parse_scss_at_root_at_rule; pub(crate) use content_at_rule::parse_scss_content_at_rule; pub(crate) use debug::parse_scss_debug_at_rule; pub(crate) use each_at_rule::parse_scss_each_at_rule; diff --git a/crates/biome_css_parser/src/syntax/scss/mod.rs b/crates/biome_css_parser/src/syntax/scss/mod.rs index 80223579e0d6..c5bba783b06d 100644 --- a/crates/biome_css_parser/src/syntax/scss/mod.rs +++ b/crates/biome_css_parser/src/syntax/scss/mod.rs @@ -9,12 +9,12 @@ mod token_sets; mod value; pub(crate) use at_rule::{ - parse_bogus_scss_else_at_rule, parse_scss_content_at_rule, parse_scss_debug_at_rule, - parse_scss_each_at_rule, parse_scss_error_at_rule, parse_scss_extend_at_rule, - parse_scss_for_at_rule, parse_scss_forward_at_rule, parse_scss_function_at_rule, - parse_scss_if_at_rule, parse_scss_import_at_rule, parse_scss_include_at_rule, - parse_scss_mixin_at_rule, parse_scss_return_at_rule, parse_scss_use_at_rule, - parse_scss_warn_at_rule, parse_scss_while_at_rule, + parse_bogus_scss_else_at_rule, parse_scss_at_root_at_rule, parse_scss_content_at_rule, + parse_scss_debug_at_rule, parse_scss_each_at_rule, parse_scss_error_at_rule, + parse_scss_extend_at_rule, parse_scss_for_at_rule, parse_scss_forward_at_rule, + parse_scss_function_at_rule, parse_scss_if_at_rule, parse_scss_import_at_rule, + parse_scss_include_at_rule, parse_scss_mixin_at_rule, parse_scss_return_at_rule, + parse_scss_use_at_rule, parse_scss_warn_at_rule, parse_scss_while_at_rule, }; pub(crate) use declaration::{ is_at_scss_declaration, is_at_scss_nesting_declaration, is_at_scss_variable_modifier_start, diff --git a/crates/biome_css_parser/tests/css_test_suite/error/scss/at-rule/at-root.scss b/crates/biome_css_parser/tests/css_test_suite/error/scss/at-rule/at-root.scss new file mode 100644 index 000000000000..de72c3e26423 --- /dev/null +++ b/crates/biome_css_parser/tests/css_test_suite/error/scss/at-rule/at-root.scss @@ -0,0 +1,17 @@ +@at-root (without media) { + .root-only { + color: red; + } +} + +@at-root (without:) { + .root-only { + color: blue; + } +} + +@at-root .root-only; + +@at-root , { + color: green; +} diff --git a/crates/biome_css_parser/tests/css_test_suite/error/scss/at-rule/at-root.scss.snap b/crates/biome_css_parser/tests/css_test_suite/error/scss/at-rule/at-root.scss.snap new file mode 100644 index 000000000000..cffbd0bf0b49 --- /dev/null +++ b/crates/biome_css_parser/tests/css_test_suite/error/scss/at-rule/at-root.scss.snap @@ -0,0 +1,470 @@ +--- +source: crates/biome_css_parser/tests/spec_test.rs +expression: snapshot +--- + +## Input + +```css +@at-root (without media) { + .root-only { + color: red; + } +} + +@at-root (without:) { + .root-only { + color: blue; + } +} + +@at-root .root-only; + +@at-root , { + color: green; +} + +``` + + +## AST + +``` +CssRoot { + bom_token: missing (optional), + items: CssRootItemList [ + CssAtRule { + at_token: AT@0..1 "@" [] [], + rule: ScssAtRootAtRule { + at_root_token: AT_ROOT_KW@1..9 "at-root" [] [Whitespace(" ")], + query: ScssAtRootQuery { + l_paren_token: L_PAREN@9..10 "(" [] [], + modifier: WITHOUT_KW@10..18 "without" [] [Whitespace(" ")], + colon_token: missing (required), + queries: ScssAtRootQueryList [ + CssCustomIdentifier { + value_token: IDENT@18..23 "media" [] [], + }, + ], + r_paren_token: R_PAREN@23..25 ")" [] [Whitespace(" ")], + }, + selector: missing (optional), + block: CssDeclarationOrRuleBlock { + l_curly_token: L_CURLY@25..26 "{" [] [], + items: CssDeclarationOrRuleList [ + CssNestedQualifiedRule { + prelude: CssRelativeSelectorList [ + CssRelativeSelector { + combinator: missing (optional), + selector: CssCompoundSelector { + nesting_selectors: CssNestedSelectorList [], + simple_selector: missing (optional), + sub_selectors: CssSubSelectorList [ + CssClassSelector { + dot_token: DOT@26..30 "." [Newline("\n"), Whitespace(" ")] [], + name: CssCustomIdentifier { + value_token: IDENT@30..40 "root-only" [] [Whitespace(" ")], + }, + }, + ], + }, + }, + ], + block: CssDeclarationOrRuleBlock { + l_curly_token: L_CURLY@40..41 "{" [] [], + items: CssDeclarationOrRuleList [ + CssDeclarationWithSemicolon { + declaration: CssDeclaration { + property: CssGenericProperty { + name: CssIdentifier { + value_token: IDENT@41..51 "color" [Newline("\n"), Whitespace(" ")] [], + }, + colon_token: COLON@51..53 ":" [] [Whitespace(" ")], + value: ScssExpression { + items: ScssExpressionItemList [ + CssIdentifier { + value_token: IDENT@53..56 "red" [] [], + }, + ], + }, + }, + important: missing (optional), + }, + semicolon_token: SEMICOLON@56..57 ";" [] [], + }, + ], + r_curly_token: R_CURLY@57..61 "}" [Newline("\n"), Whitespace(" ")] [], + }, + }, + ], + r_curly_token: R_CURLY@61..63 "}" [Newline("\n")] [], + }, + }, + }, + CssAtRule { + at_token: AT@63..66 "@" [Newline("\n"), Newline("\n")] [], + rule: ScssAtRootAtRule { + at_root_token: AT_ROOT_KW@66..74 "at-root" [] [Whitespace(" ")], + query: ScssAtRootQuery { + l_paren_token: L_PAREN@74..75 "(" [] [], + modifier: WITHOUT_KW@75..82 "without" [] [], + colon_token: COLON@82..83 ":" [] [], + queries: ScssAtRootQueryList [], + r_paren_token: R_PAREN@83..85 ")" [] [Whitespace(" ")], + }, + selector: missing (optional), + block: CssDeclarationOrRuleBlock { + l_curly_token: L_CURLY@85..86 "{" [] [], + items: CssDeclarationOrRuleList [ + CssNestedQualifiedRule { + prelude: CssRelativeSelectorList [ + CssRelativeSelector { + combinator: missing (optional), + selector: CssCompoundSelector { + nesting_selectors: CssNestedSelectorList [], + simple_selector: missing (optional), + sub_selectors: CssSubSelectorList [ + CssClassSelector { + dot_token: DOT@86..90 "." [Newline("\n"), Whitespace(" ")] [], + name: CssCustomIdentifier { + value_token: IDENT@90..100 "root-only" [] [Whitespace(" ")], + }, + }, + ], + }, + }, + ], + block: CssDeclarationOrRuleBlock { + l_curly_token: L_CURLY@100..101 "{" [] [], + items: CssDeclarationOrRuleList [ + CssDeclarationWithSemicolon { + declaration: CssDeclaration { + property: CssGenericProperty { + name: CssIdentifier { + value_token: IDENT@101..111 "color" [Newline("\n"), Whitespace(" ")] [], + }, + colon_token: COLON@111..113 ":" [] [Whitespace(" ")], + value: ScssExpression { + items: ScssExpressionItemList [ + CssIdentifier { + value_token: IDENT@113..117 "blue" [] [], + }, + ], + }, + }, + important: missing (optional), + }, + semicolon_token: SEMICOLON@117..118 ";" [] [], + }, + ], + r_curly_token: R_CURLY@118..122 "}" [Newline("\n"), Whitespace(" ")] [], + }, + }, + ], + r_curly_token: R_CURLY@122..124 "}" [Newline("\n")] [], + }, + }, + }, + CssAtRule { + at_token: AT@124..127 "@" [Newline("\n"), Newline("\n")] [], + rule: CssBogusAtRule { + items: [ + AT_ROOT_KW@127..135 "at-root" [] [Whitespace(" ")], + ScssAtRootSelector { + selector: CssSelectorList [ + CssCompoundSelector { + nesting_selectors: CssNestedSelectorList [], + simple_selector: missing (optional), + sub_selectors: CssSubSelectorList [ + CssClassSelector { + dot_token: DOT@135..136 "." [] [], + name: CssCustomIdentifier { + value_token: IDENT@136..145 "root-only" [] [], + }, + }, + ], + }, + ], + }, + CssBogusBlock { + items: [ + CssDeclarationOrRuleList [ + CssEmptyDeclaration { + semicolon_token: SEMICOLON@145..146 ";" [] [], + }, + CssAtRule { + at_token: AT@146..149 "@" [Newline("\n"), Newline("\n")] [], + rule: ScssAtRootAtRule { + at_root_token: AT_ROOT_KW@149..157 "at-root" [] [Whitespace(" ")], + query: missing (optional), + selector: ScssAtRootSelector { + selector: CssSelectorList [ + missing element, + COMMA@157..159 "," [] [Whitespace(" ")], + missing element, + ], + }, + block: CssDeclarationOrRuleBlock { + l_curly_token: L_CURLY@159..160 "{" [] [], + items: CssDeclarationOrRuleList [ + CssDeclarationWithSemicolon { + declaration: CssDeclaration { + property: CssGenericProperty { + name: CssIdentifier { + value_token: IDENT@160..168 "color" [Newline("\n"), Whitespace(" ")] [], + }, + colon_token: COLON@168..170 ":" [] [Whitespace(" ")], + value: ScssExpression { + items: ScssExpressionItemList [ + CssIdentifier { + value_token: IDENT@170..175 "green" [] [], + }, + ], + }, + }, + important: missing (optional), + }, + semicolon_token: SEMICOLON@175..176 ";" [] [], + }, + ], + r_curly_token: R_CURLY@176..178 "}" [Newline("\n")] [], + }, + }, + }, + ], + ], + }, + ], + }, + }, + ], + eof_token: EOF@178..179 "" [Newline("\n")] [], +} +``` + +## CST + +``` +0: CSS_ROOT@0..179 + 0: (empty) + 1: CSS_ROOT_ITEM_LIST@0..178 + 0: CSS_AT_RULE@0..63 + 0: AT@0..1 "@" [] [] + 1: SCSS_AT_ROOT_AT_RULE@1..63 + 0: AT_ROOT_KW@1..9 "at-root" [] [Whitespace(" ")] + 1: SCSS_AT_ROOT_QUERY@9..25 + 0: L_PAREN@9..10 "(" [] [] + 1: WITHOUT_KW@10..18 "without" [] [Whitespace(" ")] + 2: (empty) + 3: SCSS_AT_ROOT_QUERY_LIST@18..23 + 0: CSS_CUSTOM_IDENTIFIER@18..23 + 0: IDENT@18..23 "media" [] [] + 4: R_PAREN@23..25 ")" [] [Whitespace(" ")] + 2: (empty) + 3: CSS_DECLARATION_OR_RULE_BLOCK@25..63 + 0: L_CURLY@25..26 "{" [] [] + 1: CSS_DECLARATION_OR_RULE_LIST@26..61 + 0: CSS_NESTED_QUALIFIED_RULE@26..61 + 0: CSS_RELATIVE_SELECTOR_LIST@26..40 + 0: CSS_RELATIVE_SELECTOR@26..40 + 0: (empty) + 1: CSS_COMPOUND_SELECTOR@26..40 + 0: CSS_NESTED_SELECTOR_LIST@26..26 + 1: (empty) + 2: CSS_SUB_SELECTOR_LIST@26..40 + 0: CSS_CLASS_SELECTOR@26..40 + 0: DOT@26..30 "." [Newline("\n"), Whitespace(" ")] [] + 1: CSS_CUSTOM_IDENTIFIER@30..40 + 0: IDENT@30..40 "root-only" [] [Whitespace(" ")] + 1: CSS_DECLARATION_OR_RULE_BLOCK@40..61 + 0: L_CURLY@40..41 "{" [] [] + 1: CSS_DECLARATION_OR_RULE_LIST@41..57 + 0: CSS_DECLARATION_WITH_SEMICOLON@41..57 + 0: CSS_DECLARATION@41..56 + 0: CSS_GENERIC_PROPERTY@41..56 + 0: CSS_IDENTIFIER@41..51 + 0: IDENT@41..51 "color" [Newline("\n"), Whitespace(" ")] [] + 1: COLON@51..53 ":" [] [Whitespace(" ")] + 2: SCSS_EXPRESSION@53..56 + 0: SCSS_EXPRESSION_ITEM_LIST@53..56 + 0: CSS_IDENTIFIER@53..56 + 0: IDENT@53..56 "red" [] [] + 1: (empty) + 1: SEMICOLON@56..57 ";" [] [] + 2: R_CURLY@57..61 "}" [Newline("\n"), Whitespace(" ")] [] + 2: R_CURLY@61..63 "}" [Newline("\n")] [] + 1: CSS_AT_RULE@63..124 + 0: AT@63..66 "@" [Newline("\n"), Newline("\n")] [] + 1: SCSS_AT_ROOT_AT_RULE@66..124 + 0: AT_ROOT_KW@66..74 "at-root" [] [Whitespace(" ")] + 1: SCSS_AT_ROOT_QUERY@74..85 + 0: L_PAREN@74..75 "(" [] [] + 1: WITHOUT_KW@75..82 "without" [] [] + 2: COLON@82..83 ":" [] [] + 3: SCSS_AT_ROOT_QUERY_LIST@83..83 + 4: R_PAREN@83..85 ")" [] [Whitespace(" ")] + 2: (empty) + 3: CSS_DECLARATION_OR_RULE_BLOCK@85..124 + 0: L_CURLY@85..86 "{" [] [] + 1: CSS_DECLARATION_OR_RULE_LIST@86..122 + 0: CSS_NESTED_QUALIFIED_RULE@86..122 + 0: CSS_RELATIVE_SELECTOR_LIST@86..100 + 0: CSS_RELATIVE_SELECTOR@86..100 + 0: (empty) + 1: CSS_COMPOUND_SELECTOR@86..100 + 0: CSS_NESTED_SELECTOR_LIST@86..86 + 1: (empty) + 2: CSS_SUB_SELECTOR_LIST@86..100 + 0: CSS_CLASS_SELECTOR@86..100 + 0: DOT@86..90 "." [Newline("\n"), Whitespace(" ")] [] + 1: CSS_CUSTOM_IDENTIFIER@90..100 + 0: IDENT@90..100 "root-only" [] [Whitespace(" ")] + 1: CSS_DECLARATION_OR_RULE_BLOCK@100..122 + 0: L_CURLY@100..101 "{" [] [] + 1: CSS_DECLARATION_OR_RULE_LIST@101..118 + 0: CSS_DECLARATION_WITH_SEMICOLON@101..118 + 0: CSS_DECLARATION@101..117 + 0: CSS_GENERIC_PROPERTY@101..117 + 0: CSS_IDENTIFIER@101..111 + 0: IDENT@101..111 "color" [Newline("\n"), Whitespace(" ")] [] + 1: COLON@111..113 ":" [] [Whitespace(" ")] + 2: SCSS_EXPRESSION@113..117 + 0: SCSS_EXPRESSION_ITEM_LIST@113..117 + 0: CSS_IDENTIFIER@113..117 + 0: IDENT@113..117 "blue" [] [] + 1: (empty) + 1: SEMICOLON@117..118 ";" [] [] + 2: R_CURLY@118..122 "}" [Newline("\n"), Whitespace(" ")] [] + 2: R_CURLY@122..124 "}" [Newline("\n")] [] + 2: CSS_AT_RULE@124..178 + 0: AT@124..127 "@" [Newline("\n"), Newline("\n")] [] + 1: CSS_BOGUS_AT_RULE@127..178 + 0: AT_ROOT_KW@127..135 "at-root" [] [Whitespace(" ")] + 1: SCSS_AT_ROOT_SELECTOR@135..145 + 0: CSS_SELECTOR_LIST@135..145 + 0: CSS_COMPOUND_SELECTOR@135..145 + 0: CSS_NESTED_SELECTOR_LIST@135..135 + 1: (empty) + 2: CSS_SUB_SELECTOR_LIST@135..145 + 0: CSS_CLASS_SELECTOR@135..145 + 0: DOT@135..136 "." [] [] + 1: CSS_CUSTOM_IDENTIFIER@136..145 + 0: IDENT@136..145 "root-only" [] [] + 2: CSS_BOGUS_BLOCK@145..178 + 0: CSS_DECLARATION_OR_RULE_LIST@145..178 + 0: CSS_EMPTY_DECLARATION@145..146 + 0: SEMICOLON@145..146 ";" [] [] + 1: CSS_AT_RULE@146..178 + 0: AT@146..149 "@" [Newline("\n"), Newline("\n")] [] + 1: SCSS_AT_ROOT_AT_RULE@149..178 + 0: AT_ROOT_KW@149..157 "at-root" [] [Whitespace(" ")] + 1: (empty) + 2: SCSS_AT_ROOT_SELECTOR@157..159 + 0: CSS_SELECTOR_LIST@157..159 + 0: (empty) + 1: COMMA@157..159 "," [] [Whitespace(" ")] + 2: (empty) + 3: CSS_DECLARATION_OR_RULE_BLOCK@159..178 + 0: L_CURLY@159..160 "{" [] [] + 1: CSS_DECLARATION_OR_RULE_LIST@160..176 + 0: CSS_DECLARATION_WITH_SEMICOLON@160..176 + 0: CSS_DECLARATION@160..175 + 0: CSS_GENERIC_PROPERTY@160..175 + 0: CSS_IDENTIFIER@160..168 + 0: IDENT@160..168 "color" [Newline("\n"), Whitespace(" ")] [] + 1: COLON@168..170 ":" [] [Whitespace(" ")] + 2: SCSS_EXPRESSION@170..175 + 0: SCSS_EXPRESSION_ITEM_LIST@170..175 + 0: CSS_IDENTIFIER@170..175 + 0: IDENT@170..175 "green" [] [] + 1: (empty) + 1: SEMICOLON@175..176 ";" [] [] + 2: R_CURLY@176..178 "}" [Newline("\n")] [] + 2: EOF@178..179 "" [Newline("\n")] [] + +``` + +## Diagnostics + +``` +at-root.scss:1:19 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × expected `:` but instead found `media` + + > 1 │ @at-root (without media) { + │ ^^^^^ + 2 │ .root-only { + 3 │ color: red; + + i Remove media + +at-root.scss:7:19 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × Expected an identifier but instead found ')'. + + 5 │ } + 6 │ + > 7 │ @at-root (without:) { + │ ^ + 8 │ .root-only { + 9 │ color: blue; + + i Expected an identifier here. + + 5 │ } + 6 │ + > 7 │ @at-root (without:) { + │ ^ + 8 │ .root-only { + 9 │ color: blue; + +at-root.scss:13:20 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × expected `,` but instead found `;` + + 11 │ } + 12 │ + > 13 │ @at-root .root-only; + │ ^ + 14 │ + 15 │ @at-root , { + + i Remove ; + +at-root.scss:15:12 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × Expected a selector but instead found '{'. + + 13 │ @at-root .root-only; + 14 │ + > 15 │ @at-root , { + │ ^ + 16 │ color: green; + 17 │ } + + i Expected a selector here. + + 13 │ @at-root .root-only; + 14 │ + > 15 │ @at-root , { + │ ^ + 16 │ color: green; + 17 │ } + +at-root.scss:18:1 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × expected `}` but instead the file ends + + 16 │ color: green; + 17 │ } + > 18 │ + │ + + i the file ends here + + 16 │ color: green; + 17 │ } + > 18 │ + │ + +``` diff --git a/crates/biome_css_parser/tests/css_test_suite/ok/scss/at-rule/at-root.scss b/crates/biome_css_parser/tests/css_test_suite/ok/scss/at-rule/at-root.scss new file mode 100644 index 000000000000..36b07e13edb5 --- /dev/null +++ b/crates/biome_css_parser/tests/css_test_suite/ok/scss/at-rule/at-root.scss @@ -0,0 +1,31 @@ +@at-root { + .root-only { + color: red; + } +} + +@at-root .root-only, .another-root { + color: blue; +} + +@at-root (without: media supports) { + .root-only { + color: green; + } +} + +@mixin card { + @at-root (with: rule) { + .card { + display: block; + } + } +} + +@if true { + @at-root (without: all) { + .branch { + color: purple; + } + } +} diff --git a/crates/biome_css_parser/tests/css_test_suite/ok/scss/at-rule/at-root.scss.snap b/crates/biome_css_parser/tests/css_test_suite/ok/scss/at-rule/at-root.scss.snap new file mode 100644 index 000000000000..43740406c58d --- /dev/null +++ b/crates/biome_css_parser/tests/css_test_suite/ok/scss/at-rule/at-root.scss.snap @@ -0,0 +1,664 @@ +--- +source: crates/biome_css_parser/tests/spec_test.rs +expression: snapshot +--- + +## Input + +```css +@at-root { + .root-only { + color: red; + } +} + +@at-root .root-only, .another-root { + color: blue; +} + +@at-root (without: media supports) { + .root-only { + color: green; + } +} + +@mixin card { + @at-root (with: rule) { + .card { + display: block; + } + } +} + +@if true { + @at-root (without: all) { + .branch { + color: purple; + } + } +} + +``` + + +## AST + +``` +CssRoot { + bom_token: missing (optional), + items: CssRootItemList [ + CssAtRule { + at_token: AT@0..1 "@" [] [], + rule: ScssAtRootAtRule { + at_root_token: AT_ROOT_KW@1..9 "at-root" [] [Whitespace(" ")], + query: missing (optional), + selector: missing (optional), + block: CssDeclarationOrRuleBlock { + l_curly_token: L_CURLY@9..10 "{" [] [], + items: CssDeclarationOrRuleList [ + CssNestedQualifiedRule { + prelude: CssRelativeSelectorList [ + CssRelativeSelector { + combinator: missing (optional), + selector: CssCompoundSelector { + nesting_selectors: CssNestedSelectorList [], + simple_selector: missing (optional), + sub_selectors: CssSubSelectorList [ + CssClassSelector { + dot_token: DOT@10..14 "." [Newline("\n"), Whitespace(" ")] [], + name: CssCustomIdentifier { + value_token: IDENT@14..24 "root-only" [] [Whitespace(" ")], + }, + }, + ], + }, + }, + ], + block: CssDeclarationOrRuleBlock { + l_curly_token: L_CURLY@24..25 "{" [] [], + items: CssDeclarationOrRuleList [ + CssDeclarationWithSemicolon { + declaration: CssDeclaration { + property: CssGenericProperty { + name: CssIdentifier { + value_token: IDENT@25..35 "color" [Newline("\n"), Whitespace(" ")] [], + }, + colon_token: COLON@35..37 ":" [] [Whitespace(" ")], + value: ScssExpression { + items: ScssExpressionItemList [ + CssIdentifier { + value_token: IDENT@37..40 "red" [] [], + }, + ], + }, + }, + important: missing (optional), + }, + semicolon_token: SEMICOLON@40..41 ";" [] [], + }, + ], + r_curly_token: R_CURLY@41..45 "}" [Newline("\n"), Whitespace(" ")] [], + }, + }, + ], + r_curly_token: R_CURLY@45..47 "}" [Newline("\n")] [], + }, + }, + }, + CssAtRule { + at_token: AT@47..50 "@" [Newline("\n"), Newline("\n")] [], + rule: ScssAtRootAtRule { + at_root_token: AT_ROOT_KW@50..58 "at-root" [] [Whitespace(" ")], + query: missing (optional), + selector: ScssAtRootSelector { + selector: CssSelectorList [ + CssCompoundSelector { + nesting_selectors: CssNestedSelectorList [], + simple_selector: missing (optional), + sub_selectors: CssSubSelectorList [ + CssClassSelector { + dot_token: DOT@58..59 "." [] [], + name: CssCustomIdentifier { + value_token: IDENT@59..68 "root-only" [] [], + }, + }, + ], + }, + COMMA@68..70 "," [] [Whitespace(" ")], + CssCompoundSelector { + nesting_selectors: CssNestedSelectorList [], + simple_selector: missing (optional), + sub_selectors: CssSubSelectorList [ + CssClassSelector { + dot_token: DOT@70..71 "." [] [], + name: CssCustomIdentifier { + value_token: IDENT@71..84 "another-root" [] [Whitespace(" ")], + }, + }, + ], + }, + ], + }, + block: CssDeclarationOrRuleBlock { + l_curly_token: L_CURLY@84..85 "{" [] [], + items: CssDeclarationOrRuleList [ + CssDeclarationWithSemicolon { + declaration: CssDeclaration { + property: CssGenericProperty { + name: CssIdentifier { + value_token: IDENT@85..93 "color" [Newline("\n"), Whitespace(" ")] [], + }, + colon_token: COLON@93..95 ":" [] [Whitespace(" ")], + value: ScssExpression { + items: ScssExpressionItemList [ + CssIdentifier { + value_token: IDENT@95..99 "blue" [] [], + }, + ], + }, + }, + important: missing (optional), + }, + semicolon_token: SEMICOLON@99..100 ";" [] [], + }, + ], + r_curly_token: R_CURLY@100..102 "}" [Newline("\n")] [], + }, + }, + }, + CssAtRule { + at_token: AT@102..105 "@" [Newline("\n"), Newline("\n")] [], + rule: ScssAtRootAtRule { + at_root_token: AT_ROOT_KW@105..113 "at-root" [] [Whitespace(" ")], + query: ScssAtRootQuery { + l_paren_token: L_PAREN@113..114 "(" [] [], + modifier: WITHOUT_KW@114..121 "without" [] [], + colon_token: COLON@121..123 ":" [] [Whitespace(" ")], + queries: ScssAtRootQueryList [ + CssCustomIdentifier { + value_token: IDENT@123..129 "media" [] [Whitespace(" ")], + }, + CssCustomIdentifier { + value_token: IDENT@129..137 "supports" [] [], + }, + ], + r_paren_token: R_PAREN@137..139 ")" [] [Whitespace(" ")], + }, + selector: missing (optional), + block: CssDeclarationOrRuleBlock { + l_curly_token: L_CURLY@139..140 "{" [] [], + items: CssDeclarationOrRuleList [ + CssNestedQualifiedRule { + prelude: CssRelativeSelectorList [ + CssRelativeSelector { + combinator: missing (optional), + selector: CssCompoundSelector { + nesting_selectors: CssNestedSelectorList [], + simple_selector: missing (optional), + sub_selectors: CssSubSelectorList [ + CssClassSelector { + dot_token: DOT@140..144 "." [Newline("\n"), Whitespace(" ")] [], + name: CssCustomIdentifier { + value_token: IDENT@144..154 "root-only" [] [Whitespace(" ")], + }, + }, + ], + }, + }, + ], + block: CssDeclarationOrRuleBlock { + l_curly_token: L_CURLY@154..155 "{" [] [], + items: CssDeclarationOrRuleList [ + CssDeclarationWithSemicolon { + declaration: CssDeclaration { + property: CssGenericProperty { + name: CssIdentifier { + value_token: IDENT@155..165 "color" [Newline("\n"), Whitespace(" ")] [], + }, + colon_token: COLON@165..167 ":" [] [Whitespace(" ")], + value: ScssExpression { + items: ScssExpressionItemList [ + CssIdentifier { + value_token: IDENT@167..172 "green" [] [], + }, + ], + }, + }, + important: missing (optional), + }, + semicolon_token: SEMICOLON@172..173 ";" [] [], + }, + ], + r_curly_token: R_CURLY@173..177 "}" [Newline("\n"), Whitespace(" ")] [], + }, + }, + ], + r_curly_token: R_CURLY@177..179 "}" [Newline("\n")] [], + }, + }, + }, + CssAtRule { + at_token: AT@179..182 "@" [Newline("\n"), Newline("\n")] [], + rule: ScssMixinAtRule { + mixin_token: MIXIN_KW@182..188 "mixin" [] [Whitespace(" ")], + name: CssIdentifier { + value_token: IDENT@188..193 "card" [] [Whitespace(" ")], + }, + parameters: missing (optional), + block: CssDeclarationOrRuleBlock { + l_curly_token: L_CURLY@193..194 "{" [] [], + items: CssDeclarationOrRuleList [ + CssAtRule { + at_token: AT@194..198 "@" [Newline("\n"), Whitespace(" ")] [], + rule: ScssAtRootAtRule { + at_root_token: AT_ROOT_KW@198..206 "at-root" [] [Whitespace(" ")], + query: ScssAtRootQuery { + l_paren_token: L_PAREN@206..207 "(" [] [], + modifier: WITH_KW@207..211 "with" [] [], + colon_token: COLON@211..213 ":" [] [Whitespace(" ")], + queries: ScssAtRootQueryList [ + CssCustomIdentifier { + value_token: IDENT@213..217 "rule" [] [], + }, + ], + r_paren_token: R_PAREN@217..219 ")" [] [Whitespace(" ")], + }, + selector: missing (optional), + block: CssDeclarationOrRuleBlock { + l_curly_token: L_CURLY@219..220 "{" [] [], + items: CssDeclarationOrRuleList [ + CssNestedQualifiedRule { + prelude: CssRelativeSelectorList [ + CssRelativeSelector { + combinator: missing (optional), + selector: CssCompoundSelector { + nesting_selectors: CssNestedSelectorList [], + simple_selector: missing (optional), + sub_selectors: CssSubSelectorList [ + CssClassSelector { + dot_token: DOT@220..226 "." [Newline("\n"), Whitespace(" ")] [], + name: CssCustomIdentifier { + value_token: IDENT@226..231 "card" [] [Whitespace(" ")], + }, + }, + ], + }, + }, + ], + block: CssDeclarationOrRuleBlock { + l_curly_token: L_CURLY@231..232 "{" [] [], + items: CssDeclarationOrRuleList [ + CssDeclarationWithSemicolon { + declaration: CssDeclaration { + property: CssGenericProperty { + name: CssIdentifier { + value_token: IDENT@232..246 "display" [Newline("\n"), Whitespace(" ")] [], + }, + colon_token: COLON@246..248 ":" [] [Whitespace(" ")], + value: ScssExpression { + items: ScssExpressionItemList [ + CssIdentifier { + value_token: IDENT@248..253 "block" [] [], + }, + ], + }, + }, + important: missing (optional), + }, + semicolon_token: SEMICOLON@253..254 ";" [] [], + }, + ], + r_curly_token: R_CURLY@254..260 "}" [Newline("\n"), Whitespace(" ")] [], + }, + }, + ], + r_curly_token: R_CURLY@260..264 "}" [Newline("\n"), Whitespace(" ")] [], + }, + }, + }, + ], + r_curly_token: R_CURLY@264..266 "}" [Newline("\n")] [], + }, + }, + }, + CssAtRule { + at_token: AT@266..269 "@" [Newline("\n"), Newline("\n")] [], + rule: ScssIfAtRule { + if_token: IF_KW@269..272 "if" [] [Whitespace(" ")], + condition: ScssExpression { + items: ScssExpressionItemList [ + CssIdentifier { + value_token: IDENT@272..277 "true" [] [Whitespace(" ")], + }, + ], + }, + block: CssDeclarationOrRuleBlock { + l_curly_token: L_CURLY@277..278 "{" [] [], + items: CssDeclarationOrRuleList [ + CssAtRule { + at_token: AT@278..282 "@" [Newline("\n"), Whitespace(" ")] [], + rule: ScssAtRootAtRule { + at_root_token: AT_ROOT_KW@282..290 "at-root" [] [Whitespace(" ")], + query: ScssAtRootQuery { + l_paren_token: L_PAREN@290..291 "(" [] [], + modifier: WITHOUT_KW@291..298 "without" [] [], + colon_token: COLON@298..300 ":" [] [Whitespace(" ")], + queries: ScssAtRootQueryList [ + CssCustomIdentifier { + value_token: IDENT@300..303 "all" [] [], + }, + ], + r_paren_token: R_PAREN@303..305 ")" [] [Whitespace(" ")], + }, + selector: missing (optional), + block: CssDeclarationOrRuleBlock { + l_curly_token: L_CURLY@305..306 "{" [] [], + items: CssDeclarationOrRuleList [ + CssNestedQualifiedRule { + prelude: CssRelativeSelectorList [ + CssRelativeSelector { + combinator: missing (optional), + selector: CssCompoundSelector { + nesting_selectors: CssNestedSelectorList [], + simple_selector: missing (optional), + sub_selectors: CssSubSelectorList [ + CssClassSelector { + dot_token: DOT@306..312 "." [Newline("\n"), Whitespace(" ")] [], + name: CssCustomIdentifier { + value_token: IDENT@312..319 "branch" [] [Whitespace(" ")], + }, + }, + ], + }, + }, + ], + block: CssDeclarationOrRuleBlock { + l_curly_token: L_CURLY@319..320 "{" [] [], + items: CssDeclarationOrRuleList [ + CssDeclarationWithSemicolon { + declaration: CssDeclaration { + property: CssGenericProperty { + name: CssIdentifier { + value_token: IDENT@320..332 "color" [Newline("\n"), Whitespace(" ")] [], + }, + colon_token: COLON@332..334 ":" [] [Whitespace(" ")], + value: ScssExpression { + items: ScssExpressionItemList [ + CssIdentifier { + value_token: IDENT@334..340 "purple" [] [], + }, + ], + }, + }, + important: missing (optional), + }, + semicolon_token: SEMICOLON@340..341 ";" [] [], + }, + ], + r_curly_token: R_CURLY@341..347 "}" [Newline("\n"), Whitespace(" ")] [], + }, + }, + ], + r_curly_token: R_CURLY@347..351 "}" [Newline("\n"), Whitespace(" ")] [], + }, + }, + }, + ], + r_curly_token: R_CURLY@351..353 "}" [Newline("\n")] [], + }, + else_clause: missing (optional), + }, + }, + ], + eof_token: EOF@353..354 "" [Newline("\n")] [], +} +``` + +## CST + +``` +0: CSS_ROOT@0..354 + 0: (empty) + 1: CSS_ROOT_ITEM_LIST@0..353 + 0: CSS_AT_RULE@0..47 + 0: AT@0..1 "@" [] [] + 1: SCSS_AT_ROOT_AT_RULE@1..47 + 0: AT_ROOT_KW@1..9 "at-root" [] [Whitespace(" ")] + 1: (empty) + 2: (empty) + 3: CSS_DECLARATION_OR_RULE_BLOCK@9..47 + 0: L_CURLY@9..10 "{" [] [] + 1: CSS_DECLARATION_OR_RULE_LIST@10..45 + 0: CSS_NESTED_QUALIFIED_RULE@10..45 + 0: CSS_RELATIVE_SELECTOR_LIST@10..24 + 0: CSS_RELATIVE_SELECTOR@10..24 + 0: (empty) + 1: CSS_COMPOUND_SELECTOR@10..24 + 0: CSS_NESTED_SELECTOR_LIST@10..10 + 1: (empty) + 2: CSS_SUB_SELECTOR_LIST@10..24 + 0: CSS_CLASS_SELECTOR@10..24 + 0: DOT@10..14 "." [Newline("\n"), Whitespace(" ")] [] + 1: CSS_CUSTOM_IDENTIFIER@14..24 + 0: IDENT@14..24 "root-only" [] [Whitespace(" ")] + 1: CSS_DECLARATION_OR_RULE_BLOCK@24..45 + 0: L_CURLY@24..25 "{" [] [] + 1: CSS_DECLARATION_OR_RULE_LIST@25..41 + 0: CSS_DECLARATION_WITH_SEMICOLON@25..41 + 0: CSS_DECLARATION@25..40 + 0: CSS_GENERIC_PROPERTY@25..40 + 0: CSS_IDENTIFIER@25..35 + 0: IDENT@25..35 "color" [Newline("\n"), Whitespace(" ")] [] + 1: COLON@35..37 ":" [] [Whitespace(" ")] + 2: SCSS_EXPRESSION@37..40 + 0: SCSS_EXPRESSION_ITEM_LIST@37..40 + 0: CSS_IDENTIFIER@37..40 + 0: IDENT@37..40 "red" [] [] + 1: (empty) + 1: SEMICOLON@40..41 ";" [] [] + 2: R_CURLY@41..45 "}" [Newline("\n"), Whitespace(" ")] [] + 2: R_CURLY@45..47 "}" [Newline("\n")] [] + 1: CSS_AT_RULE@47..102 + 0: AT@47..50 "@" [Newline("\n"), Newline("\n")] [] + 1: SCSS_AT_ROOT_AT_RULE@50..102 + 0: AT_ROOT_KW@50..58 "at-root" [] [Whitespace(" ")] + 1: (empty) + 2: SCSS_AT_ROOT_SELECTOR@58..84 + 0: CSS_SELECTOR_LIST@58..84 + 0: CSS_COMPOUND_SELECTOR@58..68 + 0: CSS_NESTED_SELECTOR_LIST@58..58 + 1: (empty) + 2: CSS_SUB_SELECTOR_LIST@58..68 + 0: CSS_CLASS_SELECTOR@58..68 + 0: DOT@58..59 "." [] [] + 1: CSS_CUSTOM_IDENTIFIER@59..68 + 0: IDENT@59..68 "root-only" [] [] + 1: COMMA@68..70 "," [] [Whitespace(" ")] + 2: CSS_COMPOUND_SELECTOR@70..84 + 0: CSS_NESTED_SELECTOR_LIST@70..70 + 1: (empty) + 2: CSS_SUB_SELECTOR_LIST@70..84 + 0: CSS_CLASS_SELECTOR@70..84 + 0: DOT@70..71 "." [] [] + 1: CSS_CUSTOM_IDENTIFIER@71..84 + 0: IDENT@71..84 "another-root" [] [Whitespace(" ")] + 3: CSS_DECLARATION_OR_RULE_BLOCK@84..102 + 0: L_CURLY@84..85 "{" [] [] + 1: CSS_DECLARATION_OR_RULE_LIST@85..100 + 0: CSS_DECLARATION_WITH_SEMICOLON@85..100 + 0: CSS_DECLARATION@85..99 + 0: CSS_GENERIC_PROPERTY@85..99 + 0: CSS_IDENTIFIER@85..93 + 0: IDENT@85..93 "color" [Newline("\n"), Whitespace(" ")] [] + 1: COLON@93..95 ":" [] [Whitespace(" ")] + 2: SCSS_EXPRESSION@95..99 + 0: SCSS_EXPRESSION_ITEM_LIST@95..99 + 0: CSS_IDENTIFIER@95..99 + 0: IDENT@95..99 "blue" [] [] + 1: (empty) + 1: SEMICOLON@99..100 ";" [] [] + 2: R_CURLY@100..102 "}" [Newline("\n")] [] + 2: CSS_AT_RULE@102..179 + 0: AT@102..105 "@" [Newline("\n"), Newline("\n")] [] + 1: SCSS_AT_ROOT_AT_RULE@105..179 + 0: AT_ROOT_KW@105..113 "at-root" [] [Whitespace(" ")] + 1: SCSS_AT_ROOT_QUERY@113..139 + 0: L_PAREN@113..114 "(" [] [] + 1: WITHOUT_KW@114..121 "without" [] [] + 2: COLON@121..123 ":" [] [Whitespace(" ")] + 3: SCSS_AT_ROOT_QUERY_LIST@123..137 + 0: CSS_CUSTOM_IDENTIFIER@123..129 + 0: IDENT@123..129 "media" [] [Whitespace(" ")] + 1: CSS_CUSTOM_IDENTIFIER@129..137 + 0: IDENT@129..137 "supports" [] [] + 4: R_PAREN@137..139 ")" [] [Whitespace(" ")] + 2: (empty) + 3: CSS_DECLARATION_OR_RULE_BLOCK@139..179 + 0: L_CURLY@139..140 "{" [] [] + 1: CSS_DECLARATION_OR_RULE_LIST@140..177 + 0: CSS_NESTED_QUALIFIED_RULE@140..177 + 0: CSS_RELATIVE_SELECTOR_LIST@140..154 + 0: CSS_RELATIVE_SELECTOR@140..154 + 0: (empty) + 1: CSS_COMPOUND_SELECTOR@140..154 + 0: CSS_NESTED_SELECTOR_LIST@140..140 + 1: (empty) + 2: CSS_SUB_SELECTOR_LIST@140..154 + 0: CSS_CLASS_SELECTOR@140..154 + 0: DOT@140..144 "." [Newline("\n"), Whitespace(" ")] [] + 1: CSS_CUSTOM_IDENTIFIER@144..154 + 0: IDENT@144..154 "root-only" [] [Whitespace(" ")] + 1: CSS_DECLARATION_OR_RULE_BLOCK@154..177 + 0: L_CURLY@154..155 "{" [] [] + 1: CSS_DECLARATION_OR_RULE_LIST@155..173 + 0: CSS_DECLARATION_WITH_SEMICOLON@155..173 + 0: CSS_DECLARATION@155..172 + 0: CSS_GENERIC_PROPERTY@155..172 + 0: CSS_IDENTIFIER@155..165 + 0: IDENT@155..165 "color" [Newline("\n"), Whitespace(" ")] [] + 1: COLON@165..167 ":" [] [Whitespace(" ")] + 2: SCSS_EXPRESSION@167..172 + 0: SCSS_EXPRESSION_ITEM_LIST@167..172 + 0: CSS_IDENTIFIER@167..172 + 0: IDENT@167..172 "green" [] [] + 1: (empty) + 1: SEMICOLON@172..173 ";" [] [] + 2: R_CURLY@173..177 "}" [Newline("\n"), Whitespace(" ")] [] + 2: R_CURLY@177..179 "}" [Newline("\n")] [] + 3: CSS_AT_RULE@179..266 + 0: AT@179..182 "@" [Newline("\n"), Newline("\n")] [] + 1: SCSS_MIXIN_AT_RULE@182..266 + 0: MIXIN_KW@182..188 "mixin" [] [Whitespace(" ")] + 1: CSS_IDENTIFIER@188..193 + 0: IDENT@188..193 "card" [] [Whitespace(" ")] + 2: (empty) + 3: CSS_DECLARATION_OR_RULE_BLOCK@193..266 + 0: L_CURLY@193..194 "{" [] [] + 1: CSS_DECLARATION_OR_RULE_LIST@194..264 + 0: CSS_AT_RULE@194..264 + 0: AT@194..198 "@" [Newline("\n"), Whitespace(" ")] [] + 1: SCSS_AT_ROOT_AT_RULE@198..264 + 0: AT_ROOT_KW@198..206 "at-root" [] [Whitespace(" ")] + 1: SCSS_AT_ROOT_QUERY@206..219 + 0: L_PAREN@206..207 "(" [] [] + 1: WITH_KW@207..211 "with" [] [] + 2: COLON@211..213 ":" [] [Whitespace(" ")] + 3: SCSS_AT_ROOT_QUERY_LIST@213..217 + 0: CSS_CUSTOM_IDENTIFIER@213..217 + 0: IDENT@213..217 "rule" [] [] + 4: R_PAREN@217..219 ")" [] [Whitespace(" ")] + 2: (empty) + 3: CSS_DECLARATION_OR_RULE_BLOCK@219..264 + 0: L_CURLY@219..220 "{" [] [] + 1: CSS_DECLARATION_OR_RULE_LIST@220..260 + 0: CSS_NESTED_QUALIFIED_RULE@220..260 + 0: CSS_RELATIVE_SELECTOR_LIST@220..231 + 0: CSS_RELATIVE_SELECTOR@220..231 + 0: (empty) + 1: CSS_COMPOUND_SELECTOR@220..231 + 0: CSS_NESTED_SELECTOR_LIST@220..220 + 1: (empty) + 2: CSS_SUB_SELECTOR_LIST@220..231 + 0: CSS_CLASS_SELECTOR@220..231 + 0: DOT@220..226 "." [Newline("\n"), Whitespace(" ")] [] + 1: CSS_CUSTOM_IDENTIFIER@226..231 + 0: IDENT@226..231 "card" [] [Whitespace(" ")] + 1: CSS_DECLARATION_OR_RULE_BLOCK@231..260 + 0: L_CURLY@231..232 "{" [] [] + 1: CSS_DECLARATION_OR_RULE_LIST@232..254 + 0: CSS_DECLARATION_WITH_SEMICOLON@232..254 + 0: CSS_DECLARATION@232..253 + 0: CSS_GENERIC_PROPERTY@232..253 + 0: CSS_IDENTIFIER@232..246 + 0: IDENT@232..246 "display" [Newline("\n"), Whitespace(" ")] [] + 1: COLON@246..248 ":" [] [Whitespace(" ")] + 2: SCSS_EXPRESSION@248..253 + 0: SCSS_EXPRESSION_ITEM_LIST@248..253 + 0: CSS_IDENTIFIER@248..253 + 0: IDENT@248..253 "block" [] [] + 1: (empty) + 1: SEMICOLON@253..254 ";" [] [] + 2: R_CURLY@254..260 "}" [Newline("\n"), Whitespace(" ")] [] + 2: R_CURLY@260..264 "}" [Newline("\n"), Whitespace(" ")] [] + 2: R_CURLY@264..266 "}" [Newline("\n")] [] + 4: CSS_AT_RULE@266..353 + 0: AT@266..269 "@" [Newline("\n"), Newline("\n")] [] + 1: SCSS_IF_AT_RULE@269..353 + 0: IF_KW@269..272 "if" [] [Whitespace(" ")] + 1: SCSS_EXPRESSION@272..277 + 0: SCSS_EXPRESSION_ITEM_LIST@272..277 + 0: CSS_IDENTIFIER@272..277 + 0: IDENT@272..277 "true" [] [Whitespace(" ")] + 2: CSS_DECLARATION_OR_RULE_BLOCK@277..353 + 0: L_CURLY@277..278 "{" [] [] + 1: CSS_DECLARATION_OR_RULE_LIST@278..351 + 0: CSS_AT_RULE@278..351 + 0: AT@278..282 "@" [Newline("\n"), Whitespace(" ")] [] + 1: SCSS_AT_ROOT_AT_RULE@282..351 + 0: AT_ROOT_KW@282..290 "at-root" [] [Whitespace(" ")] + 1: SCSS_AT_ROOT_QUERY@290..305 + 0: L_PAREN@290..291 "(" [] [] + 1: WITHOUT_KW@291..298 "without" [] [] + 2: COLON@298..300 ":" [] [Whitespace(" ")] + 3: SCSS_AT_ROOT_QUERY_LIST@300..303 + 0: CSS_CUSTOM_IDENTIFIER@300..303 + 0: IDENT@300..303 "all" [] [] + 4: R_PAREN@303..305 ")" [] [Whitespace(" ")] + 2: (empty) + 3: CSS_DECLARATION_OR_RULE_BLOCK@305..351 + 0: L_CURLY@305..306 "{" [] [] + 1: CSS_DECLARATION_OR_RULE_LIST@306..347 + 0: CSS_NESTED_QUALIFIED_RULE@306..347 + 0: CSS_RELATIVE_SELECTOR_LIST@306..319 + 0: CSS_RELATIVE_SELECTOR@306..319 + 0: (empty) + 1: CSS_COMPOUND_SELECTOR@306..319 + 0: CSS_NESTED_SELECTOR_LIST@306..306 + 1: (empty) + 2: CSS_SUB_SELECTOR_LIST@306..319 + 0: CSS_CLASS_SELECTOR@306..319 + 0: DOT@306..312 "." [Newline("\n"), Whitespace(" ")] [] + 1: CSS_CUSTOM_IDENTIFIER@312..319 + 0: IDENT@312..319 "branch" [] [Whitespace(" ")] + 1: CSS_DECLARATION_OR_RULE_BLOCK@319..347 + 0: L_CURLY@319..320 "{" [] [] + 1: CSS_DECLARATION_OR_RULE_LIST@320..341 + 0: CSS_DECLARATION_WITH_SEMICOLON@320..341 + 0: CSS_DECLARATION@320..340 + 0: CSS_GENERIC_PROPERTY@320..340 + 0: CSS_IDENTIFIER@320..332 + 0: IDENT@320..332 "color" [Newline("\n"), Whitespace(" ")] [] + 1: COLON@332..334 ":" [] [Whitespace(" ")] + 2: SCSS_EXPRESSION@334..340 + 0: SCSS_EXPRESSION_ITEM_LIST@334..340 + 0: CSS_IDENTIFIER@334..340 + 0: IDENT@334..340 "purple" [] [] + 1: (empty) + 1: SEMICOLON@340..341 ";" [] [] + 2: R_CURLY@341..347 "}" [Newline("\n"), Whitespace(" ")] [] + 2: R_CURLY@347..351 "}" [Newline("\n"), Whitespace(" ")] [] + 2: R_CURLY@351..353 "}" [Newline("\n")] [] + 3: (empty) + 2: EOF@353..354 "" [Newline("\n")] [] + +``` diff --git a/crates/biome_css_syntax/src/generated/kind.rs b/crates/biome_css_syntax/src/generated/kind.rs index ea43495ac5d6..8d6bd8ca60e7 100644 --- a/crates/biome_css_syntax/src/generated/kind.rs +++ b/crates/biome_css_syntax/src/generated/kind.rs @@ -100,6 +100,7 @@ pub enum CssSyntaxKind { WARN_KW, ERROR_KW, CONTENT_KW, + AT_ROOT_KW, EXTEND_KW, FOR_KW, FORWARD_KW, @@ -111,6 +112,7 @@ pub enum CssSyntaxKind { SHOW_KW, USE_KW, WITH_KW, + WITHOUT_KW, WHILE_KW, SASS_KW, STYLE_KW, @@ -562,6 +564,10 @@ pub enum CssSyntaxKind { CSS_FUNCTION_PARAMETER_DEFAULT_VALUE, CSS_FUNCTION_PARAMETER_LIST, CSS_RETURNS_STATEMENT, + SCSS_AT_ROOT_AT_RULE, + SCSS_AT_ROOT_QUERY, + SCSS_AT_ROOT_QUERY_LIST, + SCSS_AT_ROOT_SELECTOR, SCSS_EACH_AT_RULE, SCSS_EACH_BINDING_LIST, SCSS_FOR_AT_RULE, @@ -792,6 +798,7 @@ impl CssSyntaxKind { | CSS_VALUE_AT_RULE_PROPERTY_LIST | CSS_VALUE_AT_RULE_IMPORT_SPECIFIER_LIST | CSS_FUNCTION_PARAMETER_LIST + | SCSS_AT_ROOT_QUERY_LIST | SCSS_EACH_BINDING_LIST | SCSS_EXTEND_TARGET_COMPONENT_LIST | SCSS_IMPORT_ITEM_LIST @@ -854,6 +861,7 @@ impl CssSyntaxKind { "warn" => WARN_KW, "error" => ERROR_KW, "content" => CONTENT_KW, + "at-root" => AT_ROOT_KW, "extend" => EXTEND_KW, "for" => FOR_KW, "forward" => FORWARD_KW, @@ -865,6 +873,7 @@ impl CssSyntaxKind { "show" => SHOW_KW, "use" => USE_KW, "with" => WITH_KW, + "without" => WITHOUT_KW, "while" => WHILE_KW, "sass" => SASS_KW, "style" => STYLE_KW, @@ -1126,6 +1135,7 @@ impl CssSyntaxKind { WARN_KW => "warn", ERROR_KW => "error", CONTENT_KW => "content", + AT_ROOT_KW => "at-root", EXTEND_KW => "extend", FOR_KW => "for", FORWARD_KW => "forward", @@ -1137,6 +1147,7 @@ impl CssSyntaxKind { SHOW_KW => "show", USE_KW => "use", WITH_KW => "with", + WITHOUT_KW => "without", WHILE_KW => "while", SASS_KW => "sass", STYLE_KW => "style", @@ -1313,4 +1324,4 @@ impl CssSyntaxKind { } #[doc = r" Utility macro for creating a SyntaxKind through simple macro syntax"] #[macro_export] -macro_rules ! T { [;] => { $ crate :: CssSyntaxKind :: SEMICOLON } ; [,] => { $ crate :: CssSyntaxKind :: COMMA } ; ['('] => { $ crate :: CssSyntaxKind :: L_PAREN } ; [')'] => { $ crate :: CssSyntaxKind :: R_PAREN } ; ['{'] => { $ crate :: CssSyntaxKind :: L_CURLY } ; ['}'] => { $ crate :: CssSyntaxKind :: R_CURLY } ; ['['] => { $ crate :: CssSyntaxKind :: L_BRACK } ; [']'] => { $ crate :: CssSyntaxKind :: R_BRACK } ; [<] => { $ crate :: CssSyntaxKind :: L_ANGLE } ; [>] => { $ crate :: CssSyntaxKind :: R_ANGLE } ; [~] => { $ crate :: CssSyntaxKind :: TILDE } ; [$] => { $ crate :: CssSyntaxKind :: DOLLAR } ; [#] => { $ crate :: CssSyntaxKind :: HASH } ; [&] => { $ crate :: CssSyntaxKind :: AMP } ; [|] => { $ crate :: CssSyntaxKind :: PIPE } ; [||] => { $ crate :: CssSyntaxKind :: PIPE2 } ; [+] => { $ crate :: CssSyntaxKind :: PLUS } ; [*] => { $ crate :: CssSyntaxKind :: STAR } ; [/] => { $ crate :: CssSyntaxKind :: SLASH } ; [^] => { $ crate :: CssSyntaxKind :: CARET } ; [%] => { $ crate :: CssSyntaxKind :: PERCENT } ; [.] => { $ crate :: CssSyntaxKind :: DOT } ; [...] => { $ crate :: CssSyntaxKind :: DOT3 } ; [:] => { $ crate :: CssSyntaxKind :: COLON } ; [::] => { $ crate :: CssSyntaxKind :: COLON2 } ; [=] => { $ crate :: CssSyntaxKind :: EQ } ; [==] => { $ crate :: CssSyntaxKind :: EQ2 } ; [!] => { $ crate :: CssSyntaxKind :: BANG } ; [!=] => { $ crate :: CssSyntaxKind :: NEQ } ; [-] => { $ crate :: CssSyntaxKind :: MINUS } ; [<=] => { $ crate :: CssSyntaxKind :: LTEQ } ; [>=] => { $ crate :: CssSyntaxKind :: GTEQ } ; [+=] => { $ crate :: CssSyntaxKind :: PLUSEQ } ; [|=] => { $ crate :: CssSyntaxKind :: PIPEEQ } ; [&=] => { $ crate :: CssSyntaxKind :: AMPEQ } ; [^=] => { $ crate :: CssSyntaxKind :: CARETEQ } ; [/=] => { $ crate :: CssSyntaxKind :: SLASHEQ } ; [*=] => { $ crate :: CssSyntaxKind :: STAREQ } ; [%=] => { $ crate :: CssSyntaxKind :: PERCENTEQ } ; [@] => { $ crate :: CssSyntaxKind :: AT } ; ["$="] => { $ crate :: CssSyntaxKind :: DOLLAR_EQ } ; [~=] => { $ crate :: CssSyntaxKind :: TILDE_EQ } ; [-->] => { $ crate :: CssSyntaxKind :: CDC } ; [] => { $ crate :: CssSyntaxKind :: CDC } ; [