diff --git a/crates/biome_css_factory/src/generated/node_factory.rs b/crates/biome_css_factory/src/generated/node_factory.rs index c4391cd0c16d..afba70a30c36 100644 --- a/crates/biome_css_factory/src/generated/node_factory.rs +++ b/crates/biome_css_factory/src/generated/node_factory.rs @@ -3016,6 +3016,12 @@ impl ScssDeclarationBuilder { )) } } +pub fn scss_expression(items: ScssExpressionItemList) -> ScssExpression { + ScssExpression::unwrap_cast(SyntaxNode::new_detached( + CssSyntaxKind::SCSS_EXPRESSION, + [Some(SyntaxElement::Node(items.into_syntax()))], + )) +} pub fn scss_identifier(dollar_token: SyntaxToken, name: CssIdentifier) -> ScssIdentifier { ScssIdentifier::unwrap_cast(SyntaxNode::new_detached( CssSyntaxKind::SCSS_IDENTIFIER, @@ -3025,6 +3031,46 @@ pub fn scss_identifier(dollar_token: SyntaxToken, name: CssIdentifier) -> ScssId ], )) } +pub fn scss_list_expression(elements: ScssListExpressionElementList) -> ScssListExpression { + ScssListExpression::unwrap_cast(SyntaxNode::new_detached( + CssSyntaxKind::SCSS_LIST_EXPRESSION, + [Some(SyntaxElement::Node(elements.into_syntax()))], + )) +} +pub fn scss_list_expression_element(value: AnyScssExpression) -> ScssListExpressionElement { + ScssListExpressionElement::unwrap_cast(SyntaxNode::new_detached( + CssSyntaxKind::SCSS_LIST_EXPRESSION_ELEMENT, + [Some(SyntaxElement::Node(value.into_syntax()))], + )) +} +pub fn scss_map_expression( + l_paren_token: SyntaxToken, + pairs: ScssMapExpressionPairList, + r_paren_token: SyntaxToken, +) -> ScssMapExpression { + ScssMapExpression::unwrap_cast(SyntaxNode::new_detached( + CssSyntaxKind::SCSS_MAP_EXPRESSION, + [ + Some(SyntaxElement::Token(l_paren_token)), + Some(SyntaxElement::Node(pairs.into_syntax())), + Some(SyntaxElement::Token(r_paren_token)), + ], + )) +} +pub fn scss_map_expression_pair( + key: AnyScssExpression, + colon_token: SyntaxToken, + value: AnyScssExpression, +) -> ScssMapExpressionPair { + ScssMapExpressionPair::unwrap_cast(SyntaxNode::new_detached( + CssSyntaxKind::SCSS_MAP_EXPRESSION_PAIR, + [ + Some(SyntaxElement::Node(key.into_syntax())), + Some(SyntaxElement::Token(colon_token)), + Some(SyntaxElement::Node(value.into_syntax())), + ], + )) +} pub fn scss_namespaced_identifier( namespace: CssIdentifier, dot_token: SyntaxToken, @@ -3061,6 +3107,20 @@ pub fn scss_parent_selector_value(amp_token: SyntaxToken) -> ScssParentSelectorV [Some(SyntaxElement::Token(amp_token))], )) } +pub fn scss_parenthesized_expression( + l_paren_token: SyntaxToken, + expression: AnyScssExpression, + r_paren_token: SyntaxToken, +) -> ScssParenthesizedExpression { + ScssParenthesizedExpression::unwrap_cast(SyntaxNode::new_detached( + CssSyntaxKind::SCSS_PARENTHESIZED_EXPRESSION, + [ + Some(SyntaxElement::Token(l_paren_token)), + Some(SyntaxElement::Node(expression.into_syntax())), + Some(SyntaxElement::Token(r_paren_token)), + ], + )) +} pub fn scss_qualified_name( module: CssIdentifier, dot_token: SyntaxToken, @@ -3986,6 +4046,63 @@ where }), )) } +pub fn scss_expression_item_list(items: I) -> ScssExpressionItemList +where + I: IntoIterator, + I::IntoIter: ExactSizeIterator, +{ + ScssExpressionItemList::unwrap_cast(SyntaxNode::new_detached( + CssSyntaxKind::SCSS_EXPRESSION_ITEM_LIST, + items + .into_iter() + .map(|item| Some(item.into_syntax().into())), + )) +} +pub fn scss_list_expression_element_list( + items: I, + separators: S, +) -> ScssListExpressionElementList +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(); + ScssListExpressionElementList::unwrap_cast(SyntaxNode::new_detached( + CssSyntaxKind::SCSS_LIST_EXPRESSION_ELEMENT_LIST, + (0..length).map(|index| { + if index % 2 == 0 { + Some(items.next()?.into_syntax().into()) + } else { + Some(separators.next()?.into()) + } + }), + )) +} +pub fn scss_map_expression_pair_list(items: I, separators: S) -> ScssMapExpressionPairList +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(); + ScssMapExpressionPairList::unwrap_cast(SyntaxNode::new_detached( + CssSyntaxKind::SCSS_MAP_EXPRESSION_PAIR_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 163f91c5df10..98be14b18214 100644 --- a/crates/biome_css_factory/src/generated/syntax_factory.rs +++ b/crates/biome_css_factory/src/generated/syntax_factory.rs @@ -6167,6 +6167,25 @@ impl SyntaxFactory for CssSyntaxFactory { } slots.into_node(SCSS_DECLARATION, children) } + SCSS_EXPRESSION => { + 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 + && ScssExpressionItemList::can_cast(element.kind()) + { + slots.mark_present(); + current_element = elements.next(); + } + slots.next_slot(); + if current_element.is_some() { + return RawSyntaxNode::new( + SCSS_EXPRESSION.to_bogus(), + children.into_iter().map(Some), + ); + } + slots.into_node(SCSS_EXPRESSION, children) + } SCSS_IDENTIFIER => { let mut elements = (&children).into_iter(); let mut slots: RawNodeSlots<2usize> = RawNodeSlots::default(); @@ -6193,6 +6212,110 @@ impl SyntaxFactory for CssSyntaxFactory { } slots.into_node(SCSS_IDENTIFIER, children) } + SCSS_LIST_EXPRESSION => { + 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 + && ScssListExpressionElementList::can_cast(element.kind()) + { + slots.mark_present(); + current_element = elements.next(); + } + slots.next_slot(); + if current_element.is_some() { + return RawSyntaxNode::new( + SCSS_LIST_EXPRESSION.to_bogus(), + children.into_iter().map(Some), + ); + } + slots.into_node(SCSS_LIST_EXPRESSION, children) + } + SCSS_LIST_EXPRESSION_ELEMENT => { + 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 + && AnyScssExpression::can_cast(element.kind()) + { + slots.mark_present(); + current_element = elements.next(); + } + slots.next_slot(); + if current_element.is_some() { + return RawSyntaxNode::new( + SCSS_LIST_EXPRESSION_ELEMENT.to_bogus(), + children.into_iter().map(Some), + ); + } + slots.into_node(SCSS_LIST_EXPRESSION_ELEMENT, children) + } + SCSS_MAP_EXPRESSION => { + 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 + && ScssMapExpressionPairList::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_MAP_EXPRESSION.to_bogus(), + children.into_iter().map(Some), + ); + } + slots.into_node(SCSS_MAP_EXPRESSION, children) + } + SCSS_MAP_EXPRESSION_PAIR => { + 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 + && AnyScssExpression::can_cast(element.kind()) + { + slots.mark_present(); + current_element = elements.next(); + } + slots.next_slot(); + if let Some(element) = ¤t_element + && element.kind() == T ! [:] + { + slots.mark_present(); + current_element = elements.next(); + } + slots.next_slot(); + if let Some(element) = ¤t_element + && AnyScssExpression::can_cast(element.kind()) + { + slots.mark_present(); + current_element = elements.next(); + } + slots.next_slot(); + if current_element.is_some() { + return RawSyntaxNode::new( + SCSS_MAP_EXPRESSION_PAIR.to_bogus(), + children.into_iter().map(Some), + ); + } + slots.into_node(SCSS_MAP_EXPRESSION_PAIR, children) + } SCSS_NAMESPACED_IDENTIFIER => { let mut elements = (&children).into_iter(); let mut slots: RawNodeSlots<3usize> = RawNodeSlots::default(); @@ -6285,6 +6408,39 @@ impl SyntaxFactory for CssSyntaxFactory { } slots.into_node(SCSS_PARENT_SELECTOR_VALUE, children) } + SCSS_PARENTHESIZED_EXPRESSION => { + 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 + && AnyScssExpression::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_PARENTHESIZED_EXPRESSION.to_bogus(), + children.into_iter().map(Some), + ); + } + slots.into_node(SCSS_PARENTHESIZED_EXPRESSION, children) + } SCSS_QUALIFIED_NAME => { let mut elements = (&children).into_iter(); let mut slots: RawNodeSlots<3usize> = RawNodeSlots::default(); @@ -7012,6 +7168,23 @@ impl SyntaxFactory for CssSyntaxFactory { T ! [,], false, ), + SCSS_EXPRESSION_ITEM_LIST => { + Self::make_node_list_syntax(kind, children, AnyScssExpressionItem::can_cast) + } + SCSS_LIST_EXPRESSION_ELEMENT_LIST => Self::make_separated_list_syntax( + kind, + children, + ScssListExpressionElement::can_cast, + T ! [,], + true, + ), + SCSS_MAP_EXPRESSION_PAIR_LIST => Self::make_separated_list_syntax( + kind, + children, + ScssMapExpressionPair::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/expression.rs b/crates/biome_css_formatter/src/css/any/expression.rs index 2a126ef86c3b..f0039bc695a1 100644 --- a/crates/biome_css_formatter/src/css/any/expression.rs +++ b/crates/biome_css_formatter/src/css/any/expression.rs @@ -13,6 +13,7 @@ impl FormatRule for FormatAnyCssExpression { AnyCssExpression::CssListOfComponentValuesExpression(node) => node.format().fmt(f), AnyCssExpression::CssParenthesizedExpression(node) => node.format().fmt(f), AnyCssExpression::CssUnaryExpression(node) => node.format().fmt(f), + AnyCssExpression::ScssExpression(node) => node.format().fmt(f), } } } diff --git a/crates/biome_css_formatter/src/generated.rs b/crates/biome_css_formatter/src/generated.rs index ba98a0c11815..71cc1ba426e2 100644 --- a/crates/biome_css_formatter/src/generated.rs +++ b/crates/biome_css_formatter/src/generated.rs @@ -7034,6 +7034,44 @@ impl IntoFormat for biome_css_syntax::ScssDeclaration { ) } } +impl FormatRule + for crate::scss::auxiliary::expression::FormatScssExpression +{ + type Context = CssFormatContext; + #[inline(always)] + fn fmt( + &self, + node: &biome_css_syntax::ScssExpression, + f: &mut CssFormatter, + ) -> FormatResult<()> { + FormatNodeRule::::fmt(self, node, f) + } +} +impl AsFormat for biome_css_syntax::ScssExpression { + type Format<'a> = FormatRefWithRule< + 'a, + biome_css_syntax::ScssExpression, + crate::scss::auxiliary::expression::FormatScssExpression, + >; + fn format(&self) -> Self::Format<'_> { + FormatRefWithRule::new( + self, + crate::scss::auxiliary::expression::FormatScssExpression::default(), + ) + } +} +impl IntoFormat for biome_css_syntax::ScssExpression { + type Format = FormatOwnedWithRule< + biome_css_syntax::ScssExpression, + crate::scss::auxiliary::expression::FormatScssExpression, + >; + fn into_format(self) -> Self::Format { + FormatOwnedWithRule::new( + self, + crate::scss::auxiliary::expression::FormatScssExpression::default(), + ) + } +} impl FormatRule for crate::scss::value::identifier::FormatScssIdentifier { @@ -7072,6 +7110,152 @@ impl IntoFormat for biome_css_syntax::ScssIdentifier { ) } } +impl FormatRule + for crate::scss::auxiliary::list_expression::FormatScssListExpression +{ + type Context = CssFormatContext; + #[inline(always)] + fn fmt( + &self, + node: &biome_css_syntax::ScssListExpression, + f: &mut CssFormatter, + ) -> FormatResult<()> { + FormatNodeRule::::fmt(self, node, f) + } +} +impl AsFormat for biome_css_syntax::ScssListExpression { + type Format<'a> = FormatRefWithRule< + 'a, + biome_css_syntax::ScssListExpression, + crate::scss::auxiliary::list_expression::FormatScssListExpression, + >; + fn format(&self) -> Self::Format<'_> { + FormatRefWithRule::new( + self, + crate::scss::auxiliary::list_expression::FormatScssListExpression::default(), + ) + } +} +impl IntoFormat for biome_css_syntax::ScssListExpression { + type Format = FormatOwnedWithRule< + biome_css_syntax::ScssListExpression, + crate::scss::auxiliary::list_expression::FormatScssListExpression, + >; + fn into_format(self) -> Self::Format { + FormatOwnedWithRule::new( + self, + crate::scss::auxiliary::list_expression::FormatScssListExpression::default(), + ) + } +} +impl FormatRule + for crate::scss::auxiliary::list_expression_element::FormatScssListExpressionElement +{ + type Context = CssFormatContext; + #[inline(always)] + fn fmt( + &self, + node: &biome_css_syntax::ScssListExpressionElement, + f: &mut CssFormatter, + ) -> FormatResult<()> { + FormatNodeRule::::fmt(self, node, f) + } +} +impl AsFormat for biome_css_syntax::ScssListExpressionElement { + type Format<'a> = FormatRefWithRule< + 'a, + biome_css_syntax::ScssListExpressionElement, + crate::scss::auxiliary::list_expression_element::FormatScssListExpressionElement, + >; + fn format(&self) -> Self::Format<'_> { + FormatRefWithRule :: new (self , crate :: scss :: auxiliary :: list_expression_element :: FormatScssListExpressionElement :: default ()) + } +} +impl IntoFormat for biome_css_syntax::ScssListExpressionElement { + type Format = FormatOwnedWithRule< + biome_css_syntax::ScssListExpressionElement, + crate::scss::auxiliary::list_expression_element::FormatScssListExpressionElement, + >; + fn into_format(self) -> Self::Format { + FormatOwnedWithRule :: new (self , crate :: scss :: auxiliary :: list_expression_element :: FormatScssListExpressionElement :: default ()) + } +} +impl FormatRule + for crate::scss::auxiliary::map_expression::FormatScssMapExpression +{ + type Context = CssFormatContext; + #[inline(always)] + fn fmt( + &self, + node: &biome_css_syntax::ScssMapExpression, + f: &mut CssFormatter, + ) -> FormatResult<()> { + FormatNodeRule::::fmt(self, node, f) + } +} +impl AsFormat for biome_css_syntax::ScssMapExpression { + type Format<'a> = FormatRefWithRule< + 'a, + biome_css_syntax::ScssMapExpression, + crate::scss::auxiliary::map_expression::FormatScssMapExpression, + >; + fn format(&self) -> Self::Format<'_> { + FormatRefWithRule::new( + self, + crate::scss::auxiliary::map_expression::FormatScssMapExpression::default(), + ) + } +} +impl IntoFormat for biome_css_syntax::ScssMapExpression { + type Format = FormatOwnedWithRule< + biome_css_syntax::ScssMapExpression, + crate::scss::auxiliary::map_expression::FormatScssMapExpression, + >; + fn into_format(self) -> Self::Format { + FormatOwnedWithRule::new( + self, + crate::scss::auxiliary::map_expression::FormatScssMapExpression::default(), + ) + } +} +impl FormatRule + for crate::scss::auxiliary::map_expression_pair::FormatScssMapExpressionPair +{ + type Context = CssFormatContext; + #[inline(always)] + fn fmt( + &self, + node: &biome_css_syntax::ScssMapExpressionPair, + f: &mut CssFormatter, + ) -> FormatResult<()> { + FormatNodeRule::::fmt(self, node, f) + } +} +impl AsFormat for biome_css_syntax::ScssMapExpressionPair { + type Format<'a> = FormatRefWithRule< + 'a, + biome_css_syntax::ScssMapExpressionPair, + crate::scss::auxiliary::map_expression_pair::FormatScssMapExpressionPair, + >; + fn format(&self) -> Self::Format<'_> { + FormatRefWithRule::new( + self, + crate::scss::auxiliary::map_expression_pair::FormatScssMapExpressionPair::default(), + ) + } +} +impl IntoFormat for biome_css_syntax::ScssMapExpressionPair { + type Format = FormatOwnedWithRule< + biome_css_syntax::ScssMapExpressionPair, + crate::scss::auxiliary::map_expression_pair::FormatScssMapExpressionPair, + >; + fn into_format(self) -> Self::Format { + FormatOwnedWithRule::new( + self, + crate::scss::auxiliary::map_expression_pair::FormatScssMapExpressionPair::default(), + ) + } +} impl FormatRule for crate::scss::value::namespaced_identifier::FormatScssNamespacedIdentifier { @@ -7186,6 +7370,38 @@ impl IntoFormat for biome_css_syntax::ScssParentSelectorValue ) } } +impl FormatRule + for crate::scss::auxiliary::parenthesized_expression::FormatScssParenthesizedExpression +{ + type Context = CssFormatContext; + #[inline(always)] + fn fmt( + &self, + node: &biome_css_syntax::ScssParenthesizedExpression, + f: &mut CssFormatter, + ) -> FormatResult<()> { + FormatNodeRule::::fmt(self, node, f) + } +} +impl AsFormat for biome_css_syntax::ScssParenthesizedExpression { + type Format<'a> = FormatRefWithRule< + 'a, + biome_css_syntax::ScssParenthesizedExpression, + crate::scss::auxiliary::parenthesized_expression::FormatScssParenthesizedExpression, + >; + fn format(&self) -> Self::Format<'_> { + FormatRefWithRule :: new (self , crate :: scss :: auxiliary :: parenthesized_expression :: FormatScssParenthesizedExpression :: default ()) + } +} +impl IntoFormat for biome_css_syntax::ScssParenthesizedExpression { + type Format = FormatOwnedWithRule< + biome_css_syntax::ScssParenthesizedExpression, + crate::scss::auxiliary::parenthesized_expression::FormatScssParenthesizedExpression, + >; + fn into_format(self) -> Self::Format { + FormatOwnedWithRule :: new (self , crate :: scss :: auxiliary :: parenthesized_expression :: FormatScssParenthesizedExpression :: default ()) + } +} impl FormatRule for crate::scss::auxiliary::qualified_name::FormatScssQualifiedName { @@ -8625,6 +8841,77 @@ 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::ScssExpressionItemList { + type Format<'a> = FormatRefWithRule< + 'a, + biome_css_syntax::ScssExpressionItemList, + crate::scss::lists::expression_item_list::FormatScssExpressionItemList, + >; + fn format(&self) -> Self::Format<'_> { + FormatRefWithRule::new( + self, + crate::scss::lists::expression_item_list::FormatScssExpressionItemList::default(), + ) + } +} +impl IntoFormat for biome_css_syntax::ScssExpressionItemList { + type Format = FormatOwnedWithRule< + biome_css_syntax::ScssExpressionItemList, + crate::scss::lists::expression_item_list::FormatScssExpressionItemList, + >; + fn into_format(self) -> Self::Format { + FormatOwnedWithRule::new( + self, + crate::scss::lists::expression_item_list::FormatScssExpressionItemList::default(), + ) + } +} +impl AsFormat for biome_css_syntax::ScssListExpressionElementList { + type Format<'a> = FormatRefWithRule< + 'a, + biome_css_syntax::ScssListExpressionElementList, + crate::scss::lists::list_expression_element_list::FormatScssListExpressionElementList, + >; + fn format(&self) -> Self::Format<'_> { + FormatRefWithRule :: new (self , crate :: scss :: lists :: list_expression_element_list :: FormatScssListExpressionElementList :: default ()) + } +} +impl IntoFormat for biome_css_syntax::ScssListExpressionElementList { + type Format = FormatOwnedWithRule< + biome_css_syntax::ScssListExpressionElementList, + crate::scss::lists::list_expression_element_list::FormatScssListExpressionElementList, + >; + fn into_format(self) -> Self::Format { + FormatOwnedWithRule :: new (self , crate :: scss :: lists :: list_expression_element_list :: FormatScssListExpressionElementList :: default ()) + } +} +impl AsFormat for biome_css_syntax::ScssMapExpressionPairList { + type Format<'a> = FormatRefWithRule< + 'a, + biome_css_syntax::ScssMapExpressionPairList, + crate::scss::lists::map_expression_pair_list::FormatScssMapExpressionPairList, + >; + fn format(&self) -> Self::Format<'_> { + FormatRefWithRule::new( + self, + crate::scss::lists::map_expression_pair_list::FormatScssMapExpressionPairList::default( + ), + ) + } +} +impl IntoFormat for biome_css_syntax::ScssMapExpressionPairList { + type Format = FormatOwnedWithRule< + biome_css_syntax::ScssMapExpressionPairList, + crate::scss::lists::map_expression_pair_list::FormatScssMapExpressionPairList, + >; + fn into_format(self) -> Self::Format { + FormatOwnedWithRule::new( + self, + crate::scss::lists::map_expression_pair_list::FormatScssMapExpressionPairList::default( + ), + ) + } +} impl AsFormat for biome_css_syntax::ScssVariableModifierList { type Format<'a> = FormatRefWithRule< 'a, @@ -12361,6 +12648,56 @@ impl IntoFormat for biome_css_syntax::AnyScssDeclarationName { ) } } +impl AsFormat for biome_css_syntax::AnyScssExpression { + type Format<'a> = FormatRefWithRule< + 'a, + biome_css_syntax::AnyScssExpression, + crate::scss::any::expression::FormatAnyScssExpression, + >; + fn format(&self) -> Self::Format<'_> { + FormatRefWithRule::new( + self, + crate::scss::any::expression::FormatAnyScssExpression::default(), + ) + } +} +impl IntoFormat for biome_css_syntax::AnyScssExpression { + type Format = FormatOwnedWithRule< + biome_css_syntax::AnyScssExpression, + crate::scss::any::expression::FormatAnyScssExpression, + >; + fn into_format(self) -> Self::Format { + FormatOwnedWithRule::new( + self, + crate::scss::any::expression::FormatAnyScssExpression::default(), + ) + } +} +impl AsFormat for biome_css_syntax::AnyScssExpressionItem { + type Format<'a> = FormatRefWithRule< + 'a, + biome_css_syntax::AnyScssExpressionItem, + crate::scss::any::expression_item::FormatAnyScssExpressionItem, + >; + fn format(&self) -> Self::Format<'_> { + FormatRefWithRule::new( + self, + crate::scss::any::expression_item::FormatAnyScssExpressionItem::default(), + ) + } +} +impl IntoFormat for biome_css_syntax::AnyScssExpressionItem { + type Format = FormatOwnedWithRule< + biome_css_syntax::AnyScssExpressionItem, + crate::scss::any::expression_item::FormatAnyScssExpressionItem, + >; + fn into_format(self) -> Self::Format { + FormatOwnedWithRule::new( + self, + crate::scss::any::expression_item::FormatAnyScssExpressionItem::default(), + ) + } +} impl AsFormat for biome_css_syntax::AnyScssModuleMember { type Format<'a> = FormatRefWithRule< 'a, diff --git a/crates/biome_css_formatter/src/scss/any/expression.rs b/crates/biome_css_formatter/src/scss/any/expression.rs new file mode 100644 index 000000000000..006148e1f049 --- /dev/null +++ b/crates/biome_css_formatter/src/scss/any/expression.rs @@ -0,0 +1,18 @@ +//! 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::AnyScssExpression; +#[derive(Debug, Clone, Default)] +pub(crate) struct FormatAnyScssExpression; +impl FormatRule for FormatAnyScssExpression { + type Context = CssFormatContext; + fn fmt(&self, node: &AnyScssExpression, f: &mut CssFormatter) -> FormatResult<()> { + match node { + AnyScssExpression::AnyCssValue(node) => node.format().fmt(f), + AnyScssExpression::ScssExpression(node) => node.format().fmt(f), + AnyScssExpression::ScssListExpression(node) => node.format().fmt(f), + AnyScssExpression::ScssMapExpression(node) => node.format().fmt(f), + AnyScssExpression::ScssParenthesizedExpression(node) => node.format().fmt(f), + } + } +} diff --git a/crates/biome_css_formatter/src/scss/any/expression_item.rs b/crates/biome_css_formatter/src/scss/any/expression_item.rs new file mode 100644 index 000000000000..ca1cb81ca393 --- /dev/null +++ b/crates/biome_css_formatter/src/scss/any/expression_item.rs @@ -0,0 +1,18 @@ +//! 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::AnyScssExpressionItem; +#[derive(Debug, Clone, Default)] +pub(crate) struct FormatAnyScssExpressionItem; +impl FormatRule for FormatAnyScssExpressionItem { + type Context = CssFormatContext; + fn fmt(&self, node: &AnyScssExpressionItem, f: &mut CssFormatter) -> FormatResult<()> { + match node { + AnyScssExpressionItem::AnyCssValue(node) => node.format().fmt(f), + AnyScssExpressionItem::CssGenericDelimiter(node) => node.format().fmt(f), + AnyScssExpressionItem::ScssListExpression(node) => node.format().fmt(f), + AnyScssExpressionItem::ScssMapExpression(node) => node.format().fmt(f), + AnyScssExpressionItem::ScssParenthesizedExpression(node) => node.format().fmt(f), + } + } +} diff --git a/crates/biome_css_formatter/src/scss/any/mod.rs b/crates/biome_css_formatter/src/scss/any/mod.rs index 40b6c122f4ee..e2ed995e720d 100644 --- a/crates/biome_css_formatter/src/scss/any/mod.rs +++ b/crates/biome_css_formatter/src/scss/any/mod.rs @@ -1,4 +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 declaration_name; +pub(crate) mod expression; +pub(crate) mod expression_item; pub(crate) mod module_member; diff --git a/crates/biome_css_formatter/src/scss/auxiliary/expression.rs b/crates/biome_css_formatter/src/scss/auxiliary/expression.rs new file mode 100644 index 000000000000..4f9a7cdf8004 --- /dev/null +++ b/crates/biome_css_formatter/src/scss/auxiliary/expression.rs @@ -0,0 +1,12 @@ +use crate::prelude::*; +use biome_css_syntax::{ScssExpression, ScssExpressionFields}; +use biome_formatter::write; + +#[derive(Debug, Clone, Default)] +pub(crate) struct FormatScssExpression; +impl FormatNodeRule for FormatScssExpression { + fn fmt_fields(&self, node: &ScssExpression, f: &mut CssFormatter) -> FormatResult<()> { + let ScssExpressionFields { items } = node.as_fields(); + write!(f, [items.format()]) + } +} diff --git a/crates/biome_css_formatter/src/scss/auxiliary/list_expression.rs b/crates/biome_css_formatter/src/scss/auxiliary/list_expression.rs new file mode 100644 index 000000000000..13ead976ae84 --- /dev/null +++ b/crates/biome_css_formatter/src/scss/auxiliary/list_expression.rs @@ -0,0 +1,18 @@ +use crate::prelude::*; +use biome_css_syntax::{ScssListExpression, ScssListExpressionFields}; +use biome_formatter::{format_args, write}; +#[derive(Debug, Clone, Default)] +pub(crate) struct FormatScssListExpression; +impl FormatNodeRule for FormatScssListExpression { + fn fmt_fields(&self, node: &ScssListExpression, f: &mut CssFormatter) -> FormatResult<()> { + let ScssListExpressionFields { elements } = node.as_fields(); + + write!( + f, + [group(&indent(&format_args![ + soft_line_break(), + elements.format() + ]))] + ) + } +} diff --git a/crates/biome_css_formatter/src/scss/auxiliary/list_expression_element.rs b/crates/biome_css_formatter/src/scss/auxiliary/list_expression_element.rs new file mode 100644 index 000000000000..894e93e61de5 --- /dev/null +++ b/crates/biome_css_formatter/src/scss/auxiliary/list_expression_element.rs @@ -0,0 +1,16 @@ +use crate::prelude::*; +use biome_css_syntax::{ScssListExpressionElement, ScssListExpressionElementFields}; +use biome_formatter::write; +#[derive(Debug, Clone, Default)] +pub(crate) struct FormatScssListExpressionElement; +impl FormatNodeRule for FormatScssListExpressionElement { + fn fmt_fields( + &self, + node: &ScssListExpressionElement, + f: &mut CssFormatter, + ) -> FormatResult<()> { + let ScssListExpressionElementFields { value } = node.as_fields(); + + write!(f, [value.format()]) + } +} diff --git a/crates/biome_css_formatter/src/scss/auxiliary/map_expression.rs b/crates/biome_css_formatter/src/scss/auxiliary/map_expression.rs new file mode 100644 index 000000000000..7e218f428a44 --- /dev/null +++ b/crates/biome_css_formatter/src/scss/auxiliary/map_expression.rs @@ -0,0 +1,25 @@ +use crate::prelude::*; +use biome_css_syntax::{ScssMapExpression, ScssMapExpressionFields}; +use biome_formatter::{format_args, write}; +#[derive(Debug, Clone, Default)] +pub(crate) struct FormatScssMapExpression; +impl FormatNodeRule for FormatScssMapExpression { + fn fmt_fields(&self, node: &ScssMapExpression, f: &mut CssFormatter) -> FormatResult<()> { + let ScssMapExpressionFields { + l_paren_token, + pairs, + r_paren_token, + } = node.as_fields(); + + write!( + f, + [group(&format_args![ + l_paren_token.format(), + indent(&format_args![soft_line_break(), pairs.format()]), + soft_line_break(), + r_paren_token.format() + ]) + .should_expand(!pairs.is_empty())] + ) + } +} diff --git a/crates/biome_css_formatter/src/scss/auxiliary/map_expression_pair.rs b/crates/biome_css_formatter/src/scss/auxiliary/map_expression_pair.rs new file mode 100644 index 000000000000..4e473048203f --- /dev/null +++ b/crates/biome_css_formatter/src/scss/auxiliary/map_expression_pair.rs @@ -0,0 +1,19 @@ +use crate::prelude::*; +use biome_css_syntax::{ScssMapExpressionPair, ScssMapExpressionPairFields}; +use biome_formatter::write; +#[derive(Debug, Clone, Default)] +pub(crate) struct FormatScssMapExpressionPair; +impl FormatNodeRule for FormatScssMapExpressionPair { + fn fmt_fields(&self, node: &ScssMapExpressionPair, f: &mut CssFormatter) -> FormatResult<()> { + let ScssMapExpressionPairFields { + key, + colon_token, + value, + } = node.as_fields(); + + write!( + f, + [key.format(), colon_token.format(), space(), value.format()] + ) + } +} diff --git a/crates/biome_css_formatter/src/scss/auxiliary/mod.rs b/crates/biome_css_formatter/src/scss/auxiliary/mod.rs index 8fff2846acd0..29017cb09662 100644 --- a/crates/biome_css_formatter/src/scss/auxiliary/mod.rs +++ b/crates/biome_css_formatter/src/scss/auxiliary/mod.rs @@ -1,7 +1,13 @@ //! This is a generated file. Don't modify it by hand! Run 'cargo codegen formatter' to re-generate the file. pub(crate) mod declaration; +pub(crate) mod expression; +pub(crate) mod list_expression; +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 parent_selector_value; +pub(crate) mod parenthesized_expression; pub(crate) mod qualified_name; pub(crate) mod variable_modifier; diff --git a/crates/biome_css_formatter/src/scss/auxiliary/parenthesized_expression.rs b/crates/biome_css_formatter/src/scss/auxiliary/parenthesized_expression.rs new file mode 100644 index 000000000000..c25982da61e9 --- /dev/null +++ b/crates/biome_css_formatter/src/scss/auxiliary/parenthesized_expression.rs @@ -0,0 +1,28 @@ +use crate::prelude::*; +use biome_css_syntax::{ScssParenthesizedExpression, ScssParenthesizedExpressionFields}; +use biome_formatter::{format_args, write}; + +#[derive(Debug, Clone, Default)] +pub(crate) struct FormatScssParenthesizedExpression; +impl FormatNodeRule for FormatScssParenthesizedExpression { + fn fmt_fields( + &self, + node: &ScssParenthesizedExpression, + f: &mut CssFormatter, + ) -> FormatResult<()> { + let ScssParenthesizedExpressionFields { + l_paren_token, + expression, + r_paren_token, + } = node.as_fields(); + + write!( + f, + [group(&format_args![ + l_paren_token.format(), + soft_block_indent(&expression.format()), + r_paren_token.format() + ])] + ) + } +} diff --git a/crates/biome_css_formatter/src/scss/lists/expression_item_list.rs b/crates/biome_css_formatter/src/scss/lists/expression_item_list.rs new file mode 100644 index 000000000000..8f2e2f6c4663 --- /dev/null +++ b/crates/biome_css_formatter/src/scss/lists/expression_item_list.rs @@ -0,0 +1,11 @@ +use crate::prelude::*; +use crate::utils::component_value_list::write_component_value_list; +use biome_css_syntax::ScssExpressionItemList; +#[derive(Debug, Clone, Default)] +pub(crate) struct FormatScssExpressionItemList; +impl FormatRule for FormatScssExpressionItemList { + type Context = CssFormatContext; + fn fmt(&self, node: &ScssExpressionItemList, f: &mut CssFormatter) -> FormatResult<()> { + write_component_value_list(node, f) + } +} diff --git a/crates/biome_css_formatter/src/scss/lists/list_expression_element_list.rs b/crates/biome_css_formatter/src/scss/lists/list_expression_element_list.rs new file mode 100644 index 000000000000..486e30f28c60 --- /dev/null +++ b/crates/biome_css_formatter/src/scss/lists/list_expression_element_list.rs @@ -0,0 +1,15 @@ +use crate::prelude::*; +use biome_css_syntax::ScssListExpressionElementList; +#[derive(Debug, Clone, Default)] +pub(crate) struct FormatScssListExpressionElementList; +impl FormatRule for FormatScssListExpressionElementList { + type Context = CssFormatContext; + fn fmt(&self, node: &ScssListExpressionElementList, 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/lists/map_expression_pair_list.rs b/crates/biome_css_formatter/src/scss/lists/map_expression_pair_list.rs new file mode 100644 index 000000000000..f049d7c0f6b3 --- /dev/null +++ b/crates/biome_css_formatter/src/scss/lists/map_expression_pair_list.rs @@ -0,0 +1,29 @@ +use crate::prelude::*; +use biome_css_syntax::ScssMapExpressionPairList; +use biome_formatter::separated::TrailingSeparator; +use biome_formatter::write; + +#[derive(Debug, Clone, Default)] +pub(crate) struct FormatScssMapExpressionPairList; +impl FormatRule for FormatScssMapExpressionPairList { + type Context = CssFormatContext; + fn fmt(&self, node: &ScssMapExpressionPairList, f: &mut CssFormatter) -> FormatResult<()> { + let separator = hard_line_break(); + + let formatted = format_with(|formatter: &mut CssFormatter| { + let mut joiner = formatter.join_with(&separator); + + let mut separated = node + .format_separated(",") + .with_trailing_separator(TrailingSeparator::Mandatory); + + for formatted in &mut separated { + joiner.entry(&formatted); + } + + joiner.finish() + }); + + write!(f, [group(&formatted)]) + } +} diff --git a/crates/biome_css_formatter/src/scss/lists/mod.rs b/crates/biome_css_formatter/src/scss/lists/mod.rs index 4dda0d9037b5..8c878701c4e5 100644 --- a/crates/biome_css_formatter/src/scss/lists/mod.rs +++ b/crates/biome_css_formatter/src/scss/lists/mod.rs @@ -1,3 +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 expression_item_list; +pub(crate) mod list_expression_element_list; +pub(crate) mod map_expression_pair_list; pub(crate) mod variable_modifier_list; diff --git a/crates/biome_css_formatter/tests/quick_test.rs b/crates/biome_css_formatter/tests/quick_test.rs index ca5200e67aea..f0c80ad26135 100644 --- a/crates/biome_css_formatter/tests/quick_test.rs +++ b/crates/biome_css_formatter/tests/quick_test.rs @@ -14,13 +14,10 @@ mod language { // use this test check if your snippet prints as you wish, without using a snapshot fn quick_test() { let src = r#" -.component { - $global-var: modified !global; - $another-var: value !default!global; - $third-var:value!global!default!default; - color: $global-var; -} +.test{ + colors: fn((primary: red)); +} "#; let parse = parse_css(src, CssFileSource::scss(), CssParserOptions::default()); diff --git a/crates/biome_css_formatter/tests/specs/css/scss/expression/list-map-paren.scss b/crates/biome_css_formatter/tests/specs/css/scss/expression/list-map-paren.scss new file mode 100644 index 000000000000..8c795b5104cd --- /dev/null +++ b/crates/biome_css_formatter/tests/specs/css/scss/expression/list-map-paren.scss @@ -0,0 +1,7 @@ +.test{ +list:fn((1,2,3)); +spaceList:fn((1 2)); +map:fn((primary:red,secondary:blue)); +colors:fn((primary: red)); +emptyMap:fn(()); +} diff --git a/crates/biome_css_formatter/tests/specs/css/scss/expression/list-map-paren.scss.snap b/crates/biome_css_formatter/tests/specs/css/scss/expression/list-map-paren.scss.snap new file mode 100644 index 000000000000..f6aa331ad039 --- /dev/null +++ b/crates/biome_css_formatter/tests/specs/css/scss/expression/list-map-paren.scss.snap @@ -0,0 +1,54 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +assertion_line: 212 +info: css/scss/expression/list-map-paren.scss +--- + +# Input + +```scss +.test{ +list:fn((1,2,3)); +spaceList:fn((1 2)); +map:fn((primary:red,secondary:blue)); +colors:fn((primary: red)); +emptyMap:fn(()); +} + +``` + + +============================= + +# Outputs + +## Output 1 + +----- +Indent style: Tab +Indent width: 2 +Line ending: LF +Line width: 80 +Quote style: Double Quotes +Trailing newline: true +----- + +```scss +.test { + list: fn((1, 2, 3)); + spaceList: fn((1 2)); + map: fn( + ( + primary: red, + secondary: blue, + ) + ); + colors: fn( + ( + primary: red, + ) + ); + emptyMap: fn(()); +} + +``` diff --git a/crates/biome_css_parser/src/syntax/parse_error.rs b/crates/biome_css_parser/src/syntax/parse_error.rs index 30c19d29326d..fbc712764e40 100644 --- a/crates/biome_css_parser/src/syntax/parse_error.rs +++ b/crates/biome_css_parser/src/syntax/parse_error.rs @@ -229,6 +229,10 @@ pub(crate) fn expected_component_value(p: &CssParser, range: TextRange) -> Parse .into_diagnostic(p) } +pub(crate) fn expected_scss_expression(p: &CssParser, range: TextRange) -> ParseDiagnostic { + expected_node("SCSS expression", range, p) +} + pub(crate) fn expected_declaration(p: &CssParser, range: TextRange) -> ParseDiagnostic { expected_node("", range, p) } diff --git a/crates/biome_css_parser/src/syntax/scss/expression/mod.rs b/crates/biome_css_parser/src/syntax/scss/expression/mod.rs new file mode 100644 index 000000000000..7e47b6cf61e5 --- /dev/null +++ b/crates/biome_css_parser/src/syntax/scss/expression/mod.rs @@ -0,0 +1,228 @@ +use crate::parser::CssParser; +use crate::syntax::parse_error::expected_scss_expression; +use crate::syntax::property::parse_generic_component_value; +use biome_css_syntax::CssSyntaxKind::{ + CSS_BOGUS_PROPERTY_VALUE, EOF, SCSS_EXPRESSION, SCSS_EXPRESSION_ITEM_LIST, + SCSS_LIST_EXPRESSION, SCSS_LIST_EXPRESSION_ELEMENT, SCSS_LIST_EXPRESSION_ELEMENT_LIST, + SCSS_MAP_EXPRESSION, SCSS_MAP_EXPRESSION_PAIR, SCSS_MAP_EXPRESSION_PAIR_LIST, + SCSS_PARENTHESIZED_EXPRESSION, +}; +use biome_css_syntax::{CssSyntaxKind, T}; +use biome_parser::parse_recovery::ParseRecoveryTokenSet; +use biome_parser::prelude::ParsedSyntax; +use biome_parser::prelude::ParsedSyntax::{Absent, Present}; +use biome_parser::{CompletedMarker, Parser, ParserProgress, TokenSet, token_set}; + +const SCSS_BINARY_OPERATOR_TOKEN_SET: TokenSet = + token_set![T![+], T![-], T![*], T![/]]; + +const SCSS_MAP_EXPRESSION_KEY_END_TOKEN_SET: TokenSet = + token_set![T![,], T![:], T![')']]; +const SCSS_MAP_EXPRESSION_VALUE_END_TOKEN_SET: TokenSet = token_set![T![,], T![')']]; +const SCSS_LIST_EXPRESSION_ELEMENT_END_TOKEN_SET: TokenSet = + token_set![T![,], T![')']]; + +pub(crate) const END_OF_SCSS_EXPRESSION_TOKEN_SET: TokenSet = + token_set![T![,], T![')'], T![;], T!['}']].union(SCSS_BINARY_OPERATOR_TOKEN_SET); + +#[inline] +pub(crate) fn parse_scss_expression(p: &mut CssParser) -> ParsedSyntax { + parse_scss_expression_until(p, END_OF_SCSS_EXPRESSION_TOKEN_SET) +} + +#[inline] +fn parse_scss_expression_until(p: &mut CssParser, end_ts: TokenSet) -> ParsedSyntax { + parse_scss_expression_with_options(p, end_ts.union(SCSS_BINARY_OPERATOR_TOKEN_SET), false) +} + +#[inline] +fn parse_scss_inner_expression_until( + p: &mut CssParser, + end_ts: TokenSet, +) -> ParsedSyntax { + parse_scss_expression_with_options(p, end_ts, false) +} + +#[inline] +fn parse_scss_expression_with_options( + p: &mut CssParser, + end_ts: TokenSet, + allow_empty: bool, +) -> ParsedSyntax { + if !allow_empty && (p.at_ts(end_ts) || p.at(EOF)) { + return Absent; + } + + let expression = p.start(); + let expression_items = p.start(); + let mut progress = ParserProgress::default(); + + while !p.at(EOF) && !p.at_ts(end_ts) { + progress.assert_progressing(p); + + if parse_scss_expression_item(p) + .or_recover_with_token_set( + p, + &ParseRecoveryTokenSet::new(CSS_BOGUS_PROPERTY_VALUE, end_ts) + .enable_recovery_on_line_break(), + expected_scss_expression, + ) + .is_err() + { + break; + } + } + + expression_items.complete(p, SCSS_EXPRESSION_ITEM_LIST); + Present(expression.complete(p, SCSS_EXPRESSION)) +} + +#[inline] +fn parse_scss_expression_item(p: &mut CssParser) -> ParsedSyntax { + if p.at(T!['(']) { + parse_scss_parenthesized_or_map_expression(p) + } else { + parse_generic_component_value(p) + } +} + +#[inline] +fn parse_scss_parenthesized_or_map_expression(p: &mut CssParser) -> ParsedSyntax { + if !p.at(T!['(']) { + return Absent; + } + + let m = p.start(); + p.bump(T!['(']); + + if p.at(T![')']) { + let empty_pairs = p.start(); + empty_pairs.complete(p, SCSS_MAP_EXPRESSION_PAIR_LIST); + p.bump(T![')']); + return Present(m.complete(p, SCSS_MAP_EXPRESSION)); + } + + let first_expression = + parse_scss_inner_expression_until(p, SCSS_MAP_EXPRESSION_KEY_END_TOKEN_SET) + .or_add_diagnostic(p, expected_scss_expression); + + let Some(first_expression) = first_expression else { + p.expect(T![')']); + return Present(m.complete(p, SCSS_PARENTHESIZED_EXPRESSION)); + }; + + if p.at(T![:]) { + let first_pair = parse_scss_map_expression_pair_with_key(p, first_expression); + complete_scss_map_expression_pair_list(p, first_pair); + p.expect(T![')']); + return Present(m.complete(p, SCSS_MAP_EXPRESSION)); + } + + if p.at(T![,]) { + let first_element = complete_scss_list_expression_element(p, first_expression); + complete_scss_list_expression(p, first_element); + p.expect(T![')']); + return Present(m.complete(p, SCSS_PARENTHESIZED_EXPRESSION)); + } + + p.expect(T![')']); + Present(m.complete(p, SCSS_PARENTHESIZED_EXPRESSION)) +} + +#[inline] +fn parse_scss_map_expression_pair_with_key( + p: &mut CssParser, + key: CompletedMarker, +) -> CompletedMarker { + let pair_marker = key.precede(p); + p.expect(T![:]); + parse_scss_inner_expression_until(p, SCSS_MAP_EXPRESSION_VALUE_END_TOKEN_SET) + .or_add_diagnostic(p, expected_scss_expression); + pair_marker.complete(p, SCSS_MAP_EXPRESSION_PAIR) +} + +#[inline] +fn complete_scss_map_expression_pair_list(p: &mut CssParser, first_pair: CompletedMarker) { + let pairs_marker = first_pair.precede(p); + let mut progress = ParserProgress::default(); + + while p.at(T![,]) { + p.bump(T![,]); + + if p.at(T![')']) { + break; + } + + progress.assert_progressing(p); + + let pair = match parse_scss_inner_expression_until(p, SCSS_MAP_EXPRESSION_KEY_END_TOKEN_SET) + { + Present(key) => Present(parse_scss_map_expression_pair_with_key(p, key)), + Absent => Absent, + }; + + if pair + .or_recover_with_token_set( + p, + &ParseRecoveryTokenSet::new( + CSS_BOGUS_PROPERTY_VALUE, + SCSS_MAP_EXPRESSION_VALUE_END_TOKEN_SET, + ) + .enable_recovery_on_line_break(), + expected_scss_expression, + ) + .is_err() + { + break; + } + } + + pairs_marker.complete(p, SCSS_MAP_EXPRESSION_PAIR_LIST); +} + +#[inline] +fn complete_scss_list_expression(p: &mut CssParser, first_element: CompletedMarker) { + let list_elements = first_element.precede(p); + let mut progress = ParserProgress::default(); + + while p.at(T![,]) { + p.bump(T![,]); + + if p.at(T![')']) { + break; + } + + progress.assert_progressing(p); + + if parse_scss_inner_expression_until(p, SCSS_LIST_EXPRESSION_ELEMENT_END_TOKEN_SET) + .map(|expression| complete_scss_list_expression_element(p, expression)) + .or_recover_with_token_set( + p, + &ParseRecoveryTokenSet::new( + CSS_BOGUS_PROPERTY_VALUE, + SCSS_LIST_EXPRESSION_ELEMENT_END_TOKEN_SET, + ) + .enable_recovery_on_line_break(), + expected_scss_expression, + ) + .is_err() + { + break; + } + } + + list_elements + .complete(p, SCSS_LIST_EXPRESSION_ELEMENT_LIST) + .precede(p) + .complete(p, SCSS_LIST_EXPRESSION); +} + +#[inline] +fn complete_scss_list_expression_element( + p: &mut CssParser, + expression: CompletedMarker, +) -> CompletedMarker { + expression + .precede(p) + .complete(p, SCSS_LIST_EXPRESSION_ELEMENT) +} diff --git a/crates/biome_css_parser/src/syntax/scss/mod.rs b/crates/biome_css_parser/src/syntax/scss/mod.rs index c90542d571d5..a9b39a3d0974 100644 --- a/crates/biome_css_parser/src/syntax/scss/mod.rs +++ b/crates/biome_css_parser/src/syntax/scss/mod.rs @@ -1,4 +1,5 @@ mod declaration; +mod expression; use crate::parser::CssParser; use crate::syntax::{CssSyntaxFeatures, is_nth_at_identifier, parse_regular_identifier}; @@ -15,6 +16,7 @@ pub(crate) use declaration::{ is_at_scss_declaration, is_at_scss_nesting_declaration, parse_scss_declaration, parse_scss_nesting_declaration, }; +pub(crate) use expression::parse_scss_expression; #[inline] pub(crate) fn is_at_scss_identifier(p: &mut CssParser) -> bool { diff --git a/crates/biome_css_parser/src/syntax/value/function.rs b/crates/biome_css_parser/src/syntax/value/function.rs index f7f14218f2cd..a0ef82238287 100644 --- a/crates/biome_css_parser/src/syntax/value/function.rs +++ b/crates/biome_css_parser/src/syntax/value/function.rs @@ -9,7 +9,8 @@ use crate::syntax::parse_error::{ }; use crate::syntax::property::parse_generic_component_value; use crate::syntax::scss::{ - is_at_scss_qualified_name, is_nth_at_scss_qualified_name, parse_scss_function_name, + is_at_scss_qualified_name, is_nth_at_scss_qualified_name, parse_scss_expression, + parse_scss_function_name, }; use crate::syntax::value::attr::{is_at_attr_function, parse_attr_function}; use crate::syntax::value::r#if::parse_if_function; @@ -278,9 +279,14 @@ pub(crate) fn parse_unary_expression(p: &mut CssParser) -> ParsedSyntax { #[inline] fn parse_unary_expression_operand(p: &mut CssParser) -> ParsedSyntax { + let scss_enabled = CssSyntaxFeatures::Scss.is_supported(p); + if is_at_unary_operator(p) { parse_unary_expression(p) - } else if is_at_parenthesized(p) { + } else if scss_enabled && (is_at_parenthesized(p) || is_at_any_value(p)) { + parse_scss_expression(p) + } else if !scss_enabled && is_at_parenthesized(p) { + // In SCSS mode, parenthesized values are consumed as SCSS expressions above. parse_parenthesized_expression(p) } else if is_at_comma_separated_value(p) { parse_comma_separated_value(p) diff --git a/crates/biome_css_parser/tests/css_test_suite/error/scss/expression/list-map-paren.scss b/crates/biome_css_parser/tests/css_test_suite/error/scss/expression/list-map-paren.scss new file mode 100644 index 000000000000..7a32711fae45 --- /dev/null +++ b/crates/biome_css_parser/tests/css_test_suite/error/scss/expression/list-map-paren.scss @@ -0,0 +1,6 @@ +.test { + missing-close: fn((1, 2); + missing-map-value: fn((primary:, secondary: blue)); + missing-map-key: fn((primary: red, : blue)); + double-comma: fn((1,, 2)); +} diff --git a/crates/biome_css_parser/tests/css_test_suite/error/scss/expression/list-map-paren.scss.snap b/crates/biome_css_parser/tests/css_test_suite/error/scss/expression/list-map-paren.scss.snap new file mode 100644 index 000000000000..69eb9026f1b3 --- /dev/null +++ b/crates/biome_css_parser/tests/css_test_suite/error/scss/expression/list-map-paren.scss.snap @@ -0,0 +1,583 @@ +--- +source: crates/biome_css_parser/tests/spec_test.rs +assertion_line: 208 +expression: snapshot +--- + +## Input + +```css +.test { + missing-close: fn((1, 2); + missing-map-value: fn((primary:, secondary: blue)); + missing-map-key: fn((primary: red, : blue)); + double-comma: fn((1,, 2)); +} + +``` + + +## AST + +``` +CssRoot { + bom_token: missing (optional), + items: CssRootItemList [ + CssQualifiedRule { + prelude: CssSelectorList [ + CssCompoundSelector { + nesting_selectors: CssNestedSelectorList [], + simple_selector: missing (optional), + sub_selectors: CssSubSelectorList [ + CssClassSelector { + dot_token: DOT@0..1 "." [] [], + name: CssCustomIdentifier { + value_token: IDENT@1..6 "test" [] [Whitespace(" ")], + }, + }, + ], + }, + ], + block: CssDeclarationOrRuleBlock { + l_curly_token: L_CURLY@6..7 "{" [] [], + items: CssDeclarationOrRuleList [ + CssDeclarationWithSemicolon { + declaration: CssDeclaration { + property: CssGenericProperty { + name: CssIdentifier { + value_token: IDENT@7..23 "missing-close" [Newline("\n"), Whitespace(" ")] [], + }, + colon_token: COLON@23..25 ":" [] [Whitespace(" ")], + value: CssGenericComponentValueList [ + CssFunction { + name: CssIdentifier { + value_token: IDENT@25..27 "fn" [] [], + }, + l_paren_token: L_PAREN@27..28 "(" [] [], + items: CssParameterList [ + CssParameter { + any_css_expression: ScssExpression { + items: ScssExpressionItemList [ + ScssParenthesizedExpression { + l_paren_token: L_PAREN@28..29 "(" [] [], + expression: ScssListExpression { + elements: ScssListExpressionElementList [ + ScssListExpressionElement { + value: ScssExpression { + items: ScssExpressionItemList [ + CssNumber { + value_token: CSS_NUMBER_LITERAL@29..30 "1" [] [], + }, + ], + }, + }, + COMMA@30..32 "," [] [Whitespace(" ")], + ScssListExpressionElement { + value: ScssExpression { + items: ScssExpressionItemList [ + CssNumber { + value_token: CSS_NUMBER_LITERAL@32..33 "2" [] [], + }, + ], + }, + }, + ], + }, + r_paren_token: R_PAREN@33..34 ")" [] [], + }, + ], + }, + }, + ], + r_paren_token: missing (required), + }, + ], + }, + important: missing (optional), + }, + semicolon_token: SEMICOLON@34..35 ";" [] [], + }, + CssDeclarationWithSemicolon { + declaration: CssDeclaration { + property: CssGenericProperty { + name: CssIdentifier { + value_token: IDENT@35..55 "missing-map-value" [Newline("\n"), Whitespace(" ")] [], + }, + colon_token: COLON@55..57 ":" [] [Whitespace(" ")], + value: CssGenericComponentValueList [ + CssFunction { + name: CssIdentifier { + value_token: IDENT@57..59 "fn" [] [], + }, + l_paren_token: L_PAREN@59..60 "(" [] [], + items: CssParameterList [ + CssParameter { + any_css_expression: ScssExpression { + items: ScssExpressionItemList [ + ScssMapExpression { + l_paren_token: L_PAREN@60..61 "(" [] [], + pairs: ScssMapExpressionPairList [ + ScssMapExpressionPair { + key: ScssExpression { + items: ScssExpressionItemList [ + CssIdentifier { + value_token: IDENT@61..68 "primary" [] [], + }, + ], + }, + colon_token: COLON@68..69 ":" [] [], + value: missing (required), + }, + COMMA@69..71 "," [] [Whitespace(" ")], + ScssMapExpressionPair { + key: ScssExpression { + items: ScssExpressionItemList [ + CssIdentifier { + value_token: IDENT@71..80 "secondary" [] [], + }, + ], + }, + colon_token: COLON@80..82 ":" [] [Whitespace(" ")], + value: ScssExpression { + items: ScssExpressionItemList [ + CssIdentifier { + value_token: IDENT@82..86 "blue" [] [], + }, + ], + }, + }, + ], + r_paren_token: R_PAREN@86..87 ")" [] [], + }, + ], + }, + }, + ], + r_paren_token: R_PAREN@87..88 ")" [] [], + }, + ], + }, + important: missing (optional), + }, + semicolon_token: SEMICOLON@88..89 ";" [] [], + }, + CssDeclarationWithSemicolon { + declaration: CssDeclaration { + property: CssBogusProperty { + items: [ + CssIdentifier { + value_token: IDENT@89..107 "missing-map-key" [Newline("\n"), Whitespace(" ")] [], + }, + COLON@107..109 ":" [] [Whitespace(" ")], + CssBogus { + items: [ + CssBogusSupportsCondition { + items: [ + CssIdentifier { + value_token: IDENT@109..111 "fn" [] [], + }, + L_PAREN@111..112 "(" [] [], + CssBogus { + items: [ + CssBogus { + items: [ + CssBogus { + items: [ + CssBogus { + items: [ + CssBogus { + items: [ + L_PAREN@112..113 "(" [] [], + CssBogus { + items: [ + ScssMapExpressionPair { + key: ScssExpression { + items: ScssExpressionItemList [ + CssIdentifier { + value_token: IDENT@113..120 "primary" [] [], + }, + ], + }, + colon_token: COLON@120..122 ":" [] [Whitespace(" ")], + value: ScssExpression { + items: ScssExpressionItemList [ + CssIdentifier { + value_token: IDENT@122..125 "red" [] [], + }, + ], + }, + }, + COMMA@125..127 "," [] [Whitespace(" ")], + CssBogusPropertyValue { + items: [ + COLON@127..129 ":" [] [Whitespace(" ")], + IDENT@129..133 "blue" [] [], + ], + }, + ], + }, + R_PAREN@133..134 ")" [] [], + ], + }, + ], + }, + ], + }, + ], + }, + ], + }, + R_PAREN@134..135 ")" [] [], + ], + }, + ], + }, + ], + }, + important: missing (optional), + }, + semicolon_token: SEMICOLON@135..136 ";" [] [], + }, + CssDeclarationWithSemicolon { + declaration: CssDeclaration { + property: CssBogusProperty { + items: [ + CssIdentifier { + value_token: IDENT@136..151 "double-comma" [Newline("\n"), Whitespace(" ")] [], + }, + COLON@151..153 ":" [] [Whitespace(" ")], + CssBogus { + items: [ + CssFunction { + name: CssIdentifier { + value_token: IDENT@153..155 "fn" [] [], + }, + l_paren_token: L_PAREN@155..156 "(" [] [], + items: CssParameterList [ + CssParameter { + any_css_expression: ScssExpression { + items: ScssExpressionItemList [ + ScssParenthesizedExpression { + l_paren_token: L_PAREN@156..157 "(" [] [], + expression: ScssListExpression { + elements: ScssListExpressionElementList [ + ScssListExpressionElement { + value: ScssExpression { + items: ScssExpressionItemList [ + CssNumber { + value_token: CSS_NUMBER_LITERAL@157..158 "1" [] [], + }, + ], + }, + }, + COMMA@158..159 "," [] [], + ], + }, + r_paren_token: missing (required), + }, + ], + }, + }, + COMMA@159..161 "," [] [Whitespace(" ")], + CssParameter { + any_css_expression: ScssExpression { + items: ScssExpressionItemList [ + CssNumber { + value_token: CSS_NUMBER_LITERAL@161..162 "2" [] [], + }, + ], + }, + }, + ], + r_paren_token: R_PAREN@162..163 ")" [] [], + }, + CssBogusPropertyValue { + items: [ + R_PAREN@163..164 ")" [] [], + ], + }, + ], + }, + ], + }, + important: missing (optional), + }, + semicolon_token: SEMICOLON@164..165 ";" [] [], + }, + ], + r_curly_token: R_CURLY@165..167 "}" [Newline("\n")] [], + }, + }, + ], + eof_token: EOF@167..168 "" [Newline("\n")] [], +} +``` + +## CST + +``` +0: CSS_ROOT@0..168 + 0: (empty) + 1: CSS_ROOT_ITEM_LIST@0..167 + 0: CSS_QUALIFIED_RULE@0..167 + 0: CSS_SELECTOR_LIST@0..6 + 0: CSS_COMPOUND_SELECTOR@0..6 + 0: CSS_NESTED_SELECTOR_LIST@0..0 + 1: (empty) + 2: CSS_SUB_SELECTOR_LIST@0..6 + 0: CSS_CLASS_SELECTOR@0..6 + 0: DOT@0..1 "." [] [] + 1: CSS_CUSTOM_IDENTIFIER@1..6 + 0: IDENT@1..6 "test" [] [Whitespace(" ")] + 1: CSS_DECLARATION_OR_RULE_BLOCK@6..167 + 0: L_CURLY@6..7 "{" [] [] + 1: CSS_DECLARATION_OR_RULE_LIST@7..165 + 0: CSS_DECLARATION_WITH_SEMICOLON@7..35 + 0: CSS_DECLARATION@7..34 + 0: CSS_GENERIC_PROPERTY@7..34 + 0: CSS_IDENTIFIER@7..23 + 0: IDENT@7..23 "missing-close" [Newline("\n"), Whitespace(" ")] [] + 1: COLON@23..25 ":" [] [Whitespace(" ")] + 2: CSS_GENERIC_COMPONENT_VALUE_LIST@25..34 + 0: CSS_FUNCTION@25..34 + 0: CSS_IDENTIFIER@25..27 + 0: IDENT@25..27 "fn" [] [] + 1: L_PAREN@27..28 "(" [] [] + 2: CSS_PARAMETER_LIST@28..34 + 0: CSS_PARAMETER@28..34 + 0: SCSS_EXPRESSION@28..34 + 0: SCSS_EXPRESSION_ITEM_LIST@28..34 + 0: SCSS_PARENTHESIZED_EXPRESSION@28..34 + 0: L_PAREN@28..29 "(" [] [] + 1: SCSS_LIST_EXPRESSION@29..33 + 0: SCSS_LIST_EXPRESSION_ELEMENT_LIST@29..33 + 0: SCSS_LIST_EXPRESSION_ELEMENT@29..30 + 0: SCSS_EXPRESSION@29..30 + 0: SCSS_EXPRESSION_ITEM_LIST@29..30 + 0: CSS_NUMBER@29..30 + 0: CSS_NUMBER_LITERAL@29..30 "1" [] [] + 1: COMMA@30..32 "," [] [Whitespace(" ")] + 2: SCSS_LIST_EXPRESSION_ELEMENT@32..33 + 0: SCSS_EXPRESSION@32..33 + 0: SCSS_EXPRESSION_ITEM_LIST@32..33 + 0: CSS_NUMBER@32..33 + 0: CSS_NUMBER_LITERAL@32..33 "2" [] [] + 2: R_PAREN@33..34 ")" [] [] + 3: (empty) + 1: (empty) + 1: SEMICOLON@34..35 ";" [] [] + 1: CSS_DECLARATION_WITH_SEMICOLON@35..89 + 0: CSS_DECLARATION@35..88 + 0: CSS_GENERIC_PROPERTY@35..88 + 0: CSS_IDENTIFIER@35..55 + 0: IDENT@35..55 "missing-map-value" [Newline("\n"), Whitespace(" ")] [] + 1: COLON@55..57 ":" [] [Whitespace(" ")] + 2: CSS_GENERIC_COMPONENT_VALUE_LIST@57..88 + 0: CSS_FUNCTION@57..88 + 0: CSS_IDENTIFIER@57..59 + 0: IDENT@57..59 "fn" [] [] + 1: L_PAREN@59..60 "(" [] [] + 2: CSS_PARAMETER_LIST@60..87 + 0: CSS_PARAMETER@60..87 + 0: SCSS_EXPRESSION@60..87 + 0: SCSS_EXPRESSION_ITEM_LIST@60..87 + 0: SCSS_MAP_EXPRESSION@60..87 + 0: L_PAREN@60..61 "(" [] [] + 1: SCSS_MAP_EXPRESSION_PAIR_LIST@61..86 + 0: SCSS_MAP_EXPRESSION_PAIR@61..69 + 0: SCSS_EXPRESSION@61..68 + 0: SCSS_EXPRESSION_ITEM_LIST@61..68 + 0: CSS_IDENTIFIER@61..68 + 0: IDENT@61..68 "primary" [] [] + 1: COLON@68..69 ":" [] [] + 2: (empty) + 1: COMMA@69..71 "," [] [Whitespace(" ")] + 2: SCSS_MAP_EXPRESSION_PAIR@71..86 + 0: SCSS_EXPRESSION@71..80 + 0: SCSS_EXPRESSION_ITEM_LIST@71..80 + 0: CSS_IDENTIFIER@71..80 + 0: IDENT@71..80 "secondary" [] [] + 1: COLON@80..82 ":" [] [Whitespace(" ")] + 2: SCSS_EXPRESSION@82..86 + 0: SCSS_EXPRESSION_ITEM_LIST@82..86 + 0: CSS_IDENTIFIER@82..86 + 0: IDENT@82..86 "blue" [] [] + 2: R_PAREN@86..87 ")" [] [] + 3: R_PAREN@87..88 ")" [] [] + 1: (empty) + 1: SEMICOLON@88..89 ";" [] [] + 2: CSS_DECLARATION_WITH_SEMICOLON@89..136 + 0: CSS_DECLARATION@89..135 + 0: CSS_BOGUS_PROPERTY@89..135 + 0: CSS_IDENTIFIER@89..107 + 0: IDENT@89..107 "missing-map-key" [Newline("\n"), Whitespace(" ")] [] + 1: COLON@107..109 ":" [] [Whitespace(" ")] + 2: CSS_BOGUS@109..135 + 0: CSS_BOGUS_SUPPORTS_CONDITION@109..135 + 0: CSS_IDENTIFIER@109..111 + 0: IDENT@109..111 "fn" [] [] + 1: L_PAREN@111..112 "(" [] [] + 2: CSS_BOGUS@112..134 + 0: CSS_BOGUS@112..134 + 0: CSS_BOGUS@112..134 + 0: CSS_BOGUS@112..134 + 0: CSS_BOGUS@112..134 + 0: L_PAREN@112..113 "(" [] [] + 1: CSS_BOGUS@113..133 + 0: SCSS_MAP_EXPRESSION_PAIR@113..125 + 0: SCSS_EXPRESSION@113..120 + 0: SCSS_EXPRESSION_ITEM_LIST@113..120 + 0: CSS_IDENTIFIER@113..120 + 0: IDENT@113..120 "primary" [] [] + 1: COLON@120..122 ":" [] [Whitespace(" ")] + 2: SCSS_EXPRESSION@122..125 + 0: SCSS_EXPRESSION_ITEM_LIST@122..125 + 0: CSS_IDENTIFIER@122..125 + 0: IDENT@122..125 "red" [] [] + 1: COMMA@125..127 "," [] [Whitespace(" ")] + 2: CSS_BOGUS_PROPERTY_VALUE@127..133 + 0: COLON@127..129 ":" [] [Whitespace(" ")] + 1: IDENT@129..133 "blue" [] [] + 2: R_PAREN@133..134 ")" [] [] + 3: R_PAREN@134..135 ")" [] [] + 1: (empty) + 1: SEMICOLON@135..136 ";" [] [] + 3: CSS_DECLARATION_WITH_SEMICOLON@136..165 + 0: CSS_DECLARATION@136..164 + 0: CSS_BOGUS_PROPERTY@136..164 + 0: CSS_IDENTIFIER@136..151 + 0: IDENT@136..151 "double-comma" [Newline("\n"), Whitespace(" ")] [] + 1: COLON@151..153 ":" [] [Whitespace(" ")] + 2: CSS_BOGUS@153..164 + 0: CSS_FUNCTION@153..163 + 0: CSS_IDENTIFIER@153..155 + 0: IDENT@153..155 "fn" [] [] + 1: L_PAREN@155..156 "(" [] [] + 2: CSS_PARAMETER_LIST@156..162 + 0: CSS_PARAMETER@156..159 + 0: SCSS_EXPRESSION@156..159 + 0: SCSS_EXPRESSION_ITEM_LIST@156..159 + 0: SCSS_PARENTHESIZED_EXPRESSION@156..159 + 0: L_PAREN@156..157 "(" [] [] + 1: SCSS_LIST_EXPRESSION@157..159 + 0: SCSS_LIST_EXPRESSION_ELEMENT_LIST@157..159 + 0: SCSS_LIST_EXPRESSION_ELEMENT@157..158 + 0: SCSS_EXPRESSION@157..158 + 0: SCSS_EXPRESSION_ITEM_LIST@157..158 + 0: CSS_NUMBER@157..158 + 0: CSS_NUMBER_LITERAL@157..158 "1" [] [] + 1: COMMA@158..159 "," [] [] + 2: (empty) + 1: COMMA@159..161 "," [] [Whitespace(" ")] + 2: CSS_PARAMETER@161..162 + 0: SCSS_EXPRESSION@161..162 + 0: SCSS_EXPRESSION_ITEM_LIST@161..162 + 0: CSS_NUMBER@161..162 + 0: CSS_NUMBER_LITERAL@161..162 "2" [] [] + 3: R_PAREN@162..163 ")" [] [] + 1: CSS_BOGUS_PROPERTY_VALUE@163..164 + 0: R_PAREN@163..164 ")" [] [] + 1: (empty) + 1: SEMICOLON@164..165 ";" [] [] + 2: R_CURLY@165..167 "}" [Newline("\n")] [] + 2: EOF@167..168 "" [Newline("\n")] [] + +``` + +## Diagnostics + +``` +list-map-paren.scss:2:27 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × expected `,` but instead found `;` + + 1 │ .test { + > 2 │ missing-close: fn((1, 2); + │ ^ + 3 │ missing-map-value: fn((primary:, secondary: blue)); + 4 │ missing-map-key: fn((primary: red, : blue)); + + i Remove ; + +list-map-paren.scss:3:34 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × Expected a SCSS expression but instead found ','. + + 1 │ .test { + 2 │ missing-close: fn((1, 2); + > 3 │ missing-map-value: fn((primary:, secondary: blue)); + │ ^ + 4 │ missing-map-key: fn((primary: red, : blue)); + 5 │ double-comma: fn((1,, 2)); + + i Expected a SCSS expression here. + + 1 │ .test { + 2 │ missing-close: fn((1, 2); + > 3 │ missing-map-value: fn((primary:, secondary: blue)); + │ ^ + 4 │ missing-map-key: fn((primary: red, : blue)); + 5 │ double-comma: fn((1,, 2)); + +list-map-paren.scss:4:38 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × Expected a SCSS expression but instead found ': blue'. + + 2 │ missing-close: fn((1, 2); + 3 │ missing-map-value: fn((primary:, secondary: blue)); + > 4 │ missing-map-key: fn((primary: red, : blue)); + │ ^^^^^^ + 5 │ double-comma: fn((1,, 2)); + 6 │ } + + i Expected a SCSS expression here. + + 2 │ missing-close: fn((1, 2); + 3 │ missing-map-value: fn((primary:, secondary: blue)); + > 4 │ missing-map-key: fn((primary: red, : blue)); + │ ^^^^^^ + 5 │ double-comma: fn((1,, 2)); + 6 │ } + +list-map-paren.scss:5:23 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × Expected a SCSS expression but instead found ','. + + 3 │ missing-map-value: fn((primary:, secondary: blue)); + 4 │ missing-map-key: fn((primary: red, : blue)); + > 5 │ double-comma: fn((1,, 2)); + │ ^ + 6 │ } + 7 │ + + i Expected a SCSS expression here. + + 3 │ missing-map-value: fn((primary:, secondary: blue)); + 4 │ missing-map-key: fn((primary: red, : blue)); + > 5 │ double-comma: fn((1,, 2)); + │ ^ + 6 │ } + 7 │ + +list-map-paren.scss:5:27 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × Unexpected value or character. + + 3 │ missing-map-value: fn((primary:, secondary: blue)); + 4 │ missing-map-key: fn((primary: red, : blue)); + > 5 │ double-comma: fn((1,, 2)); + │ ^ + 6 │ } + 7 │ + + i Expected one of: + + - identifier + - string + - number + - dimension + - ratio + - custom property + - function + +``` diff --git a/crates/biome_css_parser/tests/css_test_suite/ok/function/unary-parenthesized.css b/crates/biome_css_parser/tests/css_test_suite/ok/function/unary-parenthesized.css new file mode 100644 index 000000000000..4280d2491210 --- /dev/null +++ b/crates/biome_css_parser/tests/css_test_suite/ok/function/unary-parenthesized.css @@ -0,0 +1,3 @@ +.unary-parenthesized { + width: calc(-(1 + 2)); +} diff --git a/crates/biome_css_parser/tests/css_test_suite/ok/function/unary-parenthesized.css.snap b/crates/biome_css_parser/tests/css_test_suite/ok/function/unary-parenthesized.css.snap new file mode 100644 index 000000000000..a54ff18a19a2 --- /dev/null +++ b/crates/biome_css_parser/tests/css_test_suite/ok/function/unary-parenthesized.css.snap @@ -0,0 +1,152 @@ +--- +source: crates/biome_css_parser/tests/spec_test.rs +assertion_line: 208 +expression: snapshot +--- + +## Input + +```css +.unary-parenthesized { + width: calc(-(1 + 2)); +} + +``` + + +## AST + +``` +CssRoot { + bom_token: missing (optional), + items: CssRootItemList [ + CssQualifiedRule { + prelude: CssSelectorList [ + CssCompoundSelector { + nesting_selectors: CssNestedSelectorList [], + simple_selector: missing (optional), + sub_selectors: CssSubSelectorList [ + CssClassSelector { + dot_token: DOT@0..1 "." [] [], + name: CssCustomIdentifier { + value_token: IDENT@1..21 "unary-parenthesized" [] [Whitespace(" ")], + }, + }, + ], + }, + ], + block: CssDeclarationOrRuleBlock { + l_curly_token: L_CURLY@21..22 "{" [] [], + items: CssDeclarationOrRuleList [ + CssDeclarationWithSemicolon { + declaration: CssDeclaration { + property: CssGenericProperty { + name: CssIdentifier { + value_token: IDENT@22..30 "width" [Newline("\n"), Whitespace(" ")] [], + }, + colon_token: COLON@30..32 ":" [] [Whitespace(" ")], + value: CssGenericComponentValueList [ + CssFunction { + name: CssIdentifier { + value_token: IDENT@32..36 "calc" [] [], + }, + l_paren_token: L_PAREN@36..37 "(" [] [], + items: CssParameterList [ + CssParameter { + any_css_expression: CssUnaryExpression { + operator: MINUS@37..38 "-" [] [], + expression: CssParenthesizedExpression { + l_paren_token: L_PAREN@38..39 "(" [] [], + expression: CssBinaryExpression { + left: CssListOfComponentValuesExpression { + css_component_value_list: CssComponentValueList [ + CssNumber { + value_token: CSS_NUMBER_LITERAL@39..41 "1" [] [Whitespace(" ")], + }, + ], + }, + operator_token: PLUS@41..43 "+" [] [Whitespace(" ")], + right: CssListOfComponentValuesExpression { + css_component_value_list: CssComponentValueList [ + CssNumber { + value_token: CSS_NUMBER_LITERAL@43..44 "2" [] [], + }, + ], + }, + }, + r_paren_token: R_PAREN@44..45 ")" [] [], + }, + }, + }, + ], + r_paren_token: R_PAREN@45..46 ")" [] [], + }, + ], + }, + important: missing (optional), + }, + semicolon_token: SEMICOLON@46..47 ";" [] [], + }, + ], + r_curly_token: R_CURLY@47..49 "}" [Newline("\n")] [], + }, + }, + ], + eof_token: EOF@49..50 "" [Newline("\n")] [], +} +``` + +## CST + +``` +0: CSS_ROOT@0..50 + 0: (empty) + 1: CSS_ROOT_ITEM_LIST@0..49 + 0: CSS_QUALIFIED_RULE@0..49 + 0: CSS_SELECTOR_LIST@0..21 + 0: CSS_COMPOUND_SELECTOR@0..21 + 0: CSS_NESTED_SELECTOR_LIST@0..0 + 1: (empty) + 2: CSS_SUB_SELECTOR_LIST@0..21 + 0: CSS_CLASS_SELECTOR@0..21 + 0: DOT@0..1 "." [] [] + 1: CSS_CUSTOM_IDENTIFIER@1..21 + 0: IDENT@1..21 "unary-parenthesized" [] [Whitespace(" ")] + 1: CSS_DECLARATION_OR_RULE_BLOCK@21..49 + 0: L_CURLY@21..22 "{" [] [] + 1: CSS_DECLARATION_OR_RULE_LIST@22..47 + 0: CSS_DECLARATION_WITH_SEMICOLON@22..47 + 0: CSS_DECLARATION@22..46 + 0: CSS_GENERIC_PROPERTY@22..46 + 0: CSS_IDENTIFIER@22..30 + 0: IDENT@22..30 "width" [Newline("\n"), Whitespace(" ")] [] + 1: COLON@30..32 ":" [] [Whitespace(" ")] + 2: CSS_GENERIC_COMPONENT_VALUE_LIST@32..46 + 0: CSS_FUNCTION@32..46 + 0: CSS_IDENTIFIER@32..36 + 0: IDENT@32..36 "calc" [] [] + 1: L_PAREN@36..37 "(" [] [] + 2: CSS_PARAMETER_LIST@37..45 + 0: CSS_PARAMETER@37..45 + 0: CSS_UNARY_EXPRESSION@37..45 + 0: MINUS@37..38 "-" [] [] + 1: CSS_PARENTHESIZED_EXPRESSION@38..45 + 0: L_PAREN@38..39 "(" [] [] + 1: CSS_BINARY_EXPRESSION@39..44 + 0: CSS_LIST_OF_COMPONENT_VALUES_EXPRESSION@39..41 + 0: CSS_COMPONENT_VALUE_LIST@39..41 + 0: CSS_NUMBER@39..41 + 0: CSS_NUMBER_LITERAL@39..41 "1" [] [Whitespace(" ")] + 1: PLUS@41..43 "+" [] [Whitespace(" ")] + 2: CSS_LIST_OF_COMPONENT_VALUES_EXPRESSION@43..44 + 0: CSS_COMPONENT_VALUE_LIST@43..44 + 0: CSS_NUMBER@43..44 + 0: CSS_NUMBER_LITERAL@43..44 "2" [] [] + 2: R_PAREN@44..45 ")" [] [] + 3: R_PAREN@45..46 ")" [] [] + 1: (empty) + 1: SEMICOLON@46..47 ";" [] [] + 2: R_CURLY@47..49 "}" [Newline("\n")] [] + 2: EOF@49..50 "" [Newline("\n")] [] + +``` diff --git a/crates/biome_css_parser/tests/css_test_suite/ok/scss/declaration/namespaced-function.scss.snap b/crates/biome_css_parser/tests/css_test_suite/ok/scss/declaration/namespaced-function.scss.snap index 46c2df5444b2..93cd5b8e6c37 100644 --- a/crates/biome_css_parser/tests/css_test_suite/ok/scss/declaration/namespaced-function.scss.snap +++ b/crates/biome_css_parser/tests/css_test_suite/ok/scss/declaration/namespaced-function.scss.snap @@ -1,5 +1,6 @@ --- source: crates/biome_css_parser/tests/spec_test.rs +assertion_line: 208 expression: snapshot --- @@ -40,8 +41,8 @@ CssRoot { l_paren_token: L_PAREN@18..19 "(" [] [], items: CssParameterList [ CssParameter { - any_css_expression: CssListOfComponentValuesExpression { - css_component_value_list: CssComponentValueList [ + any_css_expression: ScssExpression { + items: ScssExpressionItemList [ CssNumber { value_token: CSS_NUMBER_LITERAL@19..20 "1" [] [], }, @@ -50,8 +51,8 @@ CssRoot { }, COMMA@20..22 "," [] [Whitespace(" ")], CssParameter { - any_css_expression: CssListOfComponentValuesExpression { - css_component_value_list: CssComponentValueList [ + any_css_expression: ScssExpression { + items: ScssExpressionItemList [ CssNumber { value_token: CSS_NUMBER_LITERAL@22..23 "2" [] [], }, @@ -87,8 +88,8 @@ CssRoot { l_paren_token: L_PAREN@47..48 "(" [] [], items: CssParameterList [ CssParameter { - any_css_expression: CssListOfComponentValuesExpression { - css_component_value_list: CssComponentValueList [ + any_css_expression: ScssExpression { + items: ScssExpressionItemList [ CssFunction { name: CssIdentifier { value_token: IDENT@48..51 "var" [] [], @@ -96,8 +97,8 @@ CssRoot { l_paren_token: L_PAREN@51..52 "(" [] [], items: CssParameterList [ CssParameter { - any_css_expression: CssListOfComponentValuesExpression { - css_component_value_list: CssComponentValueList [ + any_css_expression: ScssExpression { + items: ScssExpressionItemList [ CssDashedIdentifier { value_token: IDENT@52..59 "--token" [] [], }, @@ -145,14 +146,14 @@ CssRoot { 1: L_PAREN@18..19 "(" [] [] 2: CSS_PARAMETER_LIST@19..23 0: CSS_PARAMETER@19..20 - 0: CSS_LIST_OF_COMPONENT_VALUES_EXPRESSION@19..20 - 0: CSS_COMPONENT_VALUE_LIST@19..20 + 0: SCSS_EXPRESSION@19..20 + 0: SCSS_EXPRESSION_ITEM_LIST@19..20 0: CSS_NUMBER@19..20 0: CSS_NUMBER_LITERAL@19..20 "1" [] [] 1: COMMA@20..22 "," [] [Whitespace(" ")] 2: CSS_PARAMETER@22..23 - 0: CSS_LIST_OF_COMPONENT_VALUES_EXPRESSION@22..23 - 0: CSS_COMPONENT_VALUE_LIST@22..23 + 0: SCSS_EXPRESSION@22..23 + 0: SCSS_EXPRESSION_ITEM_LIST@22..23 0: CSS_NUMBER@22..23 0: CSS_NUMBER_LITERAL@22..23 "2" [] [] 3: R_PAREN@23..24 ")" [] [] @@ -175,16 +176,16 @@ CssRoot { 1: L_PAREN@47..48 "(" [] [] 2: CSS_PARAMETER_LIST@48..60 0: CSS_PARAMETER@48..60 - 0: CSS_LIST_OF_COMPONENT_VALUES_EXPRESSION@48..60 - 0: CSS_COMPONENT_VALUE_LIST@48..60 + 0: SCSS_EXPRESSION@48..60 + 0: SCSS_EXPRESSION_ITEM_LIST@48..60 0: CSS_FUNCTION@48..60 0: CSS_IDENTIFIER@48..51 0: IDENT@48..51 "var" [] [] 1: L_PAREN@51..52 "(" [] [] 2: CSS_PARAMETER_LIST@52..59 0: CSS_PARAMETER@52..59 - 0: CSS_LIST_OF_COMPONENT_VALUES_EXPRESSION@52..59 - 0: CSS_COMPONENT_VALUE_LIST@52..59 + 0: SCSS_EXPRESSION@52..59 + 0: SCSS_EXPRESSION_ITEM_LIST@52..59 0: CSS_DASHED_IDENTIFIER@52..59 0: IDENT@52..59 "--token" [] [] 3: R_PAREN@59..60 ")" [] [] diff --git a/crates/biome_css_parser/tests/css_test_suite/ok/scss/expression/core.scss b/crates/biome_css_parser/tests/css_test_suite/ok/scss/expression/core.scss new file mode 100644 index 000000000000..3bcdd75f4ccb --- /dev/null +++ b/crates/biome_css_parser/tests/css_test_suite/ok/scss/expression/core.scss @@ -0,0 +1,4 @@ +.test { + width: fn($alpha $beta); + height: fn(1 2 3); +} diff --git a/crates/biome_css_parser/tests/css_test_suite/ok/scss/expression/core.scss.snap b/crates/biome_css_parser/tests/css_test_suite/ok/scss/expression/core.scss.snap new file mode 100644 index 000000000000..5a77f9d1b152 --- /dev/null +++ b/crates/biome_css_parser/tests/css_test_suite/ok/scss/expression/core.scss.snap @@ -0,0 +1,202 @@ +--- +source: crates/biome_css_parser/tests/spec_test.rs +assertion_line: 208 +expression: snapshot +--- + +## Input + +```css +.test { + width: fn($alpha $beta); + height: fn(1 2 3); +} + +``` + + +## AST + +``` +CssRoot { + bom_token: missing (optional), + items: CssRootItemList [ + CssQualifiedRule { + prelude: CssSelectorList [ + CssCompoundSelector { + nesting_selectors: CssNestedSelectorList [], + simple_selector: missing (optional), + sub_selectors: CssSubSelectorList [ + CssClassSelector { + dot_token: DOT@0..1 "." [] [], + name: CssCustomIdentifier { + value_token: IDENT@1..6 "test" [] [Whitespace(" ")], + }, + }, + ], + }, + ], + block: CssDeclarationOrRuleBlock { + l_curly_token: L_CURLY@6..7 "{" [] [], + items: CssDeclarationOrRuleList [ + CssDeclarationWithSemicolon { + declaration: CssDeclaration { + property: CssGenericProperty { + name: CssIdentifier { + value_token: IDENT@7..15 "width" [Newline("\n"), Whitespace(" ")] [], + }, + colon_token: COLON@15..17 ":" [] [Whitespace(" ")], + value: CssGenericComponentValueList [ + CssFunction { + name: CssIdentifier { + value_token: IDENT@17..19 "fn" [] [], + }, + l_paren_token: L_PAREN@19..20 "(" [] [], + items: CssParameterList [ + CssParameter { + any_css_expression: ScssExpression { + items: ScssExpressionItemList [ + ScssIdentifier { + dollar_token: DOLLAR@20..21 "$" [] [], + name: CssIdentifier { + value_token: IDENT@21..27 "alpha" [] [Whitespace(" ")], + }, + }, + ScssIdentifier { + dollar_token: DOLLAR@27..28 "$" [] [], + name: CssIdentifier { + value_token: IDENT@28..32 "beta" [] [], + }, + }, + ], + }, + }, + ], + r_paren_token: R_PAREN@32..33 ")" [] [], + }, + ], + }, + important: missing (optional), + }, + semicolon_token: SEMICOLON@33..34 ";" [] [], + }, + CssDeclarationWithSemicolon { + declaration: CssDeclaration { + property: CssGenericProperty { + name: CssIdentifier { + value_token: IDENT@34..43 "height" [Newline("\n"), Whitespace(" ")] [], + }, + colon_token: COLON@43..45 ":" [] [Whitespace(" ")], + value: CssGenericComponentValueList [ + CssFunction { + name: CssIdentifier { + value_token: IDENT@45..47 "fn" [] [], + }, + l_paren_token: L_PAREN@47..48 "(" [] [], + items: CssParameterList [ + CssParameter { + any_css_expression: ScssExpression { + items: ScssExpressionItemList [ + CssNumber { + value_token: CSS_NUMBER_LITERAL@48..50 "1" [] [Whitespace(" ")], + }, + CssNumber { + value_token: CSS_NUMBER_LITERAL@50..52 "2" [] [Whitespace(" ")], + }, + CssNumber { + value_token: CSS_NUMBER_LITERAL@52..53 "3" [] [], + }, + ], + }, + }, + ], + r_paren_token: R_PAREN@53..54 ")" [] [], + }, + ], + }, + important: missing (optional), + }, + semicolon_token: SEMICOLON@54..55 ";" [] [], + }, + ], + r_curly_token: R_CURLY@55..57 "}" [Newline("\n")] [], + }, + }, + ], + eof_token: EOF@57..58 "" [Newline("\n")] [], +} +``` + +## CST + +``` +0: CSS_ROOT@0..58 + 0: (empty) + 1: CSS_ROOT_ITEM_LIST@0..57 + 0: CSS_QUALIFIED_RULE@0..57 + 0: CSS_SELECTOR_LIST@0..6 + 0: CSS_COMPOUND_SELECTOR@0..6 + 0: CSS_NESTED_SELECTOR_LIST@0..0 + 1: (empty) + 2: CSS_SUB_SELECTOR_LIST@0..6 + 0: CSS_CLASS_SELECTOR@0..6 + 0: DOT@0..1 "." [] [] + 1: CSS_CUSTOM_IDENTIFIER@1..6 + 0: IDENT@1..6 "test" [] [Whitespace(" ")] + 1: CSS_DECLARATION_OR_RULE_BLOCK@6..57 + 0: L_CURLY@6..7 "{" [] [] + 1: CSS_DECLARATION_OR_RULE_LIST@7..55 + 0: CSS_DECLARATION_WITH_SEMICOLON@7..34 + 0: CSS_DECLARATION@7..33 + 0: CSS_GENERIC_PROPERTY@7..33 + 0: CSS_IDENTIFIER@7..15 + 0: IDENT@7..15 "width" [Newline("\n"), Whitespace(" ")] [] + 1: COLON@15..17 ":" [] [Whitespace(" ")] + 2: CSS_GENERIC_COMPONENT_VALUE_LIST@17..33 + 0: CSS_FUNCTION@17..33 + 0: CSS_IDENTIFIER@17..19 + 0: IDENT@17..19 "fn" [] [] + 1: L_PAREN@19..20 "(" [] [] + 2: CSS_PARAMETER_LIST@20..32 + 0: CSS_PARAMETER@20..32 + 0: SCSS_EXPRESSION@20..32 + 0: SCSS_EXPRESSION_ITEM_LIST@20..32 + 0: SCSS_IDENTIFIER@20..27 + 0: DOLLAR@20..21 "$" [] [] + 1: CSS_IDENTIFIER@21..27 + 0: IDENT@21..27 "alpha" [] [Whitespace(" ")] + 1: SCSS_IDENTIFIER@27..32 + 0: DOLLAR@27..28 "$" [] [] + 1: CSS_IDENTIFIER@28..32 + 0: IDENT@28..32 "beta" [] [] + 3: R_PAREN@32..33 ")" [] [] + 1: (empty) + 1: SEMICOLON@33..34 ";" [] [] + 1: CSS_DECLARATION_WITH_SEMICOLON@34..55 + 0: CSS_DECLARATION@34..54 + 0: CSS_GENERIC_PROPERTY@34..54 + 0: CSS_IDENTIFIER@34..43 + 0: IDENT@34..43 "height" [Newline("\n"), Whitespace(" ")] [] + 1: COLON@43..45 ":" [] [Whitespace(" ")] + 2: CSS_GENERIC_COMPONENT_VALUE_LIST@45..54 + 0: CSS_FUNCTION@45..54 + 0: CSS_IDENTIFIER@45..47 + 0: IDENT@45..47 "fn" [] [] + 1: L_PAREN@47..48 "(" [] [] + 2: CSS_PARAMETER_LIST@48..53 + 0: CSS_PARAMETER@48..53 + 0: SCSS_EXPRESSION@48..53 + 0: SCSS_EXPRESSION_ITEM_LIST@48..53 + 0: CSS_NUMBER@48..50 + 0: CSS_NUMBER_LITERAL@48..50 "1" [] [Whitespace(" ")] + 1: CSS_NUMBER@50..52 + 0: CSS_NUMBER_LITERAL@50..52 "2" [] [Whitespace(" ")] + 2: CSS_NUMBER@52..53 + 0: CSS_NUMBER_LITERAL@52..53 "3" [] [] + 3: R_PAREN@53..54 ")" [] [] + 1: (empty) + 1: SEMICOLON@54..55 ";" [] [] + 2: R_CURLY@55..57 "}" [Newline("\n")] [] + 2: EOF@57..58 "" [Newline("\n")] [] + +``` diff --git a/crates/biome_css_parser/tests/css_test_suite/ok/scss/expression/empty-map.scss b/crates/biome_css_parser/tests/css_test_suite/ok/scss/expression/empty-map.scss new file mode 100644 index 000000000000..bab9444d66f9 --- /dev/null +++ b/crates/biome_css_parser/tests/css_test_suite/ok/scss/expression/empty-map.scss @@ -0,0 +1,3 @@ +.test { + colors: fn(()); +} diff --git a/crates/biome_css_parser/tests/css_test_suite/ok/scss/expression/empty-map.scss.snap b/crates/biome_css_parser/tests/css_test_suite/ok/scss/expression/empty-map.scss.snap new file mode 100644 index 000000000000..5412f16d8da8 --- /dev/null +++ b/crates/biome_css_parser/tests/css_test_suite/ok/scss/expression/empty-map.scss.snap @@ -0,0 +1,128 @@ +--- +source: crates/biome_css_parser/tests/spec_test.rs +assertion_line: 208 +expression: snapshot +--- + +## Input + +```css +.test { + colors: fn(()); +} + +``` + + +## AST + +``` +CssRoot { + bom_token: missing (optional), + items: CssRootItemList [ + CssQualifiedRule { + prelude: CssSelectorList [ + CssCompoundSelector { + nesting_selectors: CssNestedSelectorList [], + simple_selector: missing (optional), + sub_selectors: CssSubSelectorList [ + CssClassSelector { + dot_token: DOT@0..1 "." [] [], + name: CssCustomIdentifier { + value_token: IDENT@1..6 "test" [] [Whitespace(" ")], + }, + }, + ], + }, + ], + block: CssDeclarationOrRuleBlock { + l_curly_token: L_CURLY@6..7 "{" [] [], + items: CssDeclarationOrRuleList [ + CssDeclarationWithSemicolon { + declaration: CssDeclaration { + property: CssGenericProperty { + name: CssIdentifier { + value_token: IDENT@7..16 "colors" [Newline("\n"), Whitespace(" ")] [], + }, + colon_token: COLON@16..18 ":" [] [Whitespace(" ")], + value: CssGenericComponentValueList [ + CssFunction { + name: CssIdentifier { + value_token: IDENT@18..20 "fn" [] [], + }, + l_paren_token: L_PAREN@20..21 "(" [] [], + items: CssParameterList [ + CssParameter { + any_css_expression: ScssExpression { + items: ScssExpressionItemList [ + ScssMapExpression { + l_paren_token: L_PAREN@21..22 "(" [] [], + pairs: ScssMapExpressionPairList [], + r_paren_token: R_PAREN@22..23 ")" [] [], + }, + ], + }, + }, + ], + r_paren_token: R_PAREN@23..24 ")" [] [], + }, + ], + }, + important: missing (optional), + }, + semicolon_token: SEMICOLON@24..25 ";" [] [], + }, + ], + r_curly_token: R_CURLY@25..27 "}" [Newline("\n")] [], + }, + }, + ], + eof_token: EOF@27..28 "" [Newline("\n")] [], +} +``` + +## CST + +``` +0: CSS_ROOT@0..28 + 0: (empty) + 1: CSS_ROOT_ITEM_LIST@0..27 + 0: CSS_QUALIFIED_RULE@0..27 + 0: CSS_SELECTOR_LIST@0..6 + 0: CSS_COMPOUND_SELECTOR@0..6 + 0: CSS_NESTED_SELECTOR_LIST@0..0 + 1: (empty) + 2: CSS_SUB_SELECTOR_LIST@0..6 + 0: CSS_CLASS_SELECTOR@0..6 + 0: DOT@0..1 "." [] [] + 1: CSS_CUSTOM_IDENTIFIER@1..6 + 0: IDENT@1..6 "test" [] [Whitespace(" ")] + 1: CSS_DECLARATION_OR_RULE_BLOCK@6..27 + 0: L_CURLY@6..7 "{" [] [] + 1: CSS_DECLARATION_OR_RULE_LIST@7..25 + 0: CSS_DECLARATION_WITH_SEMICOLON@7..25 + 0: CSS_DECLARATION@7..24 + 0: CSS_GENERIC_PROPERTY@7..24 + 0: CSS_IDENTIFIER@7..16 + 0: IDENT@7..16 "colors" [Newline("\n"), Whitespace(" ")] [] + 1: COLON@16..18 ":" [] [Whitespace(" ")] + 2: CSS_GENERIC_COMPONENT_VALUE_LIST@18..24 + 0: CSS_FUNCTION@18..24 + 0: CSS_IDENTIFIER@18..20 + 0: IDENT@18..20 "fn" [] [] + 1: L_PAREN@20..21 "(" [] [] + 2: CSS_PARAMETER_LIST@21..23 + 0: CSS_PARAMETER@21..23 + 0: SCSS_EXPRESSION@21..23 + 0: SCSS_EXPRESSION_ITEM_LIST@21..23 + 0: SCSS_MAP_EXPRESSION@21..23 + 0: L_PAREN@21..22 "(" [] [] + 1: SCSS_MAP_EXPRESSION_PAIR_LIST@22..22 + 2: R_PAREN@22..23 ")" [] [] + 3: R_PAREN@23..24 ")" [] [] + 1: (empty) + 1: SEMICOLON@24..25 ";" [] [] + 2: R_CURLY@25..27 "}" [Newline("\n")] [] + 2: EOF@27..28 "" [Newline("\n")] [] + +``` diff --git a/crates/biome_css_parser/tests/css_test_suite/ok/scss/expression/list-map-paren.scss b/crates/biome_css_parser/tests/css_test_suite/ok/scss/expression/list-map-paren.scss new file mode 100644 index 000000000000..7018277fb80c --- /dev/null +++ b/crates/biome_css_parser/tests/css_test_suite/ok/scss/expression/list-map-paren.scss @@ -0,0 +1,6 @@ +.test { + width: fn((1 2)); + height: fn((1, 2, 3)); + colors: fn((primary: red, secondary: blue)); + empty-map: fn(()); +} diff --git a/crates/biome_css_parser/tests/css_test_suite/ok/scss/expression/list-map-paren.scss.snap b/crates/biome_css_parser/tests/css_test_suite/ok/scss/expression/list-map-paren.scss.snap new file mode 100644 index 000000000000..4a9cc6b10c41 --- /dev/null +++ b/crates/biome_css_parser/tests/css_test_suite/ok/scss/expression/list-map-paren.scss.snap @@ -0,0 +1,420 @@ +--- +source: crates/biome_css_parser/tests/spec_test.rs +assertion_line: 208 +expression: snapshot +--- + +## Input + +```css +.test { + width: fn((1 2)); + height: fn((1, 2, 3)); + colors: fn((primary: red, secondary: blue)); + empty-map: fn(()); +} + +``` + + +## AST + +``` +CssRoot { + bom_token: missing (optional), + items: CssRootItemList [ + CssQualifiedRule { + prelude: CssSelectorList [ + CssCompoundSelector { + nesting_selectors: CssNestedSelectorList [], + simple_selector: missing (optional), + sub_selectors: CssSubSelectorList [ + CssClassSelector { + dot_token: DOT@0..1 "." [] [], + name: CssCustomIdentifier { + value_token: IDENT@1..6 "test" [] [Whitespace(" ")], + }, + }, + ], + }, + ], + block: CssDeclarationOrRuleBlock { + l_curly_token: L_CURLY@6..7 "{" [] [], + items: CssDeclarationOrRuleList [ + CssDeclarationWithSemicolon { + declaration: CssDeclaration { + property: CssGenericProperty { + name: CssIdentifier { + value_token: IDENT@7..15 "width" [Newline("\n"), Whitespace(" ")] [], + }, + colon_token: COLON@15..17 ":" [] [Whitespace(" ")], + value: CssGenericComponentValueList [ + CssFunction { + name: CssIdentifier { + value_token: IDENT@17..19 "fn" [] [], + }, + l_paren_token: L_PAREN@19..20 "(" [] [], + items: CssParameterList [ + CssParameter { + any_css_expression: ScssExpression { + items: ScssExpressionItemList [ + ScssParenthesizedExpression { + l_paren_token: L_PAREN@20..21 "(" [] [], + expression: ScssExpression { + items: ScssExpressionItemList [ + CssNumber { + value_token: CSS_NUMBER_LITERAL@21..23 "1" [] [Whitespace(" ")], + }, + CssNumber { + value_token: CSS_NUMBER_LITERAL@23..24 "2" [] [], + }, + ], + }, + r_paren_token: R_PAREN@24..25 ")" [] [], + }, + ], + }, + }, + ], + r_paren_token: R_PAREN@25..26 ")" [] [], + }, + ], + }, + important: missing (optional), + }, + semicolon_token: SEMICOLON@26..27 ";" [] [], + }, + CssDeclarationWithSemicolon { + declaration: CssDeclaration { + property: CssGenericProperty { + name: CssIdentifier { + value_token: IDENT@27..36 "height" [Newline("\n"), Whitespace(" ")] [], + }, + colon_token: COLON@36..38 ":" [] [Whitespace(" ")], + value: CssGenericComponentValueList [ + CssFunction { + name: CssIdentifier { + value_token: IDENT@38..40 "fn" [] [], + }, + l_paren_token: L_PAREN@40..41 "(" [] [], + items: CssParameterList [ + CssParameter { + any_css_expression: ScssExpression { + items: ScssExpressionItemList [ + ScssParenthesizedExpression { + l_paren_token: L_PAREN@41..42 "(" [] [], + expression: ScssListExpression { + elements: ScssListExpressionElementList [ + ScssListExpressionElement { + value: ScssExpression { + items: ScssExpressionItemList [ + CssNumber { + value_token: CSS_NUMBER_LITERAL@42..43 "1" [] [], + }, + ], + }, + }, + COMMA@43..45 "," [] [Whitespace(" ")], + ScssListExpressionElement { + value: ScssExpression { + items: ScssExpressionItemList [ + CssNumber { + value_token: CSS_NUMBER_LITERAL@45..46 "2" [] [], + }, + ], + }, + }, + COMMA@46..48 "," [] [Whitespace(" ")], + ScssListExpressionElement { + value: ScssExpression { + items: ScssExpressionItemList [ + CssNumber { + value_token: CSS_NUMBER_LITERAL@48..49 "3" [] [], + }, + ], + }, + }, + ], + }, + r_paren_token: R_PAREN@49..50 ")" [] [], + }, + ], + }, + }, + ], + r_paren_token: R_PAREN@50..51 ")" [] [], + }, + ], + }, + important: missing (optional), + }, + semicolon_token: SEMICOLON@51..52 ";" [] [], + }, + CssDeclarationWithSemicolon { + declaration: CssDeclaration { + property: CssGenericProperty { + name: CssIdentifier { + value_token: IDENT@52..61 "colors" [Newline("\n"), Whitespace(" ")] [], + }, + colon_token: COLON@61..63 ":" [] [Whitespace(" ")], + value: CssGenericComponentValueList [ + CssFunction { + name: CssIdentifier { + value_token: IDENT@63..65 "fn" [] [], + }, + l_paren_token: L_PAREN@65..66 "(" [] [], + items: CssParameterList [ + CssParameter { + any_css_expression: ScssExpression { + items: ScssExpressionItemList [ + ScssMapExpression { + l_paren_token: L_PAREN@66..67 "(" [] [], + pairs: ScssMapExpressionPairList [ + ScssMapExpressionPair { + key: ScssExpression { + items: ScssExpressionItemList [ + CssIdentifier { + value_token: IDENT@67..74 "primary" [] [], + }, + ], + }, + colon_token: COLON@74..76 ":" [] [Whitespace(" ")], + value: ScssExpression { + items: ScssExpressionItemList [ + CssIdentifier { + value_token: IDENT@76..79 "red" [] [], + }, + ], + }, + }, + COMMA@79..81 "," [] [Whitespace(" ")], + ScssMapExpressionPair { + key: ScssExpression { + items: ScssExpressionItemList [ + CssIdentifier { + value_token: IDENT@81..90 "secondary" [] [], + }, + ], + }, + colon_token: COLON@90..92 ":" [] [Whitespace(" ")], + value: ScssExpression { + items: ScssExpressionItemList [ + CssIdentifier { + value_token: IDENT@92..96 "blue" [] [], + }, + ], + }, + }, + ], + r_paren_token: R_PAREN@96..97 ")" [] [], + }, + ], + }, + }, + ], + r_paren_token: R_PAREN@97..98 ")" [] [], + }, + ], + }, + important: missing (optional), + }, + semicolon_token: SEMICOLON@98..99 ";" [] [], + }, + CssDeclarationWithSemicolon { + declaration: CssDeclaration { + property: CssGenericProperty { + name: CssIdentifier { + value_token: IDENT@99..111 "empty-map" [Newline("\n"), Whitespace(" ")] [], + }, + colon_token: COLON@111..113 ":" [] [Whitespace(" ")], + value: CssGenericComponentValueList [ + CssFunction { + name: CssIdentifier { + value_token: IDENT@113..115 "fn" [] [], + }, + l_paren_token: L_PAREN@115..116 "(" [] [], + items: CssParameterList [ + CssParameter { + any_css_expression: ScssExpression { + items: ScssExpressionItemList [ + ScssMapExpression { + l_paren_token: L_PAREN@116..117 "(" [] [], + pairs: ScssMapExpressionPairList [], + r_paren_token: R_PAREN@117..118 ")" [] [], + }, + ], + }, + }, + ], + r_paren_token: R_PAREN@118..119 ")" [] [], + }, + ], + }, + important: missing (optional), + }, + semicolon_token: SEMICOLON@119..120 ";" [] [], + }, + ], + r_curly_token: R_CURLY@120..122 "}" [Newline("\n")] [], + }, + }, + ], + eof_token: EOF@122..123 "" [Newline("\n")] [], +} +``` + +## CST + +``` +0: CSS_ROOT@0..123 + 0: (empty) + 1: CSS_ROOT_ITEM_LIST@0..122 + 0: CSS_QUALIFIED_RULE@0..122 + 0: CSS_SELECTOR_LIST@0..6 + 0: CSS_COMPOUND_SELECTOR@0..6 + 0: CSS_NESTED_SELECTOR_LIST@0..0 + 1: (empty) + 2: CSS_SUB_SELECTOR_LIST@0..6 + 0: CSS_CLASS_SELECTOR@0..6 + 0: DOT@0..1 "." [] [] + 1: CSS_CUSTOM_IDENTIFIER@1..6 + 0: IDENT@1..6 "test" [] [Whitespace(" ")] + 1: CSS_DECLARATION_OR_RULE_BLOCK@6..122 + 0: L_CURLY@6..7 "{" [] [] + 1: CSS_DECLARATION_OR_RULE_LIST@7..120 + 0: CSS_DECLARATION_WITH_SEMICOLON@7..27 + 0: CSS_DECLARATION@7..26 + 0: CSS_GENERIC_PROPERTY@7..26 + 0: CSS_IDENTIFIER@7..15 + 0: IDENT@7..15 "width" [Newline("\n"), Whitespace(" ")] [] + 1: COLON@15..17 ":" [] [Whitespace(" ")] + 2: CSS_GENERIC_COMPONENT_VALUE_LIST@17..26 + 0: CSS_FUNCTION@17..26 + 0: CSS_IDENTIFIER@17..19 + 0: IDENT@17..19 "fn" [] [] + 1: L_PAREN@19..20 "(" [] [] + 2: CSS_PARAMETER_LIST@20..25 + 0: CSS_PARAMETER@20..25 + 0: SCSS_EXPRESSION@20..25 + 0: SCSS_EXPRESSION_ITEM_LIST@20..25 + 0: SCSS_PARENTHESIZED_EXPRESSION@20..25 + 0: L_PAREN@20..21 "(" [] [] + 1: SCSS_EXPRESSION@21..24 + 0: SCSS_EXPRESSION_ITEM_LIST@21..24 + 0: CSS_NUMBER@21..23 + 0: CSS_NUMBER_LITERAL@21..23 "1" [] [Whitespace(" ")] + 1: CSS_NUMBER@23..24 + 0: CSS_NUMBER_LITERAL@23..24 "2" [] [] + 2: R_PAREN@24..25 ")" [] [] + 3: R_PAREN@25..26 ")" [] [] + 1: (empty) + 1: SEMICOLON@26..27 ";" [] [] + 1: CSS_DECLARATION_WITH_SEMICOLON@27..52 + 0: CSS_DECLARATION@27..51 + 0: CSS_GENERIC_PROPERTY@27..51 + 0: CSS_IDENTIFIER@27..36 + 0: IDENT@27..36 "height" [Newline("\n"), Whitespace(" ")] [] + 1: COLON@36..38 ":" [] [Whitespace(" ")] + 2: CSS_GENERIC_COMPONENT_VALUE_LIST@38..51 + 0: CSS_FUNCTION@38..51 + 0: CSS_IDENTIFIER@38..40 + 0: IDENT@38..40 "fn" [] [] + 1: L_PAREN@40..41 "(" [] [] + 2: CSS_PARAMETER_LIST@41..50 + 0: CSS_PARAMETER@41..50 + 0: SCSS_EXPRESSION@41..50 + 0: SCSS_EXPRESSION_ITEM_LIST@41..50 + 0: SCSS_PARENTHESIZED_EXPRESSION@41..50 + 0: L_PAREN@41..42 "(" [] [] + 1: SCSS_LIST_EXPRESSION@42..49 + 0: SCSS_LIST_EXPRESSION_ELEMENT_LIST@42..49 + 0: SCSS_LIST_EXPRESSION_ELEMENT@42..43 + 0: SCSS_EXPRESSION@42..43 + 0: SCSS_EXPRESSION_ITEM_LIST@42..43 + 0: CSS_NUMBER@42..43 + 0: CSS_NUMBER_LITERAL@42..43 "1" [] [] + 1: COMMA@43..45 "," [] [Whitespace(" ")] + 2: SCSS_LIST_EXPRESSION_ELEMENT@45..46 + 0: SCSS_EXPRESSION@45..46 + 0: SCSS_EXPRESSION_ITEM_LIST@45..46 + 0: CSS_NUMBER@45..46 + 0: CSS_NUMBER_LITERAL@45..46 "2" [] [] + 3: COMMA@46..48 "," [] [Whitespace(" ")] + 4: SCSS_LIST_EXPRESSION_ELEMENT@48..49 + 0: SCSS_EXPRESSION@48..49 + 0: SCSS_EXPRESSION_ITEM_LIST@48..49 + 0: CSS_NUMBER@48..49 + 0: CSS_NUMBER_LITERAL@48..49 "3" [] [] + 2: R_PAREN@49..50 ")" [] [] + 3: R_PAREN@50..51 ")" [] [] + 1: (empty) + 1: SEMICOLON@51..52 ";" [] [] + 2: CSS_DECLARATION_WITH_SEMICOLON@52..99 + 0: CSS_DECLARATION@52..98 + 0: CSS_GENERIC_PROPERTY@52..98 + 0: CSS_IDENTIFIER@52..61 + 0: IDENT@52..61 "colors" [Newline("\n"), Whitespace(" ")] [] + 1: COLON@61..63 ":" [] [Whitespace(" ")] + 2: CSS_GENERIC_COMPONENT_VALUE_LIST@63..98 + 0: CSS_FUNCTION@63..98 + 0: CSS_IDENTIFIER@63..65 + 0: IDENT@63..65 "fn" [] [] + 1: L_PAREN@65..66 "(" [] [] + 2: CSS_PARAMETER_LIST@66..97 + 0: CSS_PARAMETER@66..97 + 0: SCSS_EXPRESSION@66..97 + 0: SCSS_EXPRESSION_ITEM_LIST@66..97 + 0: SCSS_MAP_EXPRESSION@66..97 + 0: L_PAREN@66..67 "(" [] [] + 1: SCSS_MAP_EXPRESSION_PAIR_LIST@67..96 + 0: SCSS_MAP_EXPRESSION_PAIR@67..79 + 0: SCSS_EXPRESSION@67..74 + 0: SCSS_EXPRESSION_ITEM_LIST@67..74 + 0: CSS_IDENTIFIER@67..74 + 0: IDENT@67..74 "primary" [] [] + 1: COLON@74..76 ":" [] [Whitespace(" ")] + 2: SCSS_EXPRESSION@76..79 + 0: SCSS_EXPRESSION_ITEM_LIST@76..79 + 0: CSS_IDENTIFIER@76..79 + 0: IDENT@76..79 "red" [] [] + 1: COMMA@79..81 "," [] [Whitespace(" ")] + 2: SCSS_MAP_EXPRESSION_PAIR@81..96 + 0: SCSS_EXPRESSION@81..90 + 0: SCSS_EXPRESSION_ITEM_LIST@81..90 + 0: CSS_IDENTIFIER@81..90 + 0: IDENT@81..90 "secondary" [] [] + 1: COLON@90..92 ":" [] [Whitespace(" ")] + 2: SCSS_EXPRESSION@92..96 + 0: SCSS_EXPRESSION_ITEM_LIST@92..96 + 0: CSS_IDENTIFIER@92..96 + 0: IDENT@92..96 "blue" [] [] + 2: R_PAREN@96..97 ")" [] [] + 3: R_PAREN@97..98 ")" [] [] + 1: (empty) + 1: SEMICOLON@98..99 ";" [] [] + 3: CSS_DECLARATION_WITH_SEMICOLON@99..120 + 0: CSS_DECLARATION@99..119 + 0: CSS_GENERIC_PROPERTY@99..119 + 0: CSS_IDENTIFIER@99..111 + 0: IDENT@99..111 "empty-map" [Newline("\n"), Whitespace(" ")] [] + 1: COLON@111..113 ":" [] [Whitespace(" ")] + 2: CSS_GENERIC_COMPONENT_VALUE_LIST@113..119 + 0: CSS_FUNCTION@113..119 + 0: CSS_IDENTIFIER@113..115 + 0: IDENT@113..115 "fn" [] [] + 1: L_PAREN@115..116 "(" [] [] + 2: CSS_PARAMETER_LIST@116..118 + 0: CSS_PARAMETER@116..118 + 0: SCSS_EXPRESSION@116..118 + 0: SCSS_EXPRESSION_ITEM_LIST@116..118 + 0: SCSS_MAP_EXPRESSION@116..118 + 0: L_PAREN@116..117 "(" [] [] + 1: SCSS_MAP_EXPRESSION_PAIR_LIST@117..117 + 2: R_PAREN@117..118 ")" [] [] + 3: R_PAREN@118..119 ")" [] [] + 1: (empty) + 1: SEMICOLON@119..120 ";" [] [] + 2: R_CURLY@120..122 "}" [Newline("\n")] [] + 2: EOF@122..123 "" [Newline("\n")] [] + +``` diff --git a/crates/biome_css_parser/tests/css_test_suite/ok/scss/expression/unary-parenthesized.scss b/crates/biome_css_parser/tests/css_test_suite/ok/scss/expression/unary-parenthesized.scss new file mode 100644 index 000000000000..8787b5b4db0b --- /dev/null +++ b/crates/biome_css_parser/tests/css_test_suite/ok/scss/expression/unary-parenthesized.scss @@ -0,0 +1,4 @@ +.test { + list: fn(-(1 2)); + map: fn(-(primary: red)); +} diff --git a/crates/biome_css_parser/tests/css_test_suite/ok/scss/expression/unary-parenthesized.scss.snap b/crates/biome_css_parser/tests/css_test_suite/ok/scss/expression/unary-parenthesized.scss.snap new file mode 100644 index 000000000000..04191365ab80 --- /dev/null +++ b/crates/biome_css_parser/tests/css_test_suite/ok/scss/expression/unary-parenthesized.scss.snap @@ -0,0 +1,237 @@ +--- +source: crates/biome_css_parser/tests/spec_test.rs +assertion_line: 208 +expression: snapshot +--- + +## Input + +```css +.test { + list: fn(-(1 2)); + map: fn(-(primary: red)); +} + +``` + + +## AST + +``` +CssRoot { + bom_token: missing (optional), + items: CssRootItemList [ + CssQualifiedRule { + prelude: CssSelectorList [ + CssCompoundSelector { + nesting_selectors: CssNestedSelectorList [], + simple_selector: missing (optional), + sub_selectors: CssSubSelectorList [ + CssClassSelector { + dot_token: DOT@0..1 "." [] [], + name: CssCustomIdentifier { + value_token: IDENT@1..6 "test" [] [Whitespace(" ")], + }, + }, + ], + }, + ], + block: CssDeclarationOrRuleBlock { + l_curly_token: L_CURLY@6..7 "{" [] [], + items: CssDeclarationOrRuleList [ + CssDeclarationWithSemicolon { + declaration: CssDeclaration { + property: CssGenericProperty { + name: CssIdentifier { + value_token: IDENT@7..14 "list" [Newline("\n"), Whitespace(" ")] [], + }, + colon_token: COLON@14..16 ":" [] [Whitespace(" ")], + value: CssGenericComponentValueList [ + CssFunction { + name: CssIdentifier { + value_token: IDENT@16..18 "fn" [] [], + }, + l_paren_token: L_PAREN@18..19 "(" [] [], + items: CssParameterList [ + CssParameter { + any_css_expression: CssUnaryExpression { + operator: MINUS@19..20 "-" [] [], + expression: ScssExpression { + items: ScssExpressionItemList [ + ScssParenthesizedExpression { + l_paren_token: L_PAREN@20..21 "(" [] [], + expression: ScssExpression { + items: ScssExpressionItemList [ + CssNumber { + value_token: CSS_NUMBER_LITERAL@21..23 "1" [] [Whitespace(" ")], + }, + CssNumber { + value_token: CSS_NUMBER_LITERAL@23..24 "2" [] [], + }, + ], + }, + r_paren_token: R_PAREN@24..25 ")" [] [], + }, + ], + }, + }, + }, + ], + r_paren_token: R_PAREN@25..26 ")" [] [], + }, + ], + }, + important: missing (optional), + }, + semicolon_token: SEMICOLON@26..27 ";" [] [], + }, + CssDeclarationWithSemicolon { + declaration: CssDeclaration { + property: CssGenericProperty { + name: CssIdentifier { + value_token: IDENT@27..33 "map" [Newline("\n"), Whitespace(" ")] [], + }, + colon_token: COLON@33..35 ":" [] [Whitespace(" ")], + value: CssGenericComponentValueList [ + CssFunction { + name: CssIdentifier { + value_token: IDENT@35..37 "fn" [] [], + }, + l_paren_token: L_PAREN@37..38 "(" [] [], + items: CssParameterList [ + CssParameter { + any_css_expression: CssUnaryExpression { + operator: MINUS@38..39 "-" [] [], + expression: ScssExpression { + items: ScssExpressionItemList [ + ScssMapExpression { + l_paren_token: L_PAREN@39..40 "(" [] [], + pairs: ScssMapExpressionPairList [ + ScssMapExpressionPair { + key: ScssExpression { + items: ScssExpressionItemList [ + CssIdentifier { + value_token: IDENT@40..47 "primary" [] [], + }, + ], + }, + colon_token: COLON@47..49 ":" [] [Whitespace(" ")], + value: ScssExpression { + items: ScssExpressionItemList [ + CssIdentifier { + value_token: IDENT@49..52 "red" [] [], + }, + ], + }, + }, + ], + r_paren_token: R_PAREN@52..53 ")" [] [], + }, + ], + }, + }, + }, + ], + r_paren_token: R_PAREN@53..54 ")" [] [], + }, + ], + }, + important: missing (optional), + }, + semicolon_token: SEMICOLON@54..55 ";" [] [], + }, + ], + r_curly_token: R_CURLY@55..57 "}" [Newline("\n")] [], + }, + }, + ], + eof_token: EOF@57..58 "" [Newline("\n")] [], +} +``` + +## CST + +``` +0: CSS_ROOT@0..58 + 0: (empty) + 1: CSS_ROOT_ITEM_LIST@0..57 + 0: CSS_QUALIFIED_RULE@0..57 + 0: CSS_SELECTOR_LIST@0..6 + 0: CSS_COMPOUND_SELECTOR@0..6 + 0: CSS_NESTED_SELECTOR_LIST@0..0 + 1: (empty) + 2: CSS_SUB_SELECTOR_LIST@0..6 + 0: CSS_CLASS_SELECTOR@0..6 + 0: DOT@0..1 "." [] [] + 1: CSS_CUSTOM_IDENTIFIER@1..6 + 0: IDENT@1..6 "test" [] [Whitespace(" ")] + 1: CSS_DECLARATION_OR_RULE_BLOCK@6..57 + 0: L_CURLY@6..7 "{" [] [] + 1: CSS_DECLARATION_OR_RULE_LIST@7..55 + 0: CSS_DECLARATION_WITH_SEMICOLON@7..27 + 0: CSS_DECLARATION@7..26 + 0: CSS_GENERIC_PROPERTY@7..26 + 0: CSS_IDENTIFIER@7..14 + 0: IDENT@7..14 "list" [Newline("\n"), Whitespace(" ")] [] + 1: COLON@14..16 ":" [] [Whitespace(" ")] + 2: CSS_GENERIC_COMPONENT_VALUE_LIST@16..26 + 0: CSS_FUNCTION@16..26 + 0: CSS_IDENTIFIER@16..18 + 0: IDENT@16..18 "fn" [] [] + 1: L_PAREN@18..19 "(" [] [] + 2: CSS_PARAMETER_LIST@19..25 + 0: CSS_PARAMETER@19..25 + 0: CSS_UNARY_EXPRESSION@19..25 + 0: MINUS@19..20 "-" [] [] + 1: SCSS_EXPRESSION@20..25 + 0: SCSS_EXPRESSION_ITEM_LIST@20..25 + 0: SCSS_PARENTHESIZED_EXPRESSION@20..25 + 0: L_PAREN@20..21 "(" [] [] + 1: SCSS_EXPRESSION@21..24 + 0: SCSS_EXPRESSION_ITEM_LIST@21..24 + 0: CSS_NUMBER@21..23 + 0: CSS_NUMBER_LITERAL@21..23 "1" [] [Whitespace(" ")] + 1: CSS_NUMBER@23..24 + 0: CSS_NUMBER_LITERAL@23..24 "2" [] [] + 2: R_PAREN@24..25 ")" [] [] + 3: R_PAREN@25..26 ")" [] [] + 1: (empty) + 1: SEMICOLON@26..27 ";" [] [] + 1: CSS_DECLARATION_WITH_SEMICOLON@27..55 + 0: CSS_DECLARATION@27..54 + 0: CSS_GENERIC_PROPERTY@27..54 + 0: CSS_IDENTIFIER@27..33 + 0: IDENT@27..33 "map" [Newline("\n"), Whitespace(" ")] [] + 1: COLON@33..35 ":" [] [Whitespace(" ")] + 2: CSS_GENERIC_COMPONENT_VALUE_LIST@35..54 + 0: CSS_FUNCTION@35..54 + 0: CSS_IDENTIFIER@35..37 + 0: IDENT@35..37 "fn" [] [] + 1: L_PAREN@37..38 "(" [] [] + 2: CSS_PARAMETER_LIST@38..53 + 0: CSS_PARAMETER@38..53 + 0: CSS_UNARY_EXPRESSION@38..53 + 0: MINUS@38..39 "-" [] [] + 1: SCSS_EXPRESSION@39..53 + 0: SCSS_EXPRESSION_ITEM_LIST@39..53 + 0: SCSS_MAP_EXPRESSION@39..53 + 0: L_PAREN@39..40 "(" [] [] + 1: SCSS_MAP_EXPRESSION_PAIR_LIST@40..52 + 0: SCSS_MAP_EXPRESSION_PAIR@40..52 + 0: SCSS_EXPRESSION@40..47 + 0: SCSS_EXPRESSION_ITEM_LIST@40..47 + 0: CSS_IDENTIFIER@40..47 + 0: IDENT@40..47 "primary" [] [] + 1: COLON@47..49 ":" [] [Whitespace(" ")] + 2: SCSS_EXPRESSION@49..52 + 0: SCSS_EXPRESSION_ITEM_LIST@49..52 + 0: CSS_IDENTIFIER@49..52 + 0: IDENT@49..52 "red" [] [] + 2: R_PAREN@52..53 ")" [] [] + 3: R_PAREN@53..54 ")" [] [] + 1: (empty) + 1: SEMICOLON@54..55 ";" [] [] + 2: R_CURLY@55..57 "}" [Newline("\n")] [] + 2: EOF@57..58 "" [Newline("\n")] [] + +``` diff --git a/crates/biome_css_parser/tests/css_test_suite/ok/scss/value/qualified-names.scss.snap b/crates/biome_css_parser/tests/css_test_suite/ok/scss/value/qualified-names.scss.snap index 5371ebd4c066..fbeca62da852 100644 --- a/crates/biome_css_parser/tests/css_test_suite/ok/scss/value/qualified-names.scss.snap +++ b/crates/biome_css_parser/tests/css_test_suite/ok/scss/value/qualified-names.scss.snap @@ -1,5 +1,6 @@ --- source: crates/biome_css_parser/tests/spec_test.rs +assertion_line: 208 expression: snapshot --- @@ -64,8 +65,8 @@ CssRoot { l_paren_token: L_PAREN@25..26 "(" [] [], items: CssParameterList [ CssParameter { - any_css_expression: CssListOfComponentValuesExpression { - css_component_value_list: CssComponentValueList [ + any_css_expression: ScssExpression { + items: ScssExpressionItemList [ CssRegularDimension { value_token: CSS_NUMBER_LITERAL@26..28 "10" [] [], unit_token: IDENT@28..30 "px" [] [], @@ -75,8 +76,8 @@ CssRoot { }, COMMA@30..32 "," [] [Whitespace(" ")], CssParameter { - any_css_expression: CssListOfComponentValuesExpression { - css_component_value_list: CssComponentValueList [ + any_css_expression: ScssExpression { + items: ScssExpressionItemList [ CssNumber { value_token: CSS_NUMBER_LITERAL@32..33 "2" [] [], }, @@ -113,8 +114,8 @@ CssRoot { l_paren_token: L_PAREN@55..56 "(" [] [], items: CssParameterList [ CssParameter { - any_css_expression: CssListOfComponentValuesExpression { - css_component_value_list: CssComponentValueList [ + any_css_expression: ScssExpression { + items: ScssExpressionItemList [ CssRegularDimension { value_token: CSS_NUMBER_LITERAL@56..59 "3.5" [] [], unit_token: IDENT@59..61 "px" [] [], @@ -152,8 +153,8 @@ CssRoot { l_paren_token: L_PAREN@80..81 "(" [] [], items: CssParameterList [ CssParameter { - any_css_expression: CssListOfComponentValuesExpression { - css_component_value_list: CssComponentValueList [ + any_css_expression: ScssExpression { + items: ScssExpressionItemList [ ScssIdentifier { dollar_token: DOLLAR@81..82 "$" [] [], name: CssIdentifier { @@ -165,8 +166,8 @@ CssRoot { }, COMMA@85..87 "," [] [Whitespace(" ")], CssParameter { - any_css_expression: CssListOfComponentValuesExpression { - css_component_value_list: CssComponentValueList [ + any_css_expression: ScssExpression { + items: ScssExpressionItemList [ CssIdentifier { value_token: IDENT@87..88 "a" [] [], }, @@ -253,15 +254,15 @@ CssRoot { 1: L_PAREN@25..26 "(" [] [] 2: CSS_PARAMETER_LIST@26..33 0: CSS_PARAMETER@26..30 - 0: CSS_LIST_OF_COMPONENT_VALUES_EXPRESSION@26..30 - 0: CSS_COMPONENT_VALUE_LIST@26..30 + 0: SCSS_EXPRESSION@26..30 + 0: SCSS_EXPRESSION_ITEM_LIST@26..30 0: CSS_REGULAR_DIMENSION@26..30 0: CSS_NUMBER_LITERAL@26..28 "10" [] [] 1: IDENT@28..30 "px" [] [] 1: COMMA@30..32 "," [] [Whitespace(" ")] 2: CSS_PARAMETER@32..33 - 0: CSS_LIST_OF_COMPONENT_VALUES_EXPRESSION@32..33 - 0: CSS_COMPONENT_VALUE_LIST@32..33 + 0: SCSS_EXPRESSION@32..33 + 0: SCSS_EXPRESSION_ITEM_LIST@32..33 0: CSS_NUMBER@32..33 0: CSS_NUMBER_LITERAL@32..33 "2" [] [] 3: R_PAREN@33..34 ")" [] [] @@ -284,8 +285,8 @@ CssRoot { 1: L_PAREN@55..56 "(" [] [] 2: CSS_PARAMETER_LIST@56..61 0: CSS_PARAMETER@56..61 - 0: CSS_LIST_OF_COMPONENT_VALUES_EXPRESSION@56..61 - 0: CSS_COMPONENT_VALUE_LIST@56..61 + 0: SCSS_EXPRESSION@56..61 + 0: SCSS_EXPRESSION_ITEM_LIST@56..61 0: CSS_REGULAR_DIMENSION@56..61 0: CSS_NUMBER_LITERAL@56..59 "3.5" [] [] 1: IDENT@59..61 "px" [] [] @@ -309,16 +310,16 @@ CssRoot { 1: L_PAREN@80..81 "(" [] [] 2: CSS_PARAMETER_LIST@81..88 0: CSS_PARAMETER@81..85 - 0: CSS_LIST_OF_COMPONENT_VALUES_EXPRESSION@81..85 - 0: CSS_COMPONENT_VALUE_LIST@81..85 + 0: SCSS_EXPRESSION@81..85 + 0: SCSS_EXPRESSION_ITEM_LIST@81..85 0: SCSS_IDENTIFIER@81..85 0: DOLLAR@81..82 "$" [] [] 1: CSS_IDENTIFIER@82..85 0: IDENT@82..85 "map" [] [] 1: COMMA@85..87 "," [] [Whitespace(" ")] 2: CSS_PARAMETER@87..88 - 0: CSS_LIST_OF_COMPONENT_VALUES_EXPRESSION@87..88 - 0: CSS_COMPONENT_VALUE_LIST@87..88 + 0: SCSS_EXPRESSION@87..88 + 0: SCSS_EXPRESSION_ITEM_LIST@87..88 0: CSS_IDENTIFIER@87..88 0: IDENT@87..88 "a" [] [] 3: R_PAREN@88..89 ")" [] [] diff --git a/crates/biome_css_syntax/src/generated/kind.rs b/crates/biome_css_syntax/src/generated/kind.rs index 2e54136d5bd8..bdb13548d1ad 100644 --- a/crates/biome_css_syntax/src/generated/kind.rs +++ b/crates/biome_css_syntax/src/generated/kind.rs @@ -536,7 +536,16 @@ pub enum CssSyntaxKind { CSS_FUNCTION_PARAMETER_LIST, CSS_RETURNS_STATEMENT, SCSS_DECLARATION, + SCSS_EXPRESSION, + SCSS_EXPRESSION_ITEM_LIST, + SCSS_LIST_EXPRESSION, + SCSS_LIST_EXPRESSION_ELEMENT, + SCSS_LIST_EXPRESSION_ELEMENT_LIST, + SCSS_MAP_EXPRESSION, + SCSS_MAP_EXPRESSION_PAIR, + SCSS_MAP_EXPRESSION_PAIR_LIST, SCSS_PARENT_SELECTOR_VALUE, + SCSS_PARENTHESIZED_EXPRESSION, SCSS_NESTING_DECLARATION, SCSS_NAMESPACED_IDENTIFIER, SCSS_QUALIFIED_NAME, @@ -713,6 +722,9 @@ impl CssSyntaxKind { | CSS_VALUE_AT_RULE_PROPERTY_LIST | CSS_VALUE_AT_RULE_IMPORT_SPECIFIER_LIST | CSS_FUNCTION_PARAMETER_LIST + | SCSS_EXPRESSION_ITEM_LIST + | SCSS_LIST_EXPRESSION_ELEMENT_LIST + | SCSS_MAP_EXPRESSION_PAIR_LIST | SCSS_VARIABLE_MODIFIER_LIST | TW_APPLY_CLASS_LIST | CSS_UNKNOWN_AT_RULE_COMPONENT_LIST diff --git a/crates/biome_css_syntax/src/generated/macros.rs b/crates/biome_css_syntax/src/generated/macros.rs index c1848813e446..92af5e993e00 100644 --- a/crates/biome_css_syntax/src/generated/macros.rs +++ b/crates/biome_css_syntax/src/generated/macros.rs @@ -917,10 +917,31 @@ macro_rules! map_syntax_node { let $pattern = unsafe { $crate::ScssDeclaration::new_unchecked(node) }; $body } + $crate::CssSyntaxKind::SCSS_EXPRESSION => { + let $pattern = unsafe { $crate::ScssExpression::new_unchecked(node) }; + $body + } $crate::CssSyntaxKind::SCSS_IDENTIFIER => { let $pattern = unsafe { $crate::ScssIdentifier::new_unchecked(node) }; $body } + $crate::CssSyntaxKind::SCSS_LIST_EXPRESSION => { + let $pattern = unsafe { $crate::ScssListExpression::new_unchecked(node) }; + $body + } + $crate::CssSyntaxKind::SCSS_LIST_EXPRESSION_ELEMENT => { + let $pattern = + unsafe { $crate::ScssListExpressionElement::new_unchecked(node) }; + $body + } + $crate::CssSyntaxKind::SCSS_MAP_EXPRESSION => { + let $pattern = unsafe { $crate::ScssMapExpression::new_unchecked(node) }; + $body + } + $crate::CssSyntaxKind::SCSS_MAP_EXPRESSION_PAIR => { + let $pattern = unsafe { $crate::ScssMapExpressionPair::new_unchecked(node) }; + $body + } $crate::CssSyntaxKind::SCSS_NAMESPACED_IDENTIFIER => { let $pattern = unsafe { $crate::ScssNamespacedIdentifier::new_unchecked(node) }; $body @@ -933,6 +954,11 @@ macro_rules! map_syntax_node { let $pattern = unsafe { $crate::ScssParentSelectorValue::new_unchecked(node) }; $body } + $crate::CssSyntaxKind::SCSS_PARENTHESIZED_EXPRESSION => { + let $pattern = + unsafe { $crate::ScssParenthesizedExpression::new_unchecked(node) }; + $body + } $crate::CssSyntaxKind::SCSS_QUALIFIED_NAME => { let $pattern = unsafe { $crate::ScssQualifiedName::new_unchecked(node) }; $body @@ -1306,6 +1332,20 @@ macro_rules! map_syntax_node { unsafe { $crate::CssValueAtRulePropertyList::new_unchecked(node) }; $body } + $crate::CssSyntaxKind::SCSS_EXPRESSION_ITEM_LIST => { + let $pattern = unsafe { $crate::ScssExpressionItemList::new_unchecked(node) }; + $body + } + $crate::CssSyntaxKind::SCSS_LIST_EXPRESSION_ELEMENT_LIST => { + let $pattern = + unsafe { $crate::ScssListExpressionElementList::new_unchecked(node) }; + $body + } + $crate::CssSyntaxKind::SCSS_MAP_EXPRESSION_PAIR_LIST => { + let $pattern = + unsafe { $crate::ScssMapExpressionPairList::new_unchecked(node) }; + $body + } $crate::CssSyntaxKind::SCSS_VARIABLE_MODIFIER_LIST => { let $pattern = unsafe { $crate::ScssVariableModifierList::new_unchecked(node) }; $body diff --git a/crates/biome_css_syntax/src/generated/nodes.rs b/crates/biome_css_syntax/src/generated/nodes.rs index 5a59adf43eca..1a86dbb487e3 100644 --- a/crates/biome_css_syntax/src/generated/nodes.rs +++ b/crates/biome_css_syntax/src/generated/nodes.rs @@ -8808,6 +8808,41 @@ pub struct ScssDeclarationFields { pub semicolon_token: Option, } #[derive(Clone, PartialEq, Eq, Hash)] +pub struct ScssExpression { + pub(crate) syntax: SyntaxNode, +} +impl ScssExpression { + #[doc = r" Create an AstNode from a SyntaxNode without checking its kind"] + #[doc = r""] + #[doc = r" # Safety"] + #[doc = r" This function must be guarded with a call to [AstNode::can_cast]"] + #[doc = r" or a match on [SyntaxNode::kind]"] + #[inline] + pub const unsafe fn new_unchecked(syntax: SyntaxNode) -> Self { + Self { syntax } + } + pub fn as_fields(&self) -> ScssExpressionFields { + ScssExpressionFields { + items: self.items(), + } + } + pub fn items(&self) -> ScssExpressionItemList { + support::list(&self.syntax, 0usize) + } +} +impl Serialize for ScssExpression { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + self.as_fields().serialize(serializer) + } +} +#[derive(Serialize)] +pub struct ScssExpressionFields { + pub items: ScssExpressionItemList, +} +#[derive(Clone, PartialEq, Eq, Hash)] pub struct ScssIdentifier { pub(crate) syntax: SyntaxNode, } @@ -8848,6 +8883,166 @@ pub struct ScssIdentifierFields { pub name: SyntaxResult, } #[derive(Clone, PartialEq, Eq, Hash)] +pub struct ScssListExpression { + pub(crate) syntax: SyntaxNode, +} +impl ScssListExpression { + #[doc = r" Create an AstNode from a SyntaxNode without checking its kind"] + #[doc = r""] + #[doc = r" # Safety"] + #[doc = r" This function must be guarded with a call to [AstNode::can_cast]"] + #[doc = r" or a match on [SyntaxNode::kind]"] + #[inline] + pub const unsafe fn new_unchecked(syntax: SyntaxNode) -> Self { + Self { syntax } + } + pub fn as_fields(&self) -> ScssListExpressionFields { + ScssListExpressionFields { + elements: self.elements(), + } + } + pub fn elements(&self) -> ScssListExpressionElementList { + support::list(&self.syntax, 0usize) + } +} +impl Serialize for ScssListExpression { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + self.as_fields().serialize(serializer) + } +} +#[derive(Serialize)] +pub struct ScssListExpressionFields { + pub elements: ScssListExpressionElementList, +} +#[derive(Clone, PartialEq, Eq, Hash)] +pub struct ScssListExpressionElement { + pub(crate) syntax: SyntaxNode, +} +impl ScssListExpressionElement { + #[doc = r" Create an AstNode from a SyntaxNode without checking its kind"] + #[doc = r""] + #[doc = r" # Safety"] + #[doc = r" This function must be guarded with a call to [AstNode::can_cast]"] + #[doc = r" or a match on [SyntaxNode::kind]"] + #[inline] + pub const unsafe fn new_unchecked(syntax: SyntaxNode) -> Self { + Self { syntax } + } + pub fn as_fields(&self) -> ScssListExpressionElementFields { + ScssListExpressionElementFields { + value: self.value(), + } + } + pub fn value(&self) -> SyntaxResult { + support::required_node(&self.syntax, 0usize) + } +} +impl Serialize for ScssListExpressionElement { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + self.as_fields().serialize(serializer) + } +} +#[derive(Serialize)] +pub struct ScssListExpressionElementFields { + pub value: SyntaxResult, +} +#[derive(Clone, PartialEq, Eq, Hash)] +pub struct ScssMapExpression { + pub(crate) syntax: SyntaxNode, +} +impl ScssMapExpression { + #[doc = r" Create an AstNode from a SyntaxNode without checking its kind"] + #[doc = r""] + #[doc = r" # Safety"] + #[doc = r" This function must be guarded with a call to [AstNode::can_cast]"] + #[doc = r" or a match on [SyntaxNode::kind]"] + #[inline] + pub const unsafe fn new_unchecked(syntax: SyntaxNode) -> Self { + Self { syntax } + } + pub fn as_fields(&self) -> ScssMapExpressionFields { + ScssMapExpressionFields { + l_paren_token: self.l_paren_token(), + pairs: self.pairs(), + r_paren_token: self.r_paren_token(), + } + } + pub fn l_paren_token(&self) -> SyntaxResult { + support::required_token(&self.syntax, 0usize) + } + pub fn pairs(&self) -> ScssMapExpressionPairList { + support::list(&self.syntax, 1usize) + } + pub fn r_paren_token(&self) -> SyntaxResult { + support::required_token(&self.syntax, 2usize) + } +} +impl Serialize for ScssMapExpression { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + self.as_fields().serialize(serializer) + } +} +#[derive(Serialize)] +pub struct ScssMapExpressionFields { + pub l_paren_token: SyntaxResult, + pub pairs: ScssMapExpressionPairList, + pub r_paren_token: SyntaxResult, +} +#[derive(Clone, PartialEq, Eq, Hash)] +pub struct ScssMapExpressionPair { + pub(crate) syntax: SyntaxNode, +} +impl ScssMapExpressionPair { + #[doc = r" Create an AstNode from a SyntaxNode without checking its kind"] + #[doc = r""] + #[doc = r" # Safety"] + #[doc = r" This function must be guarded with a call to [AstNode::can_cast]"] + #[doc = r" or a match on [SyntaxNode::kind]"] + #[inline] + pub const unsafe fn new_unchecked(syntax: SyntaxNode) -> Self { + Self { syntax } + } + pub fn as_fields(&self) -> ScssMapExpressionPairFields { + ScssMapExpressionPairFields { + key: self.key(), + colon_token: self.colon_token(), + value: self.value(), + } + } + pub fn key(&self) -> SyntaxResult { + support::required_node(&self.syntax, 0usize) + } + pub fn colon_token(&self) -> SyntaxResult { + support::required_token(&self.syntax, 1usize) + } + pub fn value(&self) -> SyntaxResult { + support::required_node(&self.syntax, 2usize) + } +} +impl Serialize for ScssMapExpressionPair { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + self.as_fields().serialize(serializer) + } +} +#[derive(Serialize)] +pub struct ScssMapExpressionPairFields { + pub key: SyntaxResult, + pub colon_token: SyntaxResult, + pub value: SyntaxResult, +} +#[derive(Clone, PartialEq, Eq, Hash)] pub struct ScssNamespacedIdentifier { pub(crate) syntax: SyntaxNode, } @@ -8978,6 +9173,51 @@ pub struct ScssParentSelectorValueFields { pub amp_token: SyntaxResult, } #[derive(Clone, PartialEq, Eq, Hash)] +pub struct ScssParenthesizedExpression { + pub(crate) syntax: SyntaxNode, +} +impl ScssParenthesizedExpression { + #[doc = r" Create an AstNode from a SyntaxNode without checking its kind"] + #[doc = r""] + #[doc = r" # Safety"] + #[doc = r" This function must be guarded with a call to [AstNode::can_cast]"] + #[doc = r" or a match on [SyntaxNode::kind]"] + #[inline] + pub const unsafe fn new_unchecked(syntax: SyntaxNode) -> Self { + Self { syntax } + } + pub fn as_fields(&self) -> ScssParenthesizedExpressionFields { + ScssParenthesizedExpressionFields { + l_paren_token: self.l_paren_token(), + expression: self.expression(), + r_paren_token: self.r_paren_token(), + } + } + pub fn l_paren_token(&self) -> SyntaxResult { + support::required_token(&self.syntax, 0usize) + } + pub fn expression(&self) -> SyntaxResult { + support::required_node(&self.syntax, 1usize) + } + pub fn r_paren_token(&self) -> SyntaxResult { + support::required_token(&self.syntax, 2usize) + } +} +impl Serialize for ScssParenthesizedExpression { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + self.as_fields().serialize(serializer) + } +} +#[derive(Serialize)] +pub struct ScssParenthesizedExpressionFields { + pub l_paren_token: SyntaxResult, + pub expression: SyntaxResult, + pub r_paren_token: SyntaxResult, +} +#[derive(Clone, PartialEq, Eq, Hash)] pub struct ScssQualifiedName { pub(crate) syntax: SyntaxNode, } @@ -10731,6 +10971,7 @@ pub enum AnyCssExpression { CssListOfComponentValuesExpression(CssListOfComponentValuesExpression), CssParenthesizedExpression(CssParenthesizedExpression), CssUnaryExpression(CssUnaryExpression), + ScssExpression(ScssExpression), } impl AnyCssExpression { pub fn as_css_binary_expression(&self) -> Option<&CssBinaryExpression> { @@ -10765,6 +11006,12 @@ impl AnyCssExpression { _ => None, } } + pub fn as_scss_expression(&self) -> Option<&ScssExpression> { + match &self { + Self::ScssExpression(item) => Some(item), + _ => None, + } + } } #[derive(Clone, PartialEq, Eq, Hash, Serialize)] pub enum AnyCssFontFamilyName { @@ -12756,6 +13003,86 @@ impl AnyScssDeclarationName { } } #[derive(Clone, PartialEq, Eq, Hash, Serialize)] +pub enum AnyScssExpression { + AnyCssValue(AnyCssValue), + ScssExpression(ScssExpression), + ScssListExpression(ScssListExpression), + ScssMapExpression(ScssMapExpression), + ScssParenthesizedExpression(ScssParenthesizedExpression), +} +impl AnyScssExpression { + pub fn as_any_css_value(&self) -> Option<&AnyCssValue> { + match &self { + Self::AnyCssValue(item) => Some(item), + _ => None, + } + } + pub fn as_scss_expression(&self) -> Option<&ScssExpression> { + match &self { + Self::ScssExpression(item) => Some(item), + _ => None, + } + } + pub fn as_scss_list_expression(&self) -> Option<&ScssListExpression> { + match &self { + Self::ScssListExpression(item) => Some(item), + _ => None, + } + } + pub fn as_scss_map_expression(&self) -> Option<&ScssMapExpression> { + match &self { + Self::ScssMapExpression(item) => Some(item), + _ => None, + } + } + pub fn as_scss_parenthesized_expression(&self) -> Option<&ScssParenthesizedExpression> { + match &self { + Self::ScssParenthesizedExpression(item) => Some(item), + _ => None, + } + } +} +#[derive(Clone, PartialEq, Eq, Hash, Serialize)] +pub enum AnyScssExpressionItem { + AnyCssValue(AnyCssValue), + CssGenericDelimiter(CssGenericDelimiter), + ScssListExpression(ScssListExpression), + ScssMapExpression(ScssMapExpression), + ScssParenthesizedExpression(ScssParenthesizedExpression), +} +impl AnyScssExpressionItem { + pub fn as_any_css_value(&self) -> Option<&AnyCssValue> { + match &self { + Self::AnyCssValue(item) => Some(item), + _ => None, + } + } + pub fn as_css_generic_delimiter(&self) -> Option<&CssGenericDelimiter> { + match &self { + Self::CssGenericDelimiter(item) => Some(item), + _ => None, + } + } + pub fn as_scss_list_expression(&self) -> Option<&ScssListExpression> { + match &self { + Self::ScssListExpression(item) => Some(item), + _ => None, + } + } + pub fn as_scss_map_expression(&self) -> Option<&ScssMapExpression> { + match &self { + Self::ScssMapExpression(item) => Some(item), + _ => None, + } + } + pub fn as_scss_parenthesized_expression(&self) -> Option<&ScssParenthesizedExpression> { + match &self { + Self::ScssParenthesizedExpression(item) => Some(item), + _ => None, + } + } +} +#[derive(Clone, PartialEq, Eq, Hash, Serialize)] pub enum AnyScssModuleMember { CssIdentifier(CssIdentifier), ScssIdentifier(ScssIdentifier), @@ -23540,12 +23867,12 @@ impl From for SyntaxElement { n.syntax.into() } } -impl AstNode for ScssIdentifier { +impl AstNode for ScssExpression { type Language = Language; const KIND_SET: SyntaxKindSet = - SyntaxKindSet::from_raw(RawSyntaxKind(SCSS_IDENTIFIER as u16)); + SyntaxKindSet::from_raw(RawSyntaxKind(SCSS_EXPRESSION as u16)); fn can_cast(kind: SyntaxKind) -> bool { - kind == SCSS_IDENTIFIER + kind == SCSS_EXPRESSION } fn cast(syntax: SyntaxNode) -> Option { if Self::can_cast(syntax.kind()) { @@ -23561,18 +23888,65 @@ impl AstNode for ScssIdentifier { self.syntax } } -impl std::fmt::Debug for ScssIdentifier { +impl std::fmt::Debug for ScssExpression { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { thread_local! { static DEPTH : std :: cell :: Cell < u8 > = const { std :: cell :: Cell :: new (0) } }; let current_depth = DEPTH.get(); let result = if current_depth < 16 { DEPTH.set(current_depth + 1); - f.debug_struct("ScssIdentifier") - .field( - "dollar_token", - &support::DebugSyntaxResult(self.dollar_token()), - ) - .field("name", &support::DebugSyntaxResult(self.name())) + f.debug_struct("ScssExpression") + .field("items", &self.items()) + .finish() + } else { + f.debug_struct("ScssExpression").finish() + }; + DEPTH.set(current_depth); + result + } +} +impl From for SyntaxNode { + fn from(n: ScssExpression) -> Self { + n.syntax + } +} +impl From for SyntaxElement { + fn from(n: ScssExpression) -> Self { + n.syntax.into() + } +} +impl AstNode for ScssIdentifier { + type Language = Language; + const KIND_SET: SyntaxKindSet = + SyntaxKindSet::from_raw(RawSyntaxKind(SCSS_IDENTIFIER as u16)); + fn can_cast(kind: SyntaxKind) -> bool { + kind == SCSS_IDENTIFIER + } + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } + fn into_syntax(self) -> SyntaxNode { + self.syntax + } +} +impl std::fmt::Debug for ScssIdentifier { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + thread_local! { static DEPTH : std :: cell :: Cell < u8 > = const { std :: cell :: Cell :: new (0) } }; + let current_depth = DEPTH.get(); + let result = if current_depth < 16 { + DEPTH.set(current_depth + 1); + f.debug_struct("ScssIdentifier") + .field( + "dollar_token", + &support::DebugSyntaxResult(self.dollar_token()), + ) + .field("name", &support::DebugSyntaxResult(self.name())) .finish() } else { f.debug_struct("ScssIdentifier").finish() @@ -23591,6 +23965,207 @@ impl From for SyntaxElement { n.syntax.into() } } +impl AstNode for ScssListExpression { + type Language = Language; + const KIND_SET: SyntaxKindSet = + SyntaxKindSet::from_raw(RawSyntaxKind(SCSS_LIST_EXPRESSION as u16)); + fn can_cast(kind: SyntaxKind) -> bool { + kind == SCSS_LIST_EXPRESSION + } + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } + fn into_syntax(self) -> SyntaxNode { + self.syntax + } +} +impl std::fmt::Debug for ScssListExpression { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + thread_local! { static DEPTH : std :: cell :: Cell < u8 > = const { std :: cell :: Cell :: new (0) } }; + let current_depth = DEPTH.get(); + let result = if current_depth < 16 { + DEPTH.set(current_depth + 1); + f.debug_struct("ScssListExpression") + .field("elements", &self.elements()) + .finish() + } else { + f.debug_struct("ScssListExpression").finish() + }; + DEPTH.set(current_depth); + result + } +} +impl From for SyntaxNode { + fn from(n: ScssListExpression) -> Self { + n.syntax + } +} +impl From for SyntaxElement { + fn from(n: ScssListExpression) -> Self { + n.syntax.into() + } +} +impl AstNode for ScssListExpressionElement { + type Language = Language; + const KIND_SET: SyntaxKindSet = + SyntaxKindSet::from_raw(RawSyntaxKind(SCSS_LIST_EXPRESSION_ELEMENT as u16)); + fn can_cast(kind: SyntaxKind) -> bool { + kind == SCSS_LIST_EXPRESSION_ELEMENT + } + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } + fn into_syntax(self) -> SyntaxNode { + self.syntax + } +} +impl std::fmt::Debug for ScssListExpressionElement { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + thread_local! { static DEPTH : std :: cell :: Cell < u8 > = const { std :: cell :: Cell :: new (0) } }; + let current_depth = DEPTH.get(); + let result = if current_depth < 16 { + DEPTH.set(current_depth + 1); + f.debug_struct("ScssListExpressionElement") + .field("value", &support::DebugSyntaxResult(self.value())) + .finish() + } else { + f.debug_struct("ScssListExpressionElement").finish() + }; + DEPTH.set(current_depth); + result + } +} +impl From for SyntaxNode { + fn from(n: ScssListExpressionElement) -> Self { + n.syntax + } +} +impl From for SyntaxElement { + fn from(n: ScssListExpressionElement) -> Self { + n.syntax.into() + } +} +impl AstNode for ScssMapExpression { + type Language = Language; + const KIND_SET: SyntaxKindSet = + SyntaxKindSet::from_raw(RawSyntaxKind(SCSS_MAP_EXPRESSION as u16)); + fn can_cast(kind: SyntaxKind) -> bool { + kind == SCSS_MAP_EXPRESSION + } + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } + fn into_syntax(self) -> SyntaxNode { + self.syntax + } +} +impl std::fmt::Debug for ScssMapExpression { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + thread_local! { static DEPTH : std :: cell :: Cell < u8 > = const { std :: cell :: Cell :: new (0) } }; + let current_depth = DEPTH.get(); + let result = if current_depth < 16 { + DEPTH.set(current_depth + 1); + f.debug_struct("ScssMapExpression") + .field( + "l_paren_token", + &support::DebugSyntaxResult(self.l_paren_token()), + ) + .field("pairs", &self.pairs()) + .field( + "r_paren_token", + &support::DebugSyntaxResult(self.r_paren_token()), + ) + .finish() + } else { + f.debug_struct("ScssMapExpression").finish() + }; + DEPTH.set(current_depth); + result + } +} +impl From for SyntaxNode { + fn from(n: ScssMapExpression) -> Self { + n.syntax + } +} +impl From for SyntaxElement { + fn from(n: ScssMapExpression) -> Self { + n.syntax.into() + } +} +impl AstNode for ScssMapExpressionPair { + type Language = Language; + const KIND_SET: SyntaxKindSet = + SyntaxKindSet::from_raw(RawSyntaxKind(SCSS_MAP_EXPRESSION_PAIR as u16)); + fn can_cast(kind: SyntaxKind) -> bool { + kind == SCSS_MAP_EXPRESSION_PAIR + } + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } + fn into_syntax(self) -> SyntaxNode { + self.syntax + } +} +impl std::fmt::Debug for ScssMapExpressionPair { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + thread_local! { static DEPTH : std :: cell :: Cell < u8 > = const { std :: cell :: Cell :: new (0) } }; + let current_depth = DEPTH.get(); + let result = if current_depth < 16 { + DEPTH.set(current_depth + 1); + f.debug_struct("ScssMapExpressionPair") + .field("key", &support::DebugSyntaxResult(self.key())) + .field( + "colon_token", + &support::DebugSyntaxResult(self.colon_token()), + ) + .field("value", &support::DebugSyntaxResult(self.value())) + .finish() + } else { + f.debug_struct("ScssMapExpressionPair").finish() + }; + DEPTH.set(current_depth); + result + } +} +impl From for SyntaxNode { + fn from(n: ScssMapExpressionPair) -> Self { + n.syntax + } +} +impl From for SyntaxElement { + fn from(n: ScssMapExpressionPair) -> Self { + n.syntax.into() + } +} impl AstNode for ScssNamespacedIdentifier { type Language = Language; const KIND_SET: SyntaxKindSet = @@ -23740,6 +24315,61 @@ impl From for SyntaxElement { n.syntax.into() } } +impl AstNode for ScssParenthesizedExpression { + type Language = Language; + const KIND_SET: SyntaxKindSet = + SyntaxKindSet::from_raw(RawSyntaxKind(SCSS_PARENTHESIZED_EXPRESSION as u16)); + fn can_cast(kind: SyntaxKind) -> bool { + kind == SCSS_PARENTHESIZED_EXPRESSION + } + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } + fn into_syntax(self) -> SyntaxNode { + self.syntax + } +} +impl std::fmt::Debug for ScssParenthesizedExpression { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + thread_local! { static DEPTH : std :: cell :: Cell < u8 > = const { std :: cell :: Cell :: new (0) } }; + let current_depth = DEPTH.get(); + let result = if current_depth < 16 { + DEPTH.set(current_depth + 1); + f.debug_struct("ScssParenthesizedExpression") + .field( + "l_paren_token", + &support::DebugSyntaxResult(self.l_paren_token()), + ) + .field("expression", &support::DebugSyntaxResult(self.expression())) + .field( + "r_paren_token", + &support::DebugSyntaxResult(self.r_paren_token()), + ) + .finish() + } else { + f.debug_struct("ScssParenthesizedExpression").finish() + }; + DEPTH.set(current_depth); + result + } +} +impl From for SyntaxNode { + fn from(n: ScssParenthesizedExpression) -> Self { + n.syntax + } +} +impl From for SyntaxElement { + fn from(n: ScssParenthesizedExpression) -> Self { + n.syntax.into() + } +} impl AstNode for ScssQualifiedName { type Language = Language; const KIND_SET: SyntaxKindSet = @@ -27278,13 +27908,19 @@ impl From for AnyCssExpression { Self::CssUnaryExpression(node) } } +impl From for AnyCssExpression { + fn from(node: ScssExpression) -> Self { + Self::ScssExpression(node) + } +} impl AstNode for AnyCssExpression { type Language = Language; const KIND_SET: SyntaxKindSet = CssBinaryExpression::KIND_SET .union(CssCommaSeparatedValue::KIND_SET) .union(CssListOfComponentValuesExpression::KIND_SET) .union(CssParenthesizedExpression::KIND_SET) - .union(CssUnaryExpression::KIND_SET); + .union(CssUnaryExpression::KIND_SET) + .union(ScssExpression::KIND_SET); fn can_cast(kind: SyntaxKind) -> bool { matches!( kind, @@ -27293,6 +27929,7 @@ impl AstNode for AnyCssExpression { | CSS_LIST_OF_COMPONENT_VALUES_EXPRESSION | CSS_PARENTHESIZED_EXPRESSION | CSS_UNARY_EXPRESSION + | SCSS_EXPRESSION ) } fn cast(syntax: SyntaxNode) -> Option { @@ -27310,6 +27947,7 @@ impl AstNode for AnyCssExpression { Self::CssParenthesizedExpression(CssParenthesizedExpression { syntax }) } CSS_UNARY_EXPRESSION => Self::CssUnaryExpression(CssUnaryExpression { syntax }), + SCSS_EXPRESSION => Self::ScssExpression(ScssExpression { syntax }), _ => return None, }; Some(res) @@ -27321,6 +27959,7 @@ impl AstNode for AnyCssExpression { Self::CssListOfComponentValuesExpression(it) => it.syntax(), Self::CssParenthesizedExpression(it) => it.syntax(), Self::CssUnaryExpression(it) => it.syntax(), + Self::ScssExpression(it) => it.syntax(), } } fn into_syntax(self) -> SyntaxNode { @@ -27330,6 +27969,7 @@ impl AstNode for AnyCssExpression { Self::CssListOfComponentValuesExpression(it) => it.into_syntax(), Self::CssParenthesizedExpression(it) => it.into_syntax(), Self::CssUnaryExpression(it) => it.into_syntax(), + Self::ScssExpression(it) => it.into_syntax(), } } } @@ -27341,6 +27981,7 @@ impl std::fmt::Debug for AnyCssExpression { Self::CssListOfComponentValuesExpression(it) => std::fmt::Debug::fmt(it, f), Self::CssParenthesizedExpression(it) => std::fmt::Debug::fmt(it, f), Self::CssUnaryExpression(it) => std::fmt::Debug::fmt(it, f), + Self::ScssExpression(it) => std::fmt::Debug::fmt(it, f), } } } @@ -27352,6 +27993,7 @@ impl From for SyntaxNode { AnyCssExpression::CssListOfComponentValuesExpression(it) => it.into_syntax(), AnyCssExpression::CssParenthesizedExpression(it) => it.into_syntax(), AnyCssExpression::CssUnaryExpression(it) => it.into_syntax(), + AnyCssExpression::ScssExpression(it) => it.into_syntax(), } } } @@ -33073,12 +33715,214 @@ impl From for SyntaxElement { node.into() } } -impl From for AnyScssModuleMember { - fn from(node: CssIdentifier) -> Self { - Self::CssIdentifier(node) +impl From for AnyScssExpression { + fn from(node: ScssExpression) -> Self { + Self::ScssExpression(node) } } -impl From for AnyScssModuleMember { +impl From for AnyScssExpression { + fn from(node: ScssListExpression) -> Self { + Self::ScssListExpression(node) + } +} +impl From for AnyScssExpression { + fn from(node: ScssMapExpression) -> Self { + Self::ScssMapExpression(node) + } +} +impl From for AnyScssExpression { + fn from(node: ScssParenthesizedExpression) -> Self { + Self::ScssParenthesizedExpression(node) + } +} +impl AstNode for AnyScssExpression { + type Language = Language; + const KIND_SET: SyntaxKindSet = AnyCssValue::KIND_SET + .union(ScssExpression::KIND_SET) + .union(ScssListExpression::KIND_SET) + .union(ScssMapExpression::KIND_SET) + .union(ScssParenthesizedExpression::KIND_SET); + fn can_cast(kind: SyntaxKind) -> bool { + match kind { + SCSS_EXPRESSION + | SCSS_LIST_EXPRESSION + | SCSS_MAP_EXPRESSION + | SCSS_PARENTHESIZED_EXPRESSION => true, + k if AnyCssValue::can_cast(k) => true, + _ => false, + } + } + fn cast(syntax: SyntaxNode) -> Option { + let res = match syntax.kind() { + SCSS_EXPRESSION => Self::ScssExpression(ScssExpression { syntax }), + SCSS_LIST_EXPRESSION => Self::ScssListExpression(ScssListExpression { syntax }), + SCSS_MAP_EXPRESSION => Self::ScssMapExpression(ScssMapExpression { syntax }), + SCSS_PARENTHESIZED_EXPRESSION => { + Self::ScssParenthesizedExpression(ScssParenthesizedExpression { syntax }) + } + _ => { + if let Some(any_css_value) = AnyCssValue::cast(syntax) { + return Some(Self::AnyCssValue(any_css_value)); + } + return None; + } + }; + Some(res) + } + fn syntax(&self) -> &SyntaxNode { + match self { + Self::ScssExpression(it) => it.syntax(), + Self::ScssListExpression(it) => it.syntax(), + Self::ScssMapExpression(it) => it.syntax(), + Self::ScssParenthesizedExpression(it) => it.syntax(), + Self::AnyCssValue(it) => it.syntax(), + } + } + fn into_syntax(self) -> SyntaxNode { + match self { + Self::ScssExpression(it) => it.into_syntax(), + Self::ScssListExpression(it) => it.into_syntax(), + Self::ScssMapExpression(it) => it.into_syntax(), + Self::ScssParenthesizedExpression(it) => it.into_syntax(), + Self::AnyCssValue(it) => it.into_syntax(), + } + } +} +impl std::fmt::Debug for AnyScssExpression { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::AnyCssValue(it) => std::fmt::Debug::fmt(it, f), + Self::ScssExpression(it) => std::fmt::Debug::fmt(it, f), + Self::ScssListExpression(it) => std::fmt::Debug::fmt(it, f), + Self::ScssMapExpression(it) => std::fmt::Debug::fmt(it, f), + Self::ScssParenthesizedExpression(it) => std::fmt::Debug::fmt(it, f), + } + } +} +impl From for SyntaxNode { + fn from(n: AnyScssExpression) -> Self { + match n { + AnyScssExpression::AnyCssValue(it) => it.into_syntax(), + AnyScssExpression::ScssExpression(it) => it.into_syntax(), + AnyScssExpression::ScssListExpression(it) => it.into_syntax(), + AnyScssExpression::ScssMapExpression(it) => it.into_syntax(), + AnyScssExpression::ScssParenthesizedExpression(it) => it.into_syntax(), + } + } +} +impl From for SyntaxElement { + fn from(n: AnyScssExpression) -> Self { + let node: SyntaxNode = n.into(); + node.into() + } +} +impl From for AnyScssExpressionItem { + fn from(node: CssGenericDelimiter) -> Self { + Self::CssGenericDelimiter(node) + } +} +impl From for AnyScssExpressionItem { + fn from(node: ScssListExpression) -> Self { + Self::ScssListExpression(node) + } +} +impl From for AnyScssExpressionItem { + fn from(node: ScssMapExpression) -> Self { + Self::ScssMapExpression(node) + } +} +impl From for AnyScssExpressionItem { + fn from(node: ScssParenthesizedExpression) -> Self { + Self::ScssParenthesizedExpression(node) + } +} +impl AstNode for AnyScssExpressionItem { + type Language = Language; + const KIND_SET: SyntaxKindSet = AnyCssValue::KIND_SET + .union(CssGenericDelimiter::KIND_SET) + .union(ScssListExpression::KIND_SET) + .union(ScssMapExpression::KIND_SET) + .union(ScssParenthesizedExpression::KIND_SET); + fn can_cast(kind: SyntaxKind) -> bool { + match kind { + CSS_GENERIC_DELIMITER + | SCSS_LIST_EXPRESSION + | SCSS_MAP_EXPRESSION + | SCSS_PARENTHESIZED_EXPRESSION => true, + k if AnyCssValue::can_cast(k) => true, + _ => false, + } + } + fn cast(syntax: SyntaxNode) -> Option { + let res = match syntax.kind() { + CSS_GENERIC_DELIMITER => Self::CssGenericDelimiter(CssGenericDelimiter { syntax }), + SCSS_LIST_EXPRESSION => Self::ScssListExpression(ScssListExpression { syntax }), + SCSS_MAP_EXPRESSION => Self::ScssMapExpression(ScssMapExpression { syntax }), + SCSS_PARENTHESIZED_EXPRESSION => { + Self::ScssParenthesizedExpression(ScssParenthesizedExpression { syntax }) + } + _ => { + if let Some(any_css_value) = AnyCssValue::cast(syntax) { + return Some(Self::AnyCssValue(any_css_value)); + } + return None; + } + }; + Some(res) + } + fn syntax(&self) -> &SyntaxNode { + match self { + Self::CssGenericDelimiter(it) => it.syntax(), + Self::ScssListExpression(it) => it.syntax(), + Self::ScssMapExpression(it) => it.syntax(), + Self::ScssParenthesizedExpression(it) => it.syntax(), + Self::AnyCssValue(it) => it.syntax(), + } + } + fn into_syntax(self) -> SyntaxNode { + match self { + Self::CssGenericDelimiter(it) => it.into_syntax(), + Self::ScssListExpression(it) => it.into_syntax(), + Self::ScssMapExpression(it) => it.into_syntax(), + Self::ScssParenthesizedExpression(it) => it.into_syntax(), + Self::AnyCssValue(it) => it.into_syntax(), + } + } +} +impl std::fmt::Debug for AnyScssExpressionItem { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::AnyCssValue(it) => std::fmt::Debug::fmt(it, f), + Self::CssGenericDelimiter(it) => std::fmt::Debug::fmt(it, f), + Self::ScssListExpression(it) => std::fmt::Debug::fmt(it, f), + Self::ScssMapExpression(it) => std::fmt::Debug::fmt(it, f), + Self::ScssParenthesizedExpression(it) => std::fmt::Debug::fmt(it, f), + } + } +} +impl From for SyntaxNode { + fn from(n: AnyScssExpressionItem) -> Self { + match n { + AnyScssExpressionItem::AnyCssValue(it) => it.into_syntax(), + AnyScssExpressionItem::CssGenericDelimiter(it) => it.into_syntax(), + AnyScssExpressionItem::ScssListExpression(it) => it.into_syntax(), + AnyScssExpressionItem::ScssMapExpression(it) => it.into_syntax(), + AnyScssExpressionItem::ScssParenthesizedExpression(it) => it.into_syntax(), + } + } +} +impl From for SyntaxElement { + fn from(n: AnyScssExpressionItem) -> Self { + let node: SyntaxNode = n.into(); + node.into() + } +} +impl From for AnyScssModuleMember { + fn from(node: CssIdentifier) -> Self { + Self::CssIdentifier(node) + } +} +impl From for AnyScssModuleMember { fn from(node: ScssIdentifier) -> Self { Self::ScssIdentifier(node) } @@ -33894,6 +34738,16 @@ impl std::fmt::Display for AnyScssDeclarationName { std::fmt::Display::fmt(self.syntax(), f) } } +impl std::fmt::Display for AnyScssExpression { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} +impl std::fmt::Display for AnyScssExpressionItem { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} impl std::fmt::Display for AnyScssModuleMember { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) @@ -34959,11 +35813,36 @@ impl std::fmt::Display for ScssDeclaration { std::fmt::Display::fmt(self.syntax(), f) } } +impl std::fmt::Display for ScssExpression { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} impl std::fmt::Display for ScssIdentifier { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } +impl std::fmt::Display for ScssListExpression { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} +impl std::fmt::Display for ScssListExpressionElement { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} +impl std::fmt::Display for ScssMapExpression { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} +impl std::fmt::Display for ScssMapExpressionPair { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} impl std::fmt::Display for ScssNamespacedIdentifier { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) @@ -34979,6 +35858,11 @@ impl std::fmt::Display for ScssParentSelectorValue { std::fmt::Display::fmt(self.syntax(), f) } } +impl std::fmt::Display for ScssParenthesizedExpression { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} impl std::fmt::Display for ScssQualifiedName { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) @@ -40059,6 +40943,252 @@ impl IntoIterator for &CssValueAtRulePropertyList { } } #[derive(Clone, Eq, PartialEq, Hash)] +pub struct ScssExpressionItemList { + syntax_list: SyntaxList, +} +impl ScssExpressionItemList { + #[doc = r" Create an AstNode from a SyntaxNode without checking its kind"] + #[doc = r""] + #[doc = r" # Safety"] + #[doc = r" This function must be guarded with a call to [AstNode::can_cast]"] + #[doc = r" or a match on [SyntaxNode::kind]"] + #[inline] + pub unsafe fn new_unchecked(syntax: SyntaxNode) -> Self { + Self { + syntax_list: syntax.into_list(), + } + } +} +impl AstNode for ScssExpressionItemList { + type Language = Language; + const KIND_SET: SyntaxKindSet = + SyntaxKindSet::from_raw(RawSyntaxKind(SCSS_EXPRESSION_ITEM_LIST as u16)); + fn can_cast(kind: SyntaxKind) -> bool { + kind == SCSS_EXPRESSION_ITEM_LIST + } + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { + syntax_list: syntax.into_list(), + }) + } else { + None + } + } + fn syntax(&self) -> &SyntaxNode { + self.syntax_list.node() + } + fn into_syntax(self) -> SyntaxNode { + self.syntax_list.into_node() + } +} +impl Serialize for ScssExpressionItemList { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut seq = serializer.serialize_seq(Some(self.len()))?; + for e in self.iter() { + seq.serialize_element(&e)?; + } + seq.end() + } +} +impl AstNodeList for ScssExpressionItemList { + type Language = Language; + type Node = AnyScssExpressionItem; + fn syntax_list(&self) -> &SyntaxList { + &self.syntax_list + } + fn into_syntax_list(self) -> SyntaxList { + self.syntax_list + } +} +impl Debug for ScssExpressionItemList { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.write_str("ScssExpressionItemList ")?; + f.debug_list().entries(self.iter()).finish() + } +} +impl IntoIterator for &ScssExpressionItemList { + type Item = AnyScssExpressionItem; + type IntoIter = AstNodeListIterator; + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} +impl IntoIterator for ScssExpressionItemList { + type Item = AnyScssExpressionItem; + type IntoIter = AstNodeListIterator; + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} +#[derive(Clone, Eq, PartialEq, Hash)] +pub struct ScssListExpressionElementList { + syntax_list: SyntaxList, +} +impl ScssListExpressionElementList { + #[doc = r" Create an AstNode from a SyntaxNode without checking its kind"] + #[doc = r""] + #[doc = r" # Safety"] + #[doc = r" This function must be guarded with a call to [AstNode::can_cast]"] + #[doc = r" or a match on [SyntaxNode::kind]"] + #[inline] + pub unsafe fn new_unchecked(syntax: SyntaxNode) -> Self { + Self { + syntax_list: syntax.into_list(), + } + } +} +impl AstNode for ScssListExpressionElementList { + type Language = Language; + const KIND_SET: SyntaxKindSet = + SyntaxKindSet::from_raw(RawSyntaxKind(SCSS_LIST_EXPRESSION_ELEMENT_LIST as u16)); + fn can_cast(kind: SyntaxKind) -> bool { + kind == SCSS_LIST_EXPRESSION_ELEMENT_LIST + } + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { + syntax_list: syntax.into_list(), + }) + } else { + None + } + } + fn syntax(&self) -> &SyntaxNode { + self.syntax_list.node() + } + fn into_syntax(self) -> SyntaxNode { + self.syntax_list.into_node() + } +} +impl Serialize for ScssListExpressionElementList { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut seq = serializer.serialize_seq(Some(self.len()))?; + for e in self.iter() { + seq.serialize_element(&e)?; + } + seq.end() + } +} +impl AstSeparatedList for ScssListExpressionElementList { + type Language = Language; + type Node = ScssListExpressionElement; + fn syntax_list(&self) -> &SyntaxList { + &self.syntax_list + } + fn into_syntax_list(self) -> SyntaxList { + self.syntax_list + } +} +impl Debug for ScssListExpressionElementList { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.write_str("ScssListExpressionElementList ")?; + f.debug_list().entries(self.elements()).finish() + } +} +impl IntoIterator for ScssListExpressionElementList { + type Item = SyntaxResult; + type IntoIter = AstSeparatedListNodesIterator; + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} +impl IntoIterator for &ScssListExpressionElementList { + type Item = SyntaxResult; + type IntoIter = AstSeparatedListNodesIterator; + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} +#[derive(Clone, Eq, PartialEq, Hash)] +pub struct ScssMapExpressionPairList { + syntax_list: SyntaxList, +} +impl ScssMapExpressionPairList { + #[doc = r" Create an AstNode from a SyntaxNode without checking its kind"] + #[doc = r""] + #[doc = r" # Safety"] + #[doc = r" This function must be guarded with a call to [AstNode::can_cast]"] + #[doc = r" or a match on [SyntaxNode::kind]"] + #[inline] + pub unsafe fn new_unchecked(syntax: SyntaxNode) -> Self { + Self { + syntax_list: syntax.into_list(), + } + } +} +impl AstNode for ScssMapExpressionPairList { + type Language = Language; + const KIND_SET: SyntaxKindSet = + SyntaxKindSet::from_raw(RawSyntaxKind(SCSS_MAP_EXPRESSION_PAIR_LIST as u16)); + fn can_cast(kind: SyntaxKind) -> bool { + kind == SCSS_MAP_EXPRESSION_PAIR_LIST + } + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { + syntax_list: syntax.into_list(), + }) + } else { + None + } + } + fn syntax(&self) -> &SyntaxNode { + self.syntax_list.node() + } + fn into_syntax(self) -> SyntaxNode { + self.syntax_list.into_node() + } +} +impl Serialize for ScssMapExpressionPairList { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut seq = serializer.serialize_seq(Some(self.len()))?; + for e in self.iter() { + seq.serialize_element(&e)?; + } + seq.end() + } +} +impl AstSeparatedList for ScssMapExpressionPairList { + type Language = Language; + type Node = ScssMapExpressionPair; + fn syntax_list(&self) -> &SyntaxList { + &self.syntax_list + } + fn into_syntax_list(self) -> SyntaxList { + self.syntax_list + } +} +impl Debug for ScssMapExpressionPairList { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.write_str("ScssMapExpressionPairList ")?; + f.debug_list().entries(self.elements()).finish() + } +} +impl IntoIterator for ScssMapExpressionPairList { + type Item = SyntaxResult; + type IntoIter = AstSeparatedListNodesIterator; + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} +impl IntoIterator for &ScssMapExpressionPairList { + type Item = SyntaxResult; + type IntoIter = AstSeparatedListNodesIterator; + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} +#[derive(Clone, Eq, PartialEq, Hash)] pub struct ScssVariableModifierList { syntax_list: SyntaxList, } diff --git a/crates/biome_css_syntax/src/generated/nodes_mut.rs b/crates/biome_css_syntax/src/generated/nodes_mut.rs index 20a6c2a410a5..3bd8fda8872c 100644 --- a/crates/biome_css_syntax/src/generated/nodes_mut.rs +++ b/crates/biome_css_syntax/src/generated/nodes_mut.rs @@ -3491,6 +3491,14 @@ impl ScssDeclaration { ) } } +impl ScssExpression { + pub fn with_items(self, element: ScssExpressionItemList) -> Self { + Self::unwrap_cast( + self.syntax + .splice_slots(0usize..=0usize, once(Some(element.into_syntax().into()))), + ) + } +} impl ScssIdentifier { pub fn with_dollar_token(self, element: SyntaxToken) -> Self { Self::unwrap_cast( @@ -3505,6 +3513,62 @@ impl ScssIdentifier { ) } } +impl ScssListExpression { + pub fn with_elements(self, element: ScssListExpressionElementList) -> Self { + Self::unwrap_cast( + self.syntax + .splice_slots(0usize..=0usize, once(Some(element.into_syntax().into()))), + ) + } +} +impl ScssListExpressionElement { + pub fn with_value(self, element: AnyScssExpression) -> Self { + Self::unwrap_cast( + self.syntax + .splice_slots(0usize..=0usize, once(Some(element.into_syntax().into()))), + ) + } +} +impl ScssMapExpression { + pub fn with_l_paren_token(self, element: SyntaxToken) -> Self { + Self::unwrap_cast( + self.syntax + .splice_slots(0usize..=0usize, once(Some(element.into()))), + ) + } + pub fn with_pairs(self, element: ScssMapExpressionPairList) -> Self { + Self::unwrap_cast( + self.syntax + .splice_slots(1usize..=1usize, once(Some(element.into_syntax().into()))), + ) + } + pub fn with_r_paren_token(self, element: SyntaxToken) -> Self { + Self::unwrap_cast( + self.syntax + .splice_slots(2usize..=2usize, once(Some(element.into()))), + ) + } +} +impl ScssMapExpressionPair { + pub fn with_key(self, element: AnyScssExpression) -> Self { + Self::unwrap_cast( + self.syntax + .splice_slots(0usize..=0usize, once(Some(element.into_syntax().into()))), + ) + } + pub fn with_colon_token(self, element: SyntaxToken) -> Self { + Self::unwrap_cast( + self.syntax + .splice_slots(1usize..=1usize, once(Some(element.into()))), + ) + } + pub fn with_value(self, element: AnyScssExpression) -> Self { + Self::unwrap_cast( + self.syntax + .splice_slots(2usize..=2usize, once(Some(element.into_syntax().into()))), + ) + } +} impl ScssNamespacedIdentifier { pub fn with_namespace(self, element: CssIdentifier) -> Self { Self::unwrap_cast( @@ -3559,6 +3623,26 @@ impl ScssParentSelectorValue { ) } } +impl ScssParenthesizedExpression { + pub fn with_l_paren_token(self, element: SyntaxToken) -> Self { + Self::unwrap_cast( + self.syntax + .splice_slots(0usize..=0usize, once(Some(element.into()))), + ) + } + pub fn with_expression(self, element: AnyScssExpression) -> Self { + Self::unwrap_cast( + self.syntax + .splice_slots(1usize..=1usize, once(Some(element.into_syntax().into()))), + ) + } + pub fn with_r_paren_token(self, element: SyntaxToken) -> Self { + Self::unwrap_cast( + self.syntax + .splice_slots(2usize..=2usize, once(Some(element.into()))), + ) + } +} impl ScssQualifiedName { pub fn with_module(self, element: CssIdentifier) -> Self { Self::unwrap_cast( diff --git a/crates/biome_grit_patterns/src/grit_target_language/css_target_language/generated_mappings.rs b/crates/biome_grit_patterns/src/grit_target_language/css_target_language/generated_mappings.rs index ef8c4dab83e6..42c965d4bdd5 100644 --- a/crates/biome_grit_patterns/src/grit_target_language/css_target_language/generated_mappings.rs +++ b/crates/biome_grit_patterns/src/grit_target_language/css_target_language/generated_mappings.rs @@ -275,10 +275,14 @@ pub fn kind_by_name(node_name: &str) -> Option { .iter() .next(), "ScssDeclaration" => lang::ScssDeclaration::KIND_SET.iter().next(), + "ScssExpression" => lang::ScssExpression::KIND_SET.iter().next(), "ScssIdentifier" => lang::ScssIdentifier::KIND_SET.iter().next(), + "ScssMapExpression" => lang::ScssMapExpression::KIND_SET.iter().next(), + "ScssMapExpressionPair" => lang::ScssMapExpressionPair::KIND_SET.iter().next(), "ScssNamespacedIdentifier" => lang::ScssNamespacedIdentifier::KIND_SET.iter().next(), "ScssNestingDeclaration" => lang::ScssNestingDeclaration::KIND_SET.iter().next(), "ScssParentSelectorValue" => lang::ScssParentSelectorValue::KIND_SET.iter().next(), + "ScssParenthesizedExpression" => lang::ScssParenthesizedExpression::KIND_SET.iter().next(), "ScssQualifiedName" => lang::ScssQualifiedName::KIND_SET.iter().next(), "ScssVariableModifier" => lang::ScssVariableModifier::KIND_SET.iter().next(), "TwApplyAtRule" => lang::TwApplyAtRule::KIND_SET.iter().next(), diff --git a/xtask/codegen/css.ungram b/xtask/codegen/css.ungram index 901307ddd162..61bdc361780e 100644 --- a/xtask/codegen/css.ungram +++ b/xtask/codegen/css.ungram @@ -2090,9 +2090,11 @@ AnyCssExpression = | CssUnaryExpression | CssParenthesizedExpression | CssListOfComponentValuesExpression + | ScssExpression | CssCommaSeparatedValue -CssListOfComponentValuesExpression = CssComponentValueList +CssListOfComponentValuesExpression = + CssComponentValueList CssCommaSeparatedValue = '{' @@ -2510,6 +2512,72 @@ ScssNestingDeclaration = value: CssGenericComponentValueList block: AnyCssDeclarationOrRuleBlock +// border: 1px solid $color; +// ^^^^^^^^^^^^^^^^ +ScssExpression = + items: ScssExpressionItemList + +// border: 1px solid $color; +// ^^^^^^^^^^^^^^^^ +ScssExpressionItemList = AnyScssExpressionItem* + +// border: 1px solid $color; +// ^^^^^^^^^^^^^^^^ +AnyScssExpressionItem = + AnyCssValue + | CssGenericDelimiter + | ScssParenthesizedExpression + | ScssListExpression + | ScssMapExpression + +// width: ($a); +// ^^^^ +ScssParenthesizedExpression = + '(' + expression: AnyScssExpression + ')' + +// box-shadow: 1px, 2px, 3px; +// ^^^^^^^^^^^^^ +ScssListExpression = + elements: ScssListExpressionElementList + +// list: 1px, 2px, 3px; +// ^^^^^^^^^^^^^ +ScssListExpressionElementList = (ScssListExpressionElement (',' ScssListExpressionElement)* ','?) + +// list: 1px, 2px, 3px; +// ^^^ ^^^ ^^^ +ScssListExpressionElement = + value: AnyScssExpression + +// $map: (key1: value1, key2: value2); +// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +ScssMapExpression = + '(' + pairs: ScssMapExpressionPairList + ')' + +// $map: (key1: value1, key2: value2); +// ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +ScssMapExpressionPairList = (ScssMapExpressionPair (',' ScssMapExpressionPair)* ','?) + +// $map: (key: value); +// ^^^^^^^^^^ +ScssMapExpressionPair = + key: AnyScssExpression + ':' + value: AnyScssExpression + +// width: $a; +// ^^ +AnyScssExpression = + ScssExpression + | ScssParenthesizedExpression + | ScssListExpression + | ScssMapExpression + | AnyCssValue + // $a: &; // ^ ScssParentSelectorValue = diff --git a/xtask/codegen/src/css_kinds_src.rs b/xtask/codegen/src/css_kinds_src.rs index 30559bf2b62d..a70799fdb9d9 100644 --- a/xtask/codegen/src/css_kinds_src.rs +++ b/xtask/codegen/src/css_kinds_src.rs @@ -567,7 +567,16 @@ pub const CSS_KINDS_SRC: KindsSrc = KindsSrc { "CSS_RETURNS_STATEMENT", // SCSS "SCSS_DECLARATION", + "SCSS_EXPRESSION", + "SCSS_EXPRESSION_ITEM_LIST", + "SCSS_LIST_EXPRESSION", + "SCSS_LIST_EXPRESSION_ELEMENT", + "SCSS_LIST_EXPRESSION_ELEMENT_LIST", + "SCSS_MAP_EXPRESSION", + "SCSS_MAP_EXPRESSION_PAIR", + "SCSS_MAP_EXPRESSION_PAIR_LIST", "SCSS_PARENT_SELECTOR_VALUE", + "SCSS_PARENTHESIZED_EXPRESSION", "SCSS_NESTING_DECLARATION", "SCSS_NAMESPACED_IDENTIFIER", "SCSS_QUALIFIED_NAME",