diff --git a/crates/biome_css_factory/src/generated/node_factory.rs b/crates/biome_css_factory/src/generated/node_factory.rs index bba9586a378a..4a5acb168576 100644 --- a/crates/biome_css_factory/src/generated/node_factory.rs +++ b/crates/biome_css_factory/src/generated/node_factory.rs @@ -3309,6 +3309,42 @@ pub fn scss_map_expression_pair( ], )) } +pub fn scss_mixin_at_rule( + mixin_token: SyntaxToken, + name: CssIdentifier, + block: CssDeclarationOrRuleBlock, +) -> ScssMixinAtRuleBuilder { + ScssMixinAtRuleBuilder { + mixin_token, + name, + block, + parameters: None, + } +} +pub struct ScssMixinAtRuleBuilder { + mixin_token: SyntaxToken, + name: CssIdentifier, + block: CssDeclarationOrRuleBlock, + parameters: Option, +} +impl ScssMixinAtRuleBuilder { + pub fn with_parameters(mut self, parameters: ScssParameterList) -> Self { + self.parameters = Some(parameters); + self + } + pub fn build(self) -> ScssMixinAtRule { + ScssMixinAtRule::unwrap_cast(SyntaxNode::new_detached( + CssSyntaxKind::SCSS_MIXIN_AT_RULE, + [ + Some(SyntaxElement::Token(self.mixin_token)), + Some(SyntaxElement::Node(self.name.into_syntax())), + self.parameters + .map(|token| SyntaxElement::Node(token.into_syntax())), + Some(SyntaxElement::Node(self.block.into_syntax())), + ], + )) + } +} pub fn scss_namespaced_identifier( namespace: CssIdentifier, dot_token: SyntaxToken, @@ -3339,6 +3375,65 @@ pub fn scss_nesting_declaration( ], )) } +pub fn scss_parameter(name: ScssIdentifier) -> ScssParameterBuilder { + ScssParameterBuilder { + name, + default_value: None, + ellipsis_token: None, + } +} +pub struct ScssParameterBuilder { + name: ScssIdentifier, + default_value: Option, + ellipsis_token: Option, +} +impl ScssParameterBuilder { + pub fn with_default_value(mut self, default_value: ScssParameterDefaultValue) -> Self { + self.default_value = Some(default_value); + self + } + pub fn with_ellipsis_token(mut self, ellipsis_token: SyntaxToken) -> Self { + self.ellipsis_token = Some(ellipsis_token); + self + } + pub fn build(self) -> ScssParameter { + ScssParameter::unwrap_cast(SyntaxNode::new_detached( + CssSyntaxKind::SCSS_PARAMETER, + [ + Some(SyntaxElement::Node(self.name.into_syntax())), + self.default_value + .map(|token| SyntaxElement::Node(token.into_syntax())), + self.ellipsis_token.map(|token| SyntaxElement::Token(token)), + ], + )) + } +} +pub fn scss_parameter_default_value( + colon_token: SyntaxToken, + value: ScssExpression, +) -> ScssParameterDefaultValue { + ScssParameterDefaultValue::unwrap_cast(SyntaxNode::new_detached( + CssSyntaxKind::SCSS_PARAMETER_DEFAULT_VALUE, + [ + Some(SyntaxElement::Token(colon_token)), + Some(SyntaxElement::Node(value.into_syntax())), + ], + )) +} +pub fn scss_parameter_list( + l_paren_token: SyntaxToken, + items: ScssParameterItemList, + r_paren_token: SyntaxToken, +) -> ScssParameterList { + ScssParameterList::unwrap_cast(SyntaxNode::new_detached( + CssSyntaxKind::SCSS_PARAMETER_LIST, + [ + Some(SyntaxElement::Token(l_paren_token)), + Some(SyntaxElement::Node(items.into_syntax())), + Some(SyntaxElement::Token(r_paren_token)), + ], + )) +} pub fn scss_parent_selector_value(amp_token: SyntaxToken) -> ScssParentSelectorValue { ScssParentSelectorValue::unwrap_cast(SyntaxNode::new_detached( CssSyntaxKind::SCSS_PARENT_SELECTOR_VALUE, @@ -4402,6 +4497,27 @@ where }), )) } +pub fn scss_parameter_item_list(items: I, separators: S) -> ScssParameterItemList +where + I: IntoIterator, + I::IntoIter: ExactSizeIterator, + S: IntoIterator, + S::IntoIter: ExactSizeIterator, +{ + let mut items = items.into_iter(); + let mut separators = separators.into_iter(); + let length = items.len() + separators.len(); + ScssParameterItemList::unwrap_cast(SyntaxNode::new_detached( + CssSyntaxKind::SCSS_PARAMETER_ITEM_LIST, + (0..length).map(|index| { + if index % 2 == 0 { + Some(items.next()?.into_syntax().into()) + } else { + Some(separators.next()?.into()) + } + }), + )) +} pub fn scss_variable_modifier_list(items: I) -> ScssVariableModifierList 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 ed7dbebf67be..bd3ca0c416d7 100644 --- a/crates/biome_css_factory/src/generated/syntax_factory.rs +++ b/crates/biome_css_factory/src/generated/syntax_factory.rs @@ -6856,6 +6856,46 @@ impl SyntaxFactory for CssSyntaxFactory { } slots.into_node(SCSS_MAP_EXPRESSION_PAIR, children) } + SCSS_MIXIN_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![mixin] + { + slots.mark_present(); + current_element = elements.next(); + } + slots.next_slot(); + 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 + && ScssParameterList::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_MIXIN_AT_RULE.to_bogus(), + children.into_iter().map(Some), + ); + } + slots.into_node(SCSS_MIXIN_AT_RULE, children) + } SCSS_NAMESPACED_IDENTIFIER => { let mut elements = (&children).into_iter(); let mut slots: RawNodeSlots<3usize> = RawNodeSlots::default(); @@ -6929,6 +6969,98 @@ impl SyntaxFactory for CssSyntaxFactory { } slots.into_node(SCSS_NESTING_DECLARATION, children) } + SCSS_PARAMETER => { + 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 + && ScssIdentifier::can_cast(element.kind()) + { + slots.mark_present(); + current_element = elements.next(); + } + slots.next_slot(); + if let Some(element) = ¤t_element + && ScssParameterDefaultValue::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_PARAMETER.to_bogus(), + children.into_iter().map(Some), + ); + } + slots.into_node(SCSS_PARAMETER, children) + } + SCSS_PARAMETER_DEFAULT_VALUE => { + 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 ! [:] + { + slots.mark_present(); + current_element = elements.next(); + } + slots.next_slot(); + if let Some(element) = ¤t_element + && ScssExpression::can_cast(element.kind()) + { + slots.mark_present(); + current_element = elements.next(); + } + slots.next_slot(); + if current_element.is_some() { + return RawSyntaxNode::new( + SCSS_PARAMETER_DEFAULT_VALUE.to_bogus(), + children.into_iter().map(Some), + ); + } + slots.into_node(SCSS_PARAMETER_DEFAULT_VALUE, children) + } + SCSS_PARAMETER_LIST => { + 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 + && ScssParameterItemList::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_PARAMETER_LIST.to_bogus(), + children.into_iter().map(Some), + ); + } + slots.into_node(SCSS_PARAMETER_LIST, children) + } SCSS_PARENT_SELECTOR_VALUE => { let mut elements = (&children).into_iter(); let mut slots: RawNodeSlots<1usize> = RawNodeSlots::default(); @@ -7824,6 +7956,13 @@ impl SyntaxFactory for CssSyntaxFactory { T ! [,], true, ), + SCSS_PARAMETER_ITEM_LIST => Self::make_separated_list_syntax( + kind, + children, + AnyScssParameter::can_cast, + T ! [,], + true, + ), SCSS_VARIABLE_MODIFIER_LIST => { Self::make_node_list_syntax(kind, children, ScssVariableModifier::can_cast) } 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 a01ae6d349c7..f51374b357c5 100644 --- a/crates/biome_css_formatter/src/css/any/at_rule.rs +++ b/crates/biome_css_formatter/src/css/any/at_rule.rs @@ -38,6 +38,7 @@ impl FormatRule for FormatAnyCssAtRule { AnyCssAtRule::ScssErrorAtRule(node) => node.format().fmt(f), AnyCssAtRule::ScssForAtRule(node) => node.format().fmt(f), AnyCssAtRule::ScssIfAtRule(node) => node.format().fmt(f), + AnyCssAtRule::ScssMixinAtRule(node) => node.format().fmt(f), AnyCssAtRule::ScssWarnAtRule(node) => node.format().fmt(f), AnyCssAtRule::ScssWhileAtRule(node) => node.format().fmt(f), AnyCssAtRule::TwApplyAtRule(node) => node.format().fmt(f), diff --git a/crates/biome_css_formatter/src/generated.rs b/crates/biome_css_formatter/src/generated.rs index 6e41dd7bc077..85ad2a32fc7b 100644 --- a/crates/biome_css_formatter/src/generated.rs +++ b/crates/biome_css_formatter/src/generated.rs @@ -7675,6 +7675,44 @@ impl IntoFormat for biome_css_syntax::ScssMapExpressionPair { ) } } +impl FormatRule + for crate::scss::statements::mixin_at_rule::FormatScssMixinAtRule +{ + type Context = CssFormatContext; + #[inline(always)] + fn fmt( + &self, + node: &biome_css_syntax::ScssMixinAtRule, + f: &mut CssFormatter, + ) -> FormatResult<()> { + FormatNodeRule::::fmt(self, node, f) + } +} +impl AsFormat for biome_css_syntax::ScssMixinAtRule { + type Format<'a> = FormatRefWithRule< + 'a, + biome_css_syntax::ScssMixinAtRule, + crate::scss::statements::mixin_at_rule::FormatScssMixinAtRule, + >; + fn format(&self) -> Self::Format<'_> { + FormatRefWithRule::new( + self, + crate::scss::statements::mixin_at_rule::FormatScssMixinAtRule::default(), + ) + } +} +impl IntoFormat for biome_css_syntax::ScssMixinAtRule { + type Format = FormatOwnedWithRule< + biome_css_syntax::ScssMixinAtRule, + crate::scss::statements::mixin_at_rule::FormatScssMixinAtRule, + >; + fn into_format(self) -> Self::Format { + FormatOwnedWithRule::new( + self, + crate::scss::statements::mixin_at_rule::FormatScssMixinAtRule::default(), + ) + } +} impl FormatRule for crate::scss::value::namespaced_identifier::FormatScssNamespacedIdentifier { @@ -7751,6 +7789,114 @@ impl IntoFormat for biome_css_syntax::ScssNestingDeclaration { ) } } +impl FormatRule + for crate::scss::auxiliary::parameter::FormatScssParameter +{ + type Context = CssFormatContext; + #[inline(always)] + fn fmt( + &self, + node: &biome_css_syntax::ScssParameter, + f: &mut CssFormatter, + ) -> FormatResult<()> { + FormatNodeRule::::fmt(self, node, f) + } +} +impl AsFormat for biome_css_syntax::ScssParameter { + type Format<'a> = FormatRefWithRule< + 'a, + biome_css_syntax::ScssParameter, + crate::scss::auxiliary::parameter::FormatScssParameter, + >; + fn format(&self) -> Self::Format<'_> { + FormatRefWithRule::new( + self, + crate::scss::auxiliary::parameter::FormatScssParameter::default(), + ) + } +} +impl IntoFormat for biome_css_syntax::ScssParameter { + type Format = FormatOwnedWithRule< + biome_css_syntax::ScssParameter, + crate::scss::auxiliary::parameter::FormatScssParameter, + >; + fn into_format(self) -> Self::Format { + FormatOwnedWithRule::new( + self, + crate::scss::auxiliary::parameter::FormatScssParameter::default(), + ) + } +} +impl FormatRule + for crate::scss::auxiliary::parameter_default_value::FormatScssParameterDefaultValue +{ + type Context = CssFormatContext; + #[inline(always)] + fn fmt( + &self, + node: &biome_css_syntax::ScssParameterDefaultValue, + f: &mut CssFormatter, + ) -> FormatResult<()> { + FormatNodeRule::::fmt(self, node, f) + } +} +impl AsFormat for biome_css_syntax::ScssParameterDefaultValue { + type Format<'a> = FormatRefWithRule< + 'a, + biome_css_syntax::ScssParameterDefaultValue, + crate::scss::auxiliary::parameter_default_value::FormatScssParameterDefaultValue, + >; + fn format(&self) -> Self::Format<'_> { + FormatRefWithRule :: new (self , crate :: scss :: auxiliary :: parameter_default_value :: FormatScssParameterDefaultValue :: default ()) + } +} +impl IntoFormat for biome_css_syntax::ScssParameterDefaultValue { + type Format = FormatOwnedWithRule< + biome_css_syntax::ScssParameterDefaultValue, + crate::scss::auxiliary::parameter_default_value::FormatScssParameterDefaultValue, + >; + fn into_format(self) -> Self::Format { + FormatOwnedWithRule :: new (self , crate :: scss :: auxiliary :: parameter_default_value :: FormatScssParameterDefaultValue :: default ()) + } +} +impl FormatRule + for crate::scss::auxiliary::parameter_list::FormatScssParameterList +{ + type Context = CssFormatContext; + #[inline(always)] + fn fmt( + &self, + node: &biome_css_syntax::ScssParameterList, + f: &mut CssFormatter, + ) -> FormatResult<()> { + FormatNodeRule::::fmt(self, node, f) + } +} +impl AsFormat for biome_css_syntax::ScssParameterList { + type Format<'a> = FormatRefWithRule< + 'a, + biome_css_syntax::ScssParameterList, + crate::scss::auxiliary::parameter_list::FormatScssParameterList, + >; + fn format(&self) -> Self::Format<'_> { + FormatRefWithRule::new( + self, + crate::scss::auxiliary::parameter_list::FormatScssParameterList::default(), + ) + } +} +impl IntoFormat for biome_css_syntax::ScssParameterList { + type Format = FormatOwnedWithRule< + biome_css_syntax::ScssParameterList, + crate::scss::auxiliary::parameter_list::FormatScssParameterList, + >; + fn into_format(self) -> Self::Format { + FormatOwnedWithRule::new( + self, + crate::scss::auxiliary::parameter_list::FormatScssParameterList::default(), + ) + } +} impl FormatRule for crate::scss::auxiliary::parent_selector_value::FormatScssParentSelectorValue { @@ -9470,6 +9616,31 @@ impl IntoFormat for biome_css_syntax::ScssMapExpressionPairLis ) } } +impl AsFormat for biome_css_syntax::ScssParameterItemList { + type Format<'a> = FormatRefWithRule< + 'a, + biome_css_syntax::ScssParameterItemList, + crate::scss::lists::parameter_item_list::FormatScssParameterItemList, + >; + fn format(&self) -> Self::Format<'_> { + FormatRefWithRule::new( + self, + crate::scss::lists::parameter_item_list::FormatScssParameterItemList::default(), + ) + } +} +impl IntoFormat for biome_css_syntax::ScssParameterItemList { + type Format = FormatOwnedWithRule< + biome_css_syntax::ScssParameterItemList, + crate::scss::lists::parameter_item_list::FormatScssParameterItemList, + >; + fn into_format(self) -> Self::Format { + FormatOwnedWithRule::new( + self, + crate::scss::lists::parameter_item_list::FormatScssParameterItemList::default(), + ) + } +} impl AsFormat for biome_css_syntax::ScssVariableModifierList { type Format<'a> = FormatRefWithRule< 'a, @@ -13384,6 +13555,31 @@ impl IntoFormat for biome_css_syntax::AnyScssModuleMember { ) } } +impl AsFormat for biome_css_syntax::AnyScssParameter { + type Format<'a> = FormatRefWithRule< + 'a, + biome_css_syntax::AnyScssParameter, + crate::scss::any::parameter::FormatAnyScssParameter, + >; + fn format(&self) -> Self::Format<'_> { + FormatRefWithRule::new( + self, + crate::scss::any::parameter::FormatAnyScssParameter::default(), + ) + } +} +impl IntoFormat for biome_css_syntax::AnyScssParameter { + type Format = FormatOwnedWithRule< + biome_css_syntax::AnyScssParameter, + crate::scss::any::parameter::FormatAnyScssParameter, + >; + fn into_format(self) -> Self::Format { + FormatOwnedWithRule::new( + self, + crate::scss::any::parameter::FormatAnyScssParameter::default(), + ) + } +} impl AsFormat for biome_css_syntax::AnyTwCustomVariantSelector { type Format<'a> = FormatRefWithRule< 'a, diff --git a/crates/biome_css_formatter/src/scss/any/mod.rs b/crates/biome_css_formatter/src/scss/any/mod.rs index 77c133524b42..5b6870ea5bdc 100644 --- a/crates/biome_css_formatter/src/scss/any/mod.rs +++ b/crates/biome_css_formatter/src/scss/any/mod.rs @@ -5,3 +5,4 @@ pub(crate) mod else_clause_body; pub(crate) mod expression; pub(crate) mod expression_item; pub(crate) mod module_member; +pub(crate) mod parameter; diff --git a/crates/biome_css_formatter/src/scss/any/parameter.rs b/crates/biome_css_formatter/src/scss/any/parameter.rs new file mode 100644 index 000000000000..40f55ceb9354 --- /dev/null +++ b/crates/biome_css_formatter/src/scss/any/parameter.rs @@ -0,0 +1,15 @@ +//! This is a generated file. Don't modify it by hand! Run 'cargo codegen formatter' to re-generate the file. + +use crate::prelude::*; +use biome_css_syntax::AnyScssParameter; +#[derive(Debug, Clone, Default)] +pub(crate) struct FormatAnyScssParameter; +impl FormatRule for FormatAnyScssParameter { + type Context = CssFormatContext; + fn fmt(&self, node: &AnyScssParameter, f: &mut CssFormatter) -> FormatResult<()> { + match node { + AnyScssParameter::CssBogusParameter(node) => node.format().fmt(f), + AnyScssParameter::ScssParameter(node) => node.format().fmt(f), + } + } +} diff --git a/crates/biome_css_formatter/src/scss/auxiliary/mod.rs b/crates/biome_css_formatter/src/scss/auxiliary/mod.rs index 266aa9b119c8..28f72b4d63fd 100644 --- a/crates/biome_css_formatter/src/scss/auxiliary/mod.rs +++ b/crates/biome_css_formatter/src/scss/auxiliary/mod.rs @@ -11,6 +11,9 @@ pub(crate) mod list_expression_element; pub(crate) mod map_expression; pub(crate) mod map_expression_pair; pub(crate) mod nesting_declaration; +pub(crate) mod parameter; +pub(crate) mod parameter_default_value; +pub(crate) mod parameter_list; pub(crate) mod parent_selector_value; pub(crate) mod parenthesized_expression; pub(crate) mod qualified_name; diff --git a/crates/biome_css_formatter/src/scss/auxiliary/parameter.rs b/crates/biome_css_formatter/src/scss/auxiliary/parameter.rs new file mode 100644 index 000000000000..da9206dc0ad6 --- /dev/null +++ b/crates/biome_css_formatter/src/scss/auxiliary/parameter.rs @@ -0,0 +1,26 @@ +use biome_css_syntax::{ScssParameter, ScssParameterFields}; +use biome_formatter::write; + +use crate::prelude::*; + +#[derive(Debug, Clone, Default)] +pub(crate) struct FormatScssParameter; + +impl FormatNodeRule for FormatScssParameter { + fn fmt_fields(&self, node: &ScssParameter, f: &mut CssFormatter) -> FormatResult<()> { + let ScssParameterFields { + name, + default_value, + ellipsis_token, + } = node.as_fields(); + + write!( + f, + [ + name.format(), + default_value.format(), + ellipsis_token.format() + ] + ) + } +} diff --git a/crates/biome_css_formatter/src/scss/auxiliary/parameter_default_value.rs b/crates/biome_css_formatter/src/scss/auxiliary/parameter_default_value.rs new file mode 100644 index 000000000000..438cabbf65cb --- /dev/null +++ b/crates/biome_css_formatter/src/scss/auxiliary/parameter_default_value.rs @@ -0,0 +1,19 @@ +use biome_css_syntax::{ScssParameterDefaultValue, ScssParameterDefaultValueFields}; +use biome_formatter::write; + +use crate::prelude::*; + +#[derive(Debug, Clone, Default)] +pub(crate) struct FormatScssParameterDefaultValue; + +impl FormatNodeRule for FormatScssParameterDefaultValue { + fn fmt_fields( + &self, + node: &ScssParameterDefaultValue, + f: &mut CssFormatter, + ) -> FormatResult<()> { + let ScssParameterDefaultValueFields { colon_token, value } = node.as_fields(); + + write!(f, [colon_token.format(), space(), value.format()]) + } +} diff --git a/crates/biome_css_formatter/src/scss/auxiliary/parameter_list.rs b/crates/biome_css_formatter/src/scss/auxiliary/parameter_list.rs new file mode 100644 index 000000000000..68b17fccf3d9 --- /dev/null +++ b/crates/biome_css_formatter/src/scss/auxiliary/parameter_list.rs @@ -0,0 +1,26 @@ +use biome_css_syntax::{ScssParameterList, ScssParameterListFields}; +use biome_formatter::{format_args, write}; + +use crate::prelude::*; + +#[derive(Debug, Clone, Default)] +pub(crate) struct FormatScssParameterList; + +impl FormatNodeRule for FormatScssParameterList { + fn fmt_fields(&self, node: &ScssParameterList, f: &mut CssFormatter) -> FormatResult<()> { + let ScssParameterListFields { + l_paren_token, + items, + r_paren_token, + } = node.as_fields(); + + write!( + f, + [group(&format_args![ + l_paren_token.format(), + soft_block_indent(&items.format()), + r_paren_token.format() + ])] + ) + } +} diff --git a/crates/biome_css_formatter/src/scss/lists/mod.rs b/crates/biome_css_formatter/src/scss/lists/mod.rs index adcd36ac8e00..653dde534ea6 100644 --- a/crates/biome_css_formatter/src/scss/lists/mod.rs +++ b/crates/biome_css_formatter/src/scss/lists/mod.rs @@ -4,4 +4,5 @@ pub(crate) mod each_binding_list; pub(crate) mod expression_item_list; pub(crate) mod list_expression_element_list; pub(crate) mod map_expression_pair_list; +pub(crate) mod parameter_item_list; pub(crate) mod variable_modifier_list; diff --git a/crates/biome_css_formatter/src/scss/lists/parameter_item_list.rs b/crates/biome_css_formatter/src/scss/lists/parameter_item_list.rs new file mode 100644 index 000000000000..f06c54160ce6 --- /dev/null +++ b/crates/biome_css_formatter/src/scss/lists/parameter_item_list.rs @@ -0,0 +1,21 @@ +use biome_css_syntax::ScssParameterItemList; + +use crate::prelude::*; + +#[derive(Debug, Clone, Default)] +pub(crate) struct FormatScssParameterItemList; + +impl FormatRule for FormatScssParameterItemList { + type Context = CssFormatContext; + + fn fmt(&self, node: &ScssParameterItemList, f: &mut CssFormatter) -> FormatResult<()> { + let separator = soft_line_break_or_space(); + let mut joiner = f.join_with(&separator); + + for formatted in node.format_separated(",") { + joiner.entry(&formatted); + } + + joiner.finish() + } +} diff --git a/crates/biome_css_formatter/src/scss/statements/mixin_at_rule.rs b/crates/biome_css_formatter/src/scss/statements/mixin_at_rule.rs new file mode 100644 index 000000000000..bad89d4ead1b --- /dev/null +++ b/crates/biome_css_formatter/src/scss/statements/mixin_at_rule.rs @@ -0,0 +1,29 @@ +use crate::prelude::*; +use biome_css_syntax::{ScssMixinAtRule, ScssMixinAtRuleFields}; +use biome_formatter::write; + +#[derive(Debug, Clone, Default)] +pub(crate) struct FormatScssMixinAtRule; + +impl FormatNodeRule for FormatScssMixinAtRule { + fn fmt_fields(&self, node: &ScssMixinAtRule, f: &mut CssFormatter) -> FormatResult<()> { + let ScssMixinAtRuleFields { + mixin_token, + name, + parameters, + block, + } = node.as_fields(); + + write!( + f, + [ + mixin_token.format(), + space(), + name.format(), + parameters.format(), + 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 8e5d2e548336..dc5a7deddbe6 100644 --- a/crates/biome_css_formatter/src/scss/statements/mod.rs +++ b/crates/biome_css_formatter/src/scss/statements/mod.rs @@ -5,5 +5,6 @@ pub(crate) mod each_at_rule; pub(crate) mod error_at_rule; pub(crate) mod for_at_rule; pub(crate) mod if_at_rule; +pub(crate) mod mixin_at_rule; pub(crate) mod warn_at_rule; pub(crate) mod while_at_rule; diff --git a/crates/biome_css_formatter/tests/specs/css/scss/at-rule/mixin.scss b/crates/biome_css_formatter/tests/specs/css/scss/at-rule/mixin.scss new file mode 100644 index 000000000000..ca8cc046917a --- /dev/null +++ b/crates/biome_css_formatter/tests/specs/css/scss/at-rule/mixin.scss @@ -0,0 +1,14 @@ +@mixin button{ +border-radius:4px; +} + +@mixin size( +$width, +$height:$width, +$args... +){ +width:$width; +@if $height > 0{ +height:$height; +} +} diff --git a/crates/biome_css_formatter/tests/specs/css/scss/at-rule/mixin.scss.snap b/crates/biome_css_formatter/tests/specs/css/scss/at-rule/mixin.scss.snap new file mode 100644 index 000000000000..6eeac799eb93 --- /dev/null +++ b/crates/biome_css_formatter/tests/specs/css/scss/at-rule/mixin.scss.snap @@ -0,0 +1,54 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: css/scss/at-rule/mixin.scss +--- + +# Input + +```scss +@mixin button{ +border-radius:4px; +} + +@mixin size( +$width, +$height:$width, +$args... +){ +width:$width; +@if $height > 0{ +height:$height; +} +} + +``` + + +============================= + +# Outputs + +## Output 1 + +----- +Indent style: Tab +Indent width: 2 +Line ending: LF +Line width: 80 +Quote style: Double Quotes +Trailing newline: true +----- + +```scss +@mixin button { + border-radius: 4px; +} + +@mixin size($width, $height: $width, $args...) { + width: $width; + @if $height > 0 { + height: $height; + } +} + +``` diff --git a/crates/biome_css_parser/src/lexer/mod.rs b/crates/biome_css_parser/src/lexer/mod.rs index 346b9a47a4b4..30db3d7a95a7 100644 --- a/crates/biome_css_parser/src/lexer/mod.rs +++ b/crates/biome_css_parser/src/lexer/mod.rs @@ -840,6 +840,7 @@ impl<'src> CssLexer<'src> { b"warn" => WARN_KW, b"error" => ERROR_KW, b"for" => FOR_KW, + b"mixin" => MIXIN_KW, b"while" => WHILE_KW, b"sass" => SASS_KW, b"style" => STYLE_KW, diff --git a/crates/biome_css_parser/src/lexer/tests.rs b/crates/biome_css_parser/src/lexer/tests.rs index a636e45a408d..69129ad2cad1 100644 --- a/crates/biome_css_parser/src/lexer/tests.rs +++ b/crates/biome_css_parser/src/lexer/tests.rs @@ -309,7 +309,7 @@ fn cdo_and_cdc() { #[test] fn keywords() { assert_lex! { - "media keyframes important from through sass each debug warn error for", + "media keyframes important from through sass each debug warn error for mixin", MEDIA_KW:5, WHITESPACE:1, KEYFRAMES_KW:9, @@ -330,7 +330,9 @@ fn keywords() { WHITESPACE:1, ERROR_KW:5, WHITESPACE:1, - FOR_KW:3 + FOR_KW:3, + WHITESPACE:1, + MIXIN_KW:5 } } 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 4209f09fedb9..865815336d69 100644 --- a/crates/biome_css_parser/src/syntax/at_rule/mod.rs +++ b/crates/biome_css_parser/src/syntax/at_rule/mod.rs @@ -77,7 +77,7 @@ use crate::syntax::parse_error::{expected_any_at_rule, tailwind_disabled}; use crate::syntax::scss::{ parse_bogus_scss_else_at_rule, parse_scss_debug_at_rule, parse_scss_each_at_rule, parse_scss_error_at_rule, parse_scss_for_at_rule, parse_scss_if_at_rule, - parse_scss_warn_at_rule, parse_scss_while_at_rule, + parse_scss_mixin_at_rule, parse_scss_warn_at_rule, parse_scss_while_at_rule, }; use biome_css_syntax::CssSyntaxKind::*; use biome_css_syntax::T; @@ -145,6 +145,9 @@ pub(crate) fn parse_any_at_rule(p: &mut CssParser) -> ParsedSyntax { T![for] => CssSyntaxFeatures::Scss .parse_supported_syntax(p, parse_scss_for_at_rule) .or_else(|| parse_unknown_at_rule(p)), + T![mixin] => CssSyntaxFeatures::Scss + .parse_supported_syntax(p, parse_scss_mixin_at_rule) + .or_else(|| parse_unknown_at_rule(p)), T![if] => CssSyntaxFeatures::Scss .parse_supported_syntax(p, parse_scss_if_at_rule) .or_else(|| parse_unknown_at_rule(p)), diff --git a/crates/biome_css_parser/src/syntax/scss/at_rule/if_at_rule.rs b/crates/biome_css_parser/src/syntax/scss/at_rule/if_at_rule.rs index 7b9e86635f0e..f61ce842764b 100644 --- a/crates/biome_css_parser/src/syntax/scss/at_rule/if_at_rule.rs +++ b/crates/biome_css_parser/src/syntax/scss/at_rule/if_at_rule.rs @@ -35,6 +35,7 @@ pub(crate) fn parse_scss_if_at_rule(p: &mut CssParser) -> ParsedSyntax { parse_scss_expression_until(p, SCSS_IF_CONDITION_END_SET) .or_add_diagnostic(p, expected_scss_expression); parse_declaration_or_rule_list_block(p); + // `@else` is optional after `@if`, so `Absent` is valid here. parse_scss_else_clause(p).ok(); Present(m.complete(p, SCSS_IF_AT_RULE)) diff --git a/crates/biome_css_parser/src/syntax/scss/at_rule/mixin_at_rule.rs b/crates/biome_css_parser/src/syntax/scss/at_rule/mixin_at_rule.rs new file mode 100644 index 000000000000..b1c4d65ef315 --- /dev/null +++ b/crates/biome_css_parser/src/syntax/scss/at_rule/mixin_at_rule.rs @@ -0,0 +1,170 @@ +use crate::parser::CssParser; +use crate::syntax::block::parse_declaration_or_rule_list_block; +use crate::syntax::parse_error::expected_identifier; +use crate::syntax::parse_regular_identifier; +use crate::syntax::scss::{ + expected_scss_expression, is_at_scss_identifier, parse_scss_expression_until, + parse_scss_identifier, +}; +use biome_css_syntax::CssSyntaxKind::{ + self, CSS_BOGUS_PARAMETER, SCSS_MIXIN_AT_RULE, SCSS_PARAMETER, SCSS_PARAMETER_DEFAULT_VALUE, + SCSS_PARAMETER_ITEM_LIST, SCSS_PARAMETER_LIST, +}; +use biome_css_syntax::T; +use biome_parser::parse_lists::ParseSeparatedList; +use biome_parser::parse_recovery::{ParseRecovery, RecoveryResult}; +use biome_parser::prelude::ParsedSyntax::{Absent, Present}; +use biome_parser::prelude::*; +use biome_parser::{Parser, TokenSet, token_set}; + +const SCSS_PARAMETER_DEFAULT_VALUE_END_SET: TokenSet = + token_set![T![,], T![')'], T![...]]; +const SCSS_PARAMETER_RECOVERY_SET: TokenSet = token_set![T![,], T![')'], T!['{']]; + +/// Parses the SCSS `@mixin` at-rule. +/// +/// # Example +/// +/// ```scss +/// @mixin button($radius: 4px, $args...) { +/// border-radius: $radius; +/// } +/// ``` +/// +/// Docs: https://sass-lang.com/documentation/at-rules/mixin/ +#[inline] +pub(crate) fn parse_scss_mixin_at_rule(p: &mut CssParser) -> ParsedSyntax { + if !is_at_scss_mixin_at_rule(p) { + return Absent; + } + + let m = p.start(); + + p.bump(T![mixin]); + parse_regular_identifier(p).or_add_diagnostic(p, expected_identifier); + // The parameter list is optional in the grammar, so a missing `(` is valid. + parse_scss_parameter_list(p).ok(); + parse_declaration_or_rule_list_block(p); + + Present(m.complete(p, SCSS_MIXIN_AT_RULE)) +} + +#[inline] +fn is_at_scss_mixin_at_rule(p: &mut CssParser) -> bool { + p.at(T![mixin]) +} + +#[inline] +fn parse_scss_parameter_list(p: &mut CssParser) -> ParsedSyntax { + if !p.at(T!['(']) { + return Absent; + } + + let m = p.start(); + + p.bump(T!['(']); + ScssParameterItemList.parse_list(p); + p.expect(T![')']); + + Present(m.complete(p, SCSS_PARAMETER_LIST)) +} + +#[inline] +fn parse_scss_parameter(p: &mut CssParser) -> ParsedSyntax { + if !is_at_scss_identifier(p) { + return Absent; + } + + let m = p.start(); + + // We only enter this branch after `is_at_scss_identifier`, so `Absent` is impossible here. + parse_scss_identifier(p).ok(); + // The default value is optional for a mixin parameter, so we only parse it when `:` is present. + if is_at_scss_parameter_default_value(p) { + parse_scss_parameter_default_value(p).ok(); + } + + if p.at(T![...]) { + p.bump(T![...]); + } + + Present(m.complete(p, SCSS_PARAMETER)) +} + +#[inline] +fn parse_scss_parameter_default_value(p: &mut CssParser) -> ParsedSyntax { + if !is_at_scss_parameter_default_value(p) { + return Absent; + } + + let m = p.start(); + + p.bump(T![:]); + parse_scss_expression_until(p, SCSS_PARAMETER_DEFAULT_VALUE_END_SET) + .or_add_diagnostic(p, expected_scss_expression); + + Present(m.complete(p, SCSS_PARAMETER_DEFAULT_VALUE)) +} + +#[inline] +fn is_at_scss_parameter_default_value(p: &mut CssParser) -> bool { + p.at(T![:]) +} + +#[inline] +fn expected_scss_parameter(p: &CssParser, range: biome_rowan::TextRange) -> ParseDiagnostic { + p.err_builder("Expected a mixin parameter.", range) + .with_hint("Add a parameter like `$value` or remove the extra separator.") +} + +struct ScssParameterItemListParseRecovery; + +impl ParseRecovery for ScssParameterItemListParseRecovery { + type Kind = CssSyntaxKind; + type Parser<'source> = CssParser<'source>; + const RECOVERED_KIND: Self::Kind = CSS_BOGUS_PARAMETER; + + fn is_at_recovered(&self, p: &mut Self::Parser<'_>) -> bool { + p.at_ts(SCSS_PARAMETER_RECOVERY_SET) + } +} + +struct ScssParameterItemList; + +impl ParseSeparatedList for ScssParameterItemList { + type Kind = CssSyntaxKind; + type Parser<'source> = CssParser<'source>; + const LIST_KIND: Self::Kind = SCSS_PARAMETER_ITEM_LIST; + + fn parse_element(&mut self, p: &mut Self::Parser<'_>) -> ParsedSyntax { + parse_scss_parameter(p) + } + + 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, + &ScssParameterItemListParseRecovery, + expected_scss_parameter, + ) + } + + fn separating_element_kind(&mut self) -> Self::Kind { + T![,] + } + + fn allow_trailing_separating_element(&self) -> bool { + true + } + + fn allow_empty(&self) -> bool { + true + } +} 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 ebdee6cd0c66..a82e46c72178 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 @@ -4,6 +4,7 @@ mod else_clause; mod error; mod for_at_rule; mod if_at_rule; +mod mixin_at_rule; mod warn; mod while_at_rule; @@ -20,6 +21,7 @@ pub(crate) use else_clause::parse_bogus_scss_else_at_rule; pub(crate) use error::parse_scss_error_at_rule; pub(crate) use for_at_rule::parse_scss_for_at_rule; pub(crate) use if_at_rule::parse_scss_if_at_rule; +pub(crate) use mixin_at_rule::parse_scss_mixin_at_rule; pub(crate) use warn::parse_scss_warn_at_rule; pub(crate) use while_at_rule::parse_scss_while_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 f74067039f46..4ab7c3c0b68f 100644 --- a/crates/biome_css_parser/src/syntax/scss/mod.rs +++ b/crates/biome_css_parser/src/syntax/scss/mod.rs @@ -10,7 +10,7 @@ mod value; pub(crate) use at_rule::{ parse_bogus_scss_else_at_rule, parse_scss_debug_at_rule, parse_scss_each_at_rule, parse_scss_error_at_rule, parse_scss_for_at_rule, parse_scss_if_at_rule, - parse_scss_warn_at_rule, parse_scss_while_at_rule, + parse_scss_mixin_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/mixin.scss b/crates/biome_css_parser/tests/css_test_suite/error/scss/at-rule/mixin.scss new file mode 100644 index 000000000000..08b301b7651c --- /dev/null +++ b/crates/biome_css_parser/tests/css_test_suite/error/scss/at-rule/mixin.scss @@ -0,0 +1,13 @@ +@mixin ($radius) { + border-radius: $radius; +} + +@mixin button($radius: ) { + border-radius: 4px; +} + +@mixin button($radius, , $padding) { + padding: $padding; +} + +@mixin button($radius) diff --git a/crates/biome_css_parser/tests/css_test_suite/error/scss/at-rule/mixin.scss.snap b/crates/biome_css_parser/tests/css_test_suite/error/scss/at-rule/mixin.scss.snap new file mode 100644 index 000000000000..90287995f10e --- /dev/null +++ b/crates/biome_css_parser/tests/css_test_suite/error/scss/at-rule/mixin.scss.snap @@ -0,0 +1,434 @@ +--- +source: crates/biome_css_parser/tests/spec_test.rs +expression: snapshot +--- + +## Input + +```css +@mixin ($radius) { + border-radius: $radius; +} + +@mixin button($radius: ) { + border-radius: 4px; +} + +@mixin button($radius, , $padding) { + padding: $padding; +} + +@mixin button($radius) + +``` + + +## AST + +``` +CssRoot { + bom_token: missing (optional), + items: CssRootItemList [ + CssAtRule { + at_token: AT@0..1 "@" [] [], + rule: ScssMixinAtRule { + mixin_token: MIXIN_KW@1..7 "mixin" [] [Whitespace(" ")], + name: missing (required), + parameters: ScssParameterList { + l_paren_token: L_PAREN@7..8 "(" [] [], + items: ScssParameterItemList [ + ScssParameter { + name: ScssIdentifier { + dollar_token: DOLLAR@8..9 "$" [] [], + name: CssIdentifier { + value_token: IDENT@9..15 "radius" [] [], + }, + }, + default_value: missing (optional), + ellipsis_token: missing (optional), + }, + ], + r_paren_token: R_PAREN@15..17 ")" [] [Whitespace(" ")], + }, + block: CssDeclarationOrRuleBlock { + l_curly_token: L_CURLY@17..18 "{" [] [], + items: CssDeclarationOrRuleList [ + CssDeclarationWithSemicolon { + declaration: CssDeclaration { + property: CssGenericProperty { + name: CssIdentifier { + value_token: IDENT@18..34 "border-radius" [Newline("\n"), Whitespace(" ")] [], + }, + colon_token: COLON@34..36 ":" [] [Whitespace(" ")], + value: ScssExpression { + items: ScssExpressionItemList [ + ScssIdentifier { + dollar_token: DOLLAR@36..37 "$" [] [], + name: CssIdentifier { + value_token: IDENT@37..43 "radius" [] [], + }, + }, + ], + }, + }, + important: missing (optional), + }, + semicolon_token: SEMICOLON@43..44 ";" [] [], + }, + ], + r_curly_token: R_CURLY@44..46 "}" [Newline("\n")] [], + }, + }, + }, + CssAtRule { + at_token: AT@46..49 "@" [Newline("\n"), Newline("\n")] [], + rule: ScssMixinAtRule { + mixin_token: MIXIN_KW@49..55 "mixin" [] [Whitespace(" ")], + name: CssIdentifier { + value_token: IDENT@55..61 "button" [] [], + }, + parameters: ScssParameterList { + l_paren_token: L_PAREN@61..62 "(" [] [], + items: ScssParameterItemList [ + ScssParameter { + name: ScssIdentifier { + dollar_token: DOLLAR@62..63 "$" [] [], + name: CssIdentifier { + value_token: IDENT@63..69 "radius" [] [], + }, + }, + default_value: ScssParameterDefaultValue { + colon_token: COLON@69..71 ":" [] [Whitespace(" ")], + value: missing (required), + }, + ellipsis_token: missing (optional), + }, + ], + r_paren_token: R_PAREN@71..73 ")" [] [Whitespace(" ")], + }, + block: CssDeclarationOrRuleBlock { + l_curly_token: L_CURLY@73..74 "{" [] [], + items: CssDeclarationOrRuleList [ + CssDeclarationWithSemicolon { + declaration: CssDeclaration { + property: CssGenericProperty { + name: CssIdentifier { + value_token: IDENT@74..90 "border-radius" [Newline("\n"), Whitespace(" ")] [], + }, + colon_token: COLON@90..92 ":" [] [Whitespace(" ")], + value: ScssExpression { + items: ScssExpressionItemList [ + CssRegularDimension { + value_token: CSS_NUMBER_LITERAL@92..93 "4" [] [], + unit_token: IDENT@93..95 "px" [] [], + }, + ], + }, + }, + important: missing (optional), + }, + semicolon_token: SEMICOLON@95..96 ";" [] [], + }, + ], + r_curly_token: R_CURLY@96..98 "}" [Newline("\n")] [], + }, + }, + }, + CssAtRule { + at_token: AT@98..101 "@" [Newline("\n"), Newline("\n")] [], + rule: ScssMixinAtRule { + mixin_token: MIXIN_KW@101..107 "mixin" [] [Whitespace(" ")], + name: CssIdentifier { + value_token: IDENT@107..113 "button" [] [], + }, + parameters: ScssParameterList { + l_paren_token: L_PAREN@113..114 "(" [] [], + items: ScssParameterItemList [ + ScssParameter { + name: ScssIdentifier { + dollar_token: DOLLAR@114..115 "$" [] [], + name: CssIdentifier { + value_token: IDENT@115..121 "radius" [] [], + }, + }, + default_value: missing (optional), + ellipsis_token: missing (optional), + }, + COMMA@121..123 "," [] [Whitespace(" ")], + missing element, + COMMA@123..125 "," [] [Whitespace(" ")], + ScssParameter { + name: ScssIdentifier { + dollar_token: DOLLAR@125..126 "$" [] [], + name: CssIdentifier { + value_token: IDENT@126..133 "padding" [] [], + }, + }, + default_value: missing (optional), + ellipsis_token: missing (optional), + }, + ], + r_paren_token: R_PAREN@133..135 ")" [] [Whitespace(" ")], + }, + block: CssDeclarationOrRuleBlock { + l_curly_token: L_CURLY@135..136 "{" [] [], + items: CssDeclarationOrRuleList [ + CssDeclarationWithSemicolon { + declaration: CssDeclaration { + property: CssGenericProperty { + name: CssIdentifier { + value_token: IDENT@136..146 "padding" [Newline("\n"), Whitespace(" ")] [], + }, + colon_token: COLON@146..148 ":" [] [Whitespace(" ")], + value: ScssExpression { + items: ScssExpressionItemList [ + ScssIdentifier { + dollar_token: DOLLAR@148..149 "$" [] [], + name: CssIdentifier { + value_token: IDENT@149..156 "padding" [] [], + }, + }, + ], + }, + }, + important: missing (optional), + }, + semicolon_token: SEMICOLON@156..157 ";" [] [], + }, + ], + r_curly_token: R_CURLY@157..159 "}" [Newline("\n")] [], + }, + }, + }, + CssAtRule { + at_token: AT@159..162 "@" [Newline("\n"), Newline("\n")] [], + rule: CssBogusAtRule { + items: [ + MIXIN_KW@162..168 "mixin" [] [Whitespace(" ")], + CssIdentifier { + value_token: IDENT@168..174 "button" [] [], + }, + ScssParameterList { + l_paren_token: L_PAREN@174..175 "(" [] [], + items: ScssParameterItemList [ + ScssParameter { + name: ScssIdentifier { + dollar_token: DOLLAR@175..176 "$" [] [], + name: CssIdentifier { + value_token: IDENT@176..182 "radius" [] [], + }, + }, + default_value: missing (optional), + ellipsis_token: missing (optional), + }, + ], + r_paren_token: R_PAREN@182..183 ")" [] [], + }, + CssBogusBlock { + items: [], + }, + ], + }, + }, + ], + eof_token: EOF@183..184 "" [Newline("\n")] [], +} +``` + +## CST + +``` +0: CSS_ROOT@0..184 + 0: (empty) + 1: CSS_ROOT_ITEM_LIST@0..183 + 0: CSS_AT_RULE@0..46 + 0: AT@0..1 "@" [] [] + 1: SCSS_MIXIN_AT_RULE@1..46 + 0: MIXIN_KW@1..7 "mixin" [] [Whitespace(" ")] + 1: (empty) + 2: SCSS_PARAMETER_LIST@7..17 + 0: L_PAREN@7..8 "(" [] [] + 1: SCSS_PARAMETER_ITEM_LIST@8..15 + 0: SCSS_PARAMETER@8..15 + 0: SCSS_IDENTIFIER@8..15 + 0: DOLLAR@8..9 "$" [] [] + 1: CSS_IDENTIFIER@9..15 + 0: IDENT@9..15 "radius" [] [] + 1: (empty) + 2: (empty) + 2: R_PAREN@15..17 ")" [] [Whitespace(" ")] + 3: CSS_DECLARATION_OR_RULE_BLOCK@17..46 + 0: L_CURLY@17..18 "{" [] [] + 1: CSS_DECLARATION_OR_RULE_LIST@18..44 + 0: CSS_DECLARATION_WITH_SEMICOLON@18..44 + 0: CSS_DECLARATION@18..43 + 0: CSS_GENERIC_PROPERTY@18..43 + 0: CSS_IDENTIFIER@18..34 + 0: IDENT@18..34 "border-radius" [Newline("\n"), Whitespace(" ")] [] + 1: COLON@34..36 ":" [] [Whitespace(" ")] + 2: SCSS_EXPRESSION@36..43 + 0: SCSS_EXPRESSION_ITEM_LIST@36..43 + 0: SCSS_IDENTIFIER@36..43 + 0: DOLLAR@36..37 "$" [] [] + 1: CSS_IDENTIFIER@37..43 + 0: IDENT@37..43 "radius" [] [] + 1: (empty) + 1: SEMICOLON@43..44 ";" [] [] + 2: R_CURLY@44..46 "}" [Newline("\n")] [] + 1: CSS_AT_RULE@46..98 + 0: AT@46..49 "@" [Newline("\n"), Newline("\n")] [] + 1: SCSS_MIXIN_AT_RULE@49..98 + 0: MIXIN_KW@49..55 "mixin" [] [Whitespace(" ")] + 1: CSS_IDENTIFIER@55..61 + 0: IDENT@55..61 "button" [] [] + 2: SCSS_PARAMETER_LIST@61..73 + 0: L_PAREN@61..62 "(" [] [] + 1: SCSS_PARAMETER_ITEM_LIST@62..71 + 0: SCSS_PARAMETER@62..71 + 0: SCSS_IDENTIFIER@62..69 + 0: DOLLAR@62..63 "$" [] [] + 1: CSS_IDENTIFIER@63..69 + 0: IDENT@63..69 "radius" [] [] + 1: SCSS_PARAMETER_DEFAULT_VALUE@69..71 + 0: COLON@69..71 ":" [] [Whitespace(" ")] + 1: (empty) + 2: (empty) + 2: R_PAREN@71..73 ")" [] [Whitespace(" ")] + 3: CSS_DECLARATION_OR_RULE_BLOCK@73..98 + 0: L_CURLY@73..74 "{" [] [] + 1: CSS_DECLARATION_OR_RULE_LIST@74..96 + 0: CSS_DECLARATION_WITH_SEMICOLON@74..96 + 0: CSS_DECLARATION@74..95 + 0: CSS_GENERIC_PROPERTY@74..95 + 0: CSS_IDENTIFIER@74..90 + 0: IDENT@74..90 "border-radius" [Newline("\n"), Whitespace(" ")] [] + 1: COLON@90..92 ":" [] [Whitespace(" ")] + 2: SCSS_EXPRESSION@92..95 + 0: SCSS_EXPRESSION_ITEM_LIST@92..95 + 0: CSS_REGULAR_DIMENSION@92..95 + 0: CSS_NUMBER_LITERAL@92..93 "4" [] [] + 1: IDENT@93..95 "px" [] [] + 1: (empty) + 1: SEMICOLON@95..96 ";" [] [] + 2: R_CURLY@96..98 "}" [Newline("\n")] [] + 2: CSS_AT_RULE@98..159 + 0: AT@98..101 "@" [Newline("\n"), Newline("\n")] [] + 1: SCSS_MIXIN_AT_RULE@101..159 + 0: MIXIN_KW@101..107 "mixin" [] [Whitespace(" ")] + 1: CSS_IDENTIFIER@107..113 + 0: IDENT@107..113 "button" [] [] + 2: SCSS_PARAMETER_LIST@113..135 + 0: L_PAREN@113..114 "(" [] [] + 1: SCSS_PARAMETER_ITEM_LIST@114..133 + 0: SCSS_PARAMETER@114..121 + 0: SCSS_IDENTIFIER@114..121 + 0: DOLLAR@114..115 "$" [] [] + 1: CSS_IDENTIFIER@115..121 + 0: IDENT@115..121 "radius" [] [] + 1: (empty) + 2: (empty) + 1: COMMA@121..123 "," [] [Whitespace(" ")] + 2: (empty) + 3: COMMA@123..125 "," [] [Whitespace(" ")] + 4: SCSS_PARAMETER@125..133 + 0: SCSS_IDENTIFIER@125..133 + 0: DOLLAR@125..126 "$" [] [] + 1: CSS_IDENTIFIER@126..133 + 0: IDENT@126..133 "padding" [] [] + 1: (empty) + 2: (empty) + 2: R_PAREN@133..135 ")" [] [Whitespace(" ")] + 3: CSS_DECLARATION_OR_RULE_BLOCK@135..159 + 0: L_CURLY@135..136 "{" [] [] + 1: CSS_DECLARATION_OR_RULE_LIST@136..157 + 0: CSS_DECLARATION_WITH_SEMICOLON@136..157 + 0: CSS_DECLARATION@136..156 + 0: CSS_GENERIC_PROPERTY@136..156 + 0: CSS_IDENTIFIER@136..146 + 0: IDENT@136..146 "padding" [Newline("\n"), Whitespace(" ")] [] + 1: COLON@146..148 ":" [] [Whitespace(" ")] + 2: SCSS_EXPRESSION@148..156 + 0: SCSS_EXPRESSION_ITEM_LIST@148..156 + 0: SCSS_IDENTIFIER@148..156 + 0: DOLLAR@148..149 "$" [] [] + 1: CSS_IDENTIFIER@149..156 + 0: IDENT@149..156 "padding" [] [] + 1: (empty) + 1: SEMICOLON@156..157 ";" [] [] + 2: R_CURLY@157..159 "}" [Newline("\n")] [] + 3: CSS_AT_RULE@159..183 + 0: AT@159..162 "@" [Newline("\n"), Newline("\n")] [] + 1: CSS_BOGUS_AT_RULE@162..183 + 0: MIXIN_KW@162..168 "mixin" [] [Whitespace(" ")] + 1: CSS_IDENTIFIER@168..174 + 0: IDENT@168..174 "button" [] [] + 2: SCSS_PARAMETER_LIST@174..183 + 0: L_PAREN@174..175 "(" [] [] + 1: SCSS_PARAMETER_ITEM_LIST@175..182 + 0: SCSS_PARAMETER@175..182 + 0: SCSS_IDENTIFIER@175..182 + 0: DOLLAR@175..176 "$" [] [] + 1: CSS_IDENTIFIER@176..182 + 0: IDENT@176..182 "radius" [] [] + 1: (empty) + 2: (empty) + 2: R_PAREN@182..183 ")" [] [] + 3: CSS_BOGUS_BLOCK@183..183 + 2: EOF@183..184 "" [Newline("\n")] [] + +``` + +## Diagnostics + +``` +mixin.scss:1:8 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × Expected an identifier but instead found '('. + + > 1 │ @mixin ($radius) { + │ ^ + 2 │ border-radius: $radius; + 3 │ } + + i Expected an identifier here. + + > 1 │ @mixin ($radius) { + │ ^ + 2 │ border-radius: $radius; + 3 │ } + +mixin.scss:5:24 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × Expected a SCSS expression but instead found ')'. + + 3 │ } + 4 │ + > 5 │ @mixin button($radius: ) { + │ ^ + 6 │ border-radius: 4px; + 7 │ } + + i Expected a SCSS expression here. + + 3 │ } + 4 │ + > 5 │ @mixin button($radius: ) { + │ ^ + 6 │ border-radius: 4px; + 7 │ } + +mixin.scss:14:1 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × expected `{` but instead the file ends + + 13 │ @mixin button($radius) + > 14 │ + │ + + i the file ends here + + 13 │ @mixin button($radius) + > 14 │ + │ + +``` diff --git a/crates/biome_css_parser/tests/css_test_suite/ok/scss/at-rule/mixin.scss b/crates/biome_css_parser/tests/css_test_suite/ok/scss/at-rule/mixin.scss new file mode 100644 index 000000000000..e497c37cec94 --- /dev/null +++ b/crates/biome_css_parser/tests/css_test_suite/ok/scss/at-rule/mixin.scss @@ -0,0 +1,11 @@ +@mixin button { + border-radius: 4px; +} + +@mixin size($width, $height: $width, $args...) { + width: $width; + + @if $height > 0 { + height: $height; + } +} diff --git a/crates/biome_css_parser/tests/css_test_suite/ok/scss/at-rule/mixin.scss.snap b/crates/biome_css_parser/tests/css_test_suite/ok/scss/at-rule/mixin.scss.snap new file mode 100644 index 000000000000..2c4aac1b9b1a --- /dev/null +++ b/crates/biome_css_parser/tests/css_test_suite/ok/scss/at-rule/mixin.scss.snap @@ -0,0 +1,332 @@ +--- +source: crates/biome_css_parser/tests/spec_test.rs +expression: snapshot +--- + +## Input + +```css +@mixin button { + border-radius: 4px; +} + +@mixin size($width, $height: $width, $args...) { + width: $width; + + @if $height > 0 { + height: $height; + } +} + +``` + + +## AST + +``` +CssRoot { + bom_token: missing (optional), + items: CssRootItemList [ + CssAtRule { + at_token: AT@0..1 "@" [] [], + rule: ScssMixinAtRule { + mixin_token: MIXIN_KW@1..7 "mixin" [] [Whitespace(" ")], + name: CssIdentifier { + value_token: IDENT@7..14 "button" [] [Whitespace(" ")], + }, + parameters: missing (optional), + block: CssDeclarationOrRuleBlock { + l_curly_token: L_CURLY@14..15 "{" [] [], + items: CssDeclarationOrRuleList [ + CssDeclarationWithSemicolon { + declaration: CssDeclaration { + property: CssGenericProperty { + name: CssIdentifier { + value_token: IDENT@15..31 "border-radius" [Newline("\n"), Whitespace(" ")] [], + }, + colon_token: COLON@31..33 ":" [] [Whitespace(" ")], + value: ScssExpression { + items: ScssExpressionItemList [ + CssRegularDimension { + value_token: CSS_NUMBER_LITERAL@33..34 "4" [] [], + unit_token: IDENT@34..36 "px" [] [], + }, + ], + }, + }, + important: missing (optional), + }, + semicolon_token: SEMICOLON@36..37 ";" [] [], + }, + ], + r_curly_token: R_CURLY@37..39 "}" [Newline("\n")] [], + }, + }, + }, + CssAtRule { + at_token: AT@39..42 "@" [Newline("\n"), Newline("\n")] [], + rule: ScssMixinAtRule { + mixin_token: MIXIN_KW@42..48 "mixin" [] [Whitespace(" ")], + name: CssIdentifier { + value_token: IDENT@48..52 "size" [] [], + }, + parameters: ScssParameterList { + l_paren_token: L_PAREN@52..53 "(" [] [], + items: ScssParameterItemList [ + ScssParameter { + name: ScssIdentifier { + dollar_token: DOLLAR@53..54 "$" [] [], + name: CssIdentifier { + value_token: IDENT@54..59 "width" [] [], + }, + }, + default_value: missing (optional), + ellipsis_token: missing (optional), + }, + COMMA@59..61 "," [] [Whitespace(" ")], + ScssParameter { + name: ScssIdentifier { + dollar_token: DOLLAR@61..62 "$" [] [], + name: CssIdentifier { + value_token: IDENT@62..68 "height" [] [], + }, + }, + default_value: ScssParameterDefaultValue { + colon_token: COLON@68..70 ":" [] [Whitespace(" ")], + value: ScssExpression { + items: ScssExpressionItemList [ + ScssIdentifier { + dollar_token: DOLLAR@70..71 "$" [] [], + name: CssIdentifier { + value_token: IDENT@71..76 "width" [] [], + }, + }, + ], + }, + }, + ellipsis_token: missing (optional), + }, + COMMA@76..78 "," [] [Whitespace(" ")], + ScssParameter { + name: ScssIdentifier { + dollar_token: DOLLAR@78..79 "$" [] [], + name: CssIdentifier { + value_token: IDENT@79..83 "args" [] [], + }, + }, + default_value: missing (optional), + ellipsis_token: DOT3@83..86 "..." [] [], + }, + ], + r_paren_token: R_PAREN@86..88 ")" [] [Whitespace(" ")], + }, + block: CssDeclarationOrRuleBlock { + l_curly_token: L_CURLY@88..89 "{" [] [], + items: CssDeclarationOrRuleList [ + CssDeclarationWithSemicolon { + declaration: CssDeclaration { + property: CssGenericProperty { + name: CssIdentifier { + value_token: IDENT@89..97 "width" [Newline("\n"), Whitespace(" ")] [], + }, + colon_token: COLON@97..99 ":" [] [Whitespace(" ")], + value: ScssExpression { + items: ScssExpressionItemList [ + ScssIdentifier { + dollar_token: DOLLAR@99..100 "$" [] [], + name: CssIdentifier { + value_token: IDENT@100..105 "width" [] [], + }, + }, + ], + }, + }, + important: missing (optional), + }, + semicolon_token: SEMICOLON@105..106 ";" [] [], + }, + CssAtRule { + at_token: AT@106..111 "@" [Newline("\n"), Newline("\n"), Whitespace(" ")] [], + rule: ScssIfAtRule { + if_token: IF_KW@111..114 "if" [] [Whitespace(" ")], + condition: ScssExpression { + items: ScssExpressionItemList [ + ScssBinaryExpression { + left: ScssIdentifier { + dollar_token: DOLLAR@114..115 "$" [] [], + name: CssIdentifier { + value_token: IDENT@115..122 "height" [] [Whitespace(" ")], + }, + }, + operator: R_ANGLE@122..124 ">" [] [Whitespace(" ")], + right: CssNumber { + value_token: CSS_NUMBER_LITERAL@124..126 "0" [] [Whitespace(" ")], + }, + }, + ], + }, + block: CssDeclarationOrRuleBlock { + l_curly_token: L_CURLY@126..127 "{" [] [], + items: CssDeclarationOrRuleList [ + CssDeclarationWithSemicolon { + declaration: CssDeclaration { + property: CssGenericProperty { + name: CssIdentifier { + value_token: IDENT@127..138 "height" [Newline("\n"), Whitespace(" ")] [], + }, + colon_token: COLON@138..140 ":" [] [Whitespace(" ")], + value: ScssExpression { + items: ScssExpressionItemList [ + ScssIdentifier { + dollar_token: DOLLAR@140..141 "$" [] [], + name: CssIdentifier { + value_token: IDENT@141..147 "height" [] [], + }, + }, + ], + }, + }, + important: missing (optional), + }, + semicolon_token: SEMICOLON@147..148 ";" [] [], + }, + ], + r_curly_token: R_CURLY@148..152 "}" [Newline("\n"), Whitespace(" ")] [], + }, + else_clause: missing (optional), + }, + }, + ], + r_curly_token: R_CURLY@152..154 "}" [Newline("\n")] [], + }, + }, + }, + ], + eof_token: EOF@154..155 "" [Newline("\n")] [], +} +``` + +## CST + +``` +0: CSS_ROOT@0..155 + 0: (empty) + 1: CSS_ROOT_ITEM_LIST@0..154 + 0: CSS_AT_RULE@0..39 + 0: AT@0..1 "@" [] [] + 1: SCSS_MIXIN_AT_RULE@1..39 + 0: MIXIN_KW@1..7 "mixin" [] [Whitespace(" ")] + 1: CSS_IDENTIFIER@7..14 + 0: IDENT@7..14 "button" [] [Whitespace(" ")] + 2: (empty) + 3: CSS_DECLARATION_OR_RULE_BLOCK@14..39 + 0: L_CURLY@14..15 "{" [] [] + 1: CSS_DECLARATION_OR_RULE_LIST@15..37 + 0: CSS_DECLARATION_WITH_SEMICOLON@15..37 + 0: CSS_DECLARATION@15..36 + 0: CSS_GENERIC_PROPERTY@15..36 + 0: CSS_IDENTIFIER@15..31 + 0: IDENT@15..31 "border-radius" [Newline("\n"), Whitespace(" ")] [] + 1: COLON@31..33 ":" [] [Whitespace(" ")] + 2: SCSS_EXPRESSION@33..36 + 0: SCSS_EXPRESSION_ITEM_LIST@33..36 + 0: CSS_REGULAR_DIMENSION@33..36 + 0: CSS_NUMBER_LITERAL@33..34 "4" [] [] + 1: IDENT@34..36 "px" [] [] + 1: (empty) + 1: SEMICOLON@36..37 ";" [] [] + 2: R_CURLY@37..39 "}" [Newline("\n")] [] + 1: CSS_AT_RULE@39..154 + 0: AT@39..42 "@" [Newline("\n"), Newline("\n")] [] + 1: SCSS_MIXIN_AT_RULE@42..154 + 0: MIXIN_KW@42..48 "mixin" [] [Whitespace(" ")] + 1: CSS_IDENTIFIER@48..52 + 0: IDENT@48..52 "size" [] [] + 2: SCSS_PARAMETER_LIST@52..88 + 0: L_PAREN@52..53 "(" [] [] + 1: SCSS_PARAMETER_ITEM_LIST@53..86 + 0: SCSS_PARAMETER@53..59 + 0: SCSS_IDENTIFIER@53..59 + 0: DOLLAR@53..54 "$" [] [] + 1: CSS_IDENTIFIER@54..59 + 0: IDENT@54..59 "width" [] [] + 1: (empty) + 2: (empty) + 1: COMMA@59..61 "," [] [Whitespace(" ")] + 2: SCSS_PARAMETER@61..76 + 0: SCSS_IDENTIFIER@61..68 + 0: DOLLAR@61..62 "$" [] [] + 1: CSS_IDENTIFIER@62..68 + 0: IDENT@62..68 "height" [] [] + 1: SCSS_PARAMETER_DEFAULT_VALUE@68..76 + 0: COLON@68..70 ":" [] [Whitespace(" ")] + 1: SCSS_EXPRESSION@70..76 + 0: SCSS_EXPRESSION_ITEM_LIST@70..76 + 0: SCSS_IDENTIFIER@70..76 + 0: DOLLAR@70..71 "$" [] [] + 1: CSS_IDENTIFIER@71..76 + 0: IDENT@71..76 "width" [] [] + 2: (empty) + 3: COMMA@76..78 "," [] [Whitespace(" ")] + 4: SCSS_PARAMETER@78..86 + 0: SCSS_IDENTIFIER@78..83 + 0: DOLLAR@78..79 "$" [] [] + 1: CSS_IDENTIFIER@79..83 + 0: IDENT@79..83 "args" [] [] + 1: (empty) + 2: DOT3@83..86 "..." [] [] + 2: R_PAREN@86..88 ")" [] [Whitespace(" ")] + 3: CSS_DECLARATION_OR_RULE_BLOCK@88..154 + 0: L_CURLY@88..89 "{" [] [] + 1: CSS_DECLARATION_OR_RULE_LIST@89..152 + 0: CSS_DECLARATION_WITH_SEMICOLON@89..106 + 0: CSS_DECLARATION@89..105 + 0: CSS_GENERIC_PROPERTY@89..105 + 0: CSS_IDENTIFIER@89..97 + 0: IDENT@89..97 "width" [Newline("\n"), Whitespace(" ")] [] + 1: COLON@97..99 ":" [] [Whitespace(" ")] + 2: SCSS_EXPRESSION@99..105 + 0: SCSS_EXPRESSION_ITEM_LIST@99..105 + 0: SCSS_IDENTIFIER@99..105 + 0: DOLLAR@99..100 "$" [] [] + 1: CSS_IDENTIFIER@100..105 + 0: IDENT@100..105 "width" [] [] + 1: (empty) + 1: SEMICOLON@105..106 ";" [] [] + 1: CSS_AT_RULE@106..152 + 0: AT@106..111 "@" [Newline("\n"), Newline("\n"), Whitespace(" ")] [] + 1: SCSS_IF_AT_RULE@111..152 + 0: IF_KW@111..114 "if" [] [Whitespace(" ")] + 1: SCSS_EXPRESSION@114..126 + 0: SCSS_EXPRESSION_ITEM_LIST@114..126 + 0: SCSS_BINARY_EXPRESSION@114..126 + 0: SCSS_IDENTIFIER@114..122 + 0: DOLLAR@114..115 "$" [] [] + 1: CSS_IDENTIFIER@115..122 + 0: IDENT@115..122 "height" [] [Whitespace(" ")] + 1: R_ANGLE@122..124 ">" [] [Whitespace(" ")] + 2: CSS_NUMBER@124..126 + 0: CSS_NUMBER_LITERAL@124..126 "0" [] [Whitespace(" ")] + 2: CSS_DECLARATION_OR_RULE_BLOCK@126..152 + 0: L_CURLY@126..127 "{" [] [] + 1: CSS_DECLARATION_OR_RULE_LIST@127..148 + 0: CSS_DECLARATION_WITH_SEMICOLON@127..148 + 0: CSS_DECLARATION@127..147 + 0: CSS_GENERIC_PROPERTY@127..147 + 0: CSS_IDENTIFIER@127..138 + 0: IDENT@127..138 "height" [Newline("\n"), Whitespace(" ")] [] + 1: COLON@138..140 ":" [] [Whitespace(" ")] + 2: SCSS_EXPRESSION@140..147 + 0: SCSS_EXPRESSION_ITEM_LIST@140..147 + 0: SCSS_IDENTIFIER@140..147 + 0: DOLLAR@140..141 "$" [] [] + 1: CSS_IDENTIFIER@141..147 + 0: IDENT@141..147 "height" [] [] + 1: (empty) + 1: SEMICOLON@147..148 ";" [] [] + 2: R_CURLY@148..152 "}" [Newline("\n"), Whitespace(" ")] [] + 3: (empty) + 2: R_CURLY@152..154 "}" [Newline("\n")] [] + 2: EOF@154..155 "" [Newline("\n")] [] + +``` diff --git a/crates/biome_css_syntax/src/generated/kind.rs b/crates/biome_css_syntax/src/generated/kind.rs index b17ed795a6ca..652f0e733439 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, FOR_KW, + MIXIN_KW, WHILE_KW, SASS_KW, STYLE_KW, @@ -555,6 +556,11 @@ pub enum CssSyntaxKind { SCSS_EACH_BINDING_LIST, SCSS_FOR_AT_RULE, SCSS_IF_AT_RULE, + SCSS_MIXIN_AT_RULE, + SCSS_PARAMETER_LIST, + SCSS_PARAMETER_ITEM_LIST, + SCSS_PARAMETER, + SCSS_PARAMETER_DEFAULT_VALUE, SCSS_WHILE_AT_RULE, SCSS_ELSE_CLAUSE, SCSS_DEBUG_AT_RULE, @@ -754,6 +760,8 @@ impl CssSyntaxKind { | CSS_VALUE_AT_RULE_IMPORT_SPECIFIER_LIST | CSS_FUNCTION_PARAMETER_LIST | SCSS_EACH_BINDING_LIST + | SCSS_PARAMETER_LIST + | SCSS_PARAMETER_ITEM_LIST | SCSS_EXPRESSION_ITEM_LIST | SCSS_LIST_EXPRESSION_ELEMENT_LIST | SCSS_MAP_EXPRESSION_PAIR_LIST @@ -807,6 +815,7 @@ impl CssSyntaxKind { "warn" => WARN_KW, "error" => ERROR_KW, "for" => FOR_KW, + "mixin" => MIXIN_KW, "while" => WHILE_KW, "sass" => SASS_KW, "style" => STYLE_KW, @@ -1068,6 +1077,7 @@ impl CssSyntaxKind { WARN_KW => "warn", ERROR_KW => "error", FOR_KW => "for", + MIXIN_KW => "mixin", WHILE_KW => "while", SASS_KW => "sass", STYLE_KW => "style", @@ -1244,4 +1254,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 } ; [