diff --git a/crates/biome_css_factory/src/generated/node_factory.rs b/crates/biome_css_factory/src/generated/node_factory.rs index c6adb91d6d50..37e78d2b48a4 100644 --- a/crates/biome_css_factory/src/generated/node_factory.rs +++ b/crates/biome_css_factory/src/generated/node_factory.rs @@ -2976,6 +2976,18 @@ pub fn css_view_transition_at_rule_declarator( [Some(SyntaxElement::Token(view_transition_token))], )) } +pub fn scss_arbitrary_argument( + value: AnyScssExpression, + dotdotdot_token: SyntaxToken, +) -> ScssArbitraryArgument { + ScssArbitraryArgument::unwrap_cast(SyntaxNode::new_detached( + CssSyntaxKind::SCSS_ARBITRARY_ARGUMENT, + [ + Some(SyntaxElement::Node(value.into_syntax())), + Some(SyntaxElement::Token(dotdotdot_token)), + ], + )) +} pub fn scss_binary_expression( left: AnyScssExpression, operator_token: SyntaxToken, @@ -3045,6 +3057,20 @@ pub fn scss_identifier(dollar_token: SyntaxToken, name: CssIdentifier) -> ScssId ], )) } +pub fn scss_keyword_argument( + name: ScssIdentifier, + colon_token: SyntaxToken, + value: AnyScssExpression, +) -> ScssKeywordArgument { + ScssKeywordArgument::unwrap_cast(SyntaxNode::new_detached( + CssSyntaxKind::SCSS_KEYWORD_ARGUMENT, + [ + Some(SyntaxElement::Node(name.into_syntax())), + Some(SyntaxElement::Token(colon_token)), + Some(SyntaxElement::Node(value.into_syntax())), + ], + )) +} pub fn scss_list_expression(elements: ScssListExpressionElementList) -> ScssListExpression { ScssListExpression::unwrap_cast(SyntaxNode::new_detached( CssSyntaxKind::SCSS_LIST_EXPRESSION, diff --git a/crates/biome_css_factory/src/generated/syntax_factory.rs b/crates/biome_css_factory/src/generated/syntax_factory.rs index e8192ee0367e..316988b99e70 100644 --- a/crates/biome_css_factory/src/generated/syntax_factory.rs +++ b/crates/biome_css_factory/src/generated/syntax_factory.rs @@ -6120,6 +6120,32 @@ impl SyntaxFactory for CssSyntaxFactory { } slots.into_node(CSS_VIEW_TRANSITION_AT_RULE_DECLARATOR, children) } + SCSS_ARBITRARY_ARGUMENT => { + let mut elements = (&children).into_iter(); + let mut slots: RawNodeSlots<2usize> = RawNodeSlots::default(); + let mut current_element = elements.next(); + if let Some(element) = ¤t_element + && 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_ARBITRARY_ARGUMENT.to_bogus(), + children.into_iter().map(Some), + ); + } + slots.into_node(SCSS_ARBITRARY_ARGUMENT, children) + } SCSS_BINARY_EXPRESSION => { let mut elements = (&children).into_iter(); let mut slots: RawNodeSlots<3usize> = RawNodeSlots::default(); @@ -6260,6 +6286,39 @@ impl SyntaxFactory for CssSyntaxFactory { } slots.into_node(SCSS_IDENTIFIER, children) } + SCSS_KEYWORD_ARGUMENT => { + let mut elements = (&children).into_iter(); + let mut slots: RawNodeSlots<3usize> = RawNodeSlots::default(); + let mut current_element = elements.next(); + if let Some(element) = ¤t_element + && ScssIdentifier::can_cast(element.kind()) + { + slots.mark_present(); + current_element = elements.next(); + } + slots.next_slot(); + if let Some(element) = ¤t_element + && 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_KEYWORD_ARGUMENT.to_bogus(), + children.into_iter().map(Some), + ); + } + slots.into_node(SCSS_KEYWORD_ARGUMENT, children) + } SCSS_LIST_EXPRESSION => { let mut elements = (&children).into_iter(); let mut slots: RawNodeSlots<1usize> = RawNodeSlots::default(); diff --git a/crates/biome_css_formatter/src/generated.rs b/crates/biome_css_formatter/src/generated.rs index 1588fcdd7de2..21cf43c70a21 100644 --- a/crates/biome_css_formatter/src/generated.rs +++ b/crates/biome_css_formatter/src/generated.rs @@ -6996,6 +6996,44 @@ impl IntoFormat for biome_css_syntax::CssViewTransitionAtRuleD FormatOwnedWithRule :: new (self , crate :: css :: auxiliary :: view_transition_at_rule_declarator :: FormatCssViewTransitionAtRuleDeclarator :: default ()) } } +impl FormatRule + for crate::scss::auxiliary::arbitrary_argument::FormatScssArbitraryArgument +{ + type Context = CssFormatContext; + #[inline(always)] + fn fmt( + &self, + node: &biome_css_syntax::ScssArbitraryArgument, + f: &mut CssFormatter, + ) -> FormatResult<()> { + FormatNodeRule::::fmt(self, node, f) + } +} +impl AsFormat for biome_css_syntax::ScssArbitraryArgument { + type Format<'a> = FormatRefWithRule< + 'a, + biome_css_syntax::ScssArbitraryArgument, + crate::scss::auxiliary::arbitrary_argument::FormatScssArbitraryArgument, + >; + fn format(&self) -> Self::Format<'_> { + FormatRefWithRule::new( + self, + crate::scss::auxiliary::arbitrary_argument::FormatScssArbitraryArgument::default(), + ) + } +} +impl IntoFormat for biome_css_syntax::ScssArbitraryArgument { + type Format = FormatOwnedWithRule< + biome_css_syntax::ScssArbitraryArgument, + crate::scss::auxiliary::arbitrary_argument::FormatScssArbitraryArgument, + >; + fn into_format(self) -> Self::Format { + FormatOwnedWithRule::new( + self, + crate::scss::auxiliary::arbitrary_argument::FormatScssArbitraryArgument::default(), + ) + } +} impl FormatRule for crate::scss::auxiliary::binary_expression::FormatScssBinaryExpression { @@ -7148,6 +7186,44 @@ impl IntoFormat for biome_css_syntax::ScssIdentifier { ) } } +impl FormatRule + for crate::scss::auxiliary::keyword_argument::FormatScssKeywordArgument +{ + type Context = CssFormatContext; + #[inline(always)] + fn fmt( + &self, + node: &biome_css_syntax::ScssKeywordArgument, + f: &mut CssFormatter, + ) -> FormatResult<()> { + FormatNodeRule::::fmt(self, node, f) + } +} +impl AsFormat for biome_css_syntax::ScssKeywordArgument { + type Format<'a> = FormatRefWithRule< + 'a, + biome_css_syntax::ScssKeywordArgument, + crate::scss::auxiliary::keyword_argument::FormatScssKeywordArgument, + >; + fn format(&self) -> Self::Format<'_> { + FormatRefWithRule::new( + self, + crate::scss::auxiliary::keyword_argument::FormatScssKeywordArgument::default(), + ) + } +} +impl IntoFormat for biome_css_syntax::ScssKeywordArgument { + type Format = FormatOwnedWithRule< + biome_css_syntax::ScssKeywordArgument, + crate::scss::auxiliary::keyword_argument::FormatScssKeywordArgument, + >; + fn into_format(self) -> Self::Format { + FormatOwnedWithRule::new( + self, + crate::scss::auxiliary::keyword_argument::FormatScssKeywordArgument::default(), + ) + } +} impl FormatRule for crate::scss::auxiliary::list_expression::FormatScssListExpression { diff --git a/crates/biome_css_formatter/src/scss/any/expression.rs b/crates/biome_css_formatter/src/scss/any/expression.rs index 5dcec8550e51..d4c20cd2e188 100644 --- a/crates/biome_css_formatter/src/scss/any/expression.rs +++ b/crates/biome_css_formatter/src/scss/any/expression.rs @@ -9,8 +9,10 @@ impl FormatRule for FormatAnyScssExpression { fn fmt(&self, node: &AnyScssExpression, f: &mut CssFormatter) -> FormatResult<()> { match node { AnyScssExpression::AnyCssValue(node) => node.format().fmt(f), + AnyScssExpression::ScssArbitraryArgument(node) => node.format().fmt(f), AnyScssExpression::ScssBinaryExpression(node) => node.format().fmt(f), AnyScssExpression::ScssExpression(node) => node.format().fmt(f), + AnyScssExpression::ScssKeywordArgument(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 index 06d7021cdea4..985d22827071 100644 --- a/crates/biome_css_formatter/src/scss/any/expression_item.rs +++ b/crates/biome_css_formatter/src/scss/any/expression_item.rs @@ -10,7 +10,9 @@ impl FormatRule for FormatAnyScssExpressionItem { match node { AnyScssExpressionItem::AnyCssValue(node) => node.format().fmt(f), AnyScssExpressionItem::CssGenericDelimiter(node) => node.format().fmt(f), + AnyScssExpressionItem::ScssArbitraryArgument(node) => node.format().fmt(f), AnyScssExpressionItem::ScssBinaryExpression(node) => node.format().fmt(f), + AnyScssExpressionItem::ScssKeywordArgument(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/auxiliary/arbitrary_argument.rs b/crates/biome_css_formatter/src/scss/auxiliary/arbitrary_argument.rs new file mode 100644 index 000000000000..dacb23c264f2 --- /dev/null +++ b/crates/biome_css_formatter/src/scss/auxiliary/arbitrary_argument.rs @@ -0,0 +1,16 @@ +use crate::prelude::*; +use biome_css_syntax::{ScssArbitraryArgument, ScssArbitraryArgumentFields}; +use biome_formatter::write; + +#[derive(Debug, Clone, Default)] +pub(crate) struct FormatScssArbitraryArgument; +impl FormatNodeRule for FormatScssArbitraryArgument { + fn fmt_fields(&self, node: &ScssArbitraryArgument, f: &mut CssFormatter) -> FormatResult<()> { + let ScssArbitraryArgumentFields { + value, + dotdotdot_token, + } = node.as_fields(); + + write!(f, [value.format(), dotdotdot_token.format()]) + } +} diff --git a/crates/biome_css_formatter/src/scss/auxiliary/keyword_argument.rs b/crates/biome_css_formatter/src/scss/auxiliary/keyword_argument.rs new file mode 100644 index 000000000000..8bbfd5c9d1bf --- /dev/null +++ b/crates/biome_css_formatter/src/scss/auxiliary/keyword_argument.rs @@ -0,0 +1,20 @@ +use crate::prelude::*; +use biome_css_syntax::{ScssKeywordArgument, ScssKeywordArgumentFields}; +use biome_formatter::write; + +#[derive(Debug, Clone, Default)] +pub(crate) struct FormatScssKeywordArgument; +impl FormatNodeRule for FormatScssKeywordArgument { + fn fmt_fields(&self, node: &ScssKeywordArgument, f: &mut CssFormatter) -> FormatResult<()> { + let ScssKeywordArgumentFields { + name, + colon_token, + value, + } = node.as_fields(); + + write!( + f, + [name.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 3c752cc56747..51286eef94ca 100644 --- a/crates/biome_css_formatter/src/scss/auxiliary/mod.rs +++ b/crates/biome_css_formatter/src/scss/auxiliary/mod.rs @@ -1,8 +1,10 @@ //! This is a generated file. Don't modify it by hand! Run 'cargo codegen formatter' to re-generate the file. +pub(crate) mod arbitrary_argument; pub(crate) mod binary_expression; pub(crate) mod declaration; pub(crate) mod expression; +pub(crate) mod keyword_argument; pub(crate) mod list_expression; pub(crate) mod list_expression_element; pub(crate) mod map_expression; diff --git a/crates/biome_css_parser/src/lexer/mod.rs b/crates/biome_css_parser/src/lexer/mod.rs index a381cf8868d1..239fb8f2cdc3 100644 --- a/crates/biome_css_parser/src/lexer/mod.rs +++ b/crates/biome_css_parser/src/lexer/mod.rs @@ -96,7 +96,6 @@ pub(crate) struct CssLexer<'src> { diagnostics: Vec, options: CssParserOptions, - source_type: CssFileSource, } @@ -249,6 +248,34 @@ impl<'src> CssLexer<'src> { tok } + /// Returns true when lexing SCSS so SCSS-only tokens (`==`, `!=`, `...`, `//`) are enabled. + /// + /// Example: + /// ```scss + /// @if $a == $b { @include foo($args...); } + /// ``` + /// + /// Docs: https://sass-lang.com/documentation/operators + #[inline] + fn is_scss(&self) -> bool { + self.source_type.is_scss() + } + + /// Enables `//` line-comment lexing only for SCSS (or permissive mode), since + /// plain CSS treats `//` as two delimiters, not a comment. + /// + /// Example: + /// ```scss + /// // overrides + /// $color: red; + /// ``` + /// + /// Docs: https://sass-lang.com/documentation/syntax/comments + #[inline] + fn is_line_comment_enabled(&self) -> bool { + self.options.allow_wrong_line_comments || self.is_scss() + } + /// Get the UTF8 char which starts at the current byte /// /// ## Safety @@ -333,21 +360,11 @@ impl<'src> CssLexer<'src> { } } - PRD => { - if self.is_number_start() { - self.consume_number(current) - } else { - self.consume_byte(T![.]) - } - } + PRD => self.consume_prd(current), LSS => self.consume_lss(), - DOL if self.peek_byte() == Some(b'=') => { - self.advance(1); - self.consume_byte(T!["$="]) - } - DOL => self.consume_byte(T![$]), + DOL => self.consume_dol(), UNI if self.options.is_metavariable_enabled() && self.is_metavariable_start() => { self.consume_metavariable(GRIT_METAVARIABLE) } @@ -673,16 +690,21 @@ impl<'src> CssLexer<'src> { // However we want to parse numbers like `1.` and `1.e10` where we don't have a number after (.) // If the next input code points are U+002E FULL STOP (.)... if matches!(self.current_byte(), Some(b'.')) { - // Consume it. - self.advance(1); + // In SCSS, leave the dot for the ellipsis token (e.g., `10...` or `$args...`). + let is_scss_ellipsis = + self.is_scss() && self.peek_byte() == Some(b'.') && self.byte_at(2) == Some(b'.'); - // U+002E FULL STOP (.) followed by a digit... - if self - .current_byte() - .is_some_and(|byte| byte.is_ascii_digit()) - { - // While the next input code point is a digit, consume it. - self.consume_number_sequence(); + if !is_scss_ellipsis { + // Consume the dot. + self.advance(1); + + // If U+002E FULL STOP (.) is followed by a digit, consume the number sequence. + if self + .current_byte() + .is_some_and(|byte| byte.is_ascii_digit()) + { + self.consume_number_sequence(); + } } } @@ -1120,7 +1142,7 @@ impl<'src> CssLexer<'src> { self.advance(2); let mut has_newline = false; - let is_scss = self.source_type.is_scss(); + let is_scss = self.is_scss(); let mut depth = 1u32; while let Some(chr) = self.current_byte() { @@ -1168,7 +1190,7 @@ impl<'src> CssLexer<'src> { COMMENT } } - Some(b'/') if self.options.allow_wrong_line_comments || self.source_type.is_scss() => { + Some(b'/') if self.is_line_comment_enabled() => { self.advance(2); while let Some(chr) = self.current_byte() { @@ -1185,33 +1207,37 @@ impl<'src> CssLexer<'src> { } #[inline] - fn consume_pipe(&mut self) -> CssSyntaxKind { - self.assert_byte(b'|'); + fn consume_eql(&mut self) -> CssSyntaxKind { + self.assert_byte(b'='); - match self.next_byte() { - Some(b'|') => self.consume_byte(T![||]), - Some(b'=') => self.consume_byte(T![|=]), - _ => T![|], + if self.is_scss() && self.peek_byte() == Some(b'=') { + self.advance(1); + self.consume_byte(T![==]) + } else { + self.consume_byte(T![=]) } } #[inline] - fn consume_eql(&mut self) -> CssSyntaxKind { - self.assert_byte(b'='); + fn consume_exl(&mut self) -> CssSyntaxKind { + self.assert_byte(b'!'); - match self.next_byte() { - Some(b'=') if self.source_type.is_scss() => self.consume_byte(T![==]), - _ => T![=], + if self.is_scss() && self.peek_byte() == Some(b'=') { + self.advance(1); + self.consume_byte(T![!=]) + } else { + self.consume_byte(T![!]) } } #[inline] - fn consume_exl(&mut self) -> CssSyntaxKind { - self.assert_byte(b'!'); + fn consume_pipe(&mut self) -> CssSyntaxKind { + self.assert_byte(b'|'); match self.next_byte() { - Some(b'=') if self.source_type.is_scss() => self.consume_byte(T![!=]), - _ => T![!], + Some(b'|') => self.consume_byte(T![||]), + Some(b'=') => self.consume_byte(T![|=]), + _ => T![|], } } @@ -1265,6 +1291,30 @@ impl<'src> CssLexer<'src> { } } + #[inline] + fn consume_prd(&mut self, current: u8) -> CssSyntaxKind { + self.assert_byte(b'.'); + + if self.is_scss() && self.peek_byte() == Some(b'.') && self.byte_at(2) == Some(b'.') { + self.advance(2); + self.consume_byte(T![...]) + } else if self.is_number_start() { + self.consume_number(current) + } else { + self.consume_byte(T![.]) + } + } + + #[inline] + fn consume_dol(&mut self) -> CssSyntaxKind { + self.assert_byte(b'$'); + + match self.next_byte() { + Some(b'=') => self.consume_byte(T!["$="]), + _ => T![$], + } + } + #[inline] fn consume_lss(&mut self) -> CssSyntaxKind { self.assert_byte(b'<'); diff --git a/crates/biome_css_parser/src/syntax/parse_error.rs b/crates/biome_css_parser/src/syntax/parse_error.rs index fbc712764e40..5190466373e4 100644 --- a/crates/biome_css_parser/src/syntax/parse_error.rs +++ b/crates/biome_css_parser/src/syntax/parse_error.rs @@ -233,6 +233,16 @@ pub(crate) fn expected_scss_expression(p: &CssParser, range: TextRange) -> Parse expected_node("SCSS expression", range, p) } +pub(crate) fn scss_ellipsis_not_allowed(p: &CssParser, range: TextRange) -> ParseDiagnostic { + p.err_builder( + "SCSS arbitrary arguments (`...`) are only allowed in function call arguments.", + range, + ) + .with_hint(markup! { + "Use `...` only for function arguments, for example `fn($args...)`." + }) +} + 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 index 9b9655a298cf..1fb6835f9850 100644 --- a/crates/biome_css_parser/src/syntax/scss/expression/mod.rs +++ b/crates/biome_css_parser/src/syntax/scss/expression/mod.rs @@ -1,11 +1,15 @@ use crate::parser::CssParser; -use crate::syntax::parse_error::{expected_component_value, expected_scss_expression}; +use crate::syntax::parse_error::{ + expected_component_value, expected_scss_expression, scss_ellipsis_not_allowed, +}; use crate::syntax::property::parse_generic_component_value; +use crate::syntax::scss::{is_at_scss_identifier, parse_scss_identifier}; use biome_css_syntax::CssSyntaxKind::{ - CSS_BOGUS_PROPERTY_VALUE, EOF, SCSS_BINARY_EXPRESSION, 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, SCSS_UNARY_EXPRESSION, + CSS_BOGUS_PROPERTY_VALUE, EOF, SCSS_ARBITRARY_ARGUMENT, SCSS_BINARY_EXPRESSION, + SCSS_EXPRESSION, SCSS_EXPRESSION_ITEM_LIST, SCSS_KEYWORD_ARGUMENT, 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, + SCSS_UNARY_EXPRESSION, }; use biome_css_syntax::{CssSyntaxKind, T}; use biome_parser::parse_recovery::ParseRecoveryTokenSet; @@ -40,6 +44,31 @@ const SCSS_LIST_EXPRESSION_ELEMENT_END_TOKEN_SET: TokenSet = pub(crate) const END_OF_SCSS_EXPRESSION_TOKEN_SET: TokenSet = token_set![T![,], T![')'], T![;], T!['}']]; +#[derive(Clone, Copy)] +struct ScssExpressionOptions { + end_ts: TokenSet, + allows_keyword_arguments: bool, + allows_ellipsis: bool, +} + +impl ScssExpressionOptions { + fn value(end_ts: TokenSet) -> Self { + Self { + end_ts, + allows_keyword_arguments: false, + allows_ellipsis: false, + } + } + + fn args(end_ts: TokenSet) -> Self { + Self { + end_ts, + allows_keyword_arguments: true, + allows_ellipsis: true, + } + } +} + #[inline] pub(crate) fn parse_scss_expression(p: &mut CssParser) -> ParsedSyntax { parse_scss_expression_until(p, END_OF_SCSS_EXPRESSION_TOKEN_SET) @@ -50,7 +79,15 @@ pub(crate) fn parse_scss_expression_until( p: &mut CssParser, end_ts: TokenSet, ) -> ParsedSyntax { - parse_scss_expression_with_options(p, end_ts, false) + parse_scss_expression_with_options(p, ScssExpressionOptions::value(end_ts)) +} + +#[inline] +pub(crate) fn parse_scss_expression_in_args_until( + p: &mut CssParser, + end_ts: TokenSet, +) -> ParsedSyntax { + parse_scss_expression_with_options(p, ScssExpressionOptions::args(end_ts)) } #[inline] @@ -58,16 +95,15 @@ fn parse_scss_inner_expression_until( p: &mut CssParser, end_ts: TokenSet, ) -> ParsedSyntax { - parse_scss_expression_with_options(p, end_ts, false) + parse_scss_expression_with_options(p, ScssExpressionOptions::value(end_ts)) } #[inline] fn parse_scss_expression_with_options( p: &mut CssParser, - end_ts: TokenSet, - allow_empty: bool, + options: ScssExpressionOptions, ) -> ParsedSyntax { - if !allow_empty && (p.at_ts(end_ts) || p.at(EOF)) { + if p.at_ts(options.end_ts) || p.at(EOF) { return Absent; } @@ -75,13 +111,14 @@ fn parse_scss_expression_with_options( let expression_items = p.start(); let mut progress = ParserProgress::default(); - while !p.at(EOF) && !p.at_ts(end_ts) { + while !p.at(EOF) && !p.at_ts(options.end_ts) { progress.assert_progressing(p); - if parse_scss_expression_item(p) + let parsed_item = parse_scss_expression_item(p, options); + if parsed_item .or_recover_with_token_set( p, - &ParseRecoveryTokenSet::new(CSS_BOGUS_PROPERTY_VALUE, end_ts) + &ParseRecoveryTokenSet::new(CSS_BOGUS_PROPERTY_VALUE, options.end_ts) .enable_recovery_on_line_break(), expected_scss_expression, ) @@ -96,8 +133,69 @@ fn parse_scss_expression_with_options( } #[inline] -fn parse_scss_expression_item(p: &mut CssParser) -> ParsedSyntax { - parse_scss_binary_expression(p, 0) +fn parse_scss_expression_item(p: &mut CssParser, options: ScssExpressionOptions) -> ParsedSyntax { + if is_at_scss_keyword_argument(p, options) { + return parse_scss_keyword_argument(p, options); + } + + let expression = parse_scss_binary_expression(p, 0); + let expression = match expression { + Present(expression) => expression, + Absent => { + if p.at(T![...]) { + let range = p.cur_range(); + p.error(scss_ellipsis_not_allowed(p, range)); + p.bump(T![...]); + } + return Absent; + } + }; + + if !p.at(T![...]) { + return Present(expression); + } + + if !options.allows_ellipsis { + let range = p.cur_range(); + p.error(scss_ellipsis_not_allowed(p, range)); + p.bump(T![...]); + return Present(expression); + } + + let m = expression.precede(p); + p.bump(T![...]); + Present(m.complete(p, SCSS_ARBITRARY_ARGUMENT)) +} + +#[inline] +fn is_at_scss_keyword_argument(p: &mut CssParser, options: ScssExpressionOptions) -> bool { + options.allows_keyword_arguments + && !options.end_ts.contains(T![:]) + && is_at_scss_identifier(p) + && p.nth_at(2, T![:]) +} + +#[inline] +fn parse_scss_keyword_argument(p: &mut CssParser, options: ScssExpressionOptions) -> ParsedSyntax { + if !is_at_scss_keyword_argument(p, options) { + return Absent; + } + + let m = p.start(); + parse_scss_identifier(p).ok(); + p.expect(T![:]); + + parse_scss_expression_with_options( + p, + ScssExpressionOptions { + end_ts: options.end_ts, + allows_keyword_arguments: false, + allows_ellipsis: false, + }, + ) + .or_add_diagnostic(p, expected_component_value); + + Present(m.complete(p, SCSS_KEYWORD_ARGUMENT)) } #[inline] diff --git a/crates/biome_css_parser/src/syntax/scss/mod.rs b/crates/biome_css_parser/src/syntax/scss/mod.rs index 9ee3ce89e144..a5b0e541e652 100644 --- a/crates/biome_css_parser/src/syntax/scss/mod.rs +++ b/crates/biome_css_parser/src/syntax/scss/mod.rs @@ -17,7 +17,8 @@ pub(crate) use declaration::{ parse_scss_nesting_declaration, }; pub(crate) use expression::{ - SCSS_UNARY_OPERATOR_TOKEN_SET, parse_scss_expression, parse_scss_expression_until, + SCSS_UNARY_OPERATOR_TOKEN_SET, parse_scss_expression, parse_scss_expression_in_args_until, + parse_scss_expression_until, }; #[inline] diff --git a/crates/biome_css_parser/src/syntax/value/function.rs b/crates/biome_css_parser/src/syntax/value/function.rs index 07e90e00bbce..b989f790f79f 100644 --- a/crates/biome_css_parser/src/syntax/value/function.rs +++ b/crates/biome_css_parser/src/syntax/value/function.rs @@ -10,7 +10,7 @@ use crate::syntax::parse_error::{ use crate::syntax::property::parse_generic_component_value; use crate::syntax::scss::{ SCSS_UNARY_OPERATOR_TOKEN_SET, is_at_scss_qualified_name, is_nth_at_scss_qualified_name, - parse_scss_expression, parse_scss_function_name, + parse_scss_expression, parse_scss_expression_in_args_until, 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; @@ -212,7 +212,11 @@ pub(crate) fn parse_parameter(p: &mut CssParser) -> ParsedSyntax { } let param = p.start(); - parse_any_expression(p).ok(); + if CssSyntaxFeatures::Scss.is_supported(p) { + parse_scss_expression_in_args_until(p, token_set![T![,], T![')'], T![;], T!['}']]).ok(); + } else { + parse_any_expression(p).ok(); + } Present(param.complete(p, CSS_PARAMETER)) } @@ -232,13 +236,13 @@ pub(crate) fn parse_any_expression(p: &mut CssParser) -> ParsedSyntax { return Absent; } - let param = if CssSyntaxFeatures::Scss.is_supported(p) + if CssSyntaxFeatures::Scss.is_supported(p) && (is_at_parenthesized(p) || is_at_any_value(p) || p.at_ts(SCSS_UNARY_OPERATOR_TOKEN_SET)) { return parse_scss_expression(p); - } else { - parse_unary_expression_operand(p) - }; + } + + let param = parse_unary_expression_operand(p); if is_at_binary_operator(p) { let binary_expression = param.precede(p); diff --git a/crates/biome_css_parser/tests/css_test_suite/ok/scss/expression/keyword-argument-context.scss b/crates/biome_css_parser/tests/css_test_suite/ok/scss/expression/keyword-argument-context.scss new file mode 100644 index 000000000000..ac1fc2953cc6 --- /dev/null +++ b/crates/biome_css_parser/tests/css_test_suite/ok/scss/expression/keyword-argument-context.scss @@ -0,0 +1,4 @@ +.demo { + first: fn($name: 1); + second: fn(($name: 1), $args...); +} diff --git a/crates/biome_css_parser/tests/css_test_suite/ok/scss/expression/keyword-argument-context.scss.snap b/crates/biome_css_parser/tests/css_test_suite/ok/scss/expression/keyword-argument-context.scss.snap new file mode 100644 index 000000000000..aa73f8722424 --- /dev/null +++ b/crates/biome_css_parser/tests/css_test_suite/ok/scss/expression/keyword-argument-context.scss.snap @@ -0,0 +1,261 @@ +--- +source: crates/biome_css_parser/tests/spec_test.rs +assertion_line: 208 +expression: snapshot +--- + +## Input + +```css +.demo { + first: fn($name: 1); + second: fn(($name: 1), $args...); +} + +``` + + +## 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 "demo" [] [Whitespace(" ")], + }, + }, + ], + }, + ], + block: CssDeclarationOrRuleBlock { + l_curly_token: L_CURLY@6..7 "{" [] [], + items: CssDeclarationOrRuleList [ + CssDeclarationWithSemicolon { + declaration: CssDeclaration { + property: CssGenericProperty { + name: CssIdentifier { + value_token: IDENT@7..15 "first" [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 [ + ScssKeywordArgument { + name: ScssIdentifier { + dollar_token: DOLLAR@20..21 "$" [] [], + name: CssIdentifier { + value_token: IDENT@21..25 "name" [] [], + }, + }, + colon_token: COLON@25..27 ":" [] [Whitespace(" ")], + value: ScssExpression { + items: ScssExpressionItemList [ + CssNumber { + value_token: CSS_NUMBER_LITERAL@27..28 "1" [] [], + }, + ], + }, + }, + ], + }, + }, + ], + r_paren_token: R_PAREN@28..29 ")" [] [], + }, + ], + }, + important: missing (optional), + }, + semicolon_token: SEMICOLON@29..30 ";" [] [], + }, + CssDeclarationWithSemicolon { + declaration: CssDeclaration { + property: CssGenericProperty { + name: CssIdentifier { + value_token: IDENT@30..39 "second" [Newline("\n"), Whitespace(" ")] [], + }, + colon_token: COLON@39..41 ":" [] [Whitespace(" ")], + value: CssGenericComponentValueList [ + CssFunction { + name: CssIdentifier { + value_token: IDENT@41..43 "fn" [] [], + }, + l_paren_token: L_PAREN@43..44 "(" [] [], + items: CssParameterList [ + CssParameter { + any_css_expression: ScssExpression { + items: ScssExpressionItemList [ + ScssMapExpression { + l_paren_token: L_PAREN@44..45 "(" [] [], + pairs: ScssMapExpressionPairList [ + ScssMapExpressionPair { + key: ScssExpression { + items: ScssExpressionItemList [ + ScssIdentifier { + dollar_token: DOLLAR@45..46 "$" [] [], + name: CssIdentifier { + value_token: IDENT@46..50 "name" [] [], + }, + }, + ], + }, + colon_token: COLON@50..52 ":" [] [Whitespace(" ")], + value: ScssExpression { + items: ScssExpressionItemList [ + CssNumber { + value_token: CSS_NUMBER_LITERAL@52..53 "1" [] [], + }, + ], + }, + }, + ], + r_paren_token: R_PAREN@53..54 ")" [] [], + }, + ], + }, + }, + COMMA@54..56 "," [] [Whitespace(" ")], + CssParameter { + any_css_expression: ScssExpression { + items: ScssExpressionItemList [ + ScssArbitraryArgument { + value: ScssIdentifier { + dollar_token: DOLLAR@56..57 "$" [] [], + name: CssIdentifier { + value_token: IDENT@57..61 "args" [] [], + }, + }, + dotdotdot_token: DOT3@61..64 "..." [] [], + }, + ], + }, + }, + ], + r_paren_token: R_PAREN@64..65 ")" [] [], + }, + ], + }, + important: missing (optional), + }, + semicolon_token: SEMICOLON@65..66 ";" [] [], + }, + ], + r_curly_token: R_CURLY@66..68 "}" [Newline("\n")] [], + }, + }, + ], + eof_token: EOF@68..69 "" [Newline("\n")] [], +} +``` + +## CST + +``` +0: CSS_ROOT@0..69 + 0: (empty) + 1: CSS_ROOT_ITEM_LIST@0..68 + 0: CSS_QUALIFIED_RULE@0..68 + 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 "demo" [] [Whitespace(" ")] + 1: CSS_DECLARATION_OR_RULE_BLOCK@6..68 + 0: L_CURLY@6..7 "{" [] [] + 1: CSS_DECLARATION_OR_RULE_LIST@7..66 + 0: CSS_DECLARATION_WITH_SEMICOLON@7..30 + 0: CSS_DECLARATION@7..29 + 0: CSS_GENERIC_PROPERTY@7..29 + 0: CSS_IDENTIFIER@7..15 + 0: IDENT@7..15 "first" [Newline("\n"), Whitespace(" ")] [] + 1: COLON@15..17 ":" [] [Whitespace(" ")] + 2: CSS_GENERIC_COMPONENT_VALUE_LIST@17..29 + 0: CSS_FUNCTION@17..29 + 0: CSS_IDENTIFIER@17..19 + 0: IDENT@17..19 "fn" [] [] + 1: L_PAREN@19..20 "(" [] [] + 2: CSS_PARAMETER_LIST@20..28 + 0: CSS_PARAMETER@20..28 + 0: SCSS_EXPRESSION@20..28 + 0: SCSS_EXPRESSION_ITEM_LIST@20..28 + 0: SCSS_KEYWORD_ARGUMENT@20..28 + 0: SCSS_IDENTIFIER@20..25 + 0: DOLLAR@20..21 "$" [] [] + 1: CSS_IDENTIFIER@21..25 + 0: IDENT@21..25 "name" [] [] + 1: COLON@25..27 ":" [] [Whitespace(" ")] + 2: SCSS_EXPRESSION@27..28 + 0: SCSS_EXPRESSION_ITEM_LIST@27..28 + 0: CSS_NUMBER@27..28 + 0: CSS_NUMBER_LITERAL@27..28 "1" [] [] + 3: R_PAREN@28..29 ")" [] [] + 1: (empty) + 1: SEMICOLON@29..30 ";" [] [] + 1: CSS_DECLARATION_WITH_SEMICOLON@30..66 + 0: CSS_DECLARATION@30..65 + 0: CSS_GENERIC_PROPERTY@30..65 + 0: CSS_IDENTIFIER@30..39 + 0: IDENT@30..39 "second" [Newline("\n"), Whitespace(" ")] [] + 1: COLON@39..41 ":" [] [Whitespace(" ")] + 2: CSS_GENERIC_COMPONENT_VALUE_LIST@41..65 + 0: CSS_FUNCTION@41..65 + 0: CSS_IDENTIFIER@41..43 + 0: IDENT@41..43 "fn" [] [] + 1: L_PAREN@43..44 "(" [] [] + 2: CSS_PARAMETER_LIST@44..64 + 0: CSS_PARAMETER@44..54 + 0: SCSS_EXPRESSION@44..54 + 0: SCSS_EXPRESSION_ITEM_LIST@44..54 + 0: SCSS_MAP_EXPRESSION@44..54 + 0: L_PAREN@44..45 "(" [] [] + 1: SCSS_MAP_EXPRESSION_PAIR_LIST@45..53 + 0: SCSS_MAP_EXPRESSION_PAIR@45..53 + 0: SCSS_EXPRESSION@45..50 + 0: SCSS_EXPRESSION_ITEM_LIST@45..50 + 0: SCSS_IDENTIFIER@45..50 + 0: DOLLAR@45..46 "$" [] [] + 1: CSS_IDENTIFIER@46..50 + 0: IDENT@46..50 "name" [] [] + 1: COLON@50..52 ":" [] [Whitespace(" ")] + 2: SCSS_EXPRESSION@52..53 + 0: SCSS_EXPRESSION_ITEM_LIST@52..53 + 0: CSS_NUMBER@52..53 + 0: CSS_NUMBER_LITERAL@52..53 "1" [] [] + 2: R_PAREN@53..54 ")" [] [] + 1: COMMA@54..56 "," [] [Whitespace(" ")] + 2: CSS_PARAMETER@56..64 + 0: SCSS_EXPRESSION@56..64 + 0: SCSS_EXPRESSION_ITEM_LIST@56..64 + 0: SCSS_ARBITRARY_ARGUMENT@56..64 + 0: SCSS_IDENTIFIER@56..61 + 0: DOLLAR@56..57 "$" [] [] + 1: CSS_IDENTIFIER@57..61 + 0: IDENT@57..61 "args" [] [] + 1: DOT3@61..64 "..." [] [] + 3: R_PAREN@64..65 ")" [] [] + 1: (empty) + 1: SEMICOLON@65..66 ";" [] [] + 2: R_CURLY@66..68 "}" [Newline("\n")] [] + 2: EOF@68..69 "" [Newline("\n")] [] + +``` diff --git a/crates/biome_css_parser/tests/css_test_suite/ok/scss/value/keyword-args-ellipsis.scss b/crates/biome_css_parser/tests/css_test_suite/ok/scss/value/keyword-args-ellipsis.scss new file mode 100644 index 000000000000..0a89186c5d34 --- /dev/null +++ b/crates/biome_css_parser/tests/css_test_suite/ok/scss/value/keyword-args-ellipsis.scss @@ -0,0 +1 @@ +$value: fn($first: 1 + 2, $second: $a and $b, $rest...); diff --git a/crates/biome_css_parser/tests/css_test_suite/ok/scss/value/keyword-args-ellipsis.scss.snap b/crates/biome_css_parser/tests/css_test_suite/ok/scss/value/keyword-args-ellipsis.scss.snap new file mode 100644 index 000000000000..e7a13214fa33 --- /dev/null +++ b/crates/biome_css_parser/tests/css_test_suite/ok/scss/value/keyword-args-ellipsis.scss.snap @@ -0,0 +1,202 @@ +--- +source: crates/biome_css_parser/tests/spec_test.rs +assertion_line: 208 +expression: snapshot +--- + +## Input + +```css +$value: fn($first: 1 + 2, $second: $a and $b, $rest...); + +``` + + +## AST + +``` +CssRoot { + bom_token: missing (optional), + items: CssRootItemList [ + ScssDeclaration { + name: ScssIdentifier { + dollar_token: DOLLAR@0..1 "$" [] [], + name: CssIdentifier { + value_token: IDENT@1..6 "value" [] [], + }, + }, + colon_token: COLON@6..8 ":" [] [Whitespace(" ")], + value: ScssExpression { + items: ScssExpressionItemList [ + CssFunction { + name: CssIdentifier { + value_token: IDENT@8..10 "fn" [] [], + }, + l_paren_token: L_PAREN@10..11 "(" [] [], + items: CssParameterList [ + CssParameter { + any_css_expression: ScssExpression { + items: ScssExpressionItemList [ + ScssKeywordArgument { + name: ScssIdentifier { + dollar_token: DOLLAR@11..12 "$" [] [], + name: CssIdentifier { + value_token: IDENT@12..17 "first" [] [], + }, + }, + colon_token: COLON@17..19 ":" [] [Whitespace(" ")], + value: ScssExpression { + items: ScssExpressionItemList [ + ScssBinaryExpression { + left: CssNumber { + value_token: CSS_NUMBER_LITERAL@19..21 "1" [] [Whitespace(" ")], + }, + operator: PLUS@21..23 "+" [] [Whitespace(" ")], + right: CssNumber { + value_token: CSS_NUMBER_LITERAL@23..24 "2" [] [], + }, + }, + ], + }, + }, + ], + }, + }, + COMMA@24..26 "," [] [Whitespace(" ")], + CssParameter { + any_css_expression: ScssExpression { + items: ScssExpressionItemList [ + ScssKeywordArgument { + name: ScssIdentifier { + dollar_token: DOLLAR@26..27 "$" [] [], + name: CssIdentifier { + value_token: IDENT@27..33 "second" [] [], + }, + }, + colon_token: COLON@33..35 ":" [] [Whitespace(" ")], + value: ScssExpression { + items: ScssExpressionItemList [ + ScssBinaryExpression { + left: ScssIdentifier { + dollar_token: DOLLAR@35..36 "$" [] [], + name: CssIdentifier { + value_token: IDENT@36..38 "a" [] [Whitespace(" ")], + }, + }, + operator: AND_KW@38..42 "and" [] [Whitespace(" ")], + right: ScssIdentifier { + dollar_token: DOLLAR@42..43 "$" [] [], + name: CssIdentifier { + value_token: IDENT@43..44 "b" [] [], + }, + }, + }, + ], + }, + }, + ], + }, + }, + COMMA@44..46 "," [] [Whitespace(" ")], + CssParameter { + any_css_expression: ScssExpression { + items: ScssExpressionItemList [ + ScssArbitraryArgument { + value: ScssIdentifier { + dollar_token: DOLLAR@46..47 "$" [] [], + name: CssIdentifier { + value_token: IDENT@47..51 "rest" [] [], + }, + }, + dotdotdot_token: DOT3@51..54 "..." [] [], + }, + ], + }, + }, + ], + r_paren_token: R_PAREN@54..55 ")" [] [], + }, + ], + }, + modifiers: ScssVariableModifierList [], + semicolon_token: SEMICOLON@55..56 ";" [] [], + }, + ], + eof_token: EOF@56..57 "" [Newline("\n")] [], +} +``` + +## CST + +``` +0: CSS_ROOT@0..57 + 0: (empty) + 1: CSS_ROOT_ITEM_LIST@0..56 + 0: SCSS_DECLARATION@0..56 + 0: SCSS_IDENTIFIER@0..6 + 0: DOLLAR@0..1 "$" [] [] + 1: CSS_IDENTIFIER@1..6 + 0: IDENT@1..6 "value" [] [] + 1: COLON@6..8 ":" [] [Whitespace(" ")] + 2: SCSS_EXPRESSION@8..55 + 0: SCSS_EXPRESSION_ITEM_LIST@8..55 + 0: CSS_FUNCTION@8..55 + 0: CSS_IDENTIFIER@8..10 + 0: IDENT@8..10 "fn" [] [] + 1: L_PAREN@10..11 "(" [] [] + 2: CSS_PARAMETER_LIST@11..54 + 0: CSS_PARAMETER@11..24 + 0: SCSS_EXPRESSION@11..24 + 0: SCSS_EXPRESSION_ITEM_LIST@11..24 + 0: SCSS_KEYWORD_ARGUMENT@11..24 + 0: SCSS_IDENTIFIER@11..17 + 0: DOLLAR@11..12 "$" [] [] + 1: CSS_IDENTIFIER@12..17 + 0: IDENT@12..17 "first" [] [] + 1: COLON@17..19 ":" [] [Whitespace(" ")] + 2: SCSS_EXPRESSION@19..24 + 0: SCSS_EXPRESSION_ITEM_LIST@19..24 + 0: SCSS_BINARY_EXPRESSION@19..24 + 0: CSS_NUMBER@19..21 + 0: CSS_NUMBER_LITERAL@19..21 "1" [] [Whitespace(" ")] + 1: PLUS@21..23 "+" [] [Whitespace(" ")] + 2: CSS_NUMBER@23..24 + 0: CSS_NUMBER_LITERAL@23..24 "2" [] [] + 1: COMMA@24..26 "," [] [Whitespace(" ")] + 2: CSS_PARAMETER@26..44 + 0: SCSS_EXPRESSION@26..44 + 0: SCSS_EXPRESSION_ITEM_LIST@26..44 + 0: SCSS_KEYWORD_ARGUMENT@26..44 + 0: SCSS_IDENTIFIER@26..33 + 0: DOLLAR@26..27 "$" [] [] + 1: CSS_IDENTIFIER@27..33 + 0: IDENT@27..33 "second" [] [] + 1: COLON@33..35 ":" [] [Whitespace(" ")] + 2: SCSS_EXPRESSION@35..44 + 0: SCSS_EXPRESSION_ITEM_LIST@35..44 + 0: SCSS_BINARY_EXPRESSION@35..44 + 0: SCSS_IDENTIFIER@35..38 + 0: DOLLAR@35..36 "$" [] [] + 1: CSS_IDENTIFIER@36..38 + 0: IDENT@36..38 "a" [] [Whitespace(" ")] + 1: AND_KW@38..42 "and" [] [Whitespace(" ")] + 2: SCSS_IDENTIFIER@42..44 + 0: DOLLAR@42..43 "$" [] [] + 1: CSS_IDENTIFIER@43..44 + 0: IDENT@43..44 "b" [] [] + 3: COMMA@44..46 "," [] [Whitespace(" ")] + 4: CSS_PARAMETER@46..54 + 0: SCSS_EXPRESSION@46..54 + 0: SCSS_EXPRESSION_ITEM_LIST@46..54 + 0: SCSS_ARBITRARY_ARGUMENT@46..54 + 0: SCSS_IDENTIFIER@46..51 + 0: DOLLAR@46..47 "$" [] [] + 1: CSS_IDENTIFIER@47..51 + 0: IDENT@47..51 "rest" [] [] + 1: DOT3@51..54 "..." [] [] + 3: R_PAREN@54..55 ")" [] [] + 3: SCSS_VARIABLE_MODIFIER_LIST@55..55 + 4: SEMICOLON@55..56 ";" [] [] + 2: EOF@56..57 "" [Newline("\n")] [] + +``` diff --git a/crates/biome_css_syntax/src/generated/kind.rs b/crates/biome_css_syntax/src/generated/kind.rs index 97c96d6dbc77..e8b005b2ec00 100644 --- a/crates/biome_css_syntax/src/generated/kind.rs +++ b/crates/biome_css_syntax/src/generated/kind.rs @@ -34,6 +34,7 @@ pub enum CssSyntaxKind { CARET, PERCENT, DOT, + DOT3, COLON, COLON2, EQ, @@ -540,6 +541,8 @@ pub enum CssSyntaxKind { SCSS_EXPRESSION, SCSS_EXPRESSION_ITEM_LIST, SCSS_BINARY_EXPRESSION, + SCSS_KEYWORD_ARGUMENT, + SCSS_ARBITRARY_ARGUMENT, SCSS_LIST_EXPRESSION, SCSS_LIST_EXPRESSION_ELEMENT, SCSS_LIST_EXPRESSION_ELEMENT_LIST, @@ -639,6 +642,7 @@ impl CssSyntaxKind { | CARET | PERCENT | DOT + | DOT3 | COLON | COLON2 | EQ @@ -966,6 +970,7 @@ impl CssSyntaxKind { CARET => "^", PERCENT => "%", DOT => ".", + DOT3 => "...", COLON => ":", COLON2 => "::", EQ => "=", @@ -1199,4 +1204,4 @@ impl CssSyntaxKind { } #[doc = r" Utility macro for creating a SyntaxKind through simple macro syntax"] #[macro_export] -macro_rules ! T { [;] => { $ crate :: CssSyntaxKind :: SEMICOLON } ; [,] => { $ crate :: CssSyntaxKind :: COMMA } ; ['('] => { $ crate :: CssSyntaxKind :: L_PAREN } ; [')'] => { $ crate :: CssSyntaxKind :: R_PAREN } ; ['{'] => { $ crate :: CssSyntaxKind :: L_CURLY } ; ['}'] => { $ crate :: CssSyntaxKind :: R_CURLY } ; ['['] => { $ crate :: CssSyntaxKind :: L_BRACK } ; [']'] => { $ crate :: CssSyntaxKind :: R_BRACK } ; [<] => { $ crate :: CssSyntaxKind :: L_ANGLE } ; [>] => { $ crate :: CssSyntaxKind :: R_ANGLE } ; [~] => { $ crate :: CssSyntaxKind :: TILDE } ; [$] => { $ crate :: CssSyntaxKind :: DOLLAR } ; [#] => { $ crate :: CssSyntaxKind :: HASH } ; [&] => { $ crate :: CssSyntaxKind :: AMP } ; [|] => { $ crate :: CssSyntaxKind :: PIPE } ; [||] => { $ crate :: CssSyntaxKind :: PIPE2 } ; [+] => { $ crate :: CssSyntaxKind :: PLUS } ; [*] => { $ crate :: CssSyntaxKind :: STAR } ; [/] => { $ crate :: CssSyntaxKind :: SLASH } ; [^] => { $ crate :: CssSyntaxKind :: CARET } ; [%] => { $ crate :: CssSyntaxKind :: PERCENT } ; [.] => { $ crate :: CssSyntaxKind :: DOT } ; [:] => { $ crate :: CssSyntaxKind :: COLON } ; [::] => { $ crate :: CssSyntaxKind :: COLON2 } ; [=] => { $ crate :: CssSyntaxKind :: EQ } ; [==] => { $ crate :: CssSyntaxKind :: EQ2 } ; [!] => { $ crate :: CssSyntaxKind :: BANG } ; [!=] => { $ crate :: CssSyntaxKind :: NEQ } ; [-] => { $ crate :: CssSyntaxKind :: MINUS } ; [<=] => { $ crate :: CssSyntaxKind :: LTEQ } ; [>=] => { $ crate :: CssSyntaxKind :: GTEQ } ; [+=] => { $ crate :: CssSyntaxKind :: PLUSEQ } ; [|=] => { $ crate :: CssSyntaxKind :: PIPEEQ } ; [&=] => { $ crate :: CssSyntaxKind :: AMPEQ } ; [^=] => { $ crate :: CssSyntaxKind :: CARETEQ } ; [/=] => { $ crate :: CssSyntaxKind :: SLASHEQ } ; [*=] => { $ crate :: CssSyntaxKind :: STAREQ } ; [%=] => { $ crate :: CssSyntaxKind :: PERCENTEQ } ; [@] => { $ crate :: CssSyntaxKind :: AT } ; ["$="] => { $ crate :: CssSyntaxKind :: DOLLAR_EQ } ; [~=] => { $ crate :: CssSyntaxKind :: TILDE_EQ } ; [-->] => { $ crate :: CssSyntaxKind :: CDC } ; [] => { $ crate :: CssSyntaxKind :: CDC } ; [