diff --git a/crates/biome_css_analyze/src/assist/source/use_sorted_properties.rs b/crates/biome_css_analyze/src/assist/source/use_sorted_properties.rs index a5d7ba8eefad..1441b50d23ac 100644 --- a/crates/biome_css_analyze/src/assist/source/use_sorted_properties.rs +++ b/crates/biome_css_analyze/src/assist/source/use_sorted_properties.rs @@ -270,6 +270,13 @@ impl RecessOrderMember { NodeKindOrder::CustomProperty } AnyCssDeclarationName::CssIdentifier(_) => NodeKindOrder::Declaration, + AnyCssDeclarationName::ScssInterpolatedIdentifier(name) => { + if name.syntax().text_trimmed().starts_with("--") { + NodeKindOrder::CustomProperty + } else { + NodeKindOrder::Declaration + } + } AnyCssDeclarationName::TwValueThemeReference(_) => { NodeKindOrder::Declaration } diff --git a/crates/biome_css_analyze/src/lint/correctness/no_unknown_property.rs b/crates/biome_css_analyze/src/lint/correctness/no_unknown_property.rs index bc86ddf070a6..d16e065f76d5 100644 --- a/crates/biome_css_analyze/src/lint/correctness/no_unknown_property.rs +++ b/crates/biome_css_analyze/src/lint/correctness/no_unknown_property.rs @@ -3,8 +3,9 @@ use biome_analyze::{ }; use biome_console::markup; use biome_css_syntax::{ - AnyCssAtRule, CssContainerAtRule, CssFunctionAtRule, CssGenericProperty, CssLayerAtRule, - CssMediaAtRule, CssScopeAtRule, CssStartingStyleAtRule, CssSupportsAtRule, TwApplyAtRule, + AnyCssAtRule, AnyCssDeclarationName, CssContainerAtRule, CssFunctionAtRule, + CssGenericProperty, CssLayerAtRule, CssMediaAtRule, CssScopeAtRule, + CssStartingStyleAtRule, CssSupportsAtRule, TwApplyAtRule, }; use biome_diagnostics::Severity; use biome_rowan::{AstNode, TextRange, declare_node_union}; @@ -117,8 +118,9 @@ impl Rule for NoUnknownProperty { return None; } - let property_name = node.name().ok()?.to_trimmed_text(); - let property_name_lower = property_name.to_ascii_lowercase_cow(); + let property_name = node.name().ok()?; + let property_name_token = declaration_name_value_token(&property_name)?; + let property_name_lower = property_name_token.text_trimmed().to_ascii_lowercase_cow(); let in_function_at_rule = node.syntax().ancestors().skip(1).any(|ancestor| { if CssFunctionAtRule::can_cast(ancestor.kind()) { @@ -140,7 +142,7 @@ impl Rule for NoUnknownProperty { && !vendor_prefixed(&property_name_lower) && !should_ignore(&property_name_lower, ctx.options()) { - return Some(node.name().ok()?.range()); + return Some(property_name.range()); } None } @@ -182,3 +184,16 @@ fn should_ignore(name: &str, options: &NoUnknownPropertyOptions) -> bool { } false } + +fn declaration_name_value_token( + name: &AnyCssDeclarationName, +) -> Option { + match name { + AnyCssDeclarationName::CssDashedIdentifier(name) => name.value_token().ok(), + AnyCssDeclarationName::CssIdentifier(name) => name.value_token().ok(), + AnyCssDeclarationName::TwValueThemeReference(name) => { + name.reference().ok()?.value_token().ok() + } + AnyCssDeclarationName::ScssInterpolatedIdentifier(_) => None, + } +} diff --git a/crates/biome_css_analyze/tests/specs/correctness/noUnknownProperty/valid.tailwind.css b/crates/biome_css_analyze/tests/specs/correctness/noUnknownProperty/valid.tailwind.css index bb3e26684130..b75e62dc3887 100644 --- a/crates/biome_css_analyze/tests/specs/correctness/noUnknownProperty/valid.tailwind.css +++ b/crates/biome_css_analyze/tests/specs/correctness/noUnknownProperty/valid.tailwind.css @@ -5,3 +5,7 @@ allow: "anything"; in: here; } + +.button { + --color-*: initial; +} diff --git a/crates/biome_css_analyze/tests/specs/correctness/noUnknownProperty/valid.tailwind.css.snap b/crates/biome_css_analyze/tests/specs/correctness/noUnknownProperty/valid.tailwind.css.snap index 4519f9458439..53a1d7963340 100644 --- a/crates/biome_css_analyze/tests/specs/correctness/noUnknownProperty/valid.tailwind.css.snap +++ b/crates/biome_css_analyze/tests/specs/correctness/noUnknownProperty/valid.tailwind.css.snap @@ -12,4 +12,8 @@ expression: valid.tailwind.css in: here; } +.button { + --color-*: initial; +} + ``` diff --git a/crates/biome_css_formatter/src/css/any/declaration_name.rs b/crates/biome_css_formatter/src/css/any/declaration_name.rs index 9a4175fa2675..9f01a77e9be8 100644 --- a/crates/biome_css_formatter/src/css/any/declaration_name.rs +++ b/crates/biome_css_formatter/src/css/any/declaration_name.rs @@ -10,6 +10,7 @@ impl FormatRule for FormatAnyCssDeclarationName { match node { AnyCssDeclarationName::CssDashedIdentifier(node) => node.format().fmt(f), AnyCssDeclarationName::CssIdentifier(node) => node.format().fmt(f), + AnyCssDeclarationName::ScssInterpolatedIdentifier(node) => node.format().fmt(f), AnyCssDeclarationName::TwValueThemeReference(node) => node.format().fmt(f), } } diff --git a/crates/biome_css_formatter/tests/specs/css/scss/at-rule/declaration-block-interpolation.scss b/crates/biome_css_formatter/tests/specs/css/scss/at-rule/declaration-block-interpolation.scss new file mode 100644 index 000000000000..0dc1cc67383f --- /dev/null +++ b/crates/biome_css_formatter/tests/specs/css/scss/at-rule/declaration-block-interpolation.scss @@ -0,0 +1,5 @@ +@font-face{ +#{$name}:1px; +margin-#{$side}:1px; +--theme-#{$slot}:red; +} diff --git a/crates/biome_css_formatter/tests/specs/css/scss/at-rule/declaration-block-interpolation.scss.snap b/crates/biome_css_formatter/tests/specs/css/scss/at-rule/declaration-block-interpolation.scss.snap new file mode 100644 index 000000000000..76db5d6a9076 --- /dev/null +++ b/crates/biome_css_formatter/tests/specs/css/scss/at-rule/declaration-block-interpolation.scss.snap @@ -0,0 +1,40 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: css/scss/at-rule/declaration-block-interpolation.scss +--- + +# Input + +```scss +@font-face{ +#{$name}:1px; +margin-#{$side}:1px; +--theme-#{$slot}:red; +} + +``` + + +============================= + +# Outputs + +## Output 1 + +----- +Indent style: Tab +Indent width: 2 +Line ending: LF +Line width: 80 +Quote style: Double Quotes +Trailing newline: true +----- + +```scss +@font-face { + #{$name}: 1px; + margin-#{$side}: 1px; + --theme-#{$slot}: red; +} + +``` diff --git a/crates/biome_css_formatter/tests/specs/css/scss/at-rule/supports-expression.scss b/crates/biome_css_formatter/tests/specs/css/scss/at-rule/supports-expression.scss new file mode 100644 index 000000000000..d7305c432d09 --- /dev/null +++ b/crates/biome_css_formatter/tests/specs/css/scss/at-rule/supports-expression.scss @@ -0,0 +1,35 @@ +$base: 10; + +@import "theme.css" supports( #{$name}:$base + 1); +@import "theme.css" supports( margin-#{$side}:$base + 1); +@import "theme.css" supports(--theme-#{$slot}:red); + +.box { + @supports not ( #{$name}:$base + 1 ) { + width:$base + 1; + } + + @supports ( #{$name}:$base + 1 ) and ( display:grid ) { + width:$base + 1; + } + + @supports ( #{$name}:$base + 1 ) or ( margin-#{$side}:$base + 1 ) { + width:$base + 1; + } + + @supports (( #{$name}:$base + 1 ) and (--theme-#{$slot}:red)) { + color:red; + } + + @supports ( margin-#{$side}:$base + 1 ) { + width:$base + 1; + } + + @supports ( --theme-#{$slot}:red ) { + color:red; + } + + @supports selector( .foo-#{$name} ) { + color:red; + } +} diff --git a/crates/biome_css_formatter/tests/specs/css/scss/at-rule/supports-expression.scss.snap b/crates/biome_css_formatter/tests/specs/css/scss/at-rule/supports-expression.scss.snap new file mode 100644 index 000000000000..b31fb6078786 --- /dev/null +++ b/crates/biome_css_formatter/tests/specs/css/scss/at-rule/supports-expression.scss.snap @@ -0,0 +1,100 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: css/scss/at-rule/supports-expression.scss +--- + +# Input + +```scss +$base: 10; + +@import "theme.css" supports( #{$name}:$base + 1); +@import "theme.css" supports( margin-#{$side}:$base + 1); +@import "theme.css" supports(--theme-#{$slot}:red); + +.box { + @supports not ( #{$name}:$base + 1 ) { + width:$base + 1; + } + + @supports ( #{$name}:$base + 1 ) and ( display:grid ) { + width:$base + 1; + } + + @supports ( #{$name}:$base + 1 ) or ( margin-#{$side}:$base + 1 ) { + width:$base + 1; + } + + @supports (( #{$name}:$base + 1 ) and (--theme-#{$slot}:red)) { + color:red; + } + + @supports ( margin-#{$side}:$base + 1 ) { + width:$base + 1; + } + + @supports ( --theme-#{$slot}:red ) { + color:red; + } + + @supports selector( .foo-#{$name} ) { + color:red; + } +} + +``` + + +============================= + +# Outputs + +## Output 1 + +----- +Indent style: Tab +Indent width: 2 +Line ending: LF +Line width: 80 +Quote style: Double Quotes +Trailing newline: true +----- + +```scss +$base: 10; + +@import "theme.css" supports(#{$name}: $base + 1); +@import "theme.css" supports(margin-#{$side}: $base + 1); +@import "theme.css" supports(--theme-#{$slot}: red); + +.box { + @supports not (#{$name}: $base + 1) { + width: $base + 1; + } + + @supports (#{$name}: $base + 1) and (display: grid) { + width: $base + 1; + } + + @supports (#{$name}: $base + 1) or (margin-#{$side}: $base + 1) { + width: $base + 1; + } + + @supports ((#{$name}: $base + 1) and (--theme-#{$slot}: red)) { + color: red; + } + + @supports (margin-#{$side}: $base + 1) { + width: $base + 1; + } + + @supports (--theme-#{$slot}: red) { + color: red; + } + + @supports selector(.foo-#{$name}) { + color: red; + } +} + +``` diff --git a/crates/biome_css_formatter/tests/specs/css/scss/declaration/ambiguous-selector-vs-nesting.scss b/crates/biome_css_formatter/tests/specs/css/scss/declaration/ambiguous-selector-vs-nesting.scss index c5da54bc142f..310ea60f8d3b 100644 --- a/crates/biome_css_formatter/tests/specs/css/scss/declaration/ambiguous-selector-vs-nesting.scss +++ b/crates/biome_css_formatter/tests/specs/css/scss/declaration/ambiguous-selector-vs-nesting.scss @@ -3,4 +3,8 @@ font:bold{color:blue;} font:12px{family:sans-serif;} font: bold{family:serif;} + #{$name}:hover{color:green;} + a:#{$pseudo}{color:teal;} + font-#{$weight}:bold{color:purple;} + font-#{$weight}::before{color:orange;} } diff --git a/crates/biome_css_formatter/tests/specs/css/scss/declaration/ambiguous-selector-vs-nesting.scss.snap b/crates/biome_css_formatter/tests/specs/css/scss/declaration/ambiguous-selector-vs-nesting.scss.snap index d55916415710..4d2a174c77a5 100644 --- a/crates/biome_css_formatter/tests/specs/css/scss/declaration/ambiguous-selector-vs-nesting.scss.snap +++ b/crates/biome_css_formatter/tests/specs/css/scss/declaration/ambiguous-selector-vs-nesting.scss.snap @@ -11,6 +11,10 @@ info: css/scss/declaration/ambiguous-selector-vs-nesting.scss font:bold{color:blue;} font:12px{family:sans-serif;} font: bold{family:serif;} + #{$name}:hover{color:green;} + a:#{$pseudo}{color:teal;} + font-#{$weight}:bold{color:purple;} + font-#{$weight}::before{color:orange;} } ``` @@ -45,6 +49,18 @@ Trailing newline: true font: bold { family: serif; } + #{$name}:hover { + color: green; + } + a: #{$pseudo} { + color: teal; + } + font-#{$weight}:bold { + color: purple; + } + font-#{$weight}::before { + color: orange; + } } ``` diff --git a/crates/biome_css_formatter/tests/specs/css/scss/declaration/interpolation.scss b/crates/biome_css_formatter/tests/specs/css/scss/declaration/interpolation.scss new file mode 100644 index 000000000000..5d3ebab6808c --- /dev/null +++ b/crates/biome_css_formatter/tests/specs/css/scss/declaration/interpolation.scss @@ -0,0 +1,5 @@ +.box{ +#{$name}:1px; +margin-#{$side}:1px; +--theme-#{$slot}:red; +} diff --git a/crates/biome_css_formatter/tests/specs/css/scss/declaration/interpolation.scss.snap b/crates/biome_css_formatter/tests/specs/css/scss/declaration/interpolation.scss.snap new file mode 100644 index 000000000000..e0010693d8b6 --- /dev/null +++ b/crates/biome_css_formatter/tests/specs/css/scss/declaration/interpolation.scss.snap @@ -0,0 +1,41 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +assertion_line: 212 +info: css/scss/declaration/interpolation.scss +--- + +# Input + +```scss +.box{ +#{$name}:1px; +margin-#{$side}:1px; +--theme-#{$slot}:red; +} + +``` + + +============================= + +# Outputs + +## Output 1 + +----- +Indent style: Tab +Indent width: 2 +Line ending: LF +Line width: 80 +Quote style: Double Quotes +Trailing newline: true +----- + +```scss +.box { + #{$name}: 1px; + margin-#{$side}: 1px; + --theme-#{$slot}: red; +} + +``` diff --git a/crates/biome_css_formatter/tests/specs/css/scss/declaration/nested-properties-empty-value.scss.snap b/crates/biome_css_formatter/tests/specs/css/scss/declaration/nested-properties-empty-value.scss.snap index 53feb60450ec..68cb2717d3fa 100644 --- a/crates/biome_css_formatter/tests/specs/css/scss/declaration/nested-properties-empty-value.scss.snap +++ b/crates/biome_css_formatter/tests/specs/css/scss/declaration/nested-properties-empty-value.scss.snap @@ -1,6 +1,5 @@ --- source: crates/biome_formatter_test/src/snapshot_builder.rs -assertion_line: 212 info: css/scss/declaration/nested-properties-empty-value.scss --- diff --git a/crates/biome_css_formatter/tests/specs/css/scss/declaration/page-at-rule.scss b/crates/biome_css_formatter/tests/specs/css/scss/declaration/page-at-rule.scss index 71c51e3a882e..d3900f0d30d4 100644 --- a/crates/biome_css_formatter/tests/specs/css/scss/declaration/page-at-rule.scss +++ b/crates/biome_css_formatter/tests/specs/css/scss/declaration/page-at-rule.scss @@ -2,6 +2,9 @@ $padding:12px !default; $scale:1+2*3; padding:$padding*$scale; +#{$name}:1px; +margin-#{$side}:1px; +--theme-#{$slot}:red; font:{ size:12px; @@ -25,5 +28,8 @@ color:$color; @top-right{ theme.$gutter:8px; padding:theme.$gutter; +#{$inner}:2px; +margin-#{$inner-side}:2px; +--theme-#{$inner-slot}:blue; } } diff --git a/crates/biome_css_formatter/tests/specs/css/scss/declaration/page-at-rule.scss.snap b/crates/biome_css_formatter/tests/specs/css/scss/declaration/page-at-rule.scss.snap index 5ed1acbb07e4..109991a0ecae 100644 --- a/crates/biome_css_formatter/tests/specs/css/scss/declaration/page-at-rule.scss.snap +++ b/crates/biome_css_formatter/tests/specs/css/scss/declaration/page-at-rule.scss.snap @@ -1,6 +1,5 @@ --- source: crates/biome_formatter_test/src/snapshot_builder.rs -assertion_line: 212 info: css/scss/declaration/page-at-rule.scss --- @@ -11,6 +10,9 @@ info: css/scss/declaration/page-at-rule.scss $padding:12px !default; $scale:1+2*3; padding:$padding*$scale; +#{$name}:1px; +margin-#{$side}:1px; +--theme-#{$slot}:red; font:{ size:12px; @@ -34,6 +36,9 @@ color:$color; @top-right{ theme.$gutter:8px; padding:theme.$gutter; +#{$inner}:2px; +margin-#{$inner-side}:2px; +--theme-#{$inner-slot}:blue; } } @@ -60,6 +65,9 @@ Trailing newline: true $padding: 12px !default; $scale: 1 + 2 * 3; padding: $padding * $scale; + #{$name}: 1px; + margin-#{$side}: 1px; + --theme-#{$slot}: red; font: { size: 12px; @@ -83,6 +91,9 @@ Trailing newline: true @top-right { theme.$gutter: 8px; padding: theme.$gutter; + #{$inner}: 2px; + margin-#{$inner-side}: 2px; + --theme-#{$inner-slot}: blue; } } diff --git a/crates/biome_css_formatter/tests/specs/css/tailwind/supports.css b/crates/biome_css_formatter/tests/specs/css/tailwind/supports.css new file mode 100644 index 000000000000..588ac360e634 --- /dev/null +++ b/crates/biome_css_formatter/tests/specs/css/tailwind/supports.css @@ -0,0 +1,7 @@ +@import "tailwindcss/theme.css" supports( --color-*:initial); + +@supports( --color-*:initial){ +.box{ +color:red; +} +} diff --git a/crates/biome_css_formatter/tests/specs/css/tailwind/supports.css.snap b/crates/biome_css_formatter/tests/specs/css/tailwind/supports.css.snap new file mode 100644 index 000000000000..3e70c0855739 --- /dev/null +++ b/crates/biome_css_formatter/tests/specs/css/tailwind/supports.css.snap @@ -0,0 +1,66 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: css/tailwind/supports.css +--- + +# Input + +```css +@import "tailwindcss/theme.css" supports( --color-*:initial); + +@supports( --color-*:initial){ +.box{ +color:red; +} +} + +``` + + +============================= + +# Outputs + +## Output 1 + +----- +Indent style: Tab +Indent width: 2 +Line ending: LF +Line width: 80 +Quote style: Double Quotes +Trailing newline: true +----- + +```css +@import "tailwindcss/theme.css" supports(--color-*: initial); + +@supports (--color-*: initial) { + .box { + color: red; + } +} + +``` + +## Output 1 + +----- +Indent style: Tab +Indent width: 2 +Line ending: LF +Line width: 80 +Quote style: Double Quotes +Trailing newline: true +----- + +```css +@import "tailwindcss/theme.css" supports(--color-*: initial); + +@supports (--color-*: initial) { + .box { + color: red; + } +} + +``` diff --git a/crates/biome_css_parser/src/lexer/mod.rs b/crates/biome_css_parser/src/lexer/mod.rs index 4b689a768dff..c916f6d45822 100644 --- a/crates/biome_css_parser/src/lexer/mod.rs +++ b/crates/biome_css_parser/src/lexer/mod.rs @@ -171,6 +171,10 @@ impl<'src> Lexer<'src> for CssLexer<'src> { self.current_flags.has_preceding_line_break() } + fn has_preceding_whitespace(&self) -> bool { + self.current_flags.has_preceding_whitespace() + } + fn has_unicode_escape(&self) -> bool { self.current_flags.has_unicode_escape() } diff --git a/crates/biome_css_parser/src/syntax/at_rule/page.rs b/crates/biome_css_parser/src/syntax/at_rule/page.rs index 10abc3c234f4..ed0f6f5148d6 100644 --- a/crates/biome_css_parser/src/syntax/at_rule/page.rs +++ b/crates/biome_css_parser/src/syntax/at_rule/page.rs @@ -5,10 +5,11 @@ use crate::syntax::at_rule::parse_error::{ }; use crate::syntax::at_rule::{is_at_at_rule, parse_at_rule}; use crate::syntax::block::{ParseBlockBody, parse_declaration_or_at_rule_list_block}; +use crate::syntax::declaration::parse_declaration_with_semicolon; use crate::syntax::parse_error::scss_only_syntax_error; use crate::syntax::scss::{ is_at_scss_declaration, is_at_scss_nesting_declaration, parse_scss_declaration, - parse_scss_nesting_declaration, + try_parse_scss_nesting_declaration, }; use crate::syntax::{ CssSyntaxFeatures, is_at_any_declaration_with_semicolon, is_at_identifier, @@ -189,7 +190,8 @@ impl ParseBlockBody for PageBlock { || is_at_at_rule(p) // SCSS allows variable declarations and nested properties inside any block. || is_at_scss_declaration(p) - || is_at_scss_nesting_declaration(p) || is_at_any_declaration_with_semicolon(p) + || is_at_scss_nesting_declaration(p) + || is_at_any_declaration_with_semicolon(p) || is_at_qualified_rule(p) } @@ -221,12 +223,20 @@ impl ParseNodeList for PageAtRuleItemList { }, ) } else if is_at_scss_nesting_declaration(p) { - // Keep nested property blocks intact inside @page. - CssSyntaxFeatures::Scss.parse_exclusive_syntax( - p, - parse_scss_nesting_declaration, - |p, marker| scss_only_syntax_error(p, "SCSS nesting declarations", marker.range(p)), - ) + if let Ok(declaration) = try_parse_scss_nesting_declaration(p, T!['}']) { + return declaration; + } + + if is_at_qualified_rule(p) + && let Present(mut syntax) = parse_qualified_rule(p) + { + let range = syntax.range(p); + p.error(expected_any_page_at_rule_item(p, range)); + syntax.change_kind(p, CSS_BOGUS); + return Present(syntax); + } + + parse_declaration_with_semicolon(p) } else if is_at_any_declaration_with_semicolon(p) { parse_any_declaration_with_semicolon(p) } else if is_at_qualified_rule(p) { diff --git a/crates/biome_css_parser/src/syntax/at_rule/supports/mod.rs b/crates/biome_css_parser/src/syntax/at_rule/supports/mod.rs index adbba54d654e..a0170d41ed1b 100644 --- a/crates/biome_css_parser/src/syntax/at_rule/supports/mod.rs +++ b/crates/biome_css_parser/src/syntax/at_rule/supports/mod.rs @@ -7,23 +7,21 @@ use crate::syntax::at_rule::supports::error::{ }; use crate::syntax::block::parse_conditional_block; use crate::syntax::declaration::parse_declaration_important; -use crate::syntax::parse_error::{ - expected_component_value, expected_declaration, expected_selector, -}; +use crate::syntax::parse_any_css_value; +use crate::syntax::parse_error::{expected_declaration, expected_selector}; use crate::syntax::property::{ - GenericComponentValueList, is_at_generic_property, parse_generic_property_name, + END_OF_PROPERTY_VALUE_TOKEN_SET, is_at_generic_property, is_nth_at_direct_generic_property, + parse_generic_property_name, parse_property_value_with_end_set, }; -use crate::syntax::scss::parse_scss_expression_allow_empty_value_until; +use crate::syntax::scss::is_nth_at_scss_interpolated_property; use crate::syntax::selector::parse_selector; -use crate::syntax::{CssSyntaxFeatures, is_nth_at_identifier, parse_any_css_value}; use biome_css_syntax::CssSyntaxKind::*; use biome_css_syntax::{CssSyntaxKind, T}; -use biome_parser::parse_lists::ParseNodeList; use biome_parser::parse_recovery::ParseRecovery; use biome_parser::parsed_syntax::ParsedSyntax::Present; use biome_parser::prelude::ParsedSyntax::Absent; use biome_parser::prelude::*; -use biome_parser::{SyntaxFeature, TokenSet, token_set}; +use biome_parser::{TokenSet, token_set}; /// Checks if the current token in the parser is an `@supports` at-rule. #[inline] @@ -294,7 +292,8 @@ fn parse_supports_feature_selector(p: &mut CssParser) -> ParsedSyntax { #[inline] fn is_at_supports_feature_declaration(p: &mut CssParser) -> bool { - p.at(T!['(']) && is_nth_at_identifier(p, 1) && p.nth_at(2, T![:]) + p.at(T!['(']) + && (is_nth_at_direct_generic_property(p, 1) || is_nth_at_scss_interpolated_property(p, 1)) } #[inline] @@ -335,7 +334,7 @@ pub(crate) fn parse_supports_declaration(p: &mut CssParser) -> ParsedSyntax { fn parse_supports_generic_property(p: &mut CssParser) -> ParsedSyntax { let m = p.start(); - parse_generic_property_name(p); + parse_generic_property_name(p).ok(); p.expect(T![:]); parse_supports_property_value(p); @@ -348,21 +347,9 @@ const END_OF_SUPPORTS_PROPERTY_VALUE_TOKEN_SET: TokenSet = #[inline] fn parse_supports_property_value(p: &mut CssParser) { - if CssSyntaxFeatures::Scss.is_supported(p) { - let missing_value = parse_scss_expression_allow_empty_value_until( - p, - END_OF_SUPPORTS_PROPERTY_VALUE_TOKEN_SET, - ) - .ok() - .is_none_or(|value| value.range(p).is_empty()); - if missing_value { - p.error(expected_component_value(p, p.cur_range())); - } - } else { - GenericComponentValueList::new( - END_OF_SUPPORTS_PROPERTY_VALUE_TOKEN_SET, - END_OF_SUPPORTS_PROPERTY_VALUE_TOKEN_SET, - ) - .parse_list(p); - } + parse_property_value_with_end_set( + p, + END_OF_SUPPORTS_PROPERTY_VALUE_TOKEN_SET, + END_OF_PROPERTY_VALUE_TOKEN_SET, + ); } diff --git a/crates/biome_css_parser/src/syntax/block/declaration_or_at_rule_list_block.rs b/crates/biome_css_parser/src/syntax/block/declaration_or_at_rule_list_block.rs index cb53b04b83ba..ed73d1262cbc 100644 --- a/crates/biome_css_parser/src/syntax/block/declaration_or_at_rule_list_block.rs +++ b/crates/biome_css_parser/src/syntax/block/declaration_or_at_rule_list_block.rs @@ -3,8 +3,8 @@ use crate::syntax::at_rule::{is_at_at_rule, parse_at_rule}; use crate::syntax::block::ParseBlockBody; use crate::syntax::parse_error::{expected_any_declaration_or_at_rule, scss_only_syntax_error}; use crate::syntax::scss::{ - is_at_scss_declaration, is_at_scss_nesting_declaration, parse_scss_declaration, - parse_scss_nesting_declaration, + is_at_scss_declaration, is_at_scss_interpolated_property, is_at_scss_nesting_declaration, + parse_scss_declaration, parse_scss_interpolated_property_declaration, }; use crate::syntax::{ CssSyntaxFeatures, is_at_any_declaration_with_semicolon, parse_any_declaration_with_semicolon, @@ -41,6 +41,7 @@ fn is_at_declaration_or_at_rule_item(p: &mut CssParser) -> bool { is_at_at_rule(p) || is_at_scss_declaration(p) || is_at_scss_nesting_declaration(p) + || is_at_scss_interpolated_property(p) || is_at_any_declaration_with_semicolon(p) } @@ -72,13 +73,8 @@ impl ParseNodeList for DeclarationOrAtRuleList { scss_only_syntax_error(p, "SCSS variable declarations", marker.range(p)) }, ) - } else if is_at_scss_nesting_declaration(p) { - // Parse nested properties before generic declarations to keep `{` blocks intact. - CssSyntaxFeatures::Scss.parse_exclusive_syntax( - p, - parse_scss_nesting_declaration, - |p, marker| scss_only_syntax_error(p, "SCSS nesting declarations", marker.range(p)), - ) + } else if is_at_scss_nesting_declaration(p) || is_at_scss_interpolated_property(p) { + parse_scss_interpolated_property_declaration(p) } else if is_at_any_declaration_with_semicolon(p) { parse_any_declaration_with_semicolon(p) } else { diff --git a/crates/biome_css_parser/src/syntax/block/declaration_or_rule_list_block.rs b/crates/biome_css_parser/src/syntax/block/declaration_or_rule_list_block.rs index 3c65657ced77..3ef8e4374431 100644 --- a/crates/biome_css_parser/src/syntax/block/declaration_or_rule_list_block.rs +++ b/crates/biome_css_parser/src/syntax/block/declaration_or_rule_list_block.rs @@ -1,15 +1,16 @@ use crate::parser::CssParser; use crate::syntax::at_rule::{is_at_at_rule, parse_at_rule}; use crate::syntax::block::ParseBlockBody; +use crate::syntax::declaration::parse_declaration_with_semicolon; use crate::syntax::parse_error::{expected_any_declaration_or_at_rule, scss_only_syntax_error}; use crate::syntax::scss::{ - is_at_scss_declaration, is_at_scss_nesting_declaration, parse_scss_declaration, - parse_scss_nesting_declaration, + is_at_scss_declaration, is_at_scss_interpolated_property, is_at_scss_nesting_declaration, + parse_scss_declaration, parse_scss_nesting_declaration, try_parse_scss_nesting_declaration, }; use crate::syntax::{ CssSyntaxFeatures, is_at_any_declaration_with_semicolon, is_at_metavariable, - is_at_nested_qualified_rule, is_nth_at_identifier, parse_any_declaration_with_semicolon, - parse_metavariable, parse_nested_qualified_rule, try_parse, + is_at_nested_qualified_rule, parse_any_declaration_with_semicolon, parse_metavariable, + parse_nested_qualified_rule, try_parse, try_parse_nested_qualified_rule_without_selector_recovery, }; use biome_css_syntax::CssSyntaxKind::*; @@ -18,7 +19,6 @@ use biome_parser::parse_lists::ParseNodeList; use biome_parser::parse_recovery::{ParseRecovery, RecoveryResult}; use biome_parser::prelude::ParsedSyntax; use biome_parser::prelude::ParsedSyntax::Absent; -use biome_parser::token_source::TokenSourceWithBufferedLexer; use biome_parser::{CompletedMarker, Parser, SyntaxFeature}; #[inline] @@ -46,30 +46,11 @@ fn is_at_declaration_or_rule_item(p: &mut CssParser) -> bool { || is_at_nested_qualified_rule(p) || is_at_scss_nesting_declaration(p) || is_at_scss_declaration(p) + || is_at_scss_interpolated_property(p) || is_at_any_declaration_with_semicolon(p) || is_at_metavariable(p) } -#[inline] -fn has_whitespace_after_scss_property_colon(p: &mut CssParser) -> bool { - // We enter this helper at `ident` in `ident:...`. - // `nth_non_trivia(1)` is the `:` token, so `nth_non_trivia(2)` is the first token - // after `:`. Its preceding flags tell us whether there was spacing after the colon. - let Some(after_colon) = p.source_mut().lexer().nth_non_trivia(2) else { - return false; - }; - - after_colon.has_preceding_whitespace() || after_colon.has_preceding_line_break() -} - -#[inline] -fn is_at_ambiguous_scss_nesting_item(p: &mut CssParser) -> bool { - // Match Sass's ambiguity gate: only no-spacing `name:ident` and `name::...` - // forms can be nested selectors. - !has_whitespace_after_scss_property_colon(p) - && (is_nth_at_identifier(p, 2) || p.nth_at(2, T![:])) -} - struct DeclarationOrRuleListParseRecovery { end_kind: CssSyntaxKind, } @@ -109,35 +90,17 @@ impl ParseNodeList for DeclarationOrRuleList { if is_at_at_rule(p) { parse_at_rule(p) } else if is_at_scss_nesting_declaration(p) { - let is_ambiguous = is_at_ambiguous_scss_nesting_item(p); - - if is_ambiguous { - // Match Sass's declaration-first strategy for ambiguous `name:ident` and - // `name::...` forms. Parse as declaration first, then backtrack to selector - // parsing when the result is declaration-like but selector-ambiguous. - let declaration = try_parse(p, |p| { - let declaration = parse_scss_nesting_declaration(p); - - match declaration.kind(p) { - Some(SCSS_NESTING_DECLARATION) => Err(()), - Some(CSS_DECLARATION_WITH_SEMICOLON) - if matches!(p.last(), Some(T![;])) || p.at(self.end_kind) => - { - Ok(declaration) - } - _ => Err(()), - } - }); - - if let Ok(declaration) = declaration { - return declaration; - } + // Match Sass's declaration-first strategy for ambiguous `name:ident` and + // `name::...` forms, but do the ambiguity check inside the same speculative + // declaration parse so we don't pay a separate probe pass first. + if let Ok(declaration) = try_parse_scss_nesting_declaration(p, self.end_kind) { + return declaration; + } - if let Ok(rule) = - try_parse_nested_qualified_rule_without_selector_recovery(p, self.end_kind) - { - return rule; - } + if let Ok(rule) = + try_parse_nested_qualified_rule_without_selector_recovery(p, self.end_kind) + { + return rule; } parse_scss_nesting_declaration(p) @@ -149,6 +112,17 @@ impl ParseNodeList for DeclarationOrRuleList { scss_only_syntax_error(p, "SCSS variable declarations", marker.range(p)) }, ) + } else if is_at_scss_interpolated_property(p) { + // The remaining interpolation-bearing property names here are the + // dashed/custom-property forms that `is_at_scss_nesting_declaration` + // intentionally excludes, such as `--theme-#{$slot}: red;`. + if let Ok(rule) = + try_parse_nested_qualified_rule_without_selector_recovery(p, self.end_kind) + { + return rule; + } + + parse_declaration_with_semicolon(p) } else if is_at_any_declaration_with_semicolon(p) { // if we are at a declaration, // we still can have a nested qualified rule or a declaration diff --git a/crates/biome_css_parser/src/syntax/declaration.rs b/crates/biome_css_parser/src/syntax/declaration.rs index 8b62f6a2fba6..0a523c58fe57 100644 --- a/crates/biome_css_parser/src/syntax/declaration.rs +++ b/crates/biome_css_parser/src/syntax/declaration.rs @@ -5,8 +5,8 @@ use crate::syntax::property::{ is_at_any_property, parse_any_property, parse_any_property_with_value_end_set, }; use crate::syntax::scss::{ - is_at_scss_declaration, is_at_scss_nesting_declaration, parse_scss_declaration, - parse_scss_nesting_declaration, + is_at_scss_declaration, is_at_scss_interpolated_property, is_at_scss_nesting_declaration, + parse_scss_declaration, parse_scss_interpolated_property_declaration, }; use biome_css_syntax::CssSyntaxKind::*; use biome_css_syntax::{CssSyntaxKind, T}; @@ -24,12 +24,8 @@ impl ParseNodeList for DeclarationList { const LIST_KIND: Self::Kind = CSS_DECLARATION_LIST; fn parse_element(&mut self, p: &mut Self::Parser<'_>) -> ParsedSyntax { - if is_at_scss_nesting_declaration(p) { - CssSyntaxFeatures::Scss.parse_exclusive_syntax( - p, - parse_scss_nesting_declaration, - |p, marker| scss_only_syntax_error(p, "SCSS nesting declarations", marker.range(p)), - ) + if is_at_scss_nesting_declaration(p) || is_at_scss_interpolated_property(p) { + parse_scss_interpolated_property_declaration(p) } else { parse_any_declaration_with_semicolon(p) } @@ -61,6 +57,10 @@ pub(crate) fn parse_declaration(p: &mut CssParser) -> ParsedSyntax { parse_declaration_with(p, parse_any_property) } +/// Parses an interpolation-bearing SCSS property in declaration-only contexts. +/// +/// Nested-property parsing gets the first chance so `name: { ... }` stays intact. +/// If no nested-property form is found, this falls back to a regular declaration. #[inline] pub(crate) fn parse_declaration_with_value_end_set( p: &mut CssParser, @@ -77,13 +77,12 @@ fn parse_declaration_with(p: &mut CssParser, parse_property: F) -> ParsedSynt where F: FnOnce(&mut CssParser) -> ParsedSyntax, { - if !is_at_declaration(p) { - return Absent; - } - let m = p.start(); - parse_property(p).ok(); + if parse_property(p).is_absent() { + m.abandon(p); + return Absent; + } parse_declaration_important(p).ok(); Present(m.complete(p, CSS_DECLARATION)) @@ -124,10 +123,6 @@ pub(crate) fn is_at_any_declaration(p: &mut CssParser) -> bool { /// that are not at the end, the parser will raise an error. #[inline] pub(crate) fn parse_declaration_with_semicolon(p: &mut CssParser) -> ParsedSyntax { - if !is_at_declaration(p) { - return Absent; - } - parse_declaration(p).map(|declaration| complete_declaration_with_semicolon(p, declaration)) } @@ -138,6 +133,13 @@ pub(crate) fn complete_declaration_with_semicolon( ) -> CompletedMarker { let declaration_with_semicolon = declaration.precede(p); + parse_optional_declaration_semicolon(p); + + declaration_with_semicolon.complete(p, CSS_DECLARATION_WITH_SEMICOLON) +} + +#[inline] +pub(crate) fn parse_optional_declaration_semicolon(p: &mut CssParser) { // If the next token is a closing brace ('}'), the semicolon is optional. // Otherwise, a semicolon is expected and the parser will enforce its presence. // div { color: red; } @@ -149,8 +151,6 @@ pub(crate) fn complete_declaration_with_semicolon( p.expect(T![;]); } } - - declaration_with_semicolon.complete(p, CSS_DECLARATION_WITH_SEMICOLON) } #[inline] diff --git a/crates/biome_css_parser/src/syntax/property/mod.rs b/crates/biome_css_parser/src/syntax/property/mod.rs index 088c834874a6..f166f2a25088 100644 --- a/crates/biome_css_parser/src/syntax/property/mod.rs +++ b/crates/biome_css_parser/src/syntax/property/mod.rs @@ -9,11 +9,14 @@ use crate::syntax::css_modules::{ use crate::syntax::parse_error::{ expected_component_value, expected_identifier, tailwind_disabled, }; -use crate::syntax::scss::parse_scss_expression_allow_empty_value_until; +use crate::syntax::scss::{ + is_at_scss_interpolated_property, parse_required_scss_value_until, + parse_scss_interpolated_property_name, +}; use crate::syntax::{ CssSyntaxFeatures, is_at_any_value, is_at_dashed_identifier, is_at_identifier, is_at_string, - parse_any_value, parse_custom_identifier_with_keywords, parse_dashed_identifier, - parse_regular_identifier, parse_string, + is_nth_at_identifier, parse_any_value, parse_custom_identifier_with_keywords, + parse_dashed_identifier, parse_regular_identifier, parse_string, }; use biome_css_syntax::CssSyntaxKind::*; use biome_css_syntax::{CssSyntaxKind, T}; @@ -207,40 +210,88 @@ impl ParseRecovery for ComposesClassListParseRecovery { } } +/// Detects the start of a generic property name. +/// +/// This covers both direct property names and SCSS interpolation-bearing names: +/// +/// ```scss +/// color: red; +/// --color-*: initial; +/// #{$name}: 1px; +/// margin-#{$side}: 1px; +/// ``` #[inline] pub(crate) fn is_at_generic_property(p: &mut CssParser) -> bool { - is_at_identifier(p) - && (p.nth_at(1, T![:]) - // handle --*: - || (p.nth_at(1, T![*]) && p.nth_at(2, T![:])) + is_at_direct_generic_property(p) || is_at_scss_interpolated_property(p) +} + +/// Detects the direct, non-interpolated property-name forms handled by the +/// generic property parser. +/// +/// This includes plain identifiers followed by `:` as well as the Tailwind +/// `--*:` and `--name-*:` theme-reference forms. +#[inline] +fn is_at_direct_generic_property(p: &mut CssParser) -> bool { + is_nth_at_direct_generic_property(p, 0) +} + +#[inline] +fn is_at_tailwind_theme_reference_property(p: &mut CssParser) -> bool { + is_nth_at_tailwind_theme_reference_property(p, 0) +} + +#[inline] +pub(crate) fn is_nth_at_direct_generic_property(p: &mut CssParser, n: usize) -> bool { + is_nth_at_identifier(p, n) + && (p.nth_at(n + 1, T![:]) || is_nth_at_tailwind_theme_reference_property(p, n)) +} + +#[inline] +fn is_nth_at_tailwind_theme_reference_property(p: &mut CssParser, n: usize) -> bool { + // handle --*: + (p.nth_at(n + 1, T![*]) && p.nth_at(n + 2, T![:])) // handle --color-*: - || (p.nth_at(1, T![-]) && p.nth_at(2, T![*]) && p.nth_at(3, T![:]))) + || (p.nth_at(n + 1, T![-]) && p.nth_at(n + 2, T![*]) && p.nth_at(n + 3, T![:])) +} + +#[inline] +pub(crate) fn parse_generic_property_name(p: &mut CssParser) -> ParsedSyntax { + if is_at_dashed_identifier(p) && is_at_tailwind_theme_reference_property(p) { + let Present(ident) = parse_dashed_identifier(p) else { + return Absent; + }; + + return if p.at_ts(token_set![T![-], T![*]]) { + CssSyntaxFeatures::Tailwind.parse_exclusive_syntax( + p, + |p| { + let m = ident.precede(p); + if p.at(T![-]) { + p.expect(T![-]); + } + p.expect(T![*]); + Present(m.complete(p, TW_VALUE_THEME_REFERENCE)) + }, + |p, m| tailwind_disabled(p, m.range(p)), + ) + } else { + Present(ident) + }; + } + + if CssSyntaxFeatures::Scss.is_supported(p) { + return parse_scss_interpolated_property_name(p).or_else(|| parse_plain_property_name(p)); + } + + parse_plain_property_name(p) } #[inline] -pub(crate) fn parse_generic_property_name(p: &mut CssParser) { +fn parse_plain_property_name(p: &mut CssParser) -> ParsedSyntax { if is_at_dashed_identifier(p) { - let ident = parse_dashed_identifier(p).ok(); - if let Some(ident) = ident - && p.at_ts(token_set![T![-], T![*]]) - { - CssSyntaxFeatures::Tailwind - .parse_exclusive_syntax( - p, - |p| { - let m = ident.precede(p); - if p.at(T![-]) { - p.expect(T![-]); - } - p.expect(T![*]); - Present(m.complete(p, TW_VALUE_THEME_REFERENCE)) - }, - |p, m| tailwind_disabled(p, m.range(p)), - ) - .ok(); - } + parse_dashed_identifier(p) } else { - parse_regular_identifier(p).ok(); + parse_regular_identifier(p) } } @@ -259,29 +310,29 @@ fn parse_generic_property_with_value_end_set( value_end_set: TokenSet, recovery_end_set: TokenSet, ) -> ParsedSyntax { - if !is_at_generic_property(p) { + let m = p.start(); + if parse_generic_property_name(p).is_absent() { + m.abandon(p); return Absent; } - let m = p.start(); - - parse_generic_property_name(p); - p.expect(T![:]); + parse_property_value_with_end_set(p, value_end_set, recovery_end_set); - if CssSyntaxFeatures::Scss.is_supported(p) { - let missing_value = parse_scss_expression_allow_empty_value_until(p, value_end_set) - .ok() - .is_none_or(|value| value.range(p).is_empty()); + Present(m.complete(p, CSS_GENERIC_PROPERTY)) +} - if missing_value { - p.error(expected_component_value(p, p.cur_range())); - } +#[inline] +pub(crate) fn parse_property_value_with_end_set( + p: &mut CssParser, + value_end_set: TokenSet, + recovery_end_set: TokenSet, +) { + if CssSyntaxFeatures::Scss.is_supported(p) { + parse_required_scss_value_until(p, value_end_set); } else { GenericComponentValueList::new(value_end_set, recovery_end_set).parse_list(p); } - - Present(m.complete(p, CSS_GENERIC_PROPERTY)) } pub(crate) const END_OF_PROPERTY_VALUE_TOKEN_SET: TokenSet = diff --git a/crates/biome_css_parser/src/syntax/scss/declaration/mod.rs b/crates/biome_css_parser/src/syntax/scss/declaration/mod.rs index 3c80987a09ca..38c22898c06a 100644 --- a/crates/biome_css_parser/src/syntax/scss/declaration/mod.rs +++ b/crates/biome_css_parser/src/syntax/scss/declaration/mod.rs @@ -2,6 +2,23 @@ mod nesting; mod variable; mod variable_modifier; -pub(crate) use nesting::{is_at_scss_nesting_declaration, parse_scss_nesting_declaration}; +use crate::parser::CssParser; +use crate::syntax::declaration::parse_declaration_with_semicolon; +use biome_css_syntax::T; +use biome_parser::prelude::ParsedSyntax; + +pub(crate) use nesting::{ + is_at_scss_nesting_declaration, parse_scss_nesting_declaration, + try_parse_scss_nesting_declaration, +}; pub(crate) use variable::{is_at_scss_declaration, parse_scss_declaration}; pub(crate) use variable_modifier::is_at_scss_variable_modifier_start; + +#[inline] +pub(crate) fn parse_scss_interpolated_property_declaration(p: &mut CssParser) -> ParsedSyntax { + try_parse_scss_nesting_declaration(p, T!['}']).unwrap_or_else(|_| { + // Declaration-only contexts have no selector fallback, so once the + // nesting-specific probe fails we can commit to a regular declaration. + parse_declaration_with_semicolon(p) + }) +} diff --git a/crates/biome_css_parser/src/syntax/scss/declaration/nesting.rs b/crates/biome_css_parser/src/syntax/scss/declaration/nesting.rs index bcd4f61fe08c..31676fee766e 100644 --- a/crates/biome_css_parser/src/syntax/scss/declaration/nesting.rs +++ b/crates/biome_css_parser/src/syntax/scss/declaration/nesting.rs @@ -1,111 +1,206 @@ use crate::parser::CssParser; use crate::syntax::block::parse_declaration_or_rule_list_block; +use crate::syntax::declaration::{ + complete_declaration_with_semicolon, parse_declaration_important, +}; use crate::syntax::parse_error::expected_component_value; use crate::syntax::scss::{ - SCSS_NESTING_VALUE_END_SET, parse_scss_expression_allow_empty_value_until, -}; -use crate::syntax::{ - CssSyntaxFeatures, is_at_dashed_identifier, is_at_identifier, parse_regular_identifier, + SCSS_NESTING_VALUE_END_SET, complete_empty_scss_expression, is_at_scss_interpolated_identifier, + is_at_scss_interpolated_property, parse_scss_interpolated_identifier, + parse_scss_optional_value_until, }; +use crate::syntax::{CssSyntaxFeatures, is_at_dashed_identifier, is_at_identifier, try_parse}; use biome_css_syntax::CssSyntaxKind::{ - CSS_DECLARATION, CSS_DECLARATION_IMPORTANT, CSS_DECLARATION_WITH_SEMICOLON, - CSS_GENERIC_PROPERTY, EOF, SCSS_NESTING_DECLARATION, + CSS_DECLARATION, CSS_DECLARATION_WITH_SEMICOLON, CSS_GENERIC_PROPERTY, SCSS_NESTING_DECLARATION, }; -use biome_css_syntax::T; +use biome_css_syntax::{CssSyntaxKind, T}; use biome_parser::prelude::ParsedSyntax; use biome_parser::prelude::ParsedSyntax::{Absent, Present}; -use biome_parser::{CompletedMarker, Parser, SyntaxFeature}; +use biome_parser::{Marker, Parser, SyntaxFeature}; -/// Detects nested property syntax (`prop: { ... }`) while excluding custom properties -/// and CSS Modules declarations that must remain regular properties. +/// Parses a SCSS nested property declaration block, or falls back to a regular +/// declaration when no block follows. /// /// Example: /// ```scss -/// font: { size: 12px; } +/// .button { +/// font: { +/// family: sans-serif; +/// size: 12px; +/// } +/// } /// ``` /// /// Docs: https://sass-lang.com/documentation/style-rules/declarations#nested-properties +#[inline] +pub(crate) fn parse_scss_nesting_declaration(p: &mut CssParser) -> ParsedSyntax { + if !is_at_scss_nesting_declaration(p) { + return Absent; + } + + parse_scss_nesting_declaration_candidate(p).map_or(Absent, |(syntax, _)| syntax) +} + #[inline] pub(crate) fn is_at_scss_nesting_declaration(p: &mut CssParser) -> bool { CssSyntaxFeatures::Scss.is_supported(p) - && is_at_identifier(p) - && p.nth_at(1, T![:]) && !is_at_dashed_identifier(p) && !p.at(T![composes]) + && (is_at_scss_interpolated_property(p) || (is_at_identifier(p) && p.nth_at(1, T![:]))) } -/// Parses a SCSS nested property declaration block, or falls back to a regular declaration -/// when no block follows. +struct ScssNestingMarkers { + declaration: Marker, + property: Marker, +} + +/// Parses a SCSS nested-property/declaration candidate and returns both the +/// parsed syntax and whether the same prefix could still be interpreted as a +/// selector by the caller. /// -/// Example: -/// ```scss -/// font: { -/// family: sans-serif; -/// size: 12px; -/// } -/// ``` +/// This keeps the real parsing shared between the committed and speculative +/// nesting entrypoints. +#[inline] +fn parse_scss_nesting_declaration_candidate(p: &mut CssParser) -> Option<(ParsedSyntax, bool)> { + let (markers, could_be_selector) = parse_scss_nesting_declaration_prefix(p)?; + let syntax = parse_scss_nesting_declaration_after_prefix(p, markers); + + Some((syntax, could_be_selector)) +} + +/// Parses the remainder of a SCSS nesting candidate after its `name:` prefix +/// has already been recognized. /// -/// Specification: https://sass-lang.com/documentation/style-rules/declarations#nested-properties +/// This decides whether the candidate becomes a nested-property block or a +/// regular declaration once the value and following token are known. #[inline] -pub(crate) fn parse_scss_nesting_declaration(p: &mut CssParser) -> ParsedSyntax { - if !is_at_scss_nesting_declaration(p) { - return Absent; +fn parse_scss_nesting_declaration_after_prefix( + p: &mut CssParser, + markers: ScssNestingMarkers, +) -> ParsedSyntax { + let missing_value = + // Allow an empty value here because nested-property syntax may continue + // directly into `{ ... }`, and the explicit missing-value diagnostic is + // handled by the caller via `missing_value`. + parse_scss_optional_value_until(p, SCSS_NESTING_VALUE_END_SET).is_absent(); + + if p.at(T!['{']) { + // A following `{` turns the parsed prefix into nested-property syntax. + if missing_value { + complete_empty_scss_expression(p); + } + complete_scss_nested_property_block(p, markers) + } else { + complete_scss_nesting_regular_declaration(p, markers, missing_value) } +} - let m = p.start(); +/// Parses the shared `name:` prefix for SCSS nested properties and regular +/// declarations. +/// +/// The prefix parser opens the marker wrappers needed to finish the construct +/// either as `ScssNestingDeclaration` or as `CssGenericProperty -> +/// CssDeclaration`, and returns `None` only when an interpolation-bearing start +/// is not actually followed by `:`. +#[inline] +fn parse_scss_nesting_declaration_prefix(p: &mut CssParser) -> Option<(ScssNestingMarkers, bool)> { + let declaration = p.start(); let property = p.start(); - parse_regular_identifier(p).ok(); - p.expect(T![:]); - let missing_value = p.at(T![;]) || p.at(T!['}']) || p.at(EOF) || p.at(T![!]); - parse_scss_expression_allow_empty_value_until(p, SCSS_NESTING_VALUE_END_SET).ok(); - if p.at(T!['{']) { - // Upgrade to a nested-property block only if `{` follows the value. + // Guarded by `is_at_scss_nesting_declaration`, so a name parse cannot fail + // here. The only real rejection point in this prefix parser is a missing `:`. + parse_scss_interpolated_identifier(p).ok(); + + if !p.at(T![:]) { + declaration.abandon(p); property.abandon(p); - parse_declaration_or_rule_list_block(p); - return Present(m.complete(p, SCSS_NESTING_DECLARATION)); + return None; } + p.expect(T![:]); + + let could_be_selector = !p.has_preceding_whitespace() + && (is_at_identifier(p) || is_at_scss_interpolated_identifier(p) || p.at(T![:])); + + Some(( + ScssNestingMarkers { + declaration, + property, + }, + could_be_selector, + )) +} + +#[inline] +fn complete_scss_nested_property_block( + p: &mut CssParser, + markers: ScssNestingMarkers, +) -> ParsedSyntax { + markers.property.abandon(p); + parse_declaration_or_rule_list_block(p); + Present(markers.declaration.complete(p, SCSS_NESTING_DECLARATION)) +} + +#[inline] +fn complete_scss_nesting_regular_declaration( + p: &mut CssParser, + markers: ScssNestingMarkers, + missing_value: bool, +) -> ParsedSyntax { if missing_value { + complete_empty_scss_expression(p); p.error(expected_component_value(p, p.cur_range())); } // Otherwise, reinterpret the parsed property/value as a regular declaration. - let property = property.complete(p, CSS_GENERIC_PROPERTY); + let property = markers.property.complete(p, CSS_GENERIC_PROPERTY); let declaration = property.precede(p); parse_declaration_important(p).ok(); let declaration = declaration.complete(p, CSS_DECLARATION); - m.abandon(p); + markers.declaration.abandon(p); Present(complete_declaration_with_semicolon(p, declaration)) } +/// Speculatively parses a SCSS nested-property/declaration candidate and keeps +/// it only if the result is unambiguously declaration-like for the caller's +/// statement boundary. +/// +/// Callers use this to prefer declaration parsing for Sass-compatible forms +/// such as `font: bold;` or `font: { ... }`, while still rewinding for selector +/// syntax like `font:bold { ... }`. +/// +/// Example: +/// ```scss +/// .button { +/// font:bold { color: red; } +/// font: bold; +/// } +/// ``` +/// +/// Docs: https://sass-lang.com/documentation/style-rules/declarations#nested-properties #[inline] -fn complete_declaration_with_semicolon( +pub(crate) fn try_parse_scss_nesting_declaration( p: &mut CssParser, - declaration: CompletedMarker, -) -> CompletedMarker { - let m = declaration.precede(p); - - if !p.at(T!['}']) { - if p.nth_at(1, T!['}']) { - p.eat(T![;]); - } else { - p.expect(T![;]); + end_kind: CssSyntaxKind, +) -> Result { + try_parse(p, |p| { + if !is_at_scss_nesting_declaration(p) { + return Err(()); } - } - m.complete(p, CSS_DECLARATION_WITH_SEMICOLON) -} + let Some((syntax, could_be_selector)) = parse_scss_nesting_declaration_candidate(p) else { + return Err(()); + }; -#[inline] -fn parse_declaration_important(p: &mut CssParser) -> ParsedSyntax { - if !(p.at(T![!]) && p.nth_at(1, T![important])) { - return Absent; - } - - let m = p.start(); - p.bump(T![!]); - p.bump(T![important]); - Present(m.complete(p, CSS_DECLARATION_IMPORTANT)) + match syntax.kind(p) { + Some(SCSS_NESTING_DECLARATION) if !could_be_selector => Ok(syntax), + Some(CSS_DECLARATION_WITH_SEMICOLON) + if matches!(p.last(), Some(T![;])) || p.at(end_kind) => + { + Ok(syntax) + } + _ => Err(()), + } + }) } diff --git a/crates/biome_css_parser/src/syntax/scss/declaration/variable.rs b/crates/biome_css_parser/src/syntax/scss/declaration/variable.rs index fcea12d6403d..6ba6a2a45dfa 100644 --- a/crates/biome_css_parser/src/syntax/scss/declaration/variable.rs +++ b/crates/biome_css_parser/src/syntax/scss/declaration/variable.rs @@ -5,6 +5,7 @@ use super::super::{ }; use super::variable_modifier::parse_scss_variable_modifiers; use crate::parser::CssParser; +use crate::syntax::declaration::parse_optional_declaration_semicolon; use crate::syntax::scss::expected_scss_expression; use biome_css_syntax::CssSyntaxKind::{EOF, SCSS_DECLARATION}; use biome_css_syntax::{CssSyntaxKind, T}; @@ -58,12 +59,7 @@ pub(crate) fn parse_scss_declaration(p: &mut CssParser) -> ParsedSyntax { parse_scss_variable_modifiers(p); if !p.at(T!['}']) && !p.at(EOF) { - if p.nth_at(1, T!['}']) { - // Allow a trailing `;` before `}` but don't require it. - p.eat(T![;]); - } else { - p.expect(T![;]); - } + parse_optional_declaration_semicolon(p); } Present(m.complete(p, SCSS_DECLARATION)) diff --git a/crates/biome_css_parser/src/syntax/scss/expression/interpolation.rs b/crates/biome_css_parser/src/syntax/scss/expression/interpolation.rs index 6ea2f1383f31..370bc6694004 100644 --- a/crates/biome_css_parser/src/syntax/scss/expression/interpolation.rs +++ b/crates/biome_css_parser/src/syntax/scss/expression/interpolation.rs @@ -27,19 +27,6 @@ pub(crate) enum ScssInterpolationMode { Selector, } -/// Parses a standalone SCSS interpolation expression such as `#{$value}`. -/// -/// Example: -/// ```scss -/// $value: #{$name}; -/// ``` -/// -/// Docs: https://sass-lang.com/documentation/interpolation/ -#[inline] -pub(crate) fn parse_scss_interpolation(p: &mut CssParser) -> ParsedSyntax { - parse_scss_interpolation_with_mode(p, ScssInterpolationMode::Regular) -} - /// Parses an SCSS interpolation, lexing the closing `}` with the caller-provided /// mode. #[inline] diff --git a/crates/biome_css_parser/src/syntax/scss/expression/list.rs b/crates/biome_css_parser/src/syntax/scss/expression/list.rs index fbc9057b4f87..9c3540da0bbd 100644 --- a/crates/biome_css_parser/src/syntax/scss/expression/list.rs +++ b/crates/biome_css_parser/src/syntax/scss/expression/list.rs @@ -36,23 +36,42 @@ pub(crate) fn parse_scss_expression(p: &mut CssParser) -> ParsedSyntax { parse_scss_expression_until(p, END_OF_SCSS_EXPRESSION_TOKEN_SET) } -/// Parses a SCSS expression list that may be empty in contexts like nested -/// properties. +/// Parses a SCSS value that may be empty, returning `Absent` when no expression +/// content was produced. +#[inline] +pub(crate) fn parse_scss_optional_value_until( + p: &mut CssParser, + end_ts: TokenSet, +) -> ParsedSyntax { + let options = ScssExpressionOptions::optional_value(end_ts); + + if is_at_scss_expression_sequence_end(p, options) { + return Absent; + } + + parse_scss_expression_with_options(p, options) +} + +/// Parses a required SCSS value and recovers missing content as an empty +/// `ScssExpression` node plus a diagnostic. /// /// Example: /// ```scss -/// font: { -/// size: 12px; -/// } +/// color: ; /// ``` -/// -/// Specification: https://sass-lang.com/documentation/style-rules/declarations#nested-properties #[inline] -pub(crate) fn parse_scss_expression_allow_empty_value_until( +pub(crate) fn parse_required_scss_value_until( p: &mut CssParser, end_ts: TokenSet, -) -> ParsedSyntax { - parse_scss_expression_with_options(p, ScssExpressionOptions::optional_value(end_ts)) +) -> CompletedMarker { + match parse_scss_optional_value_until(p, end_ts) { + Present(value) => value, + Absent => { + let empty_expression = complete_empty_scss_expression(p); + p.error(expected_component_value(p, p.cur_range())); + empty_expression + } + } } /// Parses a SCSS expression until a caller-provided terminator, used by map @@ -108,7 +127,7 @@ pub(super) fn parse_scss_inner_expression_until( } #[inline] -pub(super) fn complete_empty_scss_expression(p: &mut CssParser) -> CompletedMarker { +pub(crate) fn complete_empty_scss_expression(p: &mut CssParser) -> CompletedMarker { let expression = p.start(); let expression_items = p.start(); expression_items.complete(p, SCSS_EXPRESSION_ITEM_LIST); 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 02bcb86d4aa8..af1c530464f5 100644 --- a/crates/biome_css_parser/src/syntax/scss/expression/mod.rs +++ b/crates/biome_css_parser/src/syntax/scss/expression/mod.rs @@ -14,9 +14,9 @@ pub(crate) use interpolation::{ parse_scss_interpolation_with_mode, }; pub(crate) use list::{ - parse_scss_expression, parse_scss_expression_allow_empty_value_until, + complete_empty_scss_expression, parse_required_scss_value_until, parse_scss_expression, parse_scss_expression_in_args_until, parse_scss_expression_in_variable_value_until, - parse_scss_expression_until, + parse_scss_expression_until, parse_scss_optional_value_until, }; pub(crate) use precedence::SCSS_UNARY_OPERATOR_TOKEN_SET; diff --git a/crates/biome_css_parser/src/syntax/scss/expression/primary.rs b/crates/biome_css_parser/src/syntax/scss/expression/primary.rs index 1cf26913e769..31291a5b3918 100644 --- a/crates/biome_css_parser/src/syntax/scss/expression/primary.rs +++ b/crates/biome_css_parser/src/syntax/scss/expression/primary.rs @@ -1,12 +1,12 @@ use crate::parser::CssParser; use crate::syntax::declaration::{is_at_declaration_important, parse_declaration_important}; use crate::syntax::property::parse_generic_component_value; -use crate::syntax::scss::parse_scss_fallback_value; +use crate::syntax::scss::{parse_scss_fallback_value, parse_scss_regular_interpolation}; use biome_css_syntax::T; use biome_parser::Parser; use biome_parser::prelude::ParsedSyntax; -use super::interpolation::{is_at_scss_interpolation, parse_scss_interpolation}; +use super::interpolation::is_at_scss_interpolation; use super::map::parse_scss_parenthesized_or_map_expression; /// Parses SCSS primaries such as parenthesized values, maps, `!important`, and @@ -21,7 +21,7 @@ use super::map::parse_scss_parenthesized_or_map_expression; #[inline] pub(super) fn parse_scss_primary_expression(p: &mut CssParser) -> ParsedSyntax { if is_at_scss_interpolation(p) { - parse_scss_interpolation(p) + parse_scss_regular_interpolation(p) } else if p.at(T!['(']) { parse_scss_parenthesized_or_map_expression(p) } else if is_at_declaration_important(p) { diff --git a/crates/biome_css_parser/src/syntax/scss/identifiers/interpolated_identifier.rs b/crates/biome_css_parser/src/syntax/scss/identifiers/interpolated_identifier.rs index 7d6e6e618100..8d5a4d7ec4ab 100644 --- a/crates/biome_css_parser/src/syntax/scss/identifiers/interpolated_identifier.rs +++ b/crates/biome_css_parser/src/syntax/scss/identifiers/interpolated_identifier.rs @@ -22,14 +22,9 @@ pub(crate) fn is_nth_at_scss_interpolated_identifier(p: &mut CssParser, n: usize /// Parses an identifier that may be formed by adjacent identifier and /// interpolation fragments with no intervening trivia. /// -/// Examples: -/// ```scss -/// .icon-#{$name} -/// .foo-#{$name}-bar -/// ``` -/// -/// Docs: https://sass-lang.com/documentation/interpolation -pub(super) fn parse_scss_regular_interpolated_identifier( +/// This lower-level helper is reused by selector-specific wrappers that need +/// different fragment parsing behavior. +pub(super) fn parse_scss_interpolated_identifier_with( p: &mut CssParser, parse_fragment: fn(&mut CssParser) -> ParsedSyntax, ) -> ParsedSyntax { diff --git a/crates/biome_css_parser/src/syntax/scss/identifiers/interpolated_regular.rs b/crates/biome_css_parser/src/syntax/scss/identifiers/interpolated_regular.rs new file mode 100644 index 000000000000..436147056a8c --- /dev/null +++ b/crates/biome_css_parser/src/syntax/scss/identifiers/interpolated_regular.rs @@ -0,0 +1,43 @@ +use crate::parser::CssParser; +use crate::syntax::parse_regular_identifier; +use crate::syntax::scss::expression::{ScssInterpolationMode, parse_scss_interpolation_with_mode}; +use crate::syntax::scss::identifiers::interpolated_identifier::parse_scss_interpolated_identifier_with; +use crate::syntax::scss::is_at_scss_interpolation; +use biome_parser::prelude::ParsedSyntax; + +/// Parses an identifier that may be formed by adjacent identifier and +/// interpolation fragments with no intervening trivia. +/// +/// Examples: +/// ```scss +/// margin-#{$side} +/// #{$name} +/// ``` +/// +/// Docs: https://sass-lang.com/documentation/interpolation +#[inline] +pub(crate) fn parse_scss_interpolated_identifier(p: &mut CssParser) -> ParsedSyntax { + parse_scss_interpolated_identifier_with(p, parse_regular_interpolated_identifier_any_fragment) +} + +#[inline] +fn parse_regular_interpolated_identifier_any_fragment(p: &mut CssParser) -> ParsedSyntax { + if is_at_scss_interpolation(p) { + parse_scss_regular_interpolation(p) + } else { + parse_regular_identifier(p) + } +} + +/// Parses a standalone SCSS interpolation expression such as `#{$value}`. +/// +/// Example: +/// ```scss +/// $value: #{$name}; +/// ``` +/// +/// Docs: https://sass-lang.com/documentation/interpolation/ +#[inline] +pub(crate) fn parse_scss_regular_interpolation(p: &mut CssParser) -> ParsedSyntax { + parse_scss_interpolation_with_mode(p, ScssInterpolationMode::Regular) +} diff --git a/crates/biome_css_parser/src/syntax/scss/identifiers/interpolated_selector.rs b/crates/biome_css_parser/src/syntax/scss/identifiers/interpolated_selector.rs index 3f4397c625d6..60cdc37c33e5 100644 --- a/crates/biome_css_parser/src/syntax/scss/identifiers/interpolated_selector.rs +++ b/crates/biome_css_parser/src/syntax/scss/identifiers/interpolated_selector.rs @@ -1,6 +1,6 @@ use crate::parser::CssParser; use crate::syntax::scss::expression::{ScssInterpolationMode, parse_scss_interpolation_with_mode}; -use crate::syntax::scss::identifiers::interpolated_identifier::parse_scss_regular_interpolated_identifier; +use crate::syntax::scss::identifiers::interpolated_identifier::parse_scss_interpolated_identifier_with; use crate::syntax::scss::is_at_scss_interpolation; use crate::syntax::selector::{ parse_selector_custom_identifier_fragment, parse_selector_identifier_fragment, @@ -22,7 +22,7 @@ use biome_parser::prelude::ParsedSyntax; /// Docs: https://sass-lang.com/documentation/interpolation/ #[inline] pub(crate) fn parse_scss_selector_interpolated_identifier(p: &mut CssParser) -> ParsedSyntax { - parse_scss_regular_interpolated_identifier(p, parse_scss_selector_identifier_any_fragment) + parse_scss_interpolated_identifier_with(p, parse_scss_selector_identifier_any_fragment) } /// Parses selector custom identifiers such as `.foo-#{$name}` and preserves the @@ -42,10 +42,7 @@ pub(crate) fn parse_scss_selector_interpolated_identifier(p: &mut CssParser) -> pub(crate) fn parse_scss_selector_custom_interpolated_identifier( p: &mut CssParser, ) -> ParsedSyntax { - parse_scss_regular_interpolated_identifier( - p, - parse_scss_selector_custom_identifier_any_fragment, - ) + parse_scss_interpolated_identifier_with(p, parse_scss_selector_custom_identifier_any_fragment) } #[inline] diff --git a/crates/biome_css_parser/src/syntax/scss/identifiers/mod.rs b/crates/biome_css_parser/src/syntax/scss/identifiers/mod.rs index b246805eb6e0..57c7be8f49d8 100644 --- a/crates/biome_css_parser/src/syntax/scss/identifiers/mod.rs +++ b/crates/biome_css_parser/src/syntax/scss/identifiers/mod.rs @@ -1,5 +1,6 @@ mod identifier; mod interpolated_identifier; +mod interpolated_regular; mod interpolated_selector; mod qualified_name; @@ -10,6 +11,9 @@ pub(crate) use identifier::{ pub(crate) use interpolated_identifier::{ is_at_scss_interpolated_identifier, is_nth_at_scss_interpolated_identifier, }; +pub(crate) use interpolated_regular::{ + parse_scss_interpolated_identifier, parse_scss_regular_interpolation, +}; pub(crate) use interpolated_selector::{ parse_scss_selector_custom_interpolated_identifier, parse_scss_selector_interpolated_identifier, }; diff --git a/crates/biome_css_parser/src/syntax/scss/mod.rs b/crates/biome_css_parser/src/syntax/scss/mod.rs index 7ae32ed5f1d6..b5f4c5b3adb0 100644 --- a/crates/biome_css_parser/src/syntax/scss/mod.rs +++ b/crates/biome_css_parser/src/syntax/scss/mod.rs @@ -4,6 +4,7 @@ mod expression; mod function_name; mod identifiers; mod parse_error; +mod property; mod selector; mod token_sets; mod value; @@ -18,25 +19,31 @@ pub(crate) use at_rule::{ }; pub(crate) use declaration::{ is_at_scss_declaration, is_at_scss_nesting_declaration, is_at_scss_variable_modifier_start, - parse_scss_declaration, parse_scss_nesting_declaration, + parse_scss_declaration, parse_scss_interpolated_property_declaration, + parse_scss_nesting_declaration, try_parse_scss_nesting_declaration, }; pub(crate) use expression::{ - SCSS_UNARY_OPERATOR_TOKEN_SET, is_at_scss_interpolation, is_nth_at_scss_interpolation, - parse_scss_expression, parse_scss_expression_allow_empty_value_until, + SCSS_UNARY_OPERATOR_TOKEN_SET, complete_empty_scss_expression, is_at_scss_interpolation, + is_nth_at_scss_interpolation, parse_required_scss_value_until, parse_scss_expression, parse_scss_expression_in_args_until, parse_scss_expression_in_variable_value_until, - parse_scss_expression_until, + parse_scss_expression_until, parse_scss_optional_value_until, }; pub(crate) use function_name::parse_scss_function_name; pub(crate) use identifiers::{ is_at_scss_identifier, is_at_scss_interpolated_identifier, is_at_scss_namespaced_identifier, is_at_scss_qualified_name, is_nth_at_scss_interpolated_identifier, - is_nth_at_scss_qualified_name, parse_scss_identifier, parse_scss_namespaced_identifier, - parse_scss_qualified_name, parse_scss_selector_custom_interpolated_identifier, + is_nth_at_scss_qualified_name, parse_scss_identifier, parse_scss_interpolated_identifier, + parse_scss_namespaced_identifier, parse_scss_qualified_name, parse_scss_regular_interpolation, + parse_scss_selector_custom_interpolated_identifier, parse_scss_selector_interpolated_identifier, }; pub(crate) use parse_error::{ expected_scss_expression, expected_scss_variable_modifier, scss_ellipsis_not_allowed, }; +pub(crate) use property::{ + is_at_scss_interpolated_property, is_nth_at_scss_interpolated_property, + parse_scss_interpolated_property_name, +}; pub(crate) use selector::{is_nth_at_scss_placeholder_selector, parse_scss_placeholder_selector}; pub(crate) use token_sets::{ END_OF_SCSS_EXPRESSION_TOKEN_SET, SCSS_IDENT_CONTINUATION_SET, SCSS_NESTING_VALUE_END_SET, diff --git a/crates/biome_css_parser/src/syntax/scss/property.rs b/crates/biome_css_parser/src/syntax/scss/property.rs new file mode 100644 index 000000000000..222e1e8db72e --- /dev/null +++ b/crates/biome_css_parser/src/syntax/scss/property.rs @@ -0,0 +1,43 @@ +use crate::parser::CssParser; +use crate::syntax::{CssSyntaxFeatures, is_nth_at_identifier}; +use biome_parser::SyntaxFeature; +use biome_parser::prelude::ParsedSyntax; +use biome_parser::prelude::ParsedSyntax::Absent; + +use super::{is_nth_at_scss_interpolation, parse_scss_interpolated_identifier}; + +/// Detects an interpolation-bearing SCSS property name. +/// +/// Unlike [`super::is_at_scss_nesting_declaration`], this also covers +/// interpolation-bearing custom property names such as `--theme-#{$slot}:` +/// that cannot start SCSS nested-property syntax. +/// +/// More generally, this covers names that either start with interpolation or +/// continue into interpolation: +/// +/// ```scss +/// #{$name}: 1px; +/// margin-#{$side}: 1px; +/// ``` +#[inline] +pub(crate) fn is_at_scss_interpolated_property(p: &mut CssParser) -> bool { + is_nth_at_scss_interpolated_property(p, 0) +} + +#[inline] +pub(crate) fn is_nth_at_scss_interpolated_property(p: &mut CssParser, n: usize) -> bool { + CssSyntaxFeatures::Scss.is_supported(p) + //`#{$name}: 1px;` + && (is_nth_at_scss_interpolation(p, n) + //`margin-#{$side}: 1px;` + || (is_nth_at_identifier(p, n) && is_nth_at_scss_interpolation(p, n + 1))) +} + +#[inline] +pub(crate) fn parse_scss_interpolated_property_name(p: &mut CssParser) -> ParsedSyntax { + if !is_at_scss_interpolated_property(p) { + return Absent; + } + + parse_scss_interpolated_identifier(p) +} diff --git a/crates/biome_css_parser/src/token_source.rs b/crates/biome_css_parser/src/token_source.rs index a1913d62f691..07f2d02cb44e 100644 --- a/crates/biome_css_parser/src/token_source.rs +++ b/crates/biome_css_parser/src/token_source.rs @@ -107,6 +107,10 @@ impl TokenSource for CssTokenSource<'_> { self.lexer.has_preceding_line_break() } + fn has_preceding_whitespace(&self) -> bool { + self.lexer.has_preceding_whitespace() + } + fn bump(&mut self) { self.bump_with_context(CssLexContext::Regular) } diff --git a/crates/biome_css_parser/tests/css_test_suite/error/scss/at-rule/page-selector-like-declaration.scss b/crates/biome_css_parser/tests/css_test_suite/error/scss/at-rule/page-selector-like-declaration.scss new file mode 100644 index 000000000000..609c37d5ae08 --- /dev/null +++ b/crates/biome_css_parser/tests/css_test_suite/error/scss/at-rule/page-selector-like-declaration.scss @@ -0,0 +1,13 @@ +@page :left { + font:bold { + color: red; + } + + #{$name}:hover { + color: blue; + } + + font-#{$weight}:bold { + color: green; + } +} diff --git a/crates/biome_css_parser/tests/css_test_suite/error/scss/at-rule/page-selector-like-declaration.scss.snap b/crates/biome_css_parser/tests/css_test_suite/error/scss/at-rule/page-selector-like-declaration.scss.snap new file mode 100644 index 000000000000..43d465e053ba --- /dev/null +++ b/crates/biome_css_parser/tests/css_test_suite/error/scss/at-rule/page-selector-like-declaration.scss.snap @@ -0,0 +1,447 @@ +--- +source: crates/biome_css_parser/tests/spec_test.rs +expression: snapshot +--- + +## Input + +```css +@page :left { + font:bold { + color: red; + } + + #{$name}:hover { + color: blue; + } + + font-#{$weight}:bold { + color: green; + } +} + +``` + + +## AST + +``` +CssRoot { + bom_token: missing (optional), + items: CssRootItemList [ + CssAtRule { + at_token: AT@0..1 "@" [] [], + rule: CssPageAtRule { + page_token: PAGE_KW@1..6 "page" [] [Whitespace(" ")], + selectors: CssPageSelectorList [ + CssPageSelector { + ty: missing (optional), + pseudos: CssPageSelectorPseudoList [ + CssPageSelectorPseudo { + colon_token: COLON@6..7 ":" [] [], + selector: CssIdentifier { + value_token: IDENT@7..12 "left" [] [Whitespace(" ")], + }, + }, + ], + }, + ], + block: CssPageAtRuleBlock { + l_curly_token: L_CURLY@12..13 "{" [] [], + items: CssPageAtRuleItemList [ + CssBogus { + items: [ + CssSelectorList [ + CssCompoundSelector { + nesting_selectors: CssNestedSelectorList [], + simple_selector: CssTypeSelector { + namespace: missing (optional), + ident: CssIdentifier { + value_token: IDENT@13..20 "font" [Newline("\n"), Whitespace(" ")] [], + }, + }, + sub_selectors: CssSubSelectorList [ + CssPseudoClassSelector { + colon_token: COLON@20..21 ":" [] [], + class: CssPseudoClassIdentifier { + name: CssIdentifier { + value_token: IDENT@21..26 "bold" [] [Whitespace(" ")], + }, + }, + }, + ], + }, + ], + CssDeclarationOrRuleBlock { + l_curly_token: L_CURLY@26..27 "{" [] [], + items: CssDeclarationOrRuleList [ + CssDeclarationWithSemicolon { + declaration: CssDeclaration { + property: CssGenericProperty { + name: CssIdentifier { + value_token: IDENT@27..37 "color" [Newline("\n"), Whitespace(" ")] [], + }, + colon_token: COLON@37..39 ":" [] [Whitespace(" ")], + value: ScssExpression { + items: ScssExpressionItemList [ + CssIdentifier { + value_token: IDENT@39..42 "red" [] [], + }, + ], + }, + }, + important: missing (optional), + }, + semicolon_token: SEMICOLON@42..43 ";" [] [], + }, + ], + r_curly_token: R_CURLY@43..47 "}" [Newline("\n"), Whitespace(" ")] [], + }, + ], + }, + CssBogus { + items: [ + CssSelectorList [ + CssCompoundSelector { + nesting_selectors: CssNestedSelectorList [], + simple_selector: CssTypeSelector { + namespace: missing (optional), + ident: ScssInterpolatedIdentifier { + items: ScssInterpolatedIdentifierPartList [ + ScssInterpolation { + hash_token: HASH@47..52 "#" [Newline("\n"), Newline("\n"), Whitespace(" ")] [], + l_curly_token: L_CURLY@52..53 "{" [] [], + value: ScssExpression { + items: ScssExpressionItemList [ + ScssIdentifier { + dollar_token: DOLLAR@53..54 "$" [] [], + name: CssIdentifier { + value_token: IDENT@54..58 "name" [] [], + }, + }, + ], + }, + r_curly_token: R_CURLY@58..59 "}" [] [], + }, + ], + }, + }, + sub_selectors: CssSubSelectorList [ + CssPseudoClassSelector { + colon_token: COLON@59..60 ":" [] [], + class: CssPseudoClassIdentifier { + name: CssIdentifier { + value_token: IDENT@60..66 "hover" [] [Whitespace(" ")], + }, + }, + }, + ], + }, + ], + CssDeclarationOrRuleBlock { + l_curly_token: L_CURLY@66..67 "{" [] [], + items: CssDeclarationOrRuleList [ + CssDeclarationWithSemicolon { + declaration: CssDeclaration { + property: CssGenericProperty { + name: CssIdentifier { + value_token: IDENT@67..77 "color" [Newline("\n"), Whitespace(" ")] [], + }, + colon_token: COLON@77..79 ":" [] [Whitespace(" ")], + value: ScssExpression { + items: ScssExpressionItemList [ + CssIdentifier { + value_token: IDENT@79..83 "blue" [] [], + }, + ], + }, + }, + important: missing (optional), + }, + semicolon_token: SEMICOLON@83..84 ";" [] [], + }, + ], + r_curly_token: R_CURLY@84..88 "}" [Newline("\n"), Whitespace(" ")] [], + }, + ], + }, + CssBogus { + items: [ + CssSelectorList [ + CssCompoundSelector { + nesting_selectors: CssNestedSelectorList [], + simple_selector: CssTypeSelector { + namespace: missing (optional), + ident: ScssInterpolatedIdentifier { + items: ScssInterpolatedIdentifierPartList [ + CssIdentifier { + value_token: IDENT@88..97 "font-" [Newline("\n"), Newline("\n"), Whitespace(" ")] [], + }, + ScssInterpolation { + hash_token: HASH@97..98 "#" [] [], + l_curly_token: L_CURLY@98..99 "{" [] [], + value: ScssExpression { + items: ScssExpressionItemList [ + ScssIdentifier { + dollar_token: DOLLAR@99..100 "$" [] [], + name: CssIdentifier { + value_token: IDENT@100..106 "weight" [] [], + }, + }, + ], + }, + r_curly_token: R_CURLY@106..107 "}" [] [], + }, + ], + }, + }, + sub_selectors: CssSubSelectorList [ + CssPseudoClassSelector { + colon_token: COLON@107..108 ":" [] [], + class: CssPseudoClassIdentifier { + name: CssIdentifier { + value_token: IDENT@108..113 "bold" [] [Whitespace(" ")], + }, + }, + }, + ], + }, + ], + CssDeclarationOrRuleBlock { + l_curly_token: L_CURLY@113..114 "{" [] [], + items: CssDeclarationOrRuleList [ + CssDeclarationWithSemicolon { + declaration: CssDeclaration { + property: CssGenericProperty { + name: CssIdentifier { + value_token: IDENT@114..124 "color" [Newline("\n"), Whitespace(" ")] [], + }, + colon_token: COLON@124..126 ":" [] [Whitespace(" ")], + value: ScssExpression { + items: ScssExpressionItemList [ + CssIdentifier { + value_token: IDENT@126..131 "green" [] [], + }, + ], + }, + }, + important: missing (optional), + }, + semicolon_token: SEMICOLON@131..132 ";" [] [], + }, + ], + r_curly_token: R_CURLY@132..136 "}" [Newline("\n"), Whitespace(" ")] [], + }, + ], + }, + ], + r_curly_token: R_CURLY@136..138 "}" [Newline("\n")] [], + }, + }, + }, + ], + eof_token: EOF@138..139 "" [Newline("\n")] [], +} +``` + +## CST + +``` +0: CSS_ROOT@0..139 + 0: (empty) + 1: CSS_ROOT_ITEM_LIST@0..138 + 0: CSS_AT_RULE@0..138 + 0: AT@0..1 "@" [] [] + 1: CSS_PAGE_AT_RULE@1..138 + 0: PAGE_KW@1..6 "page" [] [Whitespace(" ")] + 1: CSS_PAGE_SELECTOR_LIST@6..12 + 0: CSS_PAGE_SELECTOR@6..12 + 0: (empty) + 1: CSS_PAGE_SELECTOR_PSEUDO_LIST@6..12 + 0: CSS_PAGE_SELECTOR_PSEUDO@6..12 + 0: COLON@6..7 ":" [] [] + 1: CSS_IDENTIFIER@7..12 + 0: IDENT@7..12 "left" [] [Whitespace(" ")] + 2: CSS_PAGE_AT_RULE_BLOCK@12..138 + 0: L_CURLY@12..13 "{" [] [] + 1: CSS_PAGE_AT_RULE_ITEM_LIST@13..136 + 0: CSS_BOGUS@13..47 + 0: CSS_SELECTOR_LIST@13..26 + 0: CSS_COMPOUND_SELECTOR@13..26 + 0: CSS_NESTED_SELECTOR_LIST@13..13 + 1: CSS_TYPE_SELECTOR@13..20 + 0: (empty) + 1: CSS_IDENTIFIER@13..20 + 0: IDENT@13..20 "font" [Newline("\n"), Whitespace(" ")] [] + 2: CSS_SUB_SELECTOR_LIST@20..26 + 0: CSS_PSEUDO_CLASS_SELECTOR@20..26 + 0: COLON@20..21 ":" [] [] + 1: CSS_PSEUDO_CLASS_IDENTIFIER@21..26 + 0: CSS_IDENTIFIER@21..26 + 0: IDENT@21..26 "bold" [] [Whitespace(" ")] + 1: CSS_DECLARATION_OR_RULE_BLOCK@26..47 + 0: L_CURLY@26..27 "{" [] [] + 1: CSS_DECLARATION_OR_RULE_LIST@27..43 + 0: CSS_DECLARATION_WITH_SEMICOLON@27..43 + 0: CSS_DECLARATION@27..42 + 0: CSS_GENERIC_PROPERTY@27..42 + 0: CSS_IDENTIFIER@27..37 + 0: IDENT@27..37 "color" [Newline("\n"), Whitespace(" ")] [] + 1: COLON@37..39 ":" [] [Whitespace(" ")] + 2: SCSS_EXPRESSION@39..42 + 0: SCSS_EXPRESSION_ITEM_LIST@39..42 + 0: CSS_IDENTIFIER@39..42 + 0: IDENT@39..42 "red" [] [] + 1: (empty) + 1: SEMICOLON@42..43 ";" [] [] + 2: R_CURLY@43..47 "}" [Newline("\n"), Whitespace(" ")] [] + 1: CSS_BOGUS@47..88 + 0: CSS_SELECTOR_LIST@47..66 + 0: CSS_COMPOUND_SELECTOR@47..66 + 0: CSS_NESTED_SELECTOR_LIST@47..47 + 1: CSS_TYPE_SELECTOR@47..59 + 0: (empty) + 1: SCSS_INTERPOLATED_IDENTIFIER@47..59 + 0: SCSS_INTERPOLATED_IDENTIFIER_PART_LIST@47..59 + 0: SCSS_INTERPOLATION@47..59 + 0: HASH@47..52 "#" [Newline("\n"), Newline("\n"), Whitespace(" ")] [] + 1: L_CURLY@52..53 "{" [] [] + 2: SCSS_EXPRESSION@53..58 + 0: SCSS_EXPRESSION_ITEM_LIST@53..58 + 0: SCSS_IDENTIFIER@53..58 + 0: DOLLAR@53..54 "$" [] [] + 1: CSS_IDENTIFIER@54..58 + 0: IDENT@54..58 "name" [] [] + 3: R_CURLY@58..59 "}" [] [] + 2: CSS_SUB_SELECTOR_LIST@59..66 + 0: CSS_PSEUDO_CLASS_SELECTOR@59..66 + 0: COLON@59..60 ":" [] [] + 1: CSS_PSEUDO_CLASS_IDENTIFIER@60..66 + 0: CSS_IDENTIFIER@60..66 + 0: IDENT@60..66 "hover" [] [Whitespace(" ")] + 1: CSS_DECLARATION_OR_RULE_BLOCK@66..88 + 0: L_CURLY@66..67 "{" [] [] + 1: CSS_DECLARATION_OR_RULE_LIST@67..84 + 0: CSS_DECLARATION_WITH_SEMICOLON@67..84 + 0: CSS_DECLARATION@67..83 + 0: CSS_GENERIC_PROPERTY@67..83 + 0: CSS_IDENTIFIER@67..77 + 0: IDENT@67..77 "color" [Newline("\n"), Whitespace(" ")] [] + 1: COLON@77..79 ":" [] [Whitespace(" ")] + 2: SCSS_EXPRESSION@79..83 + 0: SCSS_EXPRESSION_ITEM_LIST@79..83 + 0: CSS_IDENTIFIER@79..83 + 0: IDENT@79..83 "blue" [] [] + 1: (empty) + 1: SEMICOLON@83..84 ";" [] [] + 2: R_CURLY@84..88 "}" [Newline("\n"), Whitespace(" ")] [] + 2: CSS_BOGUS@88..136 + 0: CSS_SELECTOR_LIST@88..113 + 0: CSS_COMPOUND_SELECTOR@88..113 + 0: CSS_NESTED_SELECTOR_LIST@88..88 + 1: CSS_TYPE_SELECTOR@88..107 + 0: (empty) + 1: SCSS_INTERPOLATED_IDENTIFIER@88..107 + 0: SCSS_INTERPOLATED_IDENTIFIER_PART_LIST@88..107 + 0: CSS_IDENTIFIER@88..97 + 0: IDENT@88..97 "font-" [Newline("\n"), Newline("\n"), Whitespace(" ")] [] + 1: SCSS_INTERPOLATION@97..107 + 0: HASH@97..98 "#" [] [] + 1: L_CURLY@98..99 "{" [] [] + 2: SCSS_EXPRESSION@99..106 + 0: SCSS_EXPRESSION_ITEM_LIST@99..106 + 0: SCSS_IDENTIFIER@99..106 + 0: DOLLAR@99..100 "$" [] [] + 1: CSS_IDENTIFIER@100..106 + 0: IDENT@100..106 "weight" [] [] + 3: R_CURLY@106..107 "}" [] [] + 2: CSS_SUB_SELECTOR_LIST@107..113 + 0: CSS_PSEUDO_CLASS_SELECTOR@107..113 + 0: COLON@107..108 ":" [] [] + 1: CSS_PSEUDO_CLASS_IDENTIFIER@108..113 + 0: CSS_IDENTIFIER@108..113 + 0: IDENT@108..113 "bold" [] [Whitespace(" ")] + 1: CSS_DECLARATION_OR_RULE_BLOCK@113..136 + 0: L_CURLY@113..114 "{" [] [] + 1: CSS_DECLARATION_OR_RULE_LIST@114..132 + 0: CSS_DECLARATION_WITH_SEMICOLON@114..132 + 0: CSS_DECLARATION@114..131 + 0: CSS_GENERIC_PROPERTY@114..131 + 0: CSS_IDENTIFIER@114..124 + 0: IDENT@114..124 "color" [Newline("\n"), Whitespace(" ")] [] + 1: COLON@124..126 ":" [] [Whitespace(" ")] + 2: SCSS_EXPRESSION@126..131 + 0: SCSS_EXPRESSION_ITEM_LIST@126..131 + 0: CSS_IDENTIFIER@126..131 + 0: IDENT@126..131 "green" [] [] + 1: (empty) + 1: SEMICOLON@131..132 ";" [] [] + 2: R_CURLY@132..136 "}" [Newline("\n"), Whitespace(" ")] [] + 2: R_CURLY@136..138 "}" [Newline("\n")] [] + 2: EOF@138..139 "" [Newline("\n")] [] + +``` + +## Diagnostics + +``` +page-selector-like-declaration.scss:2:3 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × Unexpected value or character. + + 1 │ @page :left { + > 2 │ font:bold { + │ ^^^^^^^^^^^ + > 3 │ color: red; + > 4 │ } + │ ^ + 5 │ + 6 │ #{$name}:hover { + + i Expected one of: + + - declaration + - at rule + - margin at rule + +page-selector-like-declaration.scss:6:3 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × Unexpected value or character. + + 4 │ } + 5 │ + > 6 │ #{$name}:hover { + │ ^^^^^^^^^^^^^^^^ + > 7 │ color: blue; + > 8 │ } + │ ^ + 9 │ + 10 │ font-#{$weight}:bold { + + i Expected one of: + + - declaration + - at rule + - margin at rule + +page-selector-like-declaration.scss:10:3 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × Unexpected value or character. + + 8 │ } + 9 │ + > 10 │ font-#{$weight}:bold { + │ ^^^^^^^^^^^^^^^^^^^^^^ + > 11 │ color: green; + > 12 │ } + │ ^ + 13 │ } + 14 │ + + i Expected one of: + + - declaration + - at rule + - margin at rule + +``` diff --git a/crates/biome_css_parser/tests/css_test_suite/error/scss/at-rule/supports-interpolated-property-missing-colon.scss b/crates/biome_css_parser/tests/css_test_suite/error/scss/at-rule/supports-interpolated-property-missing-colon.scss new file mode 100644 index 000000000000..5d04c4402c1f --- /dev/null +++ b/crates/biome_css_parser/tests/css_test_suite/error/scss/at-rule/supports-interpolated-property-missing-colon.scss @@ -0,0 +1,11 @@ +@supports (#{$name}) { + .a { + color: red; + } +} + +@supports (margin-#{$side}) { + .b { + color: blue; + } +} diff --git a/crates/biome_css_parser/tests/css_test_suite/error/scss/at-rule/supports-interpolated-property-missing-colon.scss.snap b/crates/biome_css_parser/tests/css_test_suite/error/scss/at-rule/supports-interpolated-property-missing-colon.scss.snap new file mode 100644 index 000000000000..da3e4ad15a62 --- /dev/null +++ b/crates/biome_css_parser/tests/css_test_suite/error/scss/at-rule/supports-interpolated-property-missing-colon.scss.snap @@ -0,0 +1,363 @@ +--- +source: crates/biome_css_parser/tests/spec_test.rs +expression: snapshot +--- + +## Input + +```css +@supports (#{$name}) { + .a { + color: red; + } +} + +@supports (margin-#{$side}) { + .b { + color: blue; + } +} + +``` + + +## AST + +``` +CssRoot { + bom_token: missing (optional), + items: CssRootItemList [ + CssAtRule { + at_token: AT@0..1 "@" [] [], + rule: CssSupportsAtRule { + declarator: CssSupportsAtRuleDeclarator { + supports_token: SUPPORTS_KW@1..10 "supports" [] [Whitespace(" ")], + condition: CssSupportsFeatureDeclaration { + l_paren_token: L_PAREN@10..11 "(" [] [], + declaration: CssDeclaration { + property: CssGenericProperty { + name: ScssInterpolatedIdentifier { + items: ScssInterpolatedIdentifierPartList [ + ScssInterpolation { + hash_token: HASH@11..12 "#" [] [], + l_curly_token: L_CURLY@12..13 "{" [] [], + value: ScssExpression { + items: ScssExpressionItemList [ + ScssIdentifier { + dollar_token: DOLLAR@13..14 "$" [] [], + name: CssIdentifier { + value_token: IDENT@14..18 "name" [] [], + }, + }, + ], + }, + r_curly_token: R_CURLY@18..19 "}" [] [], + }, + ], + }, + colon_token: missing (required), + value: ScssExpression { + items: ScssExpressionItemList [], + }, + }, + important: missing (optional), + }, + r_paren_token: R_PAREN@19..21 ")" [] [Whitespace(" ")], + }, + }, + block: CssRuleBlock { + l_curly_token: L_CURLY@21..22 "{" [] [], + rules: CssRuleList [ + CssQualifiedRule { + prelude: CssSelectorList [ + CssCompoundSelector { + nesting_selectors: CssNestedSelectorList [], + simple_selector: missing (optional), + sub_selectors: CssSubSelectorList [ + CssClassSelector { + dot_token: DOT@22..26 "." [Newline("\n"), Whitespace(" ")] [], + name: CssCustomIdentifier { + value_token: IDENT@26..28 "a" [] [Whitespace(" ")], + }, + }, + ], + }, + ], + block: CssDeclarationOrRuleBlock { + l_curly_token: L_CURLY@28..29 "{" [] [], + items: CssDeclarationOrRuleList [ + CssDeclarationWithSemicolon { + declaration: CssDeclaration { + property: CssGenericProperty { + name: CssIdentifier { + value_token: IDENT@29..39 "color" [Newline("\n"), Whitespace(" ")] [], + }, + colon_token: COLON@39..41 ":" [] [Whitespace(" ")], + value: ScssExpression { + items: ScssExpressionItemList [ + CssIdentifier { + value_token: IDENT@41..44 "red" [] [], + }, + ], + }, + }, + important: missing (optional), + }, + semicolon_token: SEMICOLON@44..45 ";" [] [], + }, + ], + r_curly_token: R_CURLY@45..49 "}" [Newline("\n"), Whitespace(" ")] [], + }, + }, + ], + r_curly_token: R_CURLY@49..51 "}" [Newline("\n")] [], + }, + }, + }, + CssAtRule { + at_token: AT@51..54 "@" [Newline("\n"), Newline("\n")] [], + rule: CssSupportsAtRule { + declarator: CssSupportsAtRuleDeclarator { + supports_token: SUPPORTS_KW@54..63 "supports" [] [Whitespace(" ")], + condition: CssSupportsFeatureDeclaration { + l_paren_token: L_PAREN@63..64 "(" [] [], + declaration: CssDeclaration { + property: CssGenericProperty { + name: ScssInterpolatedIdentifier { + items: ScssInterpolatedIdentifierPartList [ + CssIdentifier { + value_token: IDENT@64..71 "margin-" [] [], + }, + ScssInterpolation { + hash_token: HASH@71..72 "#" [] [], + l_curly_token: L_CURLY@72..73 "{" [] [], + value: ScssExpression { + items: ScssExpressionItemList [ + ScssIdentifier { + dollar_token: DOLLAR@73..74 "$" [] [], + name: CssIdentifier { + value_token: IDENT@74..78 "side" [] [], + }, + }, + ], + }, + r_curly_token: R_CURLY@78..79 "}" [] [], + }, + ], + }, + colon_token: missing (required), + value: ScssExpression { + items: ScssExpressionItemList [], + }, + }, + important: missing (optional), + }, + r_paren_token: R_PAREN@79..81 ")" [] [Whitespace(" ")], + }, + }, + block: CssRuleBlock { + l_curly_token: L_CURLY@81..82 "{" [] [], + rules: CssRuleList [ + CssQualifiedRule { + prelude: CssSelectorList [ + CssCompoundSelector { + nesting_selectors: CssNestedSelectorList [], + simple_selector: missing (optional), + sub_selectors: CssSubSelectorList [ + CssClassSelector { + dot_token: DOT@82..86 "." [Newline("\n"), Whitespace(" ")] [], + name: CssCustomIdentifier { + value_token: IDENT@86..88 "b" [] [Whitespace(" ")], + }, + }, + ], + }, + ], + block: CssDeclarationOrRuleBlock { + l_curly_token: L_CURLY@88..89 "{" [] [], + items: CssDeclarationOrRuleList [ + CssDeclarationWithSemicolon { + declaration: CssDeclaration { + property: CssGenericProperty { + name: CssIdentifier { + value_token: IDENT@89..99 "color" [Newline("\n"), Whitespace(" ")] [], + }, + colon_token: COLON@99..101 ":" [] [Whitespace(" ")], + value: ScssExpression { + items: ScssExpressionItemList [ + CssIdentifier { + value_token: IDENT@101..105 "blue" [] [], + }, + ], + }, + }, + important: missing (optional), + }, + semicolon_token: SEMICOLON@105..106 ";" [] [], + }, + ], + r_curly_token: R_CURLY@106..110 "}" [Newline("\n"), Whitespace(" ")] [], + }, + }, + ], + r_curly_token: R_CURLY@110..112 "}" [Newline("\n")] [], + }, + }, + }, + ], + eof_token: EOF@112..113 "" [Newline("\n")] [], +} +``` + +## CST + +``` +0: CSS_ROOT@0..113 + 0: (empty) + 1: CSS_ROOT_ITEM_LIST@0..112 + 0: CSS_AT_RULE@0..51 + 0: AT@0..1 "@" [] [] + 1: CSS_SUPPORTS_AT_RULE@1..51 + 0: CSS_SUPPORTS_AT_RULE_DECLARATOR@1..21 + 0: SUPPORTS_KW@1..10 "supports" [] [Whitespace(" ")] + 1: CSS_SUPPORTS_FEATURE_DECLARATION@10..21 + 0: L_PAREN@10..11 "(" [] [] + 1: CSS_DECLARATION@11..19 + 0: CSS_GENERIC_PROPERTY@11..19 + 0: SCSS_INTERPOLATED_IDENTIFIER@11..19 + 0: SCSS_INTERPOLATED_IDENTIFIER_PART_LIST@11..19 + 0: SCSS_INTERPOLATION@11..19 + 0: HASH@11..12 "#" [] [] + 1: L_CURLY@12..13 "{" [] [] + 2: SCSS_EXPRESSION@13..18 + 0: SCSS_EXPRESSION_ITEM_LIST@13..18 + 0: SCSS_IDENTIFIER@13..18 + 0: DOLLAR@13..14 "$" [] [] + 1: CSS_IDENTIFIER@14..18 + 0: IDENT@14..18 "name" [] [] + 3: R_CURLY@18..19 "}" [] [] + 1: (empty) + 2: SCSS_EXPRESSION@19..19 + 0: SCSS_EXPRESSION_ITEM_LIST@19..19 + 1: (empty) + 2: R_PAREN@19..21 ")" [] [Whitespace(" ")] + 1: CSS_RULE_BLOCK@21..51 + 0: L_CURLY@21..22 "{" [] [] + 1: CSS_RULE_LIST@22..49 + 0: CSS_QUALIFIED_RULE@22..49 + 0: CSS_SELECTOR_LIST@22..28 + 0: CSS_COMPOUND_SELECTOR@22..28 + 0: CSS_NESTED_SELECTOR_LIST@22..22 + 1: (empty) + 2: CSS_SUB_SELECTOR_LIST@22..28 + 0: CSS_CLASS_SELECTOR@22..28 + 0: DOT@22..26 "." [Newline("\n"), Whitespace(" ")] [] + 1: CSS_CUSTOM_IDENTIFIER@26..28 + 0: IDENT@26..28 "a" [] [Whitespace(" ")] + 1: CSS_DECLARATION_OR_RULE_BLOCK@28..49 + 0: L_CURLY@28..29 "{" [] [] + 1: CSS_DECLARATION_OR_RULE_LIST@29..45 + 0: CSS_DECLARATION_WITH_SEMICOLON@29..45 + 0: CSS_DECLARATION@29..44 + 0: CSS_GENERIC_PROPERTY@29..44 + 0: CSS_IDENTIFIER@29..39 + 0: IDENT@29..39 "color" [Newline("\n"), Whitespace(" ")] [] + 1: COLON@39..41 ":" [] [Whitespace(" ")] + 2: SCSS_EXPRESSION@41..44 + 0: SCSS_EXPRESSION_ITEM_LIST@41..44 + 0: CSS_IDENTIFIER@41..44 + 0: IDENT@41..44 "red" [] [] + 1: (empty) + 1: SEMICOLON@44..45 ";" [] [] + 2: R_CURLY@45..49 "}" [Newline("\n"), Whitespace(" ")] [] + 2: R_CURLY@49..51 "}" [Newline("\n")] [] + 1: CSS_AT_RULE@51..112 + 0: AT@51..54 "@" [Newline("\n"), Newline("\n")] [] + 1: CSS_SUPPORTS_AT_RULE@54..112 + 0: CSS_SUPPORTS_AT_RULE_DECLARATOR@54..81 + 0: SUPPORTS_KW@54..63 "supports" [] [Whitespace(" ")] + 1: CSS_SUPPORTS_FEATURE_DECLARATION@63..81 + 0: L_PAREN@63..64 "(" [] [] + 1: CSS_DECLARATION@64..79 + 0: CSS_GENERIC_PROPERTY@64..79 + 0: SCSS_INTERPOLATED_IDENTIFIER@64..79 + 0: SCSS_INTERPOLATED_IDENTIFIER_PART_LIST@64..79 + 0: CSS_IDENTIFIER@64..71 + 0: IDENT@64..71 "margin-" [] [] + 1: SCSS_INTERPOLATION@71..79 + 0: HASH@71..72 "#" [] [] + 1: L_CURLY@72..73 "{" [] [] + 2: SCSS_EXPRESSION@73..78 + 0: SCSS_EXPRESSION_ITEM_LIST@73..78 + 0: SCSS_IDENTIFIER@73..78 + 0: DOLLAR@73..74 "$" [] [] + 1: CSS_IDENTIFIER@74..78 + 0: IDENT@74..78 "side" [] [] + 3: R_CURLY@78..79 "}" [] [] + 1: (empty) + 2: SCSS_EXPRESSION@79..79 + 0: SCSS_EXPRESSION_ITEM_LIST@79..79 + 1: (empty) + 2: R_PAREN@79..81 ")" [] [Whitespace(" ")] + 1: CSS_RULE_BLOCK@81..112 + 0: L_CURLY@81..82 "{" [] [] + 1: CSS_RULE_LIST@82..110 + 0: CSS_QUALIFIED_RULE@82..110 + 0: CSS_SELECTOR_LIST@82..88 + 0: CSS_COMPOUND_SELECTOR@82..88 + 0: CSS_NESTED_SELECTOR_LIST@82..82 + 1: (empty) + 2: CSS_SUB_SELECTOR_LIST@82..88 + 0: CSS_CLASS_SELECTOR@82..88 + 0: DOT@82..86 "." [Newline("\n"), Whitespace(" ")] [] + 1: CSS_CUSTOM_IDENTIFIER@86..88 + 0: IDENT@86..88 "b" [] [Whitespace(" ")] + 1: CSS_DECLARATION_OR_RULE_BLOCK@88..110 + 0: L_CURLY@88..89 "{" [] [] + 1: CSS_DECLARATION_OR_RULE_LIST@89..106 + 0: CSS_DECLARATION_WITH_SEMICOLON@89..106 + 0: CSS_DECLARATION@89..105 + 0: CSS_GENERIC_PROPERTY@89..105 + 0: CSS_IDENTIFIER@89..99 + 0: IDENT@89..99 "color" [Newline("\n"), Whitespace(" ")] [] + 1: COLON@99..101 ":" [] [Whitespace(" ")] + 2: SCSS_EXPRESSION@101..105 + 0: SCSS_EXPRESSION_ITEM_LIST@101..105 + 0: CSS_IDENTIFIER@101..105 + 0: IDENT@101..105 "blue" [] [] + 1: (empty) + 1: SEMICOLON@105..106 ";" [] [] + 2: R_CURLY@106..110 "}" [Newline("\n"), Whitespace(" ")] [] + 2: R_CURLY@110..112 "}" [Newline("\n")] [] + 2: EOF@112..113 "" [Newline("\n")] [] + +``` + +## Diagnostics + +``` +supports-interpolated-property-missing-colon.scss:1:20 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × expected `:` but instead found `)` + + > 1 │ @supports (#{$name}) { + │ ^ + 2 │ .a { + 3 │ color: red; + + i Remove ) + +supports-interpolated-property-missing-colon.scss:7:27 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × expected `:` but instead found `)` + + 5 │ } + 6 │ + > 7 │ @supports (margin-#{$side}) { + │ ^ + 8 │ .b { + 9 │ color: blue; + + i Remove ) + +``` diff --git a/crates/biome_css_parser/tests/css_test_suite/error/scss/at-rule/supports-interpolated-property-missing-value.scss b/crates/biome_css_parser/tests/css_test_suite/error/scss/at-rule/supports-interpolated-property-missing-value.scss new file mode 100644 index 000000000000..2bd576032ebe --- /dev/null +++ b/crates/biome_css_parser/tests/css_test_suite/error/scss/at-rule/supports-interpolated-property-missing-value.scss @@ -0,0 +1,29 @@ +@supports (#{$name}:) { + .a { + color: red; + } +} + +@supports not (#{$name}:) { + .a { + color: red; + } +} + +@supports (margin-#{$side}:) { + .b { + color: blue; + } +} + +@supports (#{$name}:) or (display: grid) { + .b { + color: blue; + } +} + +@supports (--theme-#{$slot}:) { + .c { + color: green; + } +} diff --git a/crates/biome_css_parser/tests/css_test_suite/error/scss/at-rule/supports-interpolated-property-missing-value.scss.snap b/crates/biome_css_parser/tests/css_test_suite/error/scss/at-rule/supports-interpolated-property-missing-value.scss.snap new file mode 100644 index 000000000000..76f66e5bf66b --- /dev/null +++ b/crates/biome_css_parser/tests/css_test_suite/error/scss/at-rule/supports-interpolated-property-missing-value.scss.snap @@ -0,0 +1,937 @@ +--- +source: crates/biome_css_parser/tests/spec_test.rs +expression: snapshot +--- + +## Input + +```css +@supports (#{$name}:) { + .a { + color: red; + } +} + +@supports not (#{$name}:) { + .a { + color: red; + } +} + +@supports (margin-#{$side}:) { + .b { + color: blue; + } +} + +@supports (#{$name}:) or (display: grid) { + .b { + color: blue; + } +} + +@supports (--theme-#{$slot}:) { + .c { + color: green; + } +} + +``` + + +## AST + +``` +CssRoot { + bom_token: missing (optional), + items: CssRootItemList [ + CssAtRule { + at_token: AT@0..1 "@" [] [], + rule: CssSupportsAtRule { + declarator: CssSupportsAtRuleDeclarator { + supports_token: SUPPORTS_KW@1..10 "supports" [] [Whitespace(" ")], + condition: CssSupportsFeatureDeclaration { + l_paren_token: L_PAREN@10..11 "(" [] [], + declaration: CssDeclaration { + property: CssGenericProperty { + name: ScssInterpolatedIdentifier { + items: ScssInterpolatedIdentifierPartList [ + ScssInterpolation { + hash_token: HASH@11..12 "#" [] [], + l_curly_token: L_CURLY@12..13 "{" [] [], + value: ScssExpression { + items: ScssExpressionItemList [ + ScssIdentifier { + dollar_token: DOLLAR@13..14 "$" [] [], + name: CssIdentifier { + value_token: IDENT@14..18 "name" [] [], + }, + }, + ], + }, + r_curly_token: R_CURLY@18..19 "}" [] [], + }, + ], + }, + colon_token: COLON@19..20 ":" [] [], + value: ScssExpression { + items: ScssExpressionItemList [], + }, + }, + important: missing (optional), + }, + r_paren_token: R_PAREN@20..22 ")" [] [Whitespace(" ")], + }, + }, + block: CssRuleBlock { + l_curly_token: L_CURLY@22..23 "{" [] [], + rules: CssRuleList [ + CssQualifiedRule { + prelude: CssSelectorList [ + CssCompoundSelector { + nesting_selectors: CssNestedSelectorList [], + simple_selector: missing (optional), + sub_selectors: CssSubSelectorList [ + CssClassSelector { + dot_token: DOT@23..27 "." [Newline("\n"), Whitespace(" ")] [], + name: CssCustomIdentifier { + value_token: IDENT@27..29 "a" [] [Whitespace(" ")], + }, + }, + ], + }, + ], + block: CssDeclarationOrRuleBlock { + l_curly_token: L_CURLY@29..30 "{" [] [], + items: CssDeclarationOrRuleList [ + CssDeclarationWithSemicolon { + declaration: CssDeclaration { + property: CssGenericProperty { + name: CssIdentifier { + value_token: IDENT@30..40 "color" [Newline("\n"), Whitespace(" ")] [], + }, + colon_token: COLON@40..42 ":" [] [Whitespace(" ")], + value: ScssExpression { + items: ScssExpressionItemList [ + CssIdentifier { + value_token: IDENT@42..45 "red" [] [], + }, + ], + }, + }, + important: missing (optional), + }, + semicolon_token: SEMICOLON@45..46 ";" [] [], + }, + ], + r_curly_token: R_CURLY@46..50 "}" [Newline("\n"), Whitespace(" ")] [], + }, + }, + ], + r_curly_token: R_CURLY@50..52 "}" [Newline("\n")] [], + }, + }, + }, + CssAtRule { + at_token: AT@52..55 "@" [Newline("\n"), Newline("\n")] [], + rule: CssSupportsAtRule { + declarator: CssSupportsAtRuleDeclarator { + supports_token: SUPPORTS_KW@55..64 "supports" [] [Whitespace(" ")], + condition: CssSupportsNotCondition { + not_token: NOT_KW@64..68 "not" [] [Whitespace(" ")], + query: CssSupportsFeatureDeclaration { + l_paren_token: L_PAREN@68..69 "(" [] [], + declaration: CssDeclaration { + property: CssGenericProperty { + name: ScssInterpolatedIdentifier { + items: ScssInterpolatedIdentifierPartList [ + ScssInterpolation { + hash_token: HASH@69..70 "#" [] [], + l_curly_token: L_CURLY@70..71 "{" [] [], + value: ScssExpression { + items: ScssExpressionItemList [ + ScssIdentifier { + dollar_token: DOLLAR@71..72 "$" [] [], + name: CssIdentifier { + value_token: IDENT@72..76 "name" [] [], + }, + }, + ], + }, + r_curly_token: R_CURLY@76..77 "}" [] [], + }, + ], + }, + colon_token: COLON@77..78 ":" [] [], + value: ScssExpression { + items: ScssExpressionItemList [], + }, + }, + important: missing (optional), + }, + r_paren_token: R_PAREN@78..80 ")" [] [Whitespace(" ")], + }, + }, + }, + block: CssRuleBlock { + l_curly_token: L_CURLY@80..81 "{" [] [], + rules: CssRuleList [ + CssQualifiedRule { + prelude: CssSelectorList [ + CssCompoundSelector { + nesting_selectors: CssNestedSelectorList [], + simple_selector: missing (optional), + sub_selectors: CssSubSelectorList [ + CssClassSelector { + dot_token: DOT@81..85 "." [Newline("\n"), Whitespace(" ")] [], + name: CssCustomIdentifier { + value_token: IDENT@85..87 "a" [] [Whitespace(" ")], + }, + }, + ], + }, + ], + block: CssDeclarationOrRuleBlock { + l_curly_token: L_CURLY@87..88 "{" [] [], + items: CssDeclarationOrRuleList [ + CssDeclarationWithSemicolon { + declaration: CssDeclaration { + property: CssGenericProperty { + name: CssIdentifier { + value_token: IDENT@88..98 "color" [Newline("\n"), Whitespace(" ")] [], + }, + colon_token: COLON@98..100 ":" [] [Whitespace(" ")], + value: ScssExpression { + items: ScssExpressionItemList [ + CssIdentifier { + value_token: IDENT@100..103 "red" [] [], + }, + ], + }, + }, + important: missing (optional), + }, + semicolon_token: SEMICOLON@103..104 ";" [] [], + }, + ], + r_curly_token: R_CURLY@104..108 "}" [Newline("\n"), Whitespace(" ")] [], + }, + }, + ], + r_curly_token: R_CURLY@108..110 "}" [Newline("\n")] [], + }, + }, + }, + CssAtRule { + at_token: AT@110..113 "@" [Newline("\n"), Newline("\n")] [], + rule: CssSupportsAtRule { + declarator: CssSupportsAtRuleDeclarator { + supports_token: SUPPORTS_KW@113..122 "supports" [] [Whitespace(" ")], + condition: CssSupportsFeatureDeclaration { + l_paren_token: L_PAREN@122..123 "(" [] [], + declaration: CssDeclaration { + property: CssGenericProperty { + name: ScssInterpolatedIdentifier { + items: ScssInterpolatedIdentifierPartList [ + CssIdentifier { + value_token: IDENT@123..130 "margin-" [] [], + }, + ScssInterpolation { + hash_token: HASH@130..131 "#" [] [], + l_curly_token: L_CURLY@131..132 "{" [] [], + value: ScssExpression { + items: ScssExpressionItemList [ + ScssIdentifier { + dollar_token: DOLLAR@132..133 "$" [] [], + name: CssIdentifier { + value_token: IDENT@133..137 "side" [] [], + }, + }, + ], + }, + r_curly_token: R_CURLY@137..138 "}" [] [], + }, + ], + }, + colon_token: COLON@138..139 ":" [] [], + value: ScssExpression { + items: ScssExpressionItemList [], + }, + }, + important: missing (optional), + }, + r_paren_token: R_PAREN@139..141 ")" [] [Whitespace(" ")], + }, + }, + block: CssRuleBlock { + l_curly_token: L_CURLY@141..142 "{" [] [], + rules: CssRuleList [ + CssQualifiedRule { + prelude: CssSelectorList [ + CssCompoundSelector { + nesting_selectors: CssNestedSelectorList [], + simple_selector: missing (optional), + sub_selectors: CssSubSelectorList [ + CssClassSelector { + dot_token: DOT@142..146 "." [Newline("\n"), Whitespace(" ")] [], + name: CssCustomIdentifier { + value_token: IDENT@146..148 "b" [] [Whitespace(" ")], + }, + }, + ], + }, + ], + block: CssDeclarationOrRuleBlock { + l_curly_token: L_CURLY@148..149 "{" [] [], + items: CssDeclarationOrRuleList [ + CssDeclarationWithSemicolon { + declaration: CssDeclaration { + property: CssGenericProperty { + name: CssIdentifier { + value_token: IDENT@149..159 "color" [Newline("\n"), Whitespace(" ")] [], + }, + colon_token: COLON@159..161 ":" [] [Whitespace(" ")], + value: ScssExpression { + items: ScssExpressionItemList [ + CssIdentifier { + value_token: IDENT@161..165 "blue" [] [], + }, + ], + }, + }, + important: missing (optional), + }, + semicolon_token: SEMICOLON@165..166 ";" [] [], + }, + ], + r_curly_token: R_CURLY@166..170 "}" [Newline("\n"), Whitespace(" ")] [], + }, + }, + ], + r_curly_token: R_CURLY@170..172 "}" [Newline("\n")] [], + }, + }, + }, + CssAtRule { + at_token: AT@172..175 "@" [Newline("\n"), Newline("\n")] [], + rule: CssSupportsAtRule { + declarator: CssSupportsAtRuleDeclarator { + supports_token: SUPPORTS_KW@175..184 "supports" [] [Whitespace(" ")], + condition: CssSupportsOrCondition { + left: CssSupportsFeatureDeclaration { + l_paren_token: L_PAREN@184..185 "(" [] [], + declaration: CssDeclaration { + property: CssGenericProperty { + name: ScssInterpolatedIdentifier { + items: ScssInterpolatedIdentifierPartList [ + ScssInterpolation { + hash_token: HASH@185..186 "#" [] [], + l_curly_token: L_CURLY@186..187 "{" [] [], + value: ScssExpression { + items: ScssExpressionItemList [ + ScssIdentifier { + dollar_token: DOLLAR@187..188 "$" [] [], + name: CssIdentifier { + value_token: IDENT@188..192 "name" [] [], + }, + }, + ], + }, + r_curly_token: R_CURLY@192..193 "}" [] [], + }, + ], + }, + colon_token: COLON@193..194 ":" [] [], + value: ScssExpression { + items: ScssExpressionItemList [], + }, + }, + important: missing (optional), + }, + r_paren_token: R_PAREN@194..196 ")" [] [Whitespace(" ")], + }, + or_token: OR_KW@196..199 "or" [] [Whitespace(" ")], + right: CssSupportsFeatureDeclaration { + l_paren_token: L_PAREN@199..200 "(" [] [], + declaration: CssDeclaration { + property: CssGenericProperty { + name: CssIdentifier { + value_token: IDENT@200..207 "display" [] [], + }, + colon_token: COLON@207..209 ":" [] [Whitespace(" ")], + value: ScssExpression { + items: ScssExpressionItemList [ + CssIdentifier { + value_token: IDENT@209..213 "grid" [] [], + }, + ], + }, + }, + important: missing (optional), + }, + r_paren_token: R_PAREN@213..215 ")" [] [Whitespace(" ")], + }, + }, + }, + block: CssRuleBlock { + l_curly_token: L_CURLY@215..216 "{" [] [], + rules: CssRuleList [ + CssQualifiedRule { + prelude: CssSelectorList [ + CssCompoundSelector { + nesting_selectors: CssNestedSelectorList [], + simple_selector: missing (optional), + sub_selectors: CssSubSelectorList [ + CssClassSelector { + dot_token: DOT@216..220 "." [Newline("\n"), Whitespace(" ")] [], + name: CssCustomIdentifier { + value_token: IDENT@220..222 "b" [] [Whitespace(" ")], + }, + }, + ], + }, + ], + block: CssDeclarationOrRuleBlock { + l_curly_token: L_CURLY@222..223 "{" [] [], + items: CssDeclarationOrRuleList [ + CssDeclarationWithSemicolon { + declaration: CssDeclaration { + property: CssGenericProperty { + name: CssIdentifier { + value_token: IDENT@223..233 "color" [Newline("\n"), Whitespace(" ")] [], + }, + colon_token: COLON@233..235 ":" [] [Whitespace(" ")], + value: ScssExpression { + items: ScssExpressionItemList [ + CssIdentifier { + value_token: IDENT@235..239 "blue" [] [], + }, + ], + }, + }, + important: missing (optional), + }, + semicolon_token: SEMICOLON@239..240 ";" [] [], + }, + ], + r_curly_token: R_CURLY@240..244 "}" [Newline("\n"), Whitespace(" ")] [], + }, + }, + ], + r_curly_token: R_CURLY@244..246 "}" [Newline("\n")] [], + }, + }, + }, + CssAtRule { + at_token: AT@246..249 "@" [Newline("\n"), Newline("\n")] [], + rule: CssSupportsAtRule { + declarator: CssSupportsAtRuleDeclarator { + supports_token: SUPPORTS_KW@249..258 "supports" [] [Whitespace(" ")], + condition: CssSupportsFeatureDeclaration { + l_paren_token: L_PAREN@258..259 "(" [] [], + declaration: CssDeclaration { + property: CssGenericProperty { + name: ScssInterpolatedIdentifier { + items: ScssInterpolatedIdentifierPartList [ + CssIdentifier { + value_token: IDENT@259..267 "--theme-" [] [], + }, + ScssInterpolation { + hash_token: HASH@267..268 "#" [] [], + l_curly_token: L_CURLY@268..269 "{" [] [], + value: ScssExpression { + items: ScssExpressionItemList [ + ScssIdentifier { + dollar_token: DOLLAR@269..270 "$" [] [], + name: CssIdentifier { + value_token: IDENT@270..274 "slot" [] [], + }, + }, + ], + }, + r_curly_token: R_CURLY@274..275 "}" [] [], + }, + ], + }, + colon_token: COLON@275..276 ":" [] [], + value: ScssExpression { + items: ScssExpressionItemList [], + }, + }, + important: missing (optional), + }, + r_paren_token: R_PAREN@276..278 ")" [] [Whitespace(" ")], + }, + }, + block: CssRuleBlock { + l_curly_token: L_CURLY@278..279 "{" [] [], + rules: CssRuleList [ + CssQualifiedRule { + prelude: CssSelectorList [ + CssCompoundSelector { + nesting_selectors: CssNestedSelectorList [], + simple_selector: missing (optional), + sub_selectors: CssSubSelectorList [ + CssClassSelector { + dot_token: DOT@279..283 "." [Newline("\n"), Whitespace(" ")] [], + name: CssCustomIdentifier { + value_token: IDENT@283..285 "c" [] [Whitespace(" ")], + }, + }, + ], + }, + ], + block: CssDeclarationOrRuleBlock { + l_curly_token: L_CURLY@285..286 "{" [] [], + items: CssDeclarationOrRuleList [ + CssDeclarationWithSemicolon { + declaration: CssDeclaration { + property: CssGenericProperty { + name: CssIdentifier { + value_token: IDENT@286..296 "color" [Newline("\n"), Whitespace(" ")] [], + }, + colon_token: COLON@296..298 ":" [] [Whitespace(" ")], + value: ScssExpression { + items: ScssExpressionItemList [ + CssIdentifier { + value_token: IDENT@298..303 "green" [] [], + }, + ], + }, + }, + important: missing (optional), + }, + semicolon_token: SEMICOLON@303..304 ";" [] [], + }, + ], + r_curly_token: R_CURLY@304..308 "}" [Newline("\n"), Whitespace(" ")] [], + }, + }, + ], + r_curly_token: R_CURLY@308..310 "}" [Newline("\n")] [], + }, + }, + }, + ], + eof_token: EOF@310..311 "" [Newline("\n")] [], +} +``` + +## CST + +``` +0: CSS_ROOT@0..311 + 0: (empty) + 1: CSS_ROOT_ITEM_LIST@0..310 + 0: CSS_AT_RULE@0..52 + 0: AT@0..1 "@" [] [] + 1: CSS_SUPPORTS_AT_RULE@1..52 + 0: CSS_SUPPORTS_AT_RULE_DECLARATOR@1..22 + 0: SUPPORTS_KW@1..10 "supports" [] [Whitespace(" ")] + 1: CSS_SUPPORTS_FEATURE_DECLARATION@10..22 + 0: L_PAREN@10..11 "(" [] [] + 1: CSS_DECLARATION@11..20 + 0: CSS_GENERIC_PROPERTY@11..20 + 0: SCSS_INTERPOLATED_IDENTIFIER@11..19 + 0: SCSS_INTERPOLATED_IDENTIFIER_PART_LIST@11..19 + 0: SCSS_INTERPOLATION@11..19 + 0: HASH@11..12 "#" [] [] + 1: L_CURLY@12..13 "{" [] [] + 2: SCSS_EXPRESSION@13..18 + 0: SCSS_EXPRESSION_ITEM_LIST@13..18 + 0: SCSS_IDENTIFIER@13..18 + 0: DOLLAR@13..14 "$" [] [] + 1: CSS_IDENTIFIER@14..18 + 0: IDENT@14..18 "name" [] [] + 3: R_CURLY@18..19 "}" [] [] + 1: COLON@19..20 ":" [] [] + 2: SCSS_EXPRESSION@20..20 + 0: SCSS_EXPRESSION_ITEM_LIST@20..20 + 1: (empty) + 2: R_PAREN@20..22 ")" [] [Whitespace(" ")] + 1: CSS_RULE_BLOCK@22..52 + 0: L_CURLY@22..23 "{" [] [] + 1: CSS_RULE_LIST@23..50 + 0: CSS_QUALIFIED_RULE@23..50 + 0: CSS_SELECTOR_LIST@23..29 + 0: CSS_COMPOUND_SELECTOR@23..29 + 0: CSS_NESTED_SELECTOR_LIST@23..23 + 1: (empty) + 2: CSS_SUB_SELECTOR_LIST@23..29 + 0: CSS_CLASS_SELECTOR@23..29 + 0: DOT@23..27 "." [Newline("\n"), Whitespace(" ")] [] + 1: CSS_CUSTOM_IDENTIFIER@27..29 + 0: IDENT@27..29 "a" [] [Whitespace(" ")] + 1: CSS_DECLARATION_OR_RULE_BLOCK@29..50 + 0: L_CURLY@29..30 "{" [] [] + 1: CSS_DECLARATION_OR_RULE_LIST@30..46 + 0: CSS_DECLARATION_WITH_SEMICOLON@30..46 + 0: CSS_DECLARATION@30..45 + 0: CSS_GENERIC_PROPERTY@30..45 + 0: CSS_IDENTIFIER@30..40 + 0: IDENT@30..40 "color" [Newline("\n"), Whitespace(" ")] [] + 1: COLON@40..42 ":" [] [Whitespace(" ")] + 2: SCSS_EXPRESSION@42..45 + 0: SCSS_EXPRESSION_ITEM_LIST@42..45 + 0: CSS_IDENTIFIER@42..45 + 0: IDENT@42..45 "red" [] [] + 1: (empty) + 1: SEMICOLON@45..46 ";" [] [] + 2: R_CURLY@46..50 "}" [Newline("\n"), Whitespace(" ")] [] + 2: R_CURLY@50..52 "}" [Newline("\n")] [] + 1: CSS_AT_RULE@52..110 + 0: AT@52..55 "@" [Newline("\n"), Newline("\n")] [] + 1: CSS_SUPPORTS_AT_RULE@55..110 + 0: CSS_SUPPORTS_AT_RULE_DECLARATOR@55..80 + 0: SUPPORTS_KW@55..64 "supports" [] [Whitespace(" ")] + 1: CSS_SUPPORTS_NOT_CONDITION@64..80 + 0: NOT_KW@64..68 "not" [] [Whitespace(" ")] + 1: CSS_SUPPORTS_FEATURE_DECLARATION@68..80 + 0: L_PAREN@68..69 "(" [] [] + 1: CSS_DECLARATION@69..78 + 0: CSS_GENERIC_PROPERTY@69..78 + 0: SCSS_INTERPOLATED_IDENTIFIER@69..77 + 0: SCSS_INTERPOLATED_IDENTIFIER_PART_LIST@69..77 + 0: SCSS_INTERPOLATION@69..77 + 0: HASH@69..70 "#" [] [] + 1: L_CURLY@70..71 "{" [] [] + 2: SCSS_EXPRESSION@71..76 + 0: SCSS_EXPRESSION_ITEM_LIST@71..76 + 0: SCSS_IDENTIFIER@71..76 + 0: DOLLAR@71..72 "$" [] [] + 1: CSS_IDENTIFIER@72..76 + 0: IDENT@72..76 "name" [] [] + 3: R_CURLY@76..77 "}" [] [] + 1: COLON@77..78 ":" [] [] + 2: SCSS_EXPRESSION@78..78 + 0: SCSS_EXPRESSION_ITEM_LIST@78..78 + 1: (empty) + 2: R_PAREN@78..80 ")" [] [Whitespace(" ")] + 1: CSS_RULE_BLOCK@80..110 + 0: L_CURLY@80..81 "{" [] [] + 1: CSS_RULE_LIST@81..108 + 0: CSS_QUALIFIED_RULE@81..108 + 0: CSS_SELECTOR_LIST@81..87 + 0: CSS_COMPOUND_SELECTOR@81..87 + 0: CSS_NESTED_SELECTOR_LIST@81..81 + 1: (empty) + 2: CSS_SUB_SELECTOR_LIST@81..87 + 0: CSS_CLASS_SELECTOR@81..87 + 0: DOT@81..85 "." [Newline("\n"), Whitespace(" ")] [] + 1: CSS_CUSTOM_IDENTIFIER@85..87 + 0: IDENT@85..87 "a" [] [Whitespace(" ")] + 1: CSS_DECLARATION_OR_RULE_BLOCK@87..108 + 0: L_CURLY@87..88 "{" [] [] + 1: CSS_DECLARATION_OR_RULE_LIST@88..104 + 0: CSS_DECLARATION_WITH_SEMICOLON@88..104 + 0: CSS_DECLARATION@88..103 + 0: CSS_GENERIC_PROPERTY@88..103 + 0: CSS_IDENTIFIER@88..98 + 0: IDENT@88..98 "color" [Newline("\n"), Whitespace(" ")] [] + 1: COLON@98..100 ":" [] [Whitespace(" ")] + 2: SCSS_EXPRESSION@100..103 + 0: SCSS_EXPRESSION_ITEM_LIST@100..103 + 0: CSS_IDENTIFIER@100..103 + 0: IDENT@100..103 "red" [] [] + 1: (empty) + 1: SEMICOLON@103..104 ";" [] [] + 2: R_CURLY@104..108 "}" [Newline("\n"), Whitespace(" ")] [] + 2: R_CURLY@108..110 "}" [Newline("\n")] [] + 2: CSS_AT_RULE@110..172 + 0: AT@110..113 "@" [Newline("\n"), Newline("\n")] [] + 1: CSS_SUPPORTS_AT_RULE@113..172 + 0: CSS_SUPPORTS_AT_RULE_DECLARATOR@113..141 + 0: SUPPORTS_KW@113..122 "supports" [] [Whitespace(" ")] + 1: CSS_SUPPORTS_FEATURE_DECLARATION@122..141 + 0: L_PAREN@122..123 "(" [] [] + 1: CSS_DECLARATION@123..139 + 0: CSS_GENERIC_PROPERTY@123..139 + 0: SCSS_INTERPOLATED_IDENTIFIER@123..138 + 0: SCSS_INTERPOLATED_IDENTIFIER_PART_LIST@123..138 + 0: CSS_IDENTIFIER@123..130 + 0: IDENT@123..130 "margin-" [] [] + 1: SCSS_INTERPOLATION@130..138 + 0: HASH@130..131 "#" [] [] + 1: L_CURLY@131..132 "{" [] [] + 2: SCSS_EXPRESSION@132..137 + 0: SCSS_EXPRESSION_ITEM_LIST@132..137 + 0: SCSS_IDENTIFIER@132..137 + 0: DOLLAR@132..133 "$" [] [] + 1: CSS_IDENTIFIER@133..137 + 0: IDENT@133..137 "side" [] [] + 3: R_CURLY@137..138 "}" [] [] + 1: COLON@138..139 ":" [] [] + 2: SCSS_EXPRESSION@139..139 + 0: SCSS_EXPRESSION_ITEM_LIST@139..139 + 1: (empty) + 2: R_PAREN@139..141 ")" [] [Whitespace(" ")] + 1: CSS_RULE_BLOCK@141..172 + 0: L_CURLY@141..142 "{" [] [] + 1: CSS_RULE_LIST@142..170 + 0: CSS_QUALIFIED_RULE@142..170 + 0: CSS_SELECTOR_LIST@142..148 + 0: CSS_COMPOUND_SELECTOR@142..148 + 0: CSS_NESTED_SELECTOR_LIST@142..142 + 1: (empty) + 2: CSS_SUB_SELECTOR_LIST@142..148 + 0: CSS_CLASS_SELECTOR@142..148 + 0: DOT@142..146 "." [Newline("\n"), Whitespace(" ")] [] + 1: CSS_CUSTOM_IDENTIFIER@146..148 + 0: IDENT@146..148 "b" [] [Whitespace(" ")] + 1: CSS_DECLARATION_OR_RULE_BLOCK@148..170 + 0: L_CURLY@148..149 "{" [] [] + 1: CSS_DECLARATION_OR_RULE_LIST@149..166 + 0: CSS_DECLARATION_WITH_SEMICOLON@149..166 + 0: CSS_DECLARATION@149..165 + 0: CSS_GENERIC_PROPERTY@149..165 + 0: CSS_IDENTIFIER@149..159 + 0: IDENT@149..159 "color" [Newline("\n"), Whitespace(" ")] [] + 1: COLON@159..161 ":" [] [Whitespace(" ")] + 2: SCSS_EXPRESSION@161..165 + 0: SCSS_EXPRESSION_ITEM_LIST@161..165 + 0: CSS_IDENTIFIER@161..165 + 0: IDENT@161..165 "blue" [] [] + 1: (empty) + 1: SEMICOLON@165..166 ";" [] [] + 2: R_CURLY@166..170 "}" [Newline("\n"), Whitespace(" ")] [] + 2: R_CURLY@170..172 "}" [Newline("\n")] [] + 3: CSS_AT_RULE@172..246 + 0: AT@172..175 "@" [Newline("\n"), Newline("\n")] [] + 1: CSS_SUPPORTS_AT_RULE@175..246 + 0: CSS_SUPPORTS_AT_RULE_DECLARATOR@175..215 + 0: SUPPORTS_KW@175..184 "supports" [] [Whitespace(" ")] + 1: CSS_SUPPORTS_OR_CONDITION@184..215 + 0: CSS_SUPPORTS_FEATURE_DECLARATION@184..196 + 0: L_PAREN@184..185 "(" [] [] + 1: CSS_DECLARATION@185..194 + 0: CSS_GENERIC_PROPERTY@185..194 + 0: SCSS_INTERPOLATED_IDENTIFIER@185..193 + 0: SCSS_INTERPOLATED_IDENTIFIER_PART_LIST@185..193 + 0: SCSS_INTERPOLATION@185..193 + 0: HASH@185..186 "#" [] [] + 1: L_CURLY@186..187 "{" [] [] + 2: SCSS_EXPRESSION@187..192 + 0: SCSS_EXPRESSION_ITEM_LIST@187..192 + 0: SCSS_IDENTIFIER@187..192 + 0: DOLLAR@187..188 "$" [] [] + 1: CSS_IDENTIFIER@188..192 + 0: IDENT@188..192 "name" [] [] + 3: R_CURLY@192..193 "}" [] [] + 1: COLON@193..194 ":" [] [] + 2: SCSS_EXPRESSION@194..194 + 0: SCSS_EXPRESSION_ITEM_LIST@194..194 + 1: (empty) + 2: R_PAREN@194..196 ")" [] [Whitespace(" ")] + 1: OR_KW@196..199 "or" [] [Whitespace(" ")] + 2: CSS_SUPPORTS_FEATURE_DECLARATION@199..215 + 0: L_PAREN@199..200 "(" [] [] + 1: CSS_DECLARATION@200..213 + 0: CSS_GENERIC_PROPERTY@200..213 + 0: CSS_IDENTIFIER@200..207 + 0: IDENT@200..207 "display" [] [] + 1: COLON@207..209 ":" [] [Whitespace(" ")] + 2: SCSS_EXPRESSION@209..213 + 0: SCSS_EXPRESSION_ITEM_LIST@209..213 + 0: CSS_IDENTIFIER@209..213 + 0: IDENT@209..213 "grid" [] [] + 1: (empty) + 2: R_PAREN@213..215 ")" [] [Whitespace(" ")] + 1: CSS_RULE_BLOCK@215..246 + 0: L_CURLY@215..216 "{" [] [] + 1: CSS_RULE_LIST@216..244 + 0: CSS_QUALIFIED_RULE@216..244 + 0: CSS_SELECTOR_LIST@216..222 + 0: CSS_COMPOUND_SELECTOR@216..222 + 0: CSS_NESTED_SELECTOR_LIST@216..216 + 1: (empty) + 2: CSS_SUB_SELECTOR_LIST@216..222 + 0: CSS_CLASS_SELECTOR@216..222 + 0: DOT@216..220 "." [Newline("\n"), Whitespace(" ")] [] + 1: CSS_CUSTOM_IDENTIFIER@220..222 + 0: IDENT@220..222 "b" [] [Whitespace(" ")] + 1: CSS_DECLARATION_OR_RULE_BLOCK@222..244 + 0: L_CURLY@222..223 "{" [] [] + 1: CSS_DECLARATION_OR_RULE_LIST@223..240 + 0: CSS_DECLARATION_WITH_SEMICOLON@223..240 + 0: CSS_DECLARATION@223..239 + 0: CSS_GENERIC_PROPERTY@223..239 + 0: CSS_IDENTIFIER@223..233 + 0: IDENT@223..233 "color" [Newline("\n"), Whitespace(" ")] [] + 1: COLON@233..235 ":" [] [Whitespace(" ")] + 2: SCSS_EXPRESSION@235..239 + 0: SCSS_EXPRESSION_ITEM_LIST@235..239 + 0: CSS_IDENTIFIER@235..239 + 0: IDENT@235..239 "blue" [] [] + 1: (empty) + 1: SEMICOLON@239..240 ";" [] [] + 2: R_CURLY@240..244 "}" [Newline("\n"), Whitespace(" ")] [] + 2: R_CURLY@244..246 "}" [Newline("\n")] [] + 4: CSS_AT_RULE@246..310 + 0: AT@246..249 "@" [Newline("\n"), Newline("\n")] [] + 1: CSS_SUPPORTS_AT_RULE@249..310 + 0: CSS_SUPPORTS_AT_RULE_DECLARATOR@249..278 + 0: SUPPORTS_KW@249..258 "supports" [] [Whitespace(" ")] + 1: CSS_SUPPORTS_FEATURE_DECLARATION@258..278 + 0: L_PAREN@258..259 "(" [] [] + 1: CSS_DECLARATION@259..276 + 0: CSS_GENERIC_PROPERTY@259..276 + 0: SCSS_INTERPOLATED_IDENTIFIER@259..275 + 0: SCSS_INTERPOLATED_IDENTIFIER_PART_LIST@259..275 + 0: CSS_IDENTIFIER@259..267 + 0: IDENT@259..267 "--theme-" [] [] + 1: SCSS_INTERPOLATION@267..275 + 0: HASH@267..268 "#" [] [] + 1: L_CURLY@268..269 "{" [] [] + 2: SCSS_EXPRESSION@269..274 + 0: SCSS_EXPRESSION_ITEM_LIST@269..274 + 0: SCSS_IDENTIFIER@269..274 + 0: DOLLAR@269..270 "$" [] [] + 1: CSS_IDENTIFIER@270..274 + 0: IDENT@270..274 "slot" [] [] + 3: R_CURLY@274..275 "}" [] [] + 1: COLON@275..276 ":" [] [] + 2: SCSS_EXPRESSION@276..276 + 0: SCSS_EXPRESSION_ITEM_LIST@276..276 + 1: (empty) + 2: R_PAREN@276..278 ")" [] [Whitespace(" ")] + 1: CSS_RULE_BLOCK@278..310 + 0: L_CURLY@278..279 "{" [] [] + 1: CSS_RULE_LIST@279..308 + 0: CSS_QUALIFIED_RULE@279..308 + 0: CSS_SELECTOR_LIST@279..285 + 0: CSS_COMPOUND_SELECTOR@279..285 + 0: CSS_NESTED_SELECTOR_LIST@279..279 + 1: (empty) + 2: CSS_SUB_SELECTOR_LIST@279..285 + 0: CSS_CLASS_SELECTOR@279..285 + 0: DOT@279..283 "." [Newline("\n"), Whitespace(" ")] [] + 1: CSS_CUSTOM_IDENTIFIER@283..285 + 0: IDENT@283..285 "c" [] [Whitespace(" ")] + 1: CSS_DECLARATION_OR_RULE_BLOCK@285..308 + 0: L_CURLY@285..286 "{" [] [] + 1: CSS_DECLARATION_OR_RULE_LIST@286..304 + 0: CSS_DECLARATION_WITH_SEMICOLON@286..304 + 0: CSS_DECLARATION@286..303 + 0: CSS_GENERIC_PROPERTY@286..303 + 0: CSS_IDENTIFIER@286..296 + 0: IDENT@286..296 "color" [Newline("\n"), Whitespace(" ")] [] + 1: COLON@296..298 ":" [] [Whitespace(" ")] + 2: SCSS_EXPRESSION@298..303 + 0: SCSS_EXPRESSION_ITEM_LIST@298..303 + 0: CSS_IDENTIFIER@298..303 + 0: IDENT@298..303 "green" [] [] + 1: (empty) + 1: SEMICOLON@303..304 ";" [] [] + 2: R_CURLY@304..308 "}" [Newline("\n"), Whitespace(" ")] [] + 2: R_CURLY@308..310 "}" [Newline("\n")] [] + 2: EOF@310..311 "" [Newline("\n")] [] + +``` + +## Diagnostics + +``` +supports-interpolated-property-missing-value.scss:1:21 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × Unexpected value or character. + + > 1 │ @supports (#{$name}:) { + │ ^ + 2 │ .a { + 3 │ color: red; + + i Expected one of: + + - identifier + - string + - number + - dimension + - ratio + - custom property + - function + +supports-interpolated-property-missing-value.scss:7:25 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × Unexpected value or character. + + 5 │ } + 6 │ + > 7 │ @supports not (#{$name}:) { + │ ^ + 8 │ .a { + 9 │ color: red; + + i Expected one of: + + - identifier + - string + - number + - dimension + - ratio + - custom property + - function + +supports-interpolated-property-missing-value.scss:13:28 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × Unexpected value or character. + + 11 │ } + 12 │ + > 13 │ @supports (margin-#{$side}:) { + │ ^ + 14 │ .b { + 15 │ color: blue; + + i Expected one of: + + - identifier + - string + - number + - dimension + - ratio + - custom property + - function + +supports-interpolated-property-missing-value.scss:19:21 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × Unexpected value or character. + + 17 │ } + 18 │ + > 19 │ @supports (#{$name}:) or (display: grid) { + │ ^ + 20 │ .b { + 21 │ color: blue; + + i Expected one of: + + - identifier + - string + - number + - dimension + - ratio + - custom property + - function + +supports-interpolated-property-missing-value.scss:25:29 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × Unexpected value or character. + + 23 │ } + 24 │ + > 25 │ @supports (--theme-#{$slot}:) { + │ ^ + 26 │ .c { + 27 │ color: green; + + i Expected one of: + + - identifier + - string + - number + - dimension + - ratio + - custom property + - function + +``` diff --git a/crates/biome_css_parser/tests/css_test_suite/error/scss/at-rule/supports-missing-value.scss.snap b/crates/biome_css_parser/tests/css_test_suite/error/scss/at-rule/supports-missing-value.scss.snap index d040d868972f..9102ebe94109 100644 --- a/crates/biome_css_parser/tests/css_test_suite/error/scss/at-rule/supports-missing-value.scss.snap +++ b/crates/biome_css_parser/tests/css_test_suite/error/scss/at-rule/supports-missing-value.scss.snap @@ -1,6 +1,5 @@ --- source: crates/biome_css_parser/tests/spec_test.rs -assertion_line: 208 expression: snapshot --- diff --git a/crates/biome_css_parser/tests/css_test_suite/error/scss/declaration/interpolated-selector-recovery.scss b/crates/biome_css_parser/tests/css_test_suite/error/scss/declaration/interpolated-selector-recovery.scss new file mode 100644 index 000000000000..4daa87f4398b --- /dev/null +++ b/crates/biome_css_parser/tests/css_test_suite/error/scss/declaration/interpolated-selector-recovery.scss @@ -0,0 +1,4 @@ +.test { + #{$name}; + width: 1px; +} diff --git a/crates/biome_css_parser/tests/css_test_suite/error/scss/declaration/interpolated-selector-recovery.scss.snap b/crates/biome_css_parser/tests/css_test_suite/error/scss/declaration/interpolated-selector-recovery.scss.snap new file mode 100644 index 000000000000..1ef50afddc37 --- /dev/null +++ b/crates/biome_css_parser/tests/css_test_suite/error/scss/declaration/interpolated-selector-recovery.scss.snap @@ -0,0 +1,295 @@ +--- +source: crates/biome_css_parser/tests/spec_test.rs +expression: snapshot +--- + +## Input + +```css +.test { + #{$name}; + width: 1px; +} + +``` + + +## 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: CssBogusBlock { + items: [ + L_CURLY@6..7 "{" [] [], + CssBogus { + items: [ + ScssInterpolatedIdentifier { + items: ScssInterpolatedIdentifierPartList [ + ScssInterpolation { + hash_token: HASH@7..11 "#" [Newline("\n"), Whitespace(" ")] [], + l_curly_token: L_CURLY@11..12 "{" [] [], + value: ScssExpression { + items: ScssExpressionItemList [ + ScssIdentifier { + dollar_token: DOLLAR@12..13 "$" [] [], + name: CssIdentifier { + value_token: IDENT@13..17 "name" [] [], + }, + }, + ], + }, + r_curly_token: R_CURLY@17..18 "}" [] [], + }, + ], + }, + ], + }, + ], + }, + }, + CssBogusRule { + items: [ + SEMICOLON@18..19 ";" [] [], + ], + }, + CssQualifiedRule { + prelude: CssSelectorList [ + CssCompoundSelector { + nesting_selectors: CssNestedSelectorList [], + simple_selector: CssTypeSelector { + namespace: missing (optional), + ident: CssIdentifier { + value_token: IDENT@19..27 "width" [Newline("\n"), Whitespace(" ")] [], + }, + }, + sub_selectors: CssSubSelectorList [ + CssBogusSubSelector { + items: [ + COLON@27..29 ":" [] [Whitespace(" ")], + ], + }, + ], + }, + missing separator, + CssBogusSelector { + items: [ + CSS_DIMENSION_VALUE@29..30 "1" [] [], + ], + }, + missing separator, + CssCompoundSelector { + nesting_selectors: CssNestedSelectorList [], + simple_selector: CssTypeSelector { + namespace: missing (optional), + ident: CssIdentifier { + value_token: IDENT@30..32 "px" [] [], + }, + }, + sub_selectors: CssSubSelectorList [], + }, + ], + block: CssBogusBlock { + items: [ + CssDeclarationOrRuleList [ + CssEmptyDeclaration { + semicolon_token: SEMICOLON@32..33 ";" [] [], + }, + ], + R_CURLY@33..35 "}" [Newline("\n")] [], + ], + }, + }, + ], + eof_token: EOF@35..36 "" [Newline("\n")] [], +} +``` + +## CST + +``` +0: CSS_ROOT@0..36 + 0: (empty) + 1: CSS_ROOT_ITEM_LIST@0..35 + 0: CSS_QUALIFIED_RULE@0..18 + 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_BOGUS_BLOCK@6..18 + 0: L_CURLY@6..7 "{" [] [] + 1: CSS_BOGUS@7..18 + 0: SCSS_INTERPOLATED_IDENTIFIER@7..18 + 0: SCSS_INTERPOLATED_IDENTIFIER_PART_LIST@7..18 + 0: SCSS_INTERPOLATION@7..18 + 0: HASH@7..11 "#" [Newline("\n"), Whitespace(" ")] [] + 1: L_CURLY@11..12 "{" [] [] + 2: SCSS_EXPRESSION@12..17 + 0: SCSS_EXPRESSION_ITEM_LIST@12..17 + 0: SCSS_IDENTIFIER@12..17 + 0: DOLLAR@12..13 "$" [] [] + 1: CSS_IDENTIFIER@13..17 + 0: IDENT@13..17 "name" [] [] + 3: R_CURLY@17..18 "}" [] [] + 1: CSS_BOGUS_RULE@18..19 + 0: SEMICOLON@18..19 ";" [] [] + 2: CSS_QUALIFIED_RULE@19..35 + 0: CSS_SELECTOR_LIST@19..32 + 0: CSS_COMPOUND_SELECTOR@19..29 + 0: CSS_NESTED_SELECTOR_LIST@19..19 + 1: CSS_TYPE_SELECTOR@19..27 + 0: (empty) + 1: CSS_IDENTIFIER@19..27 + 0: IDENT@19..27 "width" [Newline("\n"), Whitespace(" ")] [] + 2: CSS_SUB_SELECTOR_LIST@27..29 + 0: CSS_BOGUS_SUB_SELECTOR@27..29 + 0: COLON@27..29 ":" [] [Whitespace(" ")] + 1: (empty) + 2: CSS_BOGUS_SELECTOR@29..30 + 0: CSS_DIMENSION_VALUE@29..30 "1" [] [] + 3: (empty) + 4: CSS_COMPOUND_SELECTOR@30..32 + 0: CSS_NESTED_SELECTOR_LIST@30..30 + 1: CSS_TYPE_SELECTOR@30..32 + 0: (empty) + 1: CSS_IDENTIFIER@30..32 + 0: IDENT@30..32 "px" [] [] + 2: CSS_SUB_SELECTOR_LIST@32..32 + 1: CSS_BOGUS_BLOCK@32..35 + 0: CSS_DECLARATION_OR_RULE_LIST@32..33 + 0: CSS_EMPTY_DECLARATION@32..33 + 0: SEMICOLON@32..33 ";" [] [] + 1: R_CURLY@33..35 "}" [Newline("\n")] [] + 2: EOF@35..36 "" [Newline("\n")] [] + +``` + +## Diagnostics + +``` +interpolated-selector-recovery.scss:2:11 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × Expected a declaration, or an at rule but instead found ';'. + + 1 │ .test { + > 2 │ #{$name}; + │ ^ + 3 │ width: 1px; + 4 │ } + + i Expected a declaration, or an at rule here. + + 1 │ .test { + > 2 │ #{$name}; + │ ^ + 3 │ width: 1px; + 4 │ } + +interpolated-selector-recovery.scss:3:10 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × Unexpected value or character. + + 1 │ .test { + 2 │ #{$name}; + > 3 │ width: 1px; + │ ^ + 4 │ } + 5 │ + + i Expected one of: + + - hover + - focus + - active + - first-child + - last-child + - nth-child + - nth-last-child + - first-of-type + - last-of-type + - nth-of-type + - nth-last-of-type + - only-child + - only-of-type + - checked + - disabled + - enabled + - required + - optional + - valid + - invalid + - in-range + - out-of-range + - read-only + - read-write + - placeholder-shown + - default + - checked + - indeterminate + - blank + - empty + - root + - target + - lang + - not + - is + - where + - fullscreen + - link + - visited + - any-link + - local-link + - scope + - state + - current + - past + - future + +interpolated-selector-recovery.scss:3:11 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × expected `,` but instead found `px` + + 1 │ .test { + 2 │ #{$name}; + > 3 │ width: 1px; + │ ^^ + 4 │ } + 5 │ + + i Remove px + +interpolated-selector-recovery.scss:3:13 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × expected `,` but instead found `;` + + 1 │ .test { + 2 │ #{$name}; + > 3 │ width: 1px; + │ ^ + 4 │ } + 5 │ + + i Remove ; + +``` diff --git a/crates/biome_css_parser/tests/css_test_suite/error/scss/declaration/interpolation.scss b/crates/biome_css_parser/tests/css_test_suite/error/scss/declaration/interpolation.scss new file mode 100644 index 000000000000..67e00cfbea96 --- /dev/null +++ b/crates/biome_css_parser/tests/css_test_suite/error/scss/declaration/interpolation.scss @@ -0,0 +1,7 @@ +.empty { + #{}: 1px; +} + +.unclosed { + margin-#{$side: 1px; +} diff --git a/crates/biome_css_parser/tests/css_test_suite/error/scss/declaration/interpolation.scss.snap b/crates/biome_css_parser/tests/css_test_suite/error/scss/declaration/interpolation.scss.snap new file mode 100644 index 000000000000..caa56b679c1b --- /dev/null +++ b/crates/biome_css_parser/tests/css_test_suite/error/scss/declaration/interpolation.scss.snap @@ -0,0 +1,262 @@ +--- +source: crates/biome_css_parser/tests/spec_test.rs +assertion_line: 208 +expression: snapshot +--- + +## Input + +```css +.empty { + #{}: 1px; +} + +.unclosed { + margin-#{$side: 1px; +} + +``` + + +## 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..7 "empty" [] [Whitespace(" ")], + }, + }, + ], + }, + ], + block: CssDeclarationOrRuleBlock { + l_curly_token: L_CURLY@7..8 "{" [] [], + items: CssDeclarationOrRuleList [ + CssDeclarationWithSemicolon { + declaration: CssDeclaration { + property: CssGenericProperty { + name: ScssInterpolatedIdentifier { + items: ScssInterpolatedIdentifierPartList [ + ScssInterpolation { + hash_token: HASH@8..12 "#" [Newline("\n"), Whitespace(" ")] [], + l_curly_token: L_CURLY@12..13 "{" [] [], + value: missing (required), + r_curly_token: R_CURLY@13..14 "}" [] [], + }, + ], + }, + colon_token: COLON@14..16 ":" [] [Whitespace(" ")], + value: ScssExpression { + items: ScssExpressionItemList [ + CssRegularDimension { + value_token: CSS_NUMBER_LITERAL@16..17 "1" [] [], + unit_token: IDENT@17..19 "px" [] [], + }, + ], + }, + }, + important: missing (optional), + }, + semicolon_token: SEMICOLON@19..20 ";" [] [], + }, + ], + r_curly_token: R_CURLY@20..22 "}" [Newline("\n")] [], + }, + }, + CssQualifiedRule { + prelude: CssSelectorList [ + CssCompoundSelector { + nesting_selectors: CssNestedSelectorList [], + simple_selector: missing (optional), + sub_selectors: CssSubSelectorList [ + CssClassSelector { + dot_token: DOT@22..25 "." [Newline("\n"), Newline("\n")] [], + name: CssCustomIdentifier { + value_token: IDENT@25..34 "unclosed" [] [Whitespace(" ")], + }, + }, + ], + }, + ], + block: CssDeclarationOrRuleBlock { + l_curly_token: L_CURLY@34..35 "{" [] [], + items: CssDeclarationOrRuleList [ + CssDeclarationWithSemicolon { + declaration: CssDeclaration { + property: CssGenericProperty { + name: ScssInterpolatedIdentifier { + items: ScssInterpolatedIdentifierPartList [ + CssIdentifier { + value_token: IDENT@35..45 "margin-" [Newline("\n"), Whitespace(" ")] [], + }, + ScssInterpolation { + hash_token: HASH@45..46 "#" [] [], + l_curly_token: L_CURLY@46..47 "{" [] [], + value: ScssExpression { + items: ScssExpressionItemList [ + ScssIdentifier { + dollar_token: DOLLAR@47..48 "$" [] [], + name: CssIdentifier { + value_token: IDENT@48..52 "side" [] [], + }, + }, + ], + }, + r_curly_token: missing (required), + }, + ], + }, + colon_token: COLON@52..54 ":" [] [Whitespace(" ")], + value: ScssExpression { + items: ScssExpressionItemList [ + CssRegularDimension { + value_token: CSS_NUMBER_LITERAL@54..55 "1" [] [], + unit_token: IDENT@55..57 "px" [] [], + }, + ], + }, + }, + important: missing (optional), + }, + semicolon_token: SEMICOLON@57..58 ";" [] [], + }, + ], + r_curly_token: R_CURLY@58..60 "}" [Newline("\n")] [], + }, + }, + ], + eof_token: EOF@60..61 "" [Newline("\n")] [], +} +``` + +## CST + +``` +0: CSS_ROOT@0..61 + 0: (empty) + 1: CSS_ROOT_ITEM_LIST@0..60 + 0: CSS_QUALIFIED_RULE@0..22 + 0: CSS_SELECTOR_LIST@0..7 + 0: CSS_COMPOUND_SELECTOR@0..7 + 0: CSS_NESTED_SELECTOR_LIST@0..0 + 1: (empty) + 2: CSS_SUB_SELECTOR_LIST@0..7 + 0: CSS_CLASS_SELECTOR@0..7 + 0: DOT@0..1 "." [] [] + 1: CSS_CUSTOM_IDENTIFIER@1..7 + 0: IDENT@1..7 "empty" [] [Whitespace(" ")] + 1: CSS_DECLARATION_OR_RULE_BLOCK@7..22 + 0: L_CURLY@7..8 "{" [] [] + 1: CSS_DECLARATION_OR_RULE_LIST@8..20 + 0: CSS_DECLARATION_WITH_SEMICOLON@8..20 + 0: CSS_DECLARATION@8..19 + 0: CSS_GENERIC_PROPERTY@8..19 + 0: SCSS_INTERPOLATED_IDENTIFIER@8..14 + 0: SCSS_INTERPOLATED_IDENTIFIER_PART_LIST@8..14 + 0: SCSS_INTERPOLATION@8..14 + 0: HASH@8..12 "#" [Newline("\n"), Whitespace(" ")] [] + 1: L_CURLY@12..13 "{" [] [] + 2: (empty) + 3: R_CURLY@13..14 "}" [] [] + 1: COLON@14..16 ":" [] [Whitespace(" ")] + 2: SCSS_EXPRESSION@16..19 + 0: SCSS_EXPRESSION_ITEM_LIST@16..19 + 0: CSS_REGULAR_DIMENSION@16..19 + 0: CSS_NUMBER_LITERAL@16..17 "1" [] [] + 1: IDENT@17..19 "px" [] [] + 1: (empty) + 1: SEMICOLON@19..20 ";" [] [] + 2: R_CURLY@20..22 "}" [Newline("\n")] [] + 1: CSS_QUALIFIED_RULE@22..60 + 0: CSS_SELECTOR_LIST@22..34 + 0: CSS_COMPOUND_SELECTOR@22..34 + 0: CSS_NESTED_SELECTOR_LIST@22..22 + 1: (empty) + 2: CSS_SUB_SELECTOR_LIST@22..34 + 0: CSS_CLASS_SELECTOR@22..34 + 0: DOT@22..25 "." [Newline("\n"), Newline("\n")] [] + 1: CSS_CUSTOM_IDENTIFIER@25..34 + 0: IDENT@25..34 "unclosed" [] [Whitespace(" ")] + 1: CSS_DECLARATION_OR_RULE_BLOCK@34..60 + 0: L_CURLY@34..35 "{" [] [] + 1: CSS_DECLARATION_OR_RULE_LIST@35..58 + 0: CSS_DECLARATION_WITH_SEMICOLON@35..58 + 0: CSS_DECLARATION@35..57 + 0: CSS_GENERIC_PROPERTY@35..57 + 0: SCSS_INTERPOLATED_IDENTIFIER@35..52 + 0: SCSS_INTERPOLATED_IDENTIFIER_PART_LIST@35..52 + 0: CSS_IDENTIFIER@35..45 + 0: IDENT@35..45 "margin-" [Newline("\n"), Whitespace(" ")] [] + 1: SCSS_INTERPOLATION@45..52 + 0: HASH@45..46 "#" [] [] + 1: L_CURLY@46..47 "{" [] [] + 2: SCSS_EXPRESSION@47..52 + 0: SCSS_EXPRESSION_ITEM_LIST@47..52 + 0: SCSS_IDENTIFIER@47..52 + 0: DOLLAR@47..48 "$" [] [] + 1: CSS_IDENTIFIER@48..52 + 0: IDENT@48..52 "side" [] [] + 3: (empty) + 1: COLON@52..54 ":" [] [Whitespace(" ")] + 2: SCSS_EXPRESSION@54..57 + 0: SCSS_EXPRESSION_ITEM_LIST@54..57 + 0: CSS_REGULAR_DIMENSION@54..57 + 0: CSS_NUMBER_LITERAL@54..55 "1" [] [] + 1: IDENT@55..57 "px" [] [] + 1: (empty) + 1: SEMICOLON@57..58 ";" [] [] + 2: R_CURLY@58..60 "}" [Newline("\n")] [] + 2: EOF@60..61 "" [Newline("\n")] [] + +``` + +## Diagnostics + +``` +interpolation.scss:2:5 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × Expected a SCSS expression but instead found '}'. + + 1 │ .empty { + > 2 │ #{}: 1px; + │ ^ + 3 │ } + 4 │ + + i Expected a SCSS expression here. + + 1 │ .empty { + > 2 │ #{}: 1px; + │ ^ + 3 │ } + 4 │ + +interpolation.scss:6:17 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × Expected a SCSS expression but instead found ':'. + + 5 │ .unclosed { + > 6 │ margin-#{$side: 1px; + │ ^ + 7 │ } + 8 │ + + i Expected a SCSS expression here. + + 5 │ .unclosed { + > 6 │ margin-#{$side: 1px; + │ ^ + 7 │ } + 8 │ + +``` diff --git a/crates/biome_css_parser/tests/css_test_suite/error/scss/declaration/nesting-missing-semicolon.scss b/crates/biome_css_parser/tests/css_test_suite/error/scss/declaration/nesting-missing-semicolon.scss new file mode 100644 index 000000000000..572a85dec66f --- /dev/null +++ b/crates/biome_css_parser/tests/css_test_suite/error/scss/declaration/nesting-missing-semicolon.scss @@ -0,0 +1,4 @@ +.test { + font: bold + width: 1px; +} diff --git a/crates/biome_css_parser/tests/css_test_suite/error/scss/declaration/nesting-missing-semicolon.scss.snap b/crates/biome_css_parser/tests/css_test_suite/error/scss/declaration/nesting-missing-semicolon.scss.snap new file mode 100644 index 000000000000..28c1499aac93 --- /dev/null +++ b/crates/biome_css_parser/tests/css_test_suite/error/scss/declaration/nesting-missing-semicolon.scss.snap @@ -0,0 +1,150 @@ +--- +source: crates/biome_css_parser/tests/spec_test.rs +expression: snapshot +--- + +## Input + +```css +.test { + font: bold + width: 1px; +} + +``` + + +## 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: CssBogusProperty { + items: [ + CssIdentifier { + value_token: IDENT@7..14 "font" [Newline("\n"), Whitespace(" ")] [], + }, + COLON@14..16 ":" [] [Whitespace(" ")], + CssBogus { + items: [ + CssBogus { + items: [ + CssIdentifier { + value_token: IDENT@16..20 "bold" [] [], + }, + CssIdentifier { + value_token: IDENT@20..28 "width" [Newline("\n"), Whitespace(" ")] [], + }, + CssBogusPropertyValue { + items: [ + COLON@28..30 ":" [] [Whitespace(" ")], + CSS_DIMENSION_VALUE@30..31 "1" [] [], + PX_KW@31..33 "px" [] [], + ], + }, + ], + }, + ], + }, + ], + }, + important: missing (optional), + }, + semicolon_token: SEMICOLON@33..34 ";" [] [], + }, + ], + r_curly_token: R_CURLY@34..36 "}" [Newline("\n")] [], + }, + }, + ], + eof_token: EOF@36..37 "" [Newline("\n")] [], +} +``` + +## CST + +``` +0: CSS_ROOT@0..37 + 0: (empty) + 1: CSS_ROOT_ITEM_LIST@0..36 + 0: CSS_QUALIFIED_RULE@0..36 + 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..36 + 0: L_CURLY@6..7 "{" [] [] + 1: CSS_DECLARATION_OR_RULE_LIST@7..34 + 0: CSS_DECLARATION_WITH_SEMICOLON@7..34 + 0: CSS_DECLARATION@7..33 + 0: CSS_BOGUS_PROPERTY@7..33 + 0: CSS_IDENTIFIER@7..14 + 0: IDENT@7..14 "font" [Newline("\n"), Whitespace(" ")] [] + 1: COLON@14..16 ":" [] [Whitespace(" ")] + 2: CSS_BOGUS@16..33 + 0: CSS_BOGUS@16..33 + 0: CSS_IDENTIFIER@16..20 + 0: IDENT@16..20 "bold" [] [] + 1: CSS_IDENTIFIER@20..28 + 0: IDENT@20..28 "width" [Newline("\n"), Whitespace(" ")] [] + 2: CSS_BOGUS_PROPERTY_VALUE@28..33 + 0: COLON@28..30 ":" [] [Whitespace(" ")] + 1: CSS_DIMENSION_VALUE@30..31 "1" [] [] + 2: PX_KW@31..33 "px" [] [] + 1: (empty) + 1: SEMICOLON@33..34 ";" [] [] + 2: R_CURLY@34..36 "}" [Newline("\n")] [] + 2: EOF@36..37 "" [Newline("\n")] [] + +``` + +## Diagnostics + +``` +nesting-missing-semicolon.scss:3:8 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × Expected a SCSS expression but instead found ': 1px'. + + 1 │ .test { + 2 │ font: bold + > 3 │ width: 1px; + │ ^^^^^ + 4 │ } + 5 │ + + i Expected a SCSS expression here. + + 1 │ .test { + 2 │ font: bold + > 3 │ width: 1px; + │ ^^^^^ + 4 │ } + 5 │ + +``` diff --git a/crates/biome_css_parser/tests/css_test_suite/ok/scss/at-rule/declaration-block-interpolation.scss b/crates/biome_css_parser/tests/css_test_suite/ok/scss/at-rule/declaration-block-interpolation.scss new file mode 100644 index 000000000000..049d1baf54c7 --- /dev/null +++ b/crates/biome_css_parser/tests/css_test_suite/ok/scss/at-rule/declaration-block-interpolation.scss @@ -0,0 +1,5 @@ +@font-face { + #{$name}: 1px; + margin-#{$side}: 1px; + --theme-#{$slot}: red; +} diff --git a/crates/biome_css_parser/tests/css_test_suite/ok/scss/at-rule/declaration-block-interpolation.scss.snap b/crates/biome_css_parser/tests/css_test_suite/ok/scss/at-rule/declaration-block-interpolation.scss.snap new file mode 100644 index 000000000000..c59c5d5dd7e2 --- /dev/null +++ b/crates/biome_css_parser/tests/css_test_suite/ok/scss/at-rule/declaration-block-interpolation.scss.snap @@ -0,0 +1,245 @@ +--- +source: crates/biome_css_parser/tests/spec_test.rs +expression: snapshot +--- + +## Input + +```css +@font-face { + #{$name}: 1px; + margin-#{$side}: 1px; + --theme-#{$slot}: red; +} + +``` + + +## AST + +``` +CssRoot { + bom_token: missing (optional), + items: CssRootItemList [ + CssAtRule { + at_token: AT@0..1 "@" [] [], + rule: CssFontFaceAtRule { + declarator: CssFontFaceAtRuleDeclarator { + font_face_token: FONT_FACE_KW@1..11 "font-face" [] [Whitespace(" ")], + }, + block: CssDeclarationBlock { + l_curly_token: L_CURLY@11..12 "{" [] [], + declarations: CssDeclarationList [ + CssDeclarationWithSemicolon { + declaration: CssDeclaration { + property: CssGenericProperty { + name: ScssInterpolatedIdentifier { + items: ScssInterpolatedIdentifierPartList [ + ScssInterpolation { + hash_token: HASH@12..16 "#" [Newline("\n"), Whitespace(" ")] [], + l_curly_token: L_CURLY@16..17 "{" [] [], + value: ScssExpression { + items: ScssExpressionItemList [ + ScssIdentifier { + dollar_token: DOLLAR@17..18 "$" [] [], + name: CssIdentifier { + value_token: IDENT@18..22 "name" [] [], + }, + }, + ], + }, + r_curly_token: R_CURLY@22..23 "}" [] [], + }, + ], + }, + colon_token: COLON@23..25 ":" [] [Whitespace(" ")], + value: ScssExpression { + items: ScssExpressionItemList [ + CssRegularDimension { + value_token: CSS_NUMBER_LITERAL@25..26 "1" [] [], + unit_token: IDENT@26..28 "px" [] [], + }, + ], + }, + }, + important: missing (optional), + }, + semicolon_token: SEMICOLON@28..29 ";" [] [], + }, + CssDeclarationWithSemicolon { + declaration: CssDeclaration { + property: CssGenericProperty { + name: ScssInterpolatedIdentifier { + items: ScssInterpolatedIdentifierPartList [ + CssIdentifier { + value_token: IDENT@29..39 "margin-" [Newline("\n"), Whitespace(" ")] [], + }, + ScssInterpolation { + hash_token: HASH@39..40 "#" [] [], + l_curly_token: L_CURLY@40..41 "{" [] [], + value: ScssExpression { + items: ScssExpressionItemList [ + ScssIdentifier { + dollar_token: DOLLAR@41..42 "$" [] [], + name: CssIdentifier { + value_token: IDENT@42..46 "side" [] [], + }, + }, + ], + }, + r_curly_token: R_CURLY@46..47 "}" [] [], + }, + ], + }, + colon_token: COLON@47..49 ":" [] [Whitespace(" ")], + value: ScssExpression { + items: ScssExpressionItemList [ + CssRegularDimension { + value_token: CSS_NUMBER_LITERAL@49..50 "1" [] [], + unit_token: IDENT@50..52 "px" [] [], + }, + ], + }, + }, + important: missing (optional), + }, + semicolon_token: SEMICOLON@52..53 ";" [] [], + }, + CssDeclarationWithSemicolon { + declaration: CssDeclaration { + property: CssGenericProperty { + name: ScssInterpolatedIdentifier { + items: ScssInterpolatedIdentifierPartList [ + CssIdentifier { + value_token: IDENT@53..64 "--theme-" [Newline("\n"), Whitespace(" ")] [], + }, + ScssInterpolation { + hash_token: HASH@64..65 "#" [] [], + l_curly_token: L_CURLY@65..66 "{" [] [], + value: ScssExpression { + items: ScssExpressionItemList [ + ScssIdentifier { + dollar_token: DOLLAR@66..67 "$" [] [], + name: CssIdentifier { + value_token: IDENT@67..71 "slot" [] [], + }, + }, + ], + }, + r_curly_token: R_CURLY@71..72 "}" [] [], + }, + ], + }, + colon_token: COLON@72..74 ":" [] [Whitespace(" ")], + value: ScssExpression { + items: ScssExpressionItemList [ + CssIdentifier { + value_token: IDENT@74..77 "red" [] [], + }, + ], + }, + }, + important: missing (optional), + }, + semicolon_token: SEMICOLON@77..78 ";" [] [], + }, + ], + r_curly_token: R_CURLY@78..80 "}" [Newline("\n")] [], + }, + }, + }, + ], + eof_token: EOF@80..81 "" [Newline("\n")] [], +} +``` + +## CST + +``` +0: CSS_ROOT@0..81 + 0: (empty) + 1: CSS_ROOT_ITEM_LIST@0..80 + 0: CSS_AT_RULE@0..80 + 0: AT@0..1 "@" [] [] + 1: CSS_FONT_FACE_AT_RULE@1..80 + 0: CSS_FONT_FACE_AT_RULE_DECLARATOR@1..11 + 0: FONT_FACE_KW@1..11 "font-face" [] [Whitespace(" ")] + 1: CSS_DECLARATION_BLOCK@11..80 + 0: L_CURLY@11..12 "{" [] [] + 1: CSS_DECLARATION_LIST@12..78 + 0: CSS_DECLARATION_WITH_SEMICOLON@12..29 + 0: CSS_DECLARATION@12..28 + 0: CSS_GENERIC_PROPERTY@12..28 + 0: SCSS_INTERPOLATED_IDENTIFIER@12..23 + 0: SCSS_INTERPOLATED_IDENTIFIER_PART_LIST@12..23 + 0: SCSS_INTERPOLATION@12..23 + 0: HASH@12..16 "#" [Newline("\n"), Whitespace(" ")] [] + 1: L_CURLY@16..17 "{" [] [] + 2: SCSS_EXPRESSION@17..22 + 0: SCSS_EXPRESSION_ITEM_LIST@17..22 + 0: SCSS_IDENTIFIER@17..22 + 0: DOLLAR@17..18 "$" [] [] + 1: CSS_IDENTIFIER@18..22 + 0: IDENT@18..22 "name" [] [] + 3: R_CURLY@22..23 "}" [] [] + 1: COLON@23..25 ":" [] [Whitespace(" ")] + 2: SCSS_EXPRESSION@25..28 + 0: SCSS_EXPRESSION_ITEM_LIST@25..28 + 0: CSS_REGULAR_DIMENSION@25..28 + 0: CSS_NUMBER_LITERAL@25..26 "1" [] [] + 1: IDENT@26..28 "px" [] [] + 1: (empty) + 1: SEMICOLON@28..29 ";" [] [] + 1: CSS_DECLARATION_WITH_SEMICOLON@29..53 + 0: CSS_DECLARATION@29..52 + 0: CSS_GENERIC_PROPERTY@29..52 + 0: SCSS_INTERPOLATED_IDENTIFIER@29..47 + 0: SCSS_INTERPOLATED_IDENTIFIER_PART_LIST@29..47 + 0: CSS_IDENTIFIER@29..39 + 0: IDENT@29..39 "margin-" [Newline("\n"), Whitespace(" ")] [] + 1: SCSS_INTERPOLATION@39..47 + 0: HASH@39..40 "#" [] [] + 1: L_CURLY@40..41 "{" [] [] + 2: SCSS_EXPRESSION@41..46 + 0: SCSS_EXPRESSION_ITEM_LIST@41..46 + 0: SCSS_IDENTIFIER@41..46 + 0: DOLLAR@41..42 "$" [] [] + 1: CSS_IDENTIFIER@42..46 + 0: IDENT@42..46 "side" [] [] + 3: R_CURLY@46..47 "}" [] [] + 1: COLON@47..49 ":" [] [Whitespace(" ")] + 2: SCSS_EXPRESSION@49..52 + 0: SCSS_EXPRESSION_ITEM_LIST@49..52 + 0: CSS_REGULAR_DIMENSION@49..52 + 0: CSS_NUMBER_LITERAL@49..50 "1" [] [] + 1: IDENT@50..52 "px" [] [] + 1: (empty) + 1: SEMICOLON@52..53 ";" [] [] + 2: CSS_DECLARATION_WITH_SEMICOLON@53..78 + 0: CSS_DECLARATION@53..77 + 0: CSS_GENERIC_PROPERTY@53..77 + 0: SCSS_INTERPOLATED_IDENTIFIER@53..72 + 0: SCSS_INTERPOLATED_IDENTIFIER_PART_LIST@53..72 + 0: CSS_IDENTIFIER@53..64 + 0: IDENT@53..64 "--theme-" [Newline("\n"), Whitespace(" ")] [] + 1: SCSS_INTERPOLATION@64..72 + 0: HASH@64..65 "#" [] [] + 1: L_CURLY@65..66 "{" [] [] + 2: SCSS_EXPRESSION@66..71 + 0: SCSS_EXPRESSION_ITEM_LIST@66..71 + 0: SCSS_IDENTIFIER@66..71 + 0: DOLLAR@66..67 "$" [] [] + 1: CSS_IDENTIFIER@67..71 + 0: IDENT@67..71 "slot" [] [] + 3: R_CURLY@71..72 "}" [] [] + 1: COLON@72..74 ":" [] [Whitespace(" ")] + 2: SCSS_EXPRESSION@74..77 + 0: SCSS_EXPRESSION_ITEM_LIST@74..77 + 0: CSS_IDENTIFIER@74..77 + 0: IDENT@74..77 "red" [] [] + 1: (empty) + 1: SEMICOLON@77..78 ";" [] [] + 2: R_CURLY@78..80 "}" [Newline("\n")] [] + 2: EOF@80..81 "" [Newline("\n")] [] + +``` diff --git a/crates/biome_css_parser/tests/css_test_suite/ok/scss/at-rule/page.scss b/crates/biome_css_parser/tests/css_test_suite/ok/scss/at-rule/page.scss index f337df0e5061..1b9b9a981da8 100644 --- a/crates/biome_css_parser/tests/css_test_suite/ok/scss/at-rule/page.scss +++ b/crates/biome_css_parser/tests/css_test_suite/ok/scss/at-rule/page.scss @@ -2,6 +2,9 @@ $padding: 12px !default; $scale: 1 + 2 * 3; padding: $padding * $scale; + #{$name}: 1px; + margin-#{$side}: 1px; + --theme-#{$slot}: red; font: { size: 12px; family: $font-stack; @@ -23,5 +26,8 @@ @top-right { theme.$gutter: 8px; padding: theme.$gutter; + #{$inner}: 2px; + margin-#{$inner-side}: 2px; + --theme-#{$inner-slot}: blue; } } diff --git a/crates/biome_css_parser/tests/css_test_suite/ok/scss/at-rule/page.scss.snap b/crates/biome_css_parser/tests/css_test_suite/ok/scss/at-rule/page.scss.snap index 4ffc595f6e15..d3c756486ab3 100644 --- a/crates/biome_css_parser/tests/css_test_suite/ok/scss/at-rule/page.scss.snap +++ b/crates/biome_css_parser/tests/css_test_suite/ok/scss/at-rule/page.scss.snap @@ -10,6 +10,9 @@ expression: snapshot $padding: 12px !default; $scale: 1 + 2 * 3; padding: $padding * $scale; + #{$name}: 1px; + margin-#{$side}: 1px; + --theme-#{$slot}: red; font: { size: 12px; family: $font-stack; @@ -31,6 +34,9 @@ expression: snapshot @top-right { theme.$gutter: 8px; padding: theme.$gutter; + #{$inner}: 2px; + margin-#{$inner-side}: 2px; + --theme-#{$inner-slot}: blue; } } @@ -148,50 +154,163 @@ CssRoot { }, semicolon_token: SEMICOLON@90..91 ";" [] [], }, + CssDeclarationWithSemicolon { + declaration: CssDeclaration { + property: CssGenericProperty { + name: ScssInterpolatedIdentifier { + items: ScssInterpolatedIdentifierPartList [ + ScssInterpolation { + hash_token: HASH@91..95 "#" [Newline("\n"), Whitespace(" ")] [], + l_curly_token: L_CURLY@95..96 "{" [] [], + value: ScssExpression { + items: ScssExpressionItemList [ + ScssIdentifier { + dollar_token: DOLLAR@96..97 "$" [] [], + name: CssIdentifier { + value_token: IDENT@97..101 "name" [] [], + }, + }, + ], + }, + r_curly_token: R_CURLY@101..102 "}" [] [], + }, + ], + }, + colon_token: COLON@102..104 ":" [] [Whitespace(" ")], + value: ScssExpression { + items: ScssExpressionItemList [ + CssRegularDimension { + value_token: CSS_NUMBER_LITERAL@104..105 "1" [] [], + unit_token: IDENT@105..107 "px" [] [], + }, + ], + }, + }, + important: missing (optional), + }, + semicolon_token: SEMICOLON@107..108 ";" [] [], + }, + CssDeclarationWithSemicolon { + declaration: CssDeclaration { + property: CssGenericProperty { + name: ScssInterpolatedIdentifier { + items: ScssInterpolatedIdentifierPartList [ + CssIdentifier { + value_token: IDENT@108..118 "margin-" [Newline("\n"), Whitespace(" ")] [], + }, + ScssInterpolation { + hash_token: HASH@118..119 "#" [] [], + l_curly_token: L_CURLY@119..120 "{" [] [], + value: ScssExpression { + items: ScssExpressionItemList [ + ScssIdentifier { + dollar_token: DOLLAR@120..121 "$" [] [], + name: CssIdentifier { + value_token: IDENT@121..125 "side" [] [], + }, + }, + ], + }, + r_curly_token: R_CURLY@125..126 "}" [] [], + }, + ], + }, + colon_token: COLON@126..128 ":" [] [Whitespace(" ")], + value: ScssExpression { + items: ScssExpressionItemList [ + CssRegularDimension { + value_token: CSS_NUMBER_LITERAL@128..129 "1" [] [], + unit_token: IDENT@129..131 "px" [] [], + }, + ], + }, + }, + important: missing (optional), + }, + semicolon_token: SEMICOLON@131..132 ";" [] [], + }, + CssDeclarationWithSemicolon { + declaration: CssDeclaration { + property: CssGenericProperty { + name: ScssInterpolatedIdentifier { + items: ScssInterpolatedIdentifierPartList [ + CssIdentifier { + value_token: IDENT@132..143 "--theme-" [Newline("\n"), Whitespace(" ")] [], + }, + ScssInterpolation { + hash_token: HASH@143..144 "#" [] [], + l_curly_token: L_CURLY@144..145 "{" [] [], + value: ScssExpression { + items: ScssExpressionItemList [ + ScssIdentifier { + dollar_token: DOLLAR@145..146 "$" [] [], + name: CssIdentifier { + value_token: IDENT@146..150 "slot" [] [], + }, + }, + ], + }, + r_curly_token: R_CURLY@150..151 "}" [] [], + }, + ], + }, + colon_token: COLON@151..153 ":" [] [Whitespace(" ")], + value: ScssExpression { + items: ScssExpressionItemList [ + CssIdentifier { + value_token: IDENT@153..156 "red" [] [], + }, + ], + }, + }, + important: missing (optional), + }, + semicolon_token: SEMICOLON@156..157 ";" [] [], + }, ScssNestingDeclaration { name: CssIdentifier { - value_token: IDENT@91..98 "font" [Newline("\n"), Whitespace(" ")] [], + value_token: IDENT@157..164 "font" [Newline("\n"), Whitespace(" ")] [], }, - colon_token: COLON@98..100 ":" [] [Whitespace(" ")], + colon_token: COLON@164..166 ":" [] [Whitespace(" ")], value: ScssExpression { items: ScssExpressionItemList [], }, block: CssDeclarationOrRuleBlock { - l_curly_token: L_CURLY@100..101 "{" [] [], + l_curly_token: L_CURLY@166..167 "{" [] [], items: CssDeclarationOrRuleList [ CssDeclarationWithSemicolon { declaration: CssDeclaration { property: CssGenericProperty { name: CssIdentifier { - value_token: IDENT@101..110 "size" [Newline("\n"), Whitespace(" ")] [], + value_token: IDENT@167..176 "size" [Newline("\n"), Whitespace(" ")] [], }, - colon_token: COLON@110..112 ":" [] [Whitespace(" ")], + colon_token: COLON@176..178 ":" [] [Whitespace(" ")], value: ScssExpression { items: ScssExpressionItemList [ CssRegularDimension { - value_token: CSS_NUMBER_LITERAL@112..114 "12" [] [], - unit_token: IDENT@114..116 "px" [] [], + value_token: CSS_NUMBER_LITERAL@178..180 "12" [] [], + unit_token: IDENT@180..182 "px" [] [], }, ], }, }, important: missing (optional), }, - semicolon_token: SEMICOLON@116..117 ";" [] [], + semicolon_token: SEMICOLON@182..183 ";" [] [], }, CssDeclarationWithSemicolon { declaration: CssDeclaration { property: CssGenericProperty { name: CssIdentifier { - value_token: IDENT@117..128 "family" [Newline("\n"), Whitespace(" ")] [], + value_token: IDENT@183..194 "family" [Newline("\n"), Whitespace(" ")] [], }, - colon_token: COLON@128..130 ":" [] [Whitespace(" ")], + colon_token: COLON@194..196 ":" [] [Whitespace(" ")], value: ScssExpression { items: ScssExpressionItemList [ ScssIdentifier { - dollar_token: DOLLAR@130..131 "$" [] [], + dollar_token: DOLLAR@196..197 "$" [] [], name: CssIdentifier { - value_token: IDENT@131..141 "font-stack" [] [], + value_token: IDENT@197..207 "font-stack" [] [], }, }, ], @@ -199,163 +318,163 @@ CssRoot { }, important: missing (optional), }, - semicolon_token: SEMICOLON@141..142 ";" [] [], + semicolon_token: SEMICOLON@207..208 ";" [] [], }, ], - r_curly_token: R_CURLY@142..146 "}" [Newline("\n"), Whitespace(" ")] [], + r_curly_token: R_CURLY@208..212 "}" [Newline("\n"), Whitespace(" ")] [], }, }, ScssDeclaration { name: ScssIdentifier { - dollar_token: DOLLAR@146..150 "$" [Newline("\n"), Whitespace(" ")] [], + dollar_token: DOLLAR@212..216 "$" [Newline("\n"), Whitespace(" ")] [], name: CssIdentifier { - value_token: IDENT@150..153 "map" [] [], + value_token: IDENT@216..219 "map" [] [], }, }, - colon_token: COLON@153..155 ":" [] [Whitespace(" ")], + colon_token: COLON@219..221 ":" [] [Whitespace(" ")], value: ScssExpression { items: ScssExpressionItemList [ ScssMapExpression { - l_paren_token: L_PAREN@155..156 "(" [] [], + l_paren_token: L_PAREN@221..222 "(" [] [], pairs: ScssMapExpressionPairList [ ScssMapExpressionPair { key: ScssExpression { items: ScssExpressionItemList [ ScssParenthesizedExpression { - l_paren_token: L_PAREN@156..162 "(" [Newline("\n"), Whitespace(" ")] [], + l_paren_token: L_PAREN@222..228 "(" [Newline("\n"), Whitespace(" ")] [], expression: ScssExpression { items: ScssExpressionItemList [ ScssBinaryExpression { left: ScssIdentifier { - dollar_token: DOLLAR@162..163 "$" [] [], + dollar_token: DOLLAR@228..229 "$" [] [], name: CssIdentifier { - value_token: IDENT@163..171 "padding" [] [Whitespace(" ")], + value_token: IDENT@229..237 "padding" [] [Whitespace(" ")], }, }, - operator: PLUS@171..173 "+" [] [Whitespace(" ")], + operator: PLUS@237..239 "+" [] [Whitespace(" ")], right: CssNumber { - value_token: CSS_NUMBER_LITERAL@173..174 "1" [] [], + value_token: CSS_NUMBER_LITERAL@239..240 "1" [] [], }, }, ], }, - r_paren_token: R_PAREN@174..175 ")" [] [], + r_paren_token: R_PAREN@240..241 ")" [] [], }, ], }, - colon_token: COLON@175..177 ":" [] [Whitespace(" ")], + colon_token: COLON@241..243 ":" [] [Whitespace(" ")], value: ScssExpression { items: ScssExpressionItemList [ CssIdentifier { - value_token: IDENT@177..180 "foo" [] [], + value_token: IDENT@243..246 "foo" [] [], }, ], }, }, - COMMA@180..181 "," [] [], + COMMA@246..247 "," [] [], ScssMapExpressionPair { key: ScssExpression { items: ScssExpressionItemList [ ScssParenthesizedExpression { - l_paren_token: L_PAREN@181..187 "(" [Newline("\n"), Whitespace(" ")] [], + l_paren_token: L_PAREN@247..253 "(" [Newline("\n"), Whitespace(" ")] [], expression: ScssExpression { items: ScssExpressionItemList [ ScssBinaryExpression { left: CssNumber { - value_token: CSS_NUMBER_LITERAL@187..189 "1" [] [Whitespace(" ")], + value_token: CSS_NUMBER_LITERAL@253..255 "1" [] [Whitespace(" ")], }, - operator: PLUS@189..191 "+" [] [Whitespace(" ")], + operator: PLUS@255..257 "+" [] [Whitespace(" ")], right: CssNumber { - value_token: CSS_NUMBER_LITERAL@191..192 "2" [] [], + value_token: CSS_NUMBER_LITERAL@257..258 "2" [] [], }, }, ], }, - r_paren_token: R_PAREN@192..193 ")" [] [], + r_paren_token: R_PAREN@258..259 ")" [] [], }, ], }, - colon_token: COLON@193..195 ":" [] [Whitespace(" ")], + colon_token: COLON@259..261 ":" [] [Whitespace(" ")], value: ScssExpression { items: ScssExpressionItemList [ CssIdentifier { - value_token: IDENT@195..198 "bar" [] [], + value_token: IDENT@261..264 "bar" [] [], }, ], }, }, - COMMA@198..199 "," [] [], + COMMA@264..265 "," [] [], ], - r_paren_token: R_PAREN@199..203 ")" [Newline("\n"), Whitespace(" ")] [], + r_paren_token: R_PAREN@265..269 ")" [Newline("\n"), Whitespace(" ")] [], }, ], }, modifiers: ScssVariableModifierList [], - semicolon_token: SEMICOLON@203..204 ";" [] [], + semicolon_token: SEMICOLON@269..270 ";" [] [], }, CssMarginAtRule { - at_token: AT@204..209 "@" [Newline("\n"), Newline("\n"), Whitespace(" ")] [], - name: TOP_LEFT_KW@209..218 "top-left" [] [Whitespace(" ")], + at_token: AT@270..275 "@" [Newline("\n"), Newline("\n"), Whitespace(" ")] [], + name: TOP_LEFT_KW@275..284 "top-left" [] [Whitespace(" ")], block: CssDeclarationOrAtRuleBlock { - l_curly_token: L_CURLY@218..219 "{" [] [], + l_curly_token: L_CURLY@284..285 "{" [] [], items: CssDeclarationOrAtRuleList [ ScssDeclaration { name: ScssIdentifier { - dollar_token: DOLLAR@219..225 "$" [Newline("\n"), Whitespace(" ")] [], + dollar_token: DOLLAR@285..291 "$" [Newline("\n"), Whitespace(" ")] [], name: CssIdentifier { - value_token: IDENT@225..231 "margin" [] [], + value_token: IDENT@291..297 "margin" [] [], }, }, - colon_token: COLON@231..233 ":" [] [Whitespace(" ")], + colon_token: COLON@297..299 ":" [] [Whitespace(" ")], value: ScssExpression { items: ScssExpressionItemList [ CssRegularDimension { - value_token: CSS_NUMBER_LITERAL@233..234 "4" [] [], - unit_token: IDENT@234..237 "px" [] [Whitespace(" ")], + value_token: CSS_NUMBER_LITERAL@299..300 "4" [] [], + unit_token: IDENT@300..303 "px" [] [Whitespace(" ")], }, ], }, modifiers: ScssVariableModifierList [ ScssVariableModifier { - excl_token: BANG@237..238 "!" [] [], - value: GLOBAL_KW@238..244 "global" [] [], + excl_token: BANG@303..304 "!" [] [], + value: GLOBAL_KW@304..310 "global" [] [], }, ], - semicolon_token: SEMICOLON@244..245 ";" [] [], + semicolon_token: SEMICOLON@310..311 ";" [] [], }, CssDeclarationWithSemicolon { declaration: CssDeclaration { property: CssGenericProperty { name: CssIdentifier { - value_token: IDENT@245..256 "margin" [Newline("\n"), Whitespace(" ")] [], + value_token: IDENT@311..322 "margin" [Newline("\n"), Whitespace(" ")] [], }, - colon_token: COLON@256..258 ":" [] [Whitespace(" ")], + colon_token: COLON@322..324 ":" [] [Whitespace(" ")], value: ScssExpression { items: ScssExpressionItemList [ ScssBinaryExpression { left: ScssParenthesizedExpression { - l_paren_token: L_PAREN@258..259 "(" [] [], + l_paren_token: L_PAREN@324..325 "(" [] [], expression: ScssExpression { items: ScssExpressionItemList [ ScssBinaryExpression { left: ScssIdentifier { - dollar_token: DOLLAR@259..260 "$" [] [], + dollar_token: DOLLAR@325..326 "$" [] [], name: CssIdentifier { - value_token: IDENT@260..267 "margin" [] [Whitespace(" ")], + value_token: IDENT@326..333 "margin" [] [Whitespace(" ")], }, }, - operator: PLUS@267..269 "+" [] [Whitespace(" ")], + operator: PLUS@333..335 "+" [] [Whitespace(" ")], right: CssNumber { - value_token: CSS_NUMBER_LITERAL@269..270 "2" [] [], + value_token: CSS_NUMBER_LITERAL@335..336 "2" [] [], }, }, ], }, - r_paren_token: R_PAREN@270..272 ")" [] [Whitespace(" ")], + r_paren_token: R_PAREN@336..338 ")" [] [Whitespace(" ")], }, - operator: SLASH@272..274 "/" [] [Whitespace(" ")], + operator: SLASH@338..340 "/" [] [Whitespace(" ")], right: CssNumber { - value_token: CSS_NUMBER_LITERAL@274..275 "2" [] [], + value_token: CSS_NUMBER_LITERAL@340..341 "2" [] [], }, }, ], @@ -363,66 +482,66 @@ CssRoot { }, important: missing (optional), }, - semicolon_token: SEMICOLON@275..276 ";" [] [], + semicolon_token: SEMICOLON@341..342 ";" [] [], }, ScssNestingDeclaration { name: CssIdentifier { - value_token: IDENT@276..287 "border" [Newline("\n"), Whitespace(" ")] [], + value_token: IDENT@342..353 "border" [Newline("\n"), Whitespace(" ")] [], }, - colon_token: COLON@287..289 ":" [] [Whitespace(" ")], + colon_token: COLON@353..355 ":" [] [Whitespace(" ")], value: ScssExpression { items: ScssExpressionItemList [ CssRegularDimension { - value_token: CSS_NUMBER_LITERAL@289..290 "1" [] [], - unit_token: IDENT@290..293 "px" [] [Whitespace(" ")], + value_token: CSS_NUMBER_LITERAL@355..356 "1" [] [], + unit_token: IDENT@356..359 "px" [] [Whitespace(" ")], }, CssIdentifier { - value_token: IDENT@293..299 "solid" [] [Whitespace(" ")], + value_token: IDENT@359..365 "solid" [] [Whitespace(" ")], }, ScssIdentifier { - dollar_token: DOLLAR@299..300 "$" [] [], + dollar_token: DOLLAR@365..366 "$" [] [], name: CssIdentifier { - value_token: IDENT@300..306 "color" [] [Whitespace(" ")], + value_token: IDENT@366..372 "color" [] [Whitespace(" ")], }, }, ], }, block: CssDeclarationOrRuleBlock { - l_curly_token: L_CURLY@306..307 "{" [] [], + l_curly_token: L_CURLY@372..373 "{" [] [], items: CssDeclarationOrRuleList [ CssDeclarationWithSemicolon { declaration: CssDeclaration { property: CssGenericProperty { name: CssIdentifier { - value_token: IDENT@307..319 "width" [Newline("\n"), Whitespace(" ")] [], + value_token: IDENT@373..385 "width" [Newline("\n"), Whitespace(" ")] [], }, - colon_token: COLON@319..321 ":" [] [Whitespace(" ")], + colon_token: COLON@385..387 ":" [] [Whitespace(" ")], value: ScssExpression { items: ScssExpressionItemList [ CssRegularDimension { - value_token: CSS_NUMBER_LITERAL@321..322 "1" [] [], - unit_token: IDENT@322..324 "px" [] [], + value_token: CSS_NUMBER_LITERAL@387..388 "1" [] [], + unit_token: IDENT@388..390 "px" [] [], }, ], }, }, important: missing (optional), }, - semicolon_token: SEMICOLON@324..325 ";" [] [], + semicolon_token: SEMICOLON@390..391 ";" [] [], }, CssDeclarationWithSemicolon { declaration: CssDeclaration { property: CssGenericProperty { name: CssIdentifier { - value_token: IDENT@325..337 "color" [Newline("\n"), Whitespace(" ")] [], + value_token: IDENT@391..403 "color" [Newline("\n"), Whitespace(" ")] [], }, - colon_token: COLON@337..339 ":" [] [Whitespace(" ")], + colon_token: COLON@403..405 ":" [] [Whitespace(" ")], value: ScssExpression { items: ScssExpressionItemList [ ScssIdentifier { - dollar_token: DOLLAR@339..340 "$" [] [], + dollar_token: DOLLAR@405..406 "$" [] [], name: CssIdentifier { - value_token: IDENT@340..345 "color" [] [], + value_token: IDENT@406..411 "color" [] [], }, }, ], @@ -430,65 +549,65 @@ CssRoot { }, important: missing (optional), }, - semicolon_token: SEMICOLON@345..346 ";" [] [], + semicolon_token: SEMICOLON@411..412 ";" [] [], }, ], - r_curly_token: R_CURLY@346..352 "}" [Newline("\n"), Whitespace(" ")] [], + r_curly_token: R_CURLY@412..418 "}" [Newline("\n"), Whitespace(" ")] [], }, }, ], - r_curly_token: R_CURLY@352..356 "}" [Newline("\n"), Whitespace(" ")] [], + r_curly_token: R_CURLY@418..422 "}" [Newline("\n"), Whitespace(" ")] [], }, }, CssMarginAtRule { - at_token: AT@356..361 "@" [Newline("\n"), Newline("\n"), Whitespace(" ")] [], - name: TOP_RIGHT_KW@361..371 "top-right" [] [Whitespace(" ")], + at_token: AT@422..427 "@" [Newline("\n"), Newline("\n"), Whitespace(" ")] [], + name: TOP_RIGHT_KW@427..437 "top-right" [] [Whitespace(" ")], block: CssDeclarationOrAtRuleBlock { - l_curly_token: L_CURLY@371..372 "{" [] [], + l_curly_token: L_CURLY@437..438 "{" [] [], items: CssDeclarationOrAtRuleList [ ScssDeclaration { name: ScssNamespacedIdentifier { namespace: CssIdentifier { - value_token: IDENT@372..382 "theme" [Newline("\n"), Whitespace(" ")] [], + value_token: IDENT@438..448 "theme" [Newline("\n"), Whitespace(" ")] [], }, - dot_token: DOT@382..383 "." [] [], + dot_token: DOT@448..449 "." [] [], name: ScssIdentifier { - dollar_token: DOLLAR@383..384 "$" [] [], + dollar_token: DOLLAR@449..450 "$" [] [], name: CssIdentifier { - value_token: IDENT@384..390 "gutter" [] [], + value_token: IDENT@450..456 "gutter" [] [], }, }, }, - colon_token: COLON@390..392 ":" [] [Whitespace(" ")], + colon_token: COLON@456..458 ":" [] [Whitespace(" ")], value: ScssExpression { items: ScssExpressionItemList [ CssRegularDimension { - value_token: CSS_NUMBER_LITERAL@392..393 "8" [] [], - unit_token: IDENT@393..395 "px" [] [], + value_token: CSS_NUMBER_LITERAL@458..459 "8" [] [], + unit_token: IDENT@459..461 "px" [] [], }, ], }, modifiers: ScssVariableModifierList [], - semicolon_token: SEMICOLON@395..396 ";" [] [], + semicolon_token: SEMICOLON@461..462 ";" [] [], }, CssDeclarationWithSemicolon { declaration: CssDeclaration { property: CssGenericProperty { name: CssIdentifier { - value_token: IDENT@396..408 "padding" [Newline("\n"), Whitespace(" ")] [], + value_token: IDENT@462..474 "padding" [Newline("\n"), Whitespace(" ")] [], }, - colon_token: COLON@408..410 ":" [] [Whitespace(" ")], + colon_token: COLON@474..476 ":" [] [Whitespace(" ")], value: ScssExpression { items: ScssExpressionItemList [ ScssQualifiedName { module: CssIdentifier { - value_token: IDENT@410..415 "theme" [] [], + value_token: IDENT@476..481 "theme" [] [], }, - dot_token: DOT@415..416 "." [] [], + dot_token: DOT@481..482 "." [] [], member: ScssIdentifier { - dollar_token: DOLLAR@416..417 "$" [] [], + dollar_token: DOLLAR@482..483 "$" [] [], name: CssIdentifier { - value_token: IDENT@417..423 "gutter" [] [], + value_token: IDENT@483..489 "gutter" [] [], }, }, }, @@ -497,31 +616,144 @@ CssRoot { }, important: missing (optional), }, - semicolon_token: SEMICOLON@423..424 ";" [] [], + semicolon_token: SEMICOLON@489..490 ";" [] [], + }, + CssDeclarationWithSemicolon { + declaration: CssDeclaration { + property: CssGenericProperty { + name: ScssInterpolatedIdentifier { + items: ScssInterpolatedIdentifierPartList [ + ScssInterpolation { + hash_token: HASH@490..496 "#" [Newline("\n"), Whitespace(" ")] [], + l_curly_token: L_CURLY@496..497 "{" [] [], + value: ScssExpression { + items: ScssExpressionItemList [ + ScssIdentifier { + dollar_token: DOLLAR@497..498 "$" [] [], + name: CssIdentifier { + value_token: IDENT@498..503 "inner" [] [], + }, + }, + ], + }, + r_curly_token: R_CURLY@503..504 "}" [] [], + }, + ], + }, + colon_token: COLON@504..506 ":" [] [Whitespace(" ")], + value: ScssExpression { + items: ScssExpressionItemList [ + CssRegularDimension { + value_token: CSS_NUMBER_LITERAL@506..507 "2" [] [], + unit_token: IDENT@507..509 "px" [] [], + }, + ], + }, + }, + important: missing (optional), + }, + semicolon_token: SEMICOLON@509..510 ";" [] [], + }, + CssDeclarationWithSemicolon { + declaration: CssDeclaration { + property: CssGenericProperty { + name: ScssInterpolatedIdentifier { + items: ScssInterpolatedIdentifierPartList [ + CssIdentifier { + value_token: IDENT@510..522 "margin-" [Newline("\n"), Whitespace(" ")] [], + }, + ScssInterpolation { + hash_token: HASH@522..523 "#" [] [], + l_curly_token: L_CURLY@523..524 "{" [] [], + value: ScssExpression { + items: ScssExpressionItemList [ + ScssIdentifier { + dollar_token: DOLLAR@524..525 "$" [] [], + name: CssIdentifier { + value_token: IDENT@525..535 "inner-side" [] [], + }, + }, + ], + }, + r_curly_token: R_CURLY@535..536 "}" [] [], + }, + ], + }, + colon_token: COLON@536..538 ":" [] [Whitespace(" ")], + value: ScssExpression { + items: ScssExpressionItemList [ + CssRegularDimension { + value_token: CSS_NUMBER_LITERAL@538..539 "2" [] [], + unit_token: IDENT@539..541 "px" [] [], + }, + ], + }, + }, + important: missing (optional), + }, + semicolon_token: SEMICOLON@541..542 ";" [] [], + }, + CssDeclarationWithSemicolon { + declaration: CssDeclaration { + property: CssGenericProperty { + name: ScssInterpolatedIdentifier { + items: ScssInterpolatedIdentifierPartList [ + CssIdentifier { + value_token: IDENT@542..555 "--theme-" [Newline("\n"), Whitespace(" ")] [], + }, + ScssInterpolation { + hash_token: HASH@555..556 "#" [] [], + l_curly_token: L_CURLY@556..557 "{" [] [], + value: ScssExpression { + items: ScssExpressionItemList [ + ScssIdentifier { + dollar_token: DOLLAR@557..558 "$" [] [], + name: CssIdentifier { + value_token: IDENT@558..568 "inner-slot" [] [], + }, + }, + ], + }, + r_curly_token: R_CURLY@568..569 "}" [] [], + }, + ], + }, + colon_token: COLON@569..571 ":" [] [Whitespace(" ")], + value: ScssExpression { + items: ScssExpressionItemList [ + CssIdentifier { + value_token: IDENT@571..575 "blue" [] [], + }, + ], + }, + }, + important: missing (optional), + }, + semicolon_token: SEMICOLON@575..576 ";" [] [], }, ], - r_curly_token: R_CURLY@424..428 "}" [Newline("\n"), Whitespace(" ")] [], + r_curly_token: R_CURLY@576..580 "}" [Newline("\n"), Whitespace(" ")] [], }, }, ], - r_curly_token: R_CURLY@428..430 "}" [Newline("\n")] [], + r_curly_token: R_CURLY@580..582 "}" [Newline("\n")] [], }, }, }, ], - eof_token: EOF@430..431 "" [Newline("\n")] [], + eof_token: EOF@582..583 "" [Newline("\n")] [], } ``` ## CST ``` -0: CSS_ROOT@0..431 +0: CSS_ROOT@0..583 0: (empty) - 1: CSS_ROOT_ITEM_LIST@0..430 - 0: CSS_AT_RULE@0..430 + 1: CSS_ROOT_ITEM_LIST@0..582 + 0: CSS_AT_RULE@0..582 0: AT@0..1 "@" [] [] - 1: CSS_PAGE_AT_RULE@1..430 + 1: CSS_PAGE_AT_RULE@1..582 0: PAGE_KW@1..6 "page" [] [Whitespace(" ")] 1: CSS_PAGE_SELECTOR_LIST@6..12 0: CSS_PAGE_SELECTOR@6..12 @@ -531,9 +763,9 @@ CssRoot { 0: COLON@6..7 ":" [] [] 1: CSS_IDENTIFIER@7..12 0: IDENT@7..12 "left" [] [Whitespace(" ")] - 2: CSS_PAGE_AT_RULE_BLOCK@12..430 + 2: CSS_PAGE_AT_RULE_BLOCK@12..582 0: L_CURLY@12..13 "{" [] [] - 1: CSS_PAGE_AT_RULE_ITEM_LIST@13..428 + 1: CSS_PAGE_AT_RULE_ITEM_LIST@13..580 0: SCSS_DECLARATION@13..40 0: SCSS_IDENTIFIER@13..24 0: DOLLAR@13..17 "$" [Newline("\n"), Whitespace(" ")] [] @@ -590,238 +822,382 @@ CssRoot { 0: IDENT@85..90 "scale" [] [] 1: (empty) 1: SEMICOLON@90..91 ";" [] [] - 3: SCSS_NESTING_DECLARATION@91..146 - 0: CSS_IDENTIFIER@91..98 - 0: IDENT@91..98 "font" [Newline("\n"), Whitespace(" ")] [] - 1: COLON@98..100 ":" [] [Whitespace(" ")] - 2: SCSS_EXPRESSION@100..100 - 0: SCSS_EXPRESSION_ITEM_LIST@100..100 - 3: CSS_DECLARATION_OR_RULE_BLOCK@100..146 - 0: L_CURLY@100..101 "{" [] [] - 1: CSS_DECLARATION_OR_RULE_LIST@101..142 - 0: CSS_DECLARATION_WITH_SEMICOLON@101..117 - 0: CSS_DECLARATION@101..116 - 0: CSS_GENERIC_PROPERTY@101..116 - 0: CSS_IDENTIFIER@101..110 - 0: IDENT@101..110 "size" [Newline("\n"), Whitespace(" ")] [] - 1: COLON@110..112 ":" [] [Whitespace(" ")] - 2: SCSS_EXPRESSION@112..116 - 0: SCSS_EXPRESSION_ITEM_LIST@112..116 - 0: CSS_REGULAR_DIMENSION@112..116 - 0: CSS_NUMBER_LITERAL@112..114 "12" [] [] - 1: IDENT@114..116 "px" [] [] + 3: CSS_DECLARATION_WITH_SEMICOLON@91..108 + 0: CSS_DECLARATION@91..107 + 0: CSS_GENERIC_PROPERTY@91..107 + 0: SCSS_INTERPOLATED_IDENTIFIER@91..102 + 0: SCSS_INTERPOLATED_IDENTIFIER_PART_LIST@91..102 + 0: SCSS_INTERPOLATION@91..102 + 0: HASH@91..95 "#" [Newline("\n"), Whitespace(" ")] [] + 1: L_CURLY@95..96 "{" [] [] + 2: SCSS_EXPRESSION@96..101 + 0: SCSS_EXPRESSION_ITEM_LIST@96..101 + 0: SCSS_IDENTIFIER@96..101 + 0: DOLLAR@96..97 "$" [] [] + 1: CSS_IDENTIFIER@97..101 + 0: IDENT@97..101 "name" [] [] + 3: R_CURLY@101..102 "}" [] [] + 1: COLON@102..104 ":" [] [Whitespace(" ")] + 2: SCSS_EXPRESSION@104..107 + 0: SCSS_EXPRESSION_ITEM_LIST@104..107 + 0: CSS_REGULAR_DIMENSION@104..107 + 0: CSS_NUMBER_LITERAL@104..105 "1" [] [] + 1: IDENT@105..107 "px" [] [] + 1: (empty) + 1: SEMICOLON@107..108 ";" [] [] + 4: CSS_DECLARATION_WITH_SEMICOLON@108..132 + 0: CSS_DECLARATION@108..131 + 0: CSS_GENERIC_PROPERTY@108..131 + 0: SCSS_INTERPOLATED_IDENTIFIER@108..126 + 0: SCSS_INTERPOLATED_IDENTIFIER_PART_LIST@108..126 + 0: CSS_IDENTIFIER@108..118 + 0: IDENT@108..118 "margin-" [Newline("\n"), Whitespace(" ")] [] + 1: SCSS_INTERPOLATION@118..126 + 0: HASH@118..119 "#" [] [] + 1: L_CURLY@119..120 "{" [] [] + 2: SCSS_EXPRESSION@120..125 + 0: SCSS_EXPRESSION_ITEM_LIST@120..125 + 0: SCSS_IDENTIFIER@120..125 + 0: DOLLAR@120..121 "$" [] [] + 1: CSS_IDENTIFIER@121..125 + 0: IDENT@121..125 "side" [] [] + 3: R_CURLY@125..126 "}" [] [] + 1: COLON@126..128 ":" [] [Whitespace(" ")] + 2: SCSS_EXPRESSION@128..131 + 0: SCSS_EXPRESSION_ITEM_LIST@128..131 + 0: CSS_REGULAR_DIMENSION@128..131 + 0: CSS_NUMBER_LITERAL@128..129 "1" [] [] + 1: IDENT@129..131 "px" [] [] + 1: (empty) + 1: SEMICOLON@131..132 ";" [] [] + 5: CSS_DECLARATION_WITH_SEMICOLON@132..157 + 0: CSS_DECLARATION@132..156 + 0: CSS_GENERIC_PROPERTY@132..156 + 0: SCSS_INTERPOLATED_IDENTIFIER@132..151 + 0: SCSS_INTERPOLATED_IDENTIFIER_PART_LIST@132..151 + 0: CSS_IDENTIFIER@132..143 + 0: IDENT@132..143 "--theme-" [Newline("\n"), Whitespace(" ")] [] + 1: SCSS_INTERPOLATION@143..151 + 0: HASH@143..144 "#" [] [] + 1: L_CURLY@144..145 "{" [] [] + 2: SCSS_EXPRESSION@145..150 + 0: SCSS_EXPRESSION_ITEM_LIST@145..150 + 0: SCSS_IDENTIFIER@145..150 + 0: DOLLAR@145..146 "$" [] [] + 1: CSS_IDENTIFIER@146..150 + 0: IDENT@146..150 "slot" [] [] + 3: R_CURLY@150..151 "}" [] [] + 1: COLON@151..153 ":" [] [Whitespace(" ")] + 2: SCSS_EXPRESSION@153..156 + 0: SCSS_EXPRESSION_ITEM_LIST@153..156 + 0: CSS_IDENTIFIER@153..156 + 0: IDENT@153..156 "red" [] [] + 1: (empty) + 1: SEMICOLON@156..157 ";" [] [] + 6: SCSS_NESTING_DECLARATION@157..212 + 0: CSS_IDENTIFIER@157..164 + 0: IDENT@157..164 "font" [Newline("\n"), Whitespace(" ")] [] + 1: COLON@164..166 ":" [] [Whitespace(" ")] + 2: SCSS_EXPRESSION@166..166 + 0: SCSS_EXPRESSION_ITEM_LIST@166..166 + 3: CSS_DECLARATION_OR_RULE_BLOCK@166..212 + 0: L_CURLY@166..167 "{" [] [] + 1: CSS_DECLARATION_OR_RULE_LIST@167..208 + 0: CSS_DECLARATION_WITH_SEMICOLON@167..183 + 0: CSS_DECLARATION@167..182 + 0: CSS_GENERIC_PROPERTY@167..182 + 0: CSS_IDENTIFIER@167..176 + 0: IDENT@167..176 "size" [Newline("\n"), Whitespace(" ")] [] + 1: COLON@176..178 ":" [] [Whitespace(" ")] + 2: SCSS_EXPRESSION@178..182 + 0: SCSS_EXPRESSION_ITEM_LIST@178..182 + 0: CSS_REGULAR_DIMENSION@178..182 + 0: CSS_NUMBER_LITERAL@178..180 "12" [] [] + 1: IDENT@180..182 "px" [] [] 1: (empty) - 1: SEMICOLON@116..117 ";" [] [] - 1: CSS_DECLARATION_WITH_SEMICOLON@117..142 - 0: CSS_DECLARATION@117..141 - 0: CSS_GENERIC_PROPERTY@117..141 - 0: CSS_IDENTIFIER@117..128 - 0: IDENT@117..128 "family" [Newline("\n"), Whitespace(" ")] [] - 1: COLON@128..130 ":" [] [Whitespace(" ")] - 2: SCSS_EXPRESSION@130..141 - 0: SCSS_EXPRESSION_ITEM_LIST@130..141 - 0: SCSS_IDENTIFIER@130..141 - 0: DOLLAR@130..131 "$" [] [] - 1: CSS_IDENTIFIER@131..141 - 0: IDENT@131..141 "font-stack" [] [] + 1: SEMICOLON@182..183 ";" [] [] + 1: CSS_DECLARATION_WITH_SEMICOLON@183..208 + 0: CSS_DECLARATION@183..207 + 0: CSS_GENERIC_PROPERTY@183..207 + 0: CSS_IDENTIFIER@183..194 + 0: IDENT@183..194 "family" [Newline("\n"), Whitespace(" ")] [] + 1: COLON@194..196 ":" [] [Whitespace(" ")] + 2: SCSS_EXPRESSION@196..207 + 0: SCSS_EXPRESSION_ITEM_LIST@196..207 + 0: SCSS_IDENTIFIER@196..207 + 0: DOLLAR@196..197 "$" [] [] + 1: CSS_IDENTIFIER@197..207 + 0: IDENT@197..207 "font-stack" [] [] 1: (empty) - 1: SEMICOLON@141..142 ";" [] [] - 2: R_CURLY@142..146 "}" [Newline("\n"), Whitespace(" ")] [] - 4: SCSS_DECLARATION@146..204 - 0: SCSS_IDENTIFIER@146..153 - 0: DOLLAR@146..150 "$" [Newline("\n"), Whitespace(" ")] [] - 1: CSS_IDENTIFIER@150..153 - 0: IDENT@150..153 "map" [] [] - 1: COLON@153..155 ":" [] [Whitespace(" ")] - 2: SCSS_EXPRESSION@155..203 - 0: SCSS_EXPRESSION_ITEM_LIST@155..203 - 0: SCSS_MAP_EXPRESSION@155..203 - 0: L_PAREN@155..156 "(" [] [] - 1: SCSS_MAP_EXPRESSION_PAIR_LIST@156..199 - 0: SCSS_MAP_EXPRESSION_PAIR@156..180 - 0: SCSS_EXPRESSION@156..175 - 0: SCSS_EXPRESSION_ITEM_LIST@156..175 - 0: SCSS_PARENTHESIZED_EXPRESSION@156..175 - 0: L_PAREN@156..162 "(" [Newline("\n"), Whitespace(" ")] [] - 1: SCSS_EXPRESSION@162..174 - 0: SCSS_EXPRESSION_ITEM_LIST@162..174 - 0: SCSS_BINARY_EXPRESSION@162..174 - 0: SCSS_IDENTIFIER@162..171 - 0: DOLLAR@162..163 "$" [] [] - 1: CSS_IDENTIFIER@163..171 - 0: IDENT@163..171 "padding" [] [Whitespace(" ")] - 1: PLUS@171..173 "+" [] [Whitespace(" ")] - 2: CSS_NUMBER@173..174 - 0: CSS_NUMBER_LITERAL@173..174 "1" [] [] - 2: R_PAREN@174..175 ")" [] [] - 1: COLON@175..177 ":" [] [Whitespace(" ")] - 2: SCSS_EXPRESSION@177..180 - 0: SCSS_EXPRESSION_ITEM_LIST@177..180 - 0: CSS_IDENTIFIER@177..180 - 0: IDENT@177..180 "foo" [] [] - 1: COMMA@180..181 "," [] [] - 2: SCSS_MAP_EXPRESSION_PAIR@181..198 - 0: SCSS_EXPRESSION@181..193 - 0: SCSS_EXPRESSION_ITEM_LIST@181..193 - 0: SCSS_PARENTHESIZED_EXPRESSION@181..193 - 0: L_PAREN@181..187 "(" [Newline("\n"), Whitespace(" ")] [] - 1: SCSS_EXPRESSION@187..192 - 0: SCSS_EXPRESSION_ITEM_LIST@187..192 - 0: SCSS_BINARY_EXPRESSION@187..192 - 0: CSS_NUMBER@187..189 - 0: CSS_NUMBER_LITERAL@187..189 "1" [] [Whitespace(" ")] - 1: PLUS@189..191 "+" [] [Whitespace(" ")] - 2: CSS_NUMBER@191..192 - 0: CSS_NUMBER_LITERAL@191..192 "2" [] [] - 2: R_PAREN@192..193 ")" [] [] - 1: COLON@193..195 ":" [] [Whitespace(" ")] - 2: SCSS_EXPRESSION@195..198 - 0: SCSS_EXPRESSION_ITEM_LIST@195..198 - 0: CSS_IDENTIFIER@195..198 - 0: IDENT@195..198 "bar" [] [] - 3: COMMA@198..199 "," [] [] - 2: R_PAREN@199..203 ")" [Newline("\n"), Whitespace(" ")] [] - 3: SCSS_VARIABLE_MODIFIER_LIST@203..203 - 4: SEMICOLON@203..204 ";" [] [] - 5: CSS_MARGIN_AT_RULE@204..356 - 0: AT@204..209 "@" [Newline("\n"), Newline("\n"), Whitespace(" ")] [] - 1: TOP_LEFT_KW@209..218 "top-left" [] [Whitespace(" ")] - 2: CSS_DECLARATION_OR_AT_RULE_BLOCK@218..356 - 0: L_CURLY@218..219 "{" [] [] - 1: CSS_DECLARATION_OR_AT_RULE_LIST@219..352 - 0: SCSS_DECLARATION@219..245 - 0: SCSS_IDENTIFIER@219..231 - 0: DOLLAR@219..225 "$" [Newline("\n"), Whitespace(" ")] [] - 1: CSS_IDENTIFIER@225..231 - 0: IDENT@225..231 "margin" [] [] - 1: COLON@231..233 ":" [] [Whitespace(" ")] - 2: SCSS_EXPRESSION@233..237 - 0: SCSS_EXPRESSION_ITEM_LIST@233..237 - 0: CSS_REGULAR_DIMENSION@233..237 - 0: CSS_NUMBER_LITERAL@233..234 "4" [] [] - 1: IDENT@234..237 "px" [] [Whitespace(" ")] - 3: SCSS_VARIABLE_MODIFIER_LIST@237..244 - 0: SCSS_VARIABLE_MODIFIER@237..244 - 0: BANG@237..238 "!" [] [] - 1: GLOBAL_KW@238..244 "global" [] [] - 4: SEMICOLON@244..245 ";" [] [] - 1: CSS_DECLARATION_WITH_SEMICOLON@245..276 - 0: CSS_DECLARATION@245..275 - 0: CSS_GENERIC_PROPERTY@245..275 - 0: CSS_IDENTIFIER@245..256 - 0: IDENT@245..256 "margin" [Newline("\n"), Whitespace(" ")] [] - 1: COLON@256..258 ":" [] [Whitespace(" ")] - 2: SCSS_EXPRESSION@258..275 - 0: SCSS_EXPRESSION_ITEM_LIST@258..275 - 0: SCSS_BINARY_EXPRESSION@258..275 - 0: SCSS_PARENTHESIZED_EXPRESSION@258..272 - 0: L_PAREN@258..259 "(" [] [] - 1: SCSS_EXPRESSION@259..270 - 0: SCSS_EXPRESSION_ITEM_LIST@259..270 - 0: SCSS_BINARY_EXPRESSION@259..270 - 0: SCSS_IDENTIFIER@259..267 - 0: DOLLAR@259..260 "$" [] [] - 1: CSS_IDENTIFIER@260..267 - 0: IDENT@260..267 "margin" [] [Whitespace(" ")] - 1: PLUS@267..269 "+" [] [Whitespace(" ")] - 2: CSS_NUMBER@269..270 - 0: CSS_NUMBER_LITERAL@269..270 "2" [] [] - 2: R_PAREN@270..272 ")" [] [Whitespace(" ")] - 1: SLASH@272..274 "/" [] [Whitespace(" ")] - 2: CSS_NUMBER@274..275 - 0: CSS_NUMBER_LITERAL@274..275 "2" [] [] + 1: SEMICOLON@207..208 ";" [] [] + 2: R_CURLY@208..212 "}" [Newline("\n"), Whitespace(" ")] [] + 7: SCSS_DECLARATION@212..270 + 0: SCSS_IDENTIFIER@212..219 + 0: DOLLAR@212..216 "$" [Newline("\n"), Whitespace(" ")] [] + 1: CSS_IDENTIFIER@216..219 + 0: IDENT@216..219 "map" [] [] + 1: COLON@219..221 ":" [] [Whitespace(" ")] + 2: SCSS_EXPRESSION@221..269 + 0: SCSS_EXPRESSION_ITEM_LIST@221..269 + 0: SCSS_MAP_EXPRESSION@221..269 + 0: L_PAREN@221..222 "(" [] [] + 1: SCSS_MAP_EXPRESSION_PAIR_LIST@222..265 + 0: SCSS_MAP_EXPRESSION_PAIR@222..246 + 0: SCSS_EXPRESSION@222..241 + 0: SCSS_EXPRESSION_ITEM_LIST@222..241 + 0: SCSS_PARENTHESIZED_EXPRESSION@222..241 + 0: L_PAREN@222..228 "(" [Newline("\n"), Whitespace(" ")] [] + 1: SCSS_EXPRESSION@228..240 + 0: SCSS_EXPRESSION_ITEM_LIST@228..240 + 0: SCSS_BINARY_EXPRESSION@228..240 + 0: SCSS_IDENTIFIER@228..237 + 0: DOLLAR@228..229 "$" [] [] + 1: CSS_IDENTIFIER@229..237 + 0: IDENT@229..237 "padding" [] [Whitespace(" ")] + 1: PLUS@237..239 "+" [] [Whitespace(" ")] + 2: CSS_NUMBER@239..240 + 0: CSS_NUMBER_LITERAL@239..240 "1" [] [] + 2: R_PAREN@240..241 ")" [] [] + 1: COLON@241..243 ":" [] [Whitespace(" ")] + 2: SCSS_EXPRESSION@243..246 + 0: SCSS_EXPRESSION_ITEM_LIST@243..246 + 0: CSS_IDENTIFIER@243..246 + 0: IDENT@243..246 "foo" [] [] + 1: COMMA@246..247 "," [] [] + 2: SCSS_MAP_EXPRESSION_PAIR@247..264 + 0: SCSS_EXPRESSION@247..259 + 0: SCSS_EXPRESSION_ITEM_LIST@247..259 + 0: SCSS_PARENTHESIZED_EXPRESSION@247..259 + 0: L_PAREN@247..253 "(" [Newline("\n"), Whitespace(" ")] [] + 1: SCSS_EXPRESSION@253..258 + 0: SCSS_EXPRESSION_ITEM_LIST@253..258 + 0: SCSS_BINARY_EXPRESSION@253..258 + 0: CSS_NUMBER@253..255 + 0: CSS_NUMBER_LITERAL@253..255 "1" [] [Whitespace(" ")] + 1: PLUS@255..257 "+" [] [Whitespace(" ")] + 2: CSS_NUMBER@257..258 + 0: CSS_NUMBER_LITERAL@257..258 "2" [] [] + 2: R_PAREN@258..259 ")" [] [] + 1: COLON@259..261 ":" [] [Whitespace(" ")] + 2: SCSS_EXPRESSION@261..264 + 0: SCSS_EXPRESSION_ITEM_LIST@261..264 + 0: CSS_IDENTIFIER@261..264 + 0: IDENT@261..264 "bar" [] [] + 3: COMMA@264..265 "," [] [] + 2: R_PAREN@265..269 ")" [Newline("\n"), Whitespace(" ")] [] + 3: SCSS_VARIABLE_MODIFIER_LIST@269..269 + 4: SEMICOLON@269..270 ";" [] [] + 8: CSS_MARGIN_AT_RULE@270..422 + 0: AT@270..275 "@" [Newline("\n"), Newline("\n"), Whitespace(" ")] [] + 1: TOP_LEFT_KW@275..284 "top-left" [] [Whitespace(" ")] + 2: CSS_DECLARATION_OR_AT_RULE_BLOCK@284..422 + 0: L_CURLY@284..285 "{" [] [] + 1: CSS_DECLARATION_OR_AT_RULE_LIST@285..418 + 0: SCSS_DECLARATION@285..311 + 0: SCSS_IDENTIFIER@285..297 + 0: DOLLAR@285..291 "$" [Newline("\n"), Whitespace(" ")] [] + 1: CSS_IDENTIFIER@291..297 + 0: IDENT@291..297 "margin" [] [] + 1: COLON@297..299 ":" [] [Whitespace(" ")] + 2: SCSS_EXPRESSION@299..303 + 0: SCSS_EXPRESSION_ITEM_LIST@299..303 + 0: CSS_REGULAR_DIMENSION@299..303 + 0: CSS_NUMBER_LITERAL@299..300 "4" [] [] + 1: IDENT@300..303 "px" [] [Whitespace(" ")] + 3: SCSS_VARIABLE_MODIFIER_LIST@303..310 + 0: SCSS_VARIABLE_MODIFIER@303..310 + 0: BANG@303..304 "!" [] [] + 1: GLOBAL_KW@304..310 "global" [] [] + 4: SEMICOLON@310..311 ";" [] [] + 1: CSS_DECLARATION_WITH_SEMICOLON@311..342 + 0: CSS_DECLARATION@311..341 + 0: CSS_GENERIC_PROPERTY@311..341 + 0: CSS_IDENTIFIER@311..322 + 0: IDENT@311..322 "margin" [Newline("\n"), Whitespace(" ")] [] + 1: COLON@322..324 ":" [] [Whitespace(" ")] + 2: SCSS_EXPRESSION@324..341 + 0: SCSS_EXPRESSION_ITEM_LIST@324..341 + 0: SCSS_BINARY_EXPRESSION@324..341 + 0: SCSS_PARENTHESIZED_EXPRESSION@324..338 + 0: L_PAREN@324..325 "(" [] [] + 1: SCSS_EXPRESSION@325..336 + 0: SCSS_EXPRESSION_ITEM_LIST@325..336 + 0: SCSS_BINARY_EXPRESSION@325..336 + 0: SCSS_IDENTIFIER@325..333 + 0: DOLLAR@325..326 "$" [] [] + 1: CSS_IDENTIFIER@326..333 + 0: IDENT@326..333 "margin" [] [Whitespace(" ")] + 1: PLUS@333..335 "+" [] [Whitespace(" ")] + 2: CSS_NUMBER@335..336 + 0: CSS_NUMBER_LITERAL@335..336 "2" [] [] + 2: R_PAREN@336..338 ")" [] [Whitespace(" ")] + 1: SLASH@338..340 "/" [] [Whitespace(" ")] + 2: CSS_NUMBER@340..341 + 0: CSS_NUMBER_LITERAL@340..341 "2" [] [] 1: (empty) - 1: SEMICOLON@275..276 ";" [] [] - 2: SCSS_NESTING_DECLARATION@276..352 - 0: CSS_IDENTIFIER@276..287 - 0: IDENT@276..287 "border" [Newline("\n"), Whitespace(" ")] [] - 1: COLON@287..289 ":" [] [Whitespace(" ")] - 2: SCSS_EXPRESSION@289..306 - 0: SCSS_EXPRESSION_ITEM_LIST@289..306 - 0: CSS_REGULAR_DIMENSION@289..293 - 0: CSS_NUMBER_LITERAL@289..290 "1" [] [] - 1: IDENT@290..293 "px" [] [Whitespace(" ")] - 1: CSS_IDENTIFIER@293..299 - 0: IDENT@293..299 "solid" [] [Whitespace(" ")] - 2: SCSS_IDENTIFIER@299..306 - 0: DOLLAR@299..300 "$" [] [] - 1: CSS_IDENTIFIER@300..306 - 0: IDENT@300..306 "color" [] [Whitespace(" ")] - 3: CSS_DECLARATION_OR_RULE_BLOCK@306..352 - 0: L_CURLY@306..307 "{" [] [] - 1: CSS_DECLARATION_OR_RULE_LIST@307..346 - 0: CSS_DECLARATION_WITH_SEMICOLON@307..325 - 0: CSS_DECLARATION@307..324 - 0: CSS_GENERIC_PROPERTY@307..324 - 0: CSS_IDENTIFIER@307..319 - 0: IDENT@307..319 "width" [Newline("\n"), Whitespace(" ")] [] - 1: COLON@319..321 ":" [] [Whitespace(" ")] - 2: SCSS_EXPRESSION@321..324 - 0: SCSS_EXPRESSION_ITEM_LIST@321..324 - 0: CSS_REGULAR_DIMENSION@321..324 - 0: CSS_NUMBER_LITERAL@321..322 "1" [] [] - 1: IDENT@322..324 "px" [] [] + 1: SEMICOLON@341..342 ";" [] [] + 2: SCSS_NESTING_DECLARATION@342..418 + 0: CSS_IDENTIFIER@342..353 + 0: IDENT@342..353 "border" [Newline("\n"), Whitespace(" ")] [] + 1: COLON@353..355 ":" [] [Whitespace(" ")] + 2: SCSS_EXPRESSION@355..372 + 0: SCSS_EXPRESSION_ITEM_LIST@355..372 + 0: CSS_REGULAR_DIMENSION@355..359 + 0: CSS_NUMBER_LITERAL@355..356 "1" [] [] + 1: IDENT@356..359 "px" [] [Whitespace(" ")] + 1: CSS_IDENTIFIER@359..365 + 0: IDENT@359..365 "solid" [] [Whitespace(" ")] + 2: SCSS_IDENTIFIER@365..372 + 0: DOLLAR@365..366 "$" [] [] + 1: CSS_IDENTIFIER@366..372 + 0: IDENT@366..372 "color" [] [Whitespace(" ")] + 3: CSS_DECLARATION_OR_RULE_BLOCK@372..418 + 0: L_CURLY@372..373 "{" [] [] + 1: CSS_DECLARATION_OR_RULE_LIST@373..412 + 0: CSS_DECLARATION_WITH_SEMICOLON@373..391 + 0: CSS_DECLARATION@373..390 + 0: CSS_GENERIC_PROPERTY@373..390 + 0: CSS_IDENTIFIER@373..385 + 0: IDENT@373..385 "width" [Newline("\n"), Whitespace(" ")] [] + 1: COLON@385..387 ":" [] [Whitespace(" ")] + 2: SCSS_EXPRESSION@387..390 + 0: SCSS_EXPRESSION_ITEM_LIST@387..390 + 0: CSS_REGULAR_DIMENSION@387..390 + 0: CSS_NUMBER_LITERAL@387..388 "1" [] [] + 1: IDENT@388..390 "px" [] [] 1: (empty) - 1: SEMICOLON@324..325 ";" [] [] - 1: CSS_DECLARATION_WITH_SEMICOLON@325..346 - 0: CSS_DECLARATION@325..345 - 0: CSS_GENERIC_PROPERTY@325..345 - 0: CSS_IDENTIFIER@325..337 - 0: IDENT@325..337 "color" [Newline("\n"), Whitespace(" ")] [] - 1: COLON@337..339 ":" [] [Whitespace(" ")] - 2: SCSS_EXPRESSION@339..345 - 0: SCSS_EXPRESSION_ITEM_LIST@339..345 - 0: SCSS_IDENTIFIER@339..345 - 0: DOLLAR@339..340 "$" [] [] - 1: CSS_IDENTIFIER@340..345 - 0: IDENT@340..345 "color" [] [] + 1: SEMICOLON@390..391 ";" [] [] + 1: CSS_DECLARATION_WITH_SEMICOLON@391..412 + 0: CSS_DECLARATION@391..411 + 0: CSS_GENERIC_PROPERTY@391..411 + 0: CSS_IDENTIFIER@391..403 + 0: IDENT@391..403 "color" [Newline("\n"), Whitespace(" ")] [] + 1: COLON@403..405 ":" [] [Whitespace(" ")] + 2: SCSS_EXPRESSION@405..411 + 0: SCSS_EXPRESSION_ITEM_LIST@405..411 + 0: SCSS_IDENTIFIER@405..411 + 0: DOLLAR@405..406 "$" [] [] + 1: CSS_IDENTIFIER@406..411 + 0: IDENT@406..411 "color" [] [] 1: (empty) - 1: SEMICOLON@345..346 ";" [] [] - 2: R_CURLY@346..352 "}" [Newline("\n"), Whitespace(" ")] [] - 2: R_CURLY@352..356 "}" [Newline("\n"), Whitespace(" ")] [] - 6: CSS_MARGIN_AT_RULE@356..428 - 0: AT@356..361 "@" [Newline("\n"), Newline("\n"), Whitespace(" ")] [] - 1: TOP_RIGHT_KW@361..371 "top-right" [] [Whitespace(" ")] - 2: CSS_DECLARATION_OR_AT_RULE_BLOCK@371..428 - 0: L_CURLY@371..372 "{" [] [] - 1: CSS_DECLARATION_OR_AT_RULE_LIST@372..424 - 0: SCSS_DECLARATION@372..396 - 0: SCSS_NAMESPACED_IDENTIFIER@372..390 - 0: CSS_IDENTIFIER@372..382 - 0: IDENT@372..382 "theme" [Newline("\n"), Whitespace(" ")] [] - 1: DOT@382..383 "." [] [] - 2: SCSS_IDENTIFIER@383..390 - 0: DOLLAR@383..384 "$" [] [] - 1: CSS_IDENTIFIER@384..390 - 0: IDENT@384..390 "gutter" [] [] - 1: COLON@390..392 ":" [] [Whitespace(" ")] - 2: SCSS_EXPRESSION@392..395 - 0: SCSS_EXPRESSION_ITEM_LIST@392..395 - 0: CSS_REGULAR_DIMENSION@392..395 - 0: CSS_NUMBER_LITERAL@392..393 "8" [] [] - 1: IDENT@393..395 "px" [] [] - 3: SCSS_VARIABLE_MODIFIER_LIST@395..395 - 4: SEMICOLON@395..396 ";" [] [] - 1: CSS_DECLARATION_WITH_SEMICOLON@396..424 - 0: CSS_DECLARATION@396..423 - 0: CSS_GENERIC_PROPERTY@396..423 - 0: CSS_IDENTIFIER@396..408 - 0: IDENT@396..408 "padding" [Newline("\n"), Whitespace(" ")] [] - 1: COLON@408..410 ":" [] [Whitespace(" ")] - 2: SCSS_EXPRESSION@410..423 - 0: SCSS_EXPRESSION_ITEM_LIST@410..423 - 0: SCSS_QUALIFIED_NAME@410..423 - 0: CSS_IDENTIFIER@410..415 - 0: IDENT@410..415 "theme" [] [] - 1: DOT@415..416 "." [] [] - 2: SCSS_IDENTIFIER@416..423 - 0: DOLLAR@416..417 "$" [] [] - 1: CSS_IDENTIFIER@417..423 - 0: IDENT@417..423 "gutter" [] [] + 1: SEMICOLON@411..412 ";" [] [] + 2: R_CURLY@412..418 "}" [Newline("\n"), Whitespace(" ")] [] + 2: R_CURLY@418..422 "}" [Newline("\n"), Whitespace(" ")] [] + 9: CSS_MARGIN_AT_RULE@422..580 + 0: AT@422..427 "@" [Newline("\n"), Newline("\n"), Whitespace(" ")] [] + 1: TOP_RIGHT_KW@427..437 "top-right" [] [Whitespace(" ")] + 2: CSS_DECLARATION_OR_AT_RULE_BLOCK@437..580 + 0: L_CURLY@437..438 "{" [] [] + 1: CSS_DECLARATION_OR_AT_RULE_LIST@438..576 + 0: SCSS_DECLARATION@438..462 + 0: SCSS_NAMESPACED_IDENTIFIER@438..456 + 0: CSS_IDENTIFIER@438..448 + 0: IDENT@438..448 "theme" [Newline("\n"), Whitespace(" ")] [] + 1: DOT@448..449 "." [] [] + 2: SCSS_IDENTIFIER@449..456 + 0: DOLLAR@449..450 "$" [] [] + 1: CSS_IDENTIFIER@450..456 + 0: IDENT@450..456 "gutter" [] [] + 1: COLON@456..458 ":" [] [Whitespace(" ")] + 2: SCSS_EXPRESSION@458..461 + 0: SCSS_EXPRESSION_ITEM_LIST@458..461 + 0: CSS_REGULAR_DIMENSION@458..461 + 0: CSS_NUMBER_LITERAL@458..459 "8" [] [] + 1: IDENT@459..461 "px" [] [] + 3: SCSS_VARIABLE_MODIFIER_LIST@461..461 + 4: SEMICOLON@461..462 ";" [] [] + 1: CSS_DECLARATION_WITH_SEMICOLON@462..490 + 0: CSS_DECLARATION@462..489 + 0: CSS_GENERIC_PROPERTY@462..489 + 0: CSS_IDENTIFIER@462..474 + 0: IDENT@462..474 "padding" [Newline("\n"), Whitespace(" ")] [] + 1: COLON@474..476 ":" [] [Whitespace(" ")] + 2: SCSS_EXPRESSION@476..489 + 0: SCSS_EXPRESSION_ITEM_LIST@476..489 + 0: SCSS_QUALIFIED_NAME@476..489 + 0: CSS_IDENTIFIER@476..481 + 0: IDENT@476..481 "theme" [] [] + 1: DOT@481..482 "." [] [] + 2: SCSS_IDENTIFIER@482..489 + 0: DOLLAR@482..483 "$" [] [] + 1: CSS_IDENTIFIER@483..489 + 0: IDENT@483..489 "gutter" [] [] + 1: (empty) + 1: SEMICOLON@489..490 ";" [] [] + 2: CSS_DECLARATION_WITH_SEMICOLON@490..510 + 0: CSS_DECLARATION@490..509 + 0: CSS_GENERIC_PROPERTY@490..509 + 0: SCSS_INTERPOLATED_IDENTIFIER@490..504 + 0: SCSS_INTERPOLATED_IDENTIFIER_PART_LIST@490..504 + 0: SCSS_INTERPOLATION@490..504 + 0: HASH@490..496 "#" [Newline("\n"), Whitespace(" ")] [] + 1: L_CURLY@496..497 "{" [] [] + 2: SCSS_EXPRESSION@497..503 + 0: SCSS_EXPRESSION_ITEM_LIST@497..503 + 0: SCSS_IDENTIFIER@497..503 + 0: DOLLAR@497..498 "$" [] [] + 1: CSS_IDENTIFIER@498..503 + 0: IDENT@498..503 "inner" [] [] + 3: R_CURLY@503..504 "}" [] [] + 1: COLON@504..506 ":" [] [Whitespace(" ")] + 2: SCSS_EXPRESSION@506..509 + 0: SCSS_EXPRESSION_ITEM_LIST@506..509 + 0: CSS_REGULAR_DIMENSION@506..509 + 0: CSS_NUMBER_LITERAL@506..507 "2" [] [] + 1: IDENT@507..509 "px" [] [] + 1: (empty) + 1: SEMICOLON@509..510 ";" [] [] + 3: CSS_DECLARATION_WITH_SEMICOLON@510..542 + 0: CSS_DECLARATION@510..541 + 0: CSS_GENERIC_PROPERTY@510..541 + 0: SCSS_INTERPOLATED_IDENTIFIER@510..536 + 0: SCSS_INTERPOLATED_IDENTIFIER_PART_LIST@510..536 + 0: CSS_IDENTIFIER@510..522 + 0: IDENT@510..522 "margin-" [Newline("\n"), Whitespace(" ")] [] + 1: SCSS_INTERPOLATION@522..536 + 0: HASH@522..523 "#" [] [] + 1: L_CURLY@523..524 "{" [] [] + 2: SCSS_EXPRESSION@524..535 + 0: SCSS_EXPRESSION_ITEM_LIST@524..535 + 0: SCSS_IDENTIFIER@524..535 + 0: DOLLAR@524..525 "$" [] [] + 1: CSS_IDENTIFIER@525..535 + 0: IDENT@525..535 "inner-side" [] [] + 3: R_CURLY@535..536 "}" [] [] + 1: COLON@536..538 ":" [] [Whitespace(" ")] + 2: SCSS_EXPRESSION@538..541 + 0: SCSS_EXPRESSION_ITEM_LIST@538..541 + 0: CSS_REGULAR_DIMENSION@538..541 + 0: CSS_NUMBER_LITERAL@538..539 "2" [] [] + 1: IDENT@539..541 "px" [] [] + 1: (empty) + 1: SEMICOLON@541..542 ";" [] [] + 4: CSS_DECLARATION_WITH_SEMICOLON@542..576 + 0: CSS_DECLARATION@542..575 + 0: CSS_GENERIC_PROPERTY@542..575 + 0: SCSS_INTERPOLATED_IDENTIFIER@542..569 + 0: SCSS_INTERPOLATED_IDENTIFIER_PART_LIST@542..569 + 0: CSS_IDENTIFIER@542..555 + 0: IDENT@542..555 "--theme-" [Newline("\n"), Whitespace(" ")] [] + 1: SCSS_INTERPOLATION@555..569 + 0: HASH@555..556 "#" [] [] + 1: L_CURLY@556..557 "{" [] [] + 2: SCSS_EXPRESSION@557..568 + 0: SCSS_EXPRESSION_ITEM_LIST@557..568 + 0: SCSS_IDENTIFIER@557..568 + 0: DOLLAR@557..558 "$" [] [] + 1: CSS_IDENTIFIER@558..568 + 0: IDENT@558..568 "inner-slot" [] [] + 3: R_CURLY@568..569 "}" [] [] + 1: COLON@569..571 ":" [] [Whitespace(" ")] + 2: SCSS_EXPRESSION@571..575 + 0: SCSS_EXPRESSION_ITEM_LIST@571..575 + 0: CSS_IDENTIFIER@571..575 + 0: IDENT@571..575 "blue" [] [] 1: (empty) - 1: SEMICOLON@423..424 ";" [] [] - 2: R_CURLY@424..428 "}" [Newline("\n"), Whitespace(" ")] [] - 2: R_CURLY@428..430 "}" [Newline("\n")] [] - 2: EOF@430..431 "" [Newline("\n")] [] + 1: SEMICOLON@575..576 ";" [] [] + 2: R_CURLY@576..580 "}" [Newline("\n"), Whitespace(" ")] [] + 2: R_CURLY@580..582 "}" [Newline("\n")] [] + 2: EOF@582..583 "" [Newline("\n")] [] ``` diff --git a/crates/biome_css_parser/tests/css_test_suite/ok/scss/at-rule/supports-expression.scss b/crates/biome_css_parser/tests/css_test_suite/ok/scss/at-rule/supports-expression.scss index 574fea235e64..41510acaf36a 100644 --- a/crates/biome_css_parser/tests/css_test_suite/ok/scss/at-rule/supports-expression.scss +++ b/crates/biome_css_parser/tests/css_test_suite/ok/scss/at-rule/supports-expression.scss @@ -1,9 +1,40 @@ $base: 10; @import "theme.css" supports(width: $base + 1); +@import "theme.css" supports(#{$name}: $base + 1); +@import "theme.css" supports(margin-#{$side}: $base + 1); +@import "theme.css" supports(--theme-#{$slot}: red); .box { @supports (width: $base + 1) { width: $base + 1; } + + @supports not (#{$name}: $base + 1) { + width: $base + 1; + } + + @supports (#{$name}: $base + 1) and (display: grid) { + width: $base + 1; + } + + @supports (#{$name}: $base + 1) or (margin-#{$side}: $base + 1) { + width: $base + 1; + } + + @supports ((#{$name}: $base + 1) and (--theme-#{$slot}: red)) { + color: red; + } + + @supports (margin-#{$side}: $base + 1) { + width: $base + 1; + } + + @supports (--theme-#{$slot}: red) { + color: red; + } + + @supports selector(.foo-#{$name}) { + color: red; + } } diff --git a/crates/biome_css_parser/tests/css_test_suite/ok/scss/at-rule/supports-expression.scss.snap b/crates/biome_css_parser/tests/css_test_suite/ok/scss/at-rule/supports-expression.scss.snap index 05b89962037b..22a180065410 100644 --- a/crates/biome_css_parser/tests/css_test_suite/ok/scss/at-rule/supports-expression.scss.snap +++ b/crates/biome_css_parser/tests/css_test_suite/ok/scss/at-rule/supports-expression.scss.snap @@ -1,6 +1,5 @@ --- source: crates/biome_css_parser/tests/spec_test.rs -assertion_line: 208 expression: snapshot --- @@ -10,11 +9,42 @@ expression: snapshot $base: 10; @import "theme.css" supports(width: $base + 1); +@import "theme.css" supports(#{$name}: $base + 1); +@import "theme.css" supports(margin-#{$side}: $base + 1); +@import "theme.css" supports(--theme-#{$slot}: red); .box { @supports (width: $base + 1) { width: $base + 1; } + + @supports not (#{$name}: $base + 1) { + width: $base + 1; + } + + @supports (#{$name}: $base + 1) and (display: grid) { + width: $base + 1; + } + + @supports (#{$name}: $base + 1) or (margin-#{$side}: $base + 1) { + width: $base + 1; + } + + @supports ((#{$name}: $base + 1) and (--theme-#{$slot}: red)) { + color: red; + } + + @supports (margin-#{$side}: $base + 1) { + width: $base + 1; + } + + @supports (--theme-#{$slot}: red) { + color: red; + } + + @supports selector(.foo-#{$name}) { + color: red; + } } ``` @@ -90,6 +120,189 @@ CssRoot { semicolon_token: SEMICOLON@58..59 ";" [] [], }, }, + CssAtRule { + at_token: AT@59..61 "@" [Newline("\n")] [], + rule: ScssImportAtRule { + import_token: IMPORT_KW@61..68 "import" [] [Whitespace(" ")], + imports: ScssImportItemList [ + ScssPlainImport { + url: CssString { + value_token: CSS_STRING_LITERAL@68..80 "\"theme.css\"" [] [Whitespace(" ")], + }, + layer: missing (optional), + supports: CssImportSupports { + supports_token: SUPPORTS_KW@80..88 "supports" [] [], + l_paren_token: L_PAREN@88..89 "(" [] [], + condition: CssDeclaration { + property: CssGenericProperty { + name: ScssInterpolatedIdentifier { + items: ScssInterpolatedIdentifierPartList [ + ScssInterpolation { + hash_token: HASH@89..90 "#" [] [], + l_curly_token: L_CURLY@90..91 "{" [] [], + value: ScssExpression { + items: ScssExpressionItemList [ + ScssIdentifier { + dollar_token: DOLLAR@91..92 "$" [] [], + name: CssIdentifier { + value_token: IDENT@92..96 "name" [] [], + }, + }, + ], + }, + r_curly_token: R_CURLY@96..97 "}" [] [], + }, + ], + }, + colon_token: COLON@97..99 ":" [] [Whitespace(" ")], + value: ScssExpression { + items: ScssExpressionItemList [ + ScssBinaryExpression { + left: ScssIdentifier { + dollar_token: DOLLAR@99..100 "$" [] [], + name: CssIdentifier { + value_token: IDENT@100..105 "base" [] [Whitespace(" ")], + }, + }, + operator: PLUS@105..107 "+" [] [Whitespace(" ")], + right: CssNumber { + value_token: CSS_NUMBER_LITERAL@107..108 "1" [] [], + }, + }, + ], + }, + }, + important: missing (optional), + }, + r_paren_token: R_PAREN@108..109 ")" [] [], + }, + media: CssMediaQueryList [], + }, + ], + semicolon_token: SEMICOLON@109..110 ";" [] [], + }, + }, + CssAtRule { + at_token: AT@110..112 "@" [Newline("\n")] [], + rule: ScssImportAtRule { + import_token: IMPORT_KW@112..119 "import" [] [Whitespace(" ")], + imports: ScssImportItemList [ + ScssPlainImport { + url: CssString { + value_token: CSS_STRING_LITERAL@119..131 "\"theme.css\"" [] [Whitespace(" ")], + }, + layer: missing (optional), + supports: CssImportSupports { + supports_token: SUPPORTS_KW@131..139 "supports" [] [], + l_paren_token: L_PAREN@139..140 "(" [] [], + condition: CssDeclaration { + property: CssGenericProperty { + name: ScssInterpolatedIdentifier { + items: ScssInterpolatedIdentifierPartList [ + CssIdentifier { + value_token: IDENT@140..147 "margin-" [] [], + }, + ScssInterpolation { + hash_token: HASH@147..148 "#" [] [], + l_curly_token: L_CURLY@148..149 "{" [] [], + value: ScssExpression { + items: ScssExpressionItemList [ + ScssIdentifier { + dollar_token: DOLLAR@149..150 "$" [] [], + name: CssIdentifier { + value_token: IDENT@150..154 "side" [] [], + }, + }, + ], + }, + r_curly_token: R_CURLY@154..155 "}" [] [], + }, + ], + }, + colon_token: COLON@155..157 ":" [] [Whitespace(" ")], + value: ScssExpression { + items: ScssExpressionItemList [ + ScssBinaryExpression { + left: ScssIdentifier { + dollar_token: DOLLAR@157..158 "$" [] [], + name: CssIdentifier { + value_token: IDENT@158..163 "base" [] [Whitespace(" ")], + }, + }, + operator: PLUS@163..165 "+" [] [Whitespace(" ")], + right: CssNumber { + value_token: CSS_NUMBER_LITERAL@165..166 "1" [] [], + }, + }, + ], + }, + }, + important: missing (optional), + }, + r_paren_token: R_PAREN@166..167 ")" [] [], + }, + media: CssMediaQueryList [], + }, + ], + semicolon_token: SEMICOLON@167..168 ";" [] [], + }, + }, + CssAtRule { + at_token: AT@168..170 "@" [Newline("\n")] [], + rule: ScssImportAtRule { + import_token: IMPORT_KW@170..177 "import" [] [Whitespace(" ")], + imports: ScssImportItemList [ + ScssPlainImport { + url: CssString { + value_token: CSS_STRING_LITERAL@177..189 "\"theme.css\"" [] [Whitespace(" ")], + }, + layer: missing (optional), + supports: CssImportSupports { + supports_token: SUPPORTS_KW@189..197 "supports" [] [], + l_paren_token: L_PAREN@197..198 "(" [] [], + condition: CssDeclaration { + property: CssGenericProperty { + name: ScssInterpolatedIdentifier { + items: ScssInterpolatedIdentifierPartList [ + CssIdentifier { + value_token: IDENT@198..206 "--theme-" [] [], + }, + ScssInterpolation { + hash_token: HASH@206..207 "#" [] [], + l_curly_token: L_CURLY@207..208 "{" [] [], + value: ScssExpression { + items: ScssExpressionItemList [ + ScssIdentifier { + dollar_token: DOLLAR@208..209 "$" [] [], + name: CssIdentifier { + value_token: IDENT@209..213 "slot" [] [], + }, + }, + ], + }, + r_curly_token: R_CURLY@213..214 "}" [] [], + }, + ], + }, + colon_token: COLON@214..216 ":" [] [Whitespace(" ")], + value: ScssExpression { + items: ScssExpressionItemList [ + CssIdentifier { + value_token: IDENT@216..219 "red" [] [], + }, + ], + }, + }, + important: missing (optional), + }, + r_paren_token: R_PAREN@219..220 ")" [] [], + }, + media: CssMediaQueryList [], + }, + ], + semicolon_token: SEMICOLON@220..221 ";" [] [], + }, + }, CssQualifiedRule { prelude: CssSelectorList [ CssCompoundSelector { @@ -97,42 +310,42 @@ CssRoot { simple_selector: missing (optional), sub_selectors: CssSubSelectorList [ CssClassSelector { - dot_token: DOT@59..62 "." [Newline("\n"), Newline("\n")] [], + dot_token: DOT@221..224 "." [Newline("\n"), Newline("\n")] [], name: CssCustomIdentifier { - value_token: IDENT@62..66 "box" [] [Whitespace(" ")], + value_token: IDENT@224..228 "box" [] [Whitespace(" ")], }, }, ], }, ], block: CssDeclarationOrRuleBlock { - l_curly_token: L_CURLY@66..67 "{" [] [], + l_curly_token: L_CURLY@228..229 "{" [] [], items: CssDeclarationOrRuleList [ CssAtRule { - at_token: AT@67..71 "@" [Newline("\n"), Whitespace(" ")] [], + at_token: AT@229..233 "@" [Newline("\n"), Whitespace(" ")] [], rule: CssSupportsAtRule { declarator: CssSupportsAtRuleDeclarator { - supports_token: SUPPORTS_KW@71..80 "supports" [] [Whitespace(" ")], + supports_token: SUPPORTS_KW@233..242 "supports" [] [Whitespace(" ")], condition: CssSupportsFeatureDeclaration { - l_paren_token: L_PAREN@80..81 "(" [] [], + l_paren_token: L_PAREN@242..243 "(" [] [], declaration: CssDeclaration { property: CssGenericProperty { name: CssIdentifier { - value_token: IDENT@81..86 "width" [] [], + value_token: IDENT@243..248 "width" [] [], }, - colon_token: COLON@86..88 ":" [] [Whitespace(" ")], + colon_token: COLON@248..250 ":" [] [Whitespace(" ")], value: ScssExpression { items: ScssExpressionItemList [ ScssBinaryExpression { left: ScssIdentifier { - dollar_token: DOLLAR@88..89 "$" [] [], + dollar_token: DOLLAR@250..251 "$" [] [], name: CssIdentifier { - value_token: IDENT@89..94 "base" [] [Whitespace(" ")], + value_token: IDENT@251..256 "base" [] [Whitespace(" ")], }, }, - operator: PLUS@94..96 "+" [] [Whitespace(" ")], + operator: PLUS@256..258 "+" [] [Whitespace(" ")], right: CssNumber { - value_token: CSS_NUMBER_LITERAL@96..97 "1" [] [], + value_token: CSS_NUMBER_LITERAL@258..259 "1" [] [], }, }, ], @@ -140,31 +353,31 @@ CssRoot { }, important: missing (optional), }, - r_paren_token: R_PAREN@97..99 ")" [] [Whitespace(" ")], + r_paren_token: R_PAREN@259..261 ")" [] [Whitespace(" ")], }, }, block: CssDeclarationOrRuleBlock { - l_curly_token: L_CURLY@99..100 "{" [] [], + l_curly_token: L_CURLY@261..262 "{" [] [], items: CssDeclarationOrRuleList [ CssDeclarationWithSemicolon { declaration: CssDeclaration { property: CssGenericProperty { name: CssIdentifier { - value_token: IDENT@100..110 "width" [Newline("\n"), Whitespace(" ")] [], + value_token: IDENT@262..272 "width" [Newline("\n"), Whitespace(" ")] [], }, - colon_token: COLON@110..112 ":" [] [Whitespace(" ")], + colon_token: COLON@272..274 ":" [] [Whitespace(" ")], value: ScssExpression { items: ScssExpressionItemList [ ScssBinaryExpression { left: ScssIdentifier { - dollar_token: DOLLAR@112..113 "$" [] [], + dollar_token: DOLLAR@274..275 "$" [] [], name: CssIdentifier { - value_token: IDENT@113..118 "base" [] [Whitespace(" ")], + value_token: IDENT@275..280 "base" [] [Whitespace(" ")], }, }, - operator: PLUS@118..120 "+" [] [Whitespace(" ")], + operator: PLUS@280..282 "+" [] [Whitespace(" ")], right: CssNumber { - value_token: CSS_NUMBER_LITERAL@120..121 "1" [] [], + value_token: CSS_NUMBER_LITERAL@282..283 "1" [] [], }, }, ], @@ -172,131 +385,1392 @@ CssRoot { }, important: missing (optional), }, - semicolon_token: SEMICOLON@121..122 ";" [] [], + semicolon_token: SEMICOLON@283..284 ";" [] [], }, ], - r_curly_token: R_CURLY@122..126 "}" [Newline("\n"), Whitespace(" ")] [], + r_curly_token: R_CURLY@284..288 "}" [Newline("\n"), Whitespace(" ")] [], }, }, }, - ], - r_curly_token: R_CURLY@126..128 "}" [Newline("\n")] [], - }, - }, - ], - eof_token: EOF@128..129 "" [Newline("\n")] [], -} -``` - -## CST - -``` -0: CSS_ROOT@0..129 - 0: (empty) - 1: CSS_ROOT_ITEM_LIST@0..128 - 0: SCSS_DECLARATION@0..10 - 0: SCSS_IDENTIFIER@0..5 - 0: DOLLAR@0..1 "$" [] [] - 1: CSS_IDENTIFIER@1..5 - 0: IDENT@1..5 "base" [] [] - 1: COLON@5..7 ":" [] [Whitespace(" ")] - 2: SCSS_EXPRESSION@7..9 - 0: SCSS_EXPRESSION_ITEM_LIST@7..9 - 0: CSS_NUMBER@7..9 - 0: CSS_NUMBER_LITERAL@7..9 "10" [] [] - 3: SCSS_VARIABLE_MODIFIER_LIST@9..9 - 4: SEMICOLON@9..10 ";" [] [] - 1: CSS_AT_RULE@10..59 - 0: AT@10..13 "@" [Newline("\n"), Newline("\n")] [] - 1: SCSS_IMPORT_AT_RULE@13..59 - 0: IMPORT_KW@13..20 "import" [] [Whitespace(" ")] - 1: SCSS_IMPORT_ITEM_LIST@20..58 - 0: SCSS_PLAIN_IMPORT@20..58 - 0: CSS_STRING@20..32 - 0: CSS_STRING_LITERAL@20..32 "\"theme.css\"" [] [Whitespace(" ")] - 1: (empty) - 2: CSS_IMPORT_SUPPORTS@32..58 - 0: SUPPORTS_KW@32..40 "supports" [] [] - 1: L_PAREN@40..41 "(" [] [] - 2: CSS_DECLARATION@41..57 - 0: CSS_GENERIC_PROPERTY@41..57 - 0: CSS_IDENTIFIER@41..46 - 0: IDENT@41..46 "width" [] [] - 1: COLON@46..48 ":" [] [Whitespace(" ")] - 2: SCSS_EXPRESSION@48..57 - 0: SCSS_EXPRESSION_ITEM_LIST@48..57 - 0: SCSS_BINARY_EXPRESSION@48..57 - 0: SCSS_IDENTIFIER@48..54 - 0: DOLLAR@48..49 "$" [] [] - 1: CSS_IDENTIFIER@49..54 - 0: IDENT@49..54 "base" [] [Whitespace(" ")] - 1: PLUS@54..56 "+" [] [Whitespace(" ")] - 2: CSS_NUMBER@56..57 - 0: CSS_NUMBER_LITERAL@56..57 "1" [] [] - 1: (empty) - 3: R_PAREN@57..58 ")" [] [] - 3: CSS_MEDIA_QUERY_LIST@58..58 - 2: SEMICOLON@58..59 ";" [] [] - 2: CSS_QUALIFIED_RULE@59..128 - 0: CSS_SELECTOR_LIST@59..66 - 0: CSS_COMPOUND_SELECTOR@59..66 - 0: CSS_NESTED_SELECTOR_LIST@59..59 - 1: (empty) - 2: CSS_SUB_SELECTOR_LIST@59..66 - 0: CSS_CLASS_SELECTOR@59..66 - 0: DOT@59..62 "." [Newline("\n"), Newline("\n")] [] - 1: CSS_CUSTOM_IDENTIFIER@62..66 - 0: IDENT@62..66 "box" [] [Whitespace(" ")] - 1: CSS_DECLARATION_OR_RULE_BLOCK@66..128 - 0: L_CURLY@66..67 "{" [] [] - 1: CSS_DECLARATION_OR_RULE_LIST@67..126 - 0: CSS_AT_RULE@67..126 - 0: AT@67..71 "@" [Newline("\n"), Whitespace(" ")] [] - 1: CSS_SUPPORTS_AT_RULE@71..126 - 0: CSS_SUPPORTS_AT_RULE_DECLARATOR@71..99 - 0: SUPPORTS_KW@71..80 "supports" [] [Whitespace(" ")] - 1: CSS_SUPPORTS_FEATURE_DECLARATION@80..99 - 0: L_PAREN@80..81 "(" [] [] - 1: CSS_DECLARATION@81..97 - 0: CSS_GENERIC_PROPERTY@81..97 - 0: CSS_IDENTIFIER@81..86 - 0: IDENT@81..86 "width" [] [] - 1: COLON@86..88 ":" [] [Whitespace(" ")] - 2: SCSS_EXPRESSION@88..97 - 0: SCSS_EXPRESSION_ITEM_LIST@88..97 - 0: SCSS_BINARY_EXPRESSION@88..97 - 0: SCSS_IDENTIFIER@88..94 - 0: DOLLAR@88..89 "$" [] [] - 1: CSS_IDENTIFIER@89..94 - 0: IDENT@89..94 "base" [] [Whitespace(" ")] - 1: PLUS@94..96 "+" [] [Whitespace(" ")] - 2: CSS_NUMBER@96..97 - 0: CSS_NUMBER_LITERAL@96..97 "1" [] [] + CssAtRule { + at_token: AT@288..293 "@" [Newline("\n"), Newline("\n"), Whitespace(" ")] [], + rule: CssSupportsAtRule { + declarator: CssSupportsAtRuleDeclarator { + supports_token: SUPPORTS_KW@293..302 "supports" [] [Whitespace(" ")], + condition: CssSupportsNotCondition { + not_token: NOT_KW@302..306 "not" [] [Whitespace(" ")], + query: CssSupportsFeatureDeclaration { + l_paren_token: L_PAREN@306..307 "(" [] [], + declaration: CssDeclaration { + property: CssGenericProperty { + name: ScssInterpolatedIdentifier { + items: ScssInterpolatedIdentifierPartList [ + ScssInterpolation { + hash_token: HASH@307..308 "#" [] [], + l_curly_token: L_CURLY@308..309 "{" [] [], + value: ScssExpression { + items: ScssExpressionItemList [ + ScssIdentifier { + dollar_token: DOLLAR@309..310 "$" [] [], + name: CssIdentifier { + value_token: IDENT@310..314 "name" [] [], + }, + }, + ], + }, + r_curly_token: R_CURLY@314..315 "}" [] [], + }, + ], + }, + colon_token: COLON@315..317 ":" [] [Whitespace(" ")], + value: ScssExpression { + items: ScssExpressionItemList [ + ScssBinaryExpression { + left: ScssIdentifier { + dollar_token: DOLLAR@317..318 "$" [] [], + name: CssIdentifier { + value_token: IDENT@318..323 "base" [] [Whitespace(" ")], + }, + }, + operator: PLUS@323..325 "+" [] [Whitespace(" ")], + right: CssNumber { + value_token: CSS_NUMBER_LITERAL@325..326 "1" [] [], + }, + }, + ], + }, + }, + important: missing (optional), + }, + r_paren_token: R_PAREN@326..328 ")" [] [Whitespace(" ")], + }, + }, + }, + block: CssDeclarationOrRuleBlock { + l_curly_token: L_CURLY@328..329 "{" [] [], + items: CssDeclarationOrRuleList [ + CssDeclarationWithSemicolon { + declaration: CssDeclaration { + property: CssGenericProperty { + name: CssIdentifier { + value_token: IDENT@329..339 "width" [Newline("\n"), Whitespace(" ")] [], + }, + colon_token: COLON@339..341 ":" [] [Whitespace(" ")], + value: ScssExpression { + items: ScssExpressionItemList [ + ScssBinaryExpression { + left: ScssIdentifier { + dollar_token: DOLLAR@341..342 "$" [] [], + name: CssIdentifier { + value_token: IDENT@342..347 "base" [] [Whitespace(" ")], + }, + }, + operator: PLUS@347..349 "+" [] [Whitespace(" ")], + right: CssNumber { + value_token: CSS_NUMBER_LITERAL@349..350 "1" [] [], + }, + }, + ], + }, + }, + important: missing (optional), + }, + semicolon_token: SEMICOLON@350..351 ";" [] [], + }, + ], + r_curly_token: R_CURLY@351..355 "}" [Newline("\n"), Whitespace(" ")] [], + }, + }, + }, + CssAtRule { + at_token: AT@355..360 "@" [Newline("\n"), Newline("\n"), Whitespace(" ")] [], + rule: CssSupportsAtRule { + declarator: CssSupportsAtRuleDeclarator { + supports_token: SUPPORTS_KW@360..369 "supports" [] [Whitespace(" ")], + condition: CssSupportsAndCondition { + left: CssSupportsFeatureDeclaration { + l_paren_token: L_PAREN@369..370 "(" [] [], + declaration: CssDeclaration { + property: CssGenericProperty { + name: ScssInterpolatedIdentifier { + items: ScssInterpolatedIdentifierPartList [ + ScssInterpolation { + hash_token: HASH@370..371 "#" [] [], + l_curly_token: L_CURLY@371..372 "{" [] [], + value: ScssExpression { + items: ScssExpressionItemList [ + ScssIdentifier { + dollar_token: DOLLAR@372..373 "$" [] [], + name: CssIdentifier { + value_token: IDENT@373..377 "name" [] [], + }, + }, + ], + }, + r_curly_token: R_CURLY@377..378 "}" [] [], + }, + ], + }, + colon_token: COLON@378..380 ":" [] [Whitespace(" ")], + value: ScssExpression { + items: ScssExpressionItemList [ + ScssBinaryExpression { + left: ScssIdentifier { + dollar_token: DOLLAR@380..381 "$" [] [], + name: CssIdentifier { + value_token: IDENT@381..386 "base" [] [Whitespace(" ")], + }, + }, + operator: PLUS@386..388 "+" [] [Whitespace(" ")], + right: CssNumber { + value_token: CSS_NUMBER_LITERAL@388..389 "1" [] [], + }, + }, + ], + }, + }, + important: missing (optional), + }, + r_paren_token: R_PAREN@389..391 ")" [] [Whitespace(" ")], + }, + and_token: AND_KW@391..395 "and" [] [Whitespace(" ")], + right: CssSupportsFeatureDeclaration { + l_paren_token: L_PAREN@395..396 "(" [] [], + declaration: CssDeclaration { + property: CssGenericProperty { + name: CssIdentifier { + value_token: IDENT@396..403 "display" [] [], + }, + colon_token: COLON@403..405 ":" [] [Whitespace(" ")], + value: ScssExpression { + items: ScssExpressionItemList [ + CssIdentifier { + value_token: IDENT@405..409 "grid" [] [], + }, + ], + }, + }, + important: missing (optional), + }, + r_paren_token: R_PAREN@409..411 ")" [] [Whitespace(" ")], + }, + }, + }, + block: CssDeclarationOrRuleBlock { + l_curly_token: L_CURLY@411..412 "{" [] [], + items: CssDeclarationOrRuleList [ + CssDeclarationWithSemicolon { + declaration: CssDeclaration { + property: CssGenericProperty { + name: CssIdentifier { + value_token: IDENT@412..422 "width" [Newline("\n"), Whitespace(" ")] [], + }, + colon_token: COLON@422..424 ":" [] [Whitespace(" ")], + value: ScssExpression { + items: ScssExpressionItemList [ + ScssBinaryExpression { + left: ScssIdentifier { + dollar_token: DOLLAR@424..425 "$" [] [], + name: CssIdentifier { + value_token: IDENT@425..430 "base" [] [Whitespace(" ")], + }, + }, + operator: PLUS@430..432 "+" [] [Whitespace(" ")], + right: CssNumber { + value_token: CSS_NUMBER_LITERAL@432..433 "1" [] [], + }, + }, + ], + }, + }, + important: missing (optional), + }, + semicolon_token: SEMICOLON@433..434 ";" [] [], + }, + ], + r_curly_token: R_CURLY@434..438 "}" [Newline("\n"), Whitespace(" ")] [], + }, + }, + }, + CssAtRule { + at_token: AT@438..443 "@" [Newline("\n"), Newline("\n"), Whitespace(" ")] [], + rule: CssSupportsAtRule { + declarator: CssSupportsAtRuleDeclarator { + supports_token: SUPPORTS_KW@443..452 "supports" [] [Whitespace(" ")], + condition: CssSupportsOrCondition { + left: CssSupportsFeatureDeclaration { + l_paren_token: L_PAREN@452..453 "(" [] [], + declaration: CssDeclaration { + property: CssGenericProperty { + name: ScssInterpolatedIdentifier { + items: ScssInterpolatedIdentifierPartList [ + ScssInterpolation { + hash_token: HASH@453..454 "#" [] [], + l_curly_token: L_CURLY@454..455 "{" [] [], + value: ScssExpression { + items: ScssExpressionItemList [ + ScssIdentifier { + dollar_token: DOLLAR@455..456 "$" [] [], + name: CssIdentifier { + value_token: IDENT@456..460 "name" [] [], + }, + }, + ], + }, + r_curly_token: R_CURLY@460..461 "}" [] [], + }, + ], + }, + colon_token: COLON@461..463 ":" [] [Whitespace(" ")], + value: ScssExpression { + items: ScssExpressionItemList [ + ScssBinaryExpression { + left: ScssIdentifier { + dollar_token: DOLLAR@463..464 "$" [] [], + name: CssIdentifier { + value_token: IDENT@464..469 "base" [] [Whitespace(" ")], + }, + }, + operator: PLUS@469..471 "+" [] [Whitespace(" ")], + right: CssNumber { + value_token: CSS_NUMBER_LITERAL@471..472 "1" [] [], + }, + }, + ], + }, + }, + important: missing (optional), + }, + r_paren_token: R_PAREN@472..474 ")" [] [Whitespace(" ")], + }, + or_token: OR_KW@474..477 "or" [] [Whitespace(" ")], + right: CssSupportsFeatureDeclaration { + l_paren_token: L_PAREN@477..478 "(" [] [], + declaration: CssDeclaration { + property: CssGenericProperty { + name: ScssInterpolatedIdentifier { + items: ScssInterpolatedIdentifierPartList [ + CssIdentifier { + value_token: IDENT@478..485 "margin-" [] [], + }, + ScssInterpolation { + hash_token: HASH@485..486 "#" [] [], + l_curly_token: L_CURLY@486..487 "{" [] [], + value: ScssExpression { + items: ScssExpressionItemList [ + ScssIdentifier { + dollar_token: DOLLAR@487..488 "$" [] [], + name: CssIdentifier { + value_token: IDENT@488..492 "side" [] [], + }, + }, + ], + }, + r_curly_token: R_CURLY@492..493 "}" [] [], + }, + ], + }, + colon_token: COLON@493..495 ":" [] [Whitespace(" ")], + value: ScssExpression { + items: ScssExpressionItemList [ + ScssBinaryExpression { + left: ScssIdentifier { + dollar_token: DOLLAR@495..496 "$" [] [], + name: CssIdentifier { + value_token: IDENT@496..501 "base" [] [Whitespace(" ")], + }, + }, + operator: PLUS@501..503 "+" [] [Whitespace(" ")], + right: CssNumber { + value_token: CSS_NUMBER_LITERAL@503..504 "1" [] [], + }, + }, + ], + }, + }, + important: missing (optional), + }, + r_paren_token: R_PAREN@504..506 ")" [] [Whitespace(" ")], + }, + }, + }, + block: CssDeclarationOrRuleBlock { + l_curly_token: L_CURLY@506..507 "{" [] [], + items: CssDeclarationOrRuleList [ + CssDeclarationWithSemicolon { + declaration: CssDeclaration { + property: CssGenericProperty { + name: CssIdentifier { + value_token: IDENT@507..517 "width" [Newline("\n"), Whitespace(" ")] [], + }, + colon_token: COLON@517..519 ":" [] [Whitespace(" ")], + value: ScssExpression { + items: ScssExpressionItemList [ + ScssBinaryExpression { + left: ScssIdentifier { + dollar_token: DOLLAR@519..520 "$" [] [], + name: CssIdentifier { + value_token: IDENT@520..525 "base" [] [Whitespace(" ")], + }, + }, + operator: PLUS@525..527 "+" [] [Whitespace(" ")], + right: CssNumber { + value_token: CSS_NUMBER_LITERAL@527..528 "1" [] [], + }, + }, + ], + }, + }, + important: missing (optional), + }, + semicolon_token: SEMICOLON@528..529 ";" [] [], + }, + ], + r_curly_token: R_CURLY@529..533 "}" [Newline("\n"), Whitespace(" ")] [], + }, + }, + }, + CssAtRule { + at_token: AT@533..538 "@" [Newline("\n"), Newline("\n"), Whitespace(" ")] [], + rule: CssSupportsAtRule { + declarator: CssSupportsAtRuleDeclarator { + supports_token: SUPPORTS_KW@538..547 "supports" [] [Whitespace(" ")], + condition: CssSupportsConditionInParens { + l_paren_token: L_PAREN@547..548 "(" [] [], + condition: CssSupportsAndCondition { + left: CssSupportsFeatureDeclaration { + l_paren_token: L_PAREN@548..549 "(" [] [], + declaration: CssDeclaration { + property: CssGenericProperty { + name: ScssInterpolatedIdentifier { + items: ScssInterpolatedIdentifierPartList [ + ScssInterpolation { + hash_token: HASH@549..550 "#" [] [], + l_curly_token: L_CURLY@550..551 "{" [] [], + value: ScssExpression { + items: ScssExpressionItemList [ + ScssIdentifier { + dollar_token: DOLLAR@551..552 "$" [] [], + name: CssIdentifier { + value_token: IDENT@552..556 "name" [] [], + }, + }, + ], + }, + r_curly_token: R_CURLY@556..557 "}" [] [], + }, + ], + }, + colon_token: COLON@557..559 ":" [] [Whitespace(" ")], + value: ScssExpression { + items: ScssExpressionItemList [ + ScssBinaryExpression { + left: ScssIdentifier { + dollar_token: DOLLAR@559..560 "$" [] [], + name: CssIdentifier { + value_token: IDENT@560..565 "base" [] [Whitespace(" ")], + }, + }, + operator: PLUS@565..567 "+" [] [Whitespace(" ")], + right: CssNumber { + value_token: CSS_NUMBER_LITERAL@567..568 "1" [] [], + }, + }, + ], + }, + }, + important: missing (optional), + }, + r_paren_token: R_PAREN@568..570 ")" [] [Whitespace(" ")], + }, + and_token: AND_KW@570..574 "and" [] [Whitespace(" ")], + right: CssSupportsFeatureDeclaration { + l_paren_token: L_PAREN@574..575 "(" [] [], + declaration: CssDeclaration { + property: CssGenericProperty { + name: ScssInterpolatedIdentifier { + items: ScssInterpolatedIdentifierPartList [ + CssIdentifier { + value_token: IDENT@575..583 "--theme-" [] [], + }, + ScssInterpolation { + hash_token: HASH@583..584 "#" [] [], + l_curly_token: L_CURLY@584..585 "{" [] [], + value: ScssExpression { + items: ScssExpressionItemList [ + ScssIdentifier { + dollar_token: DOLLAR@585..586 "$" [] [], + name: CssIdentifier { + value_token: IDENT@586..590 "slot" [] [], + }, + }, + ], + }, + r_curly_token: R_CURLY@590..591 "}" [] [], + }, + ], + }, + colon_token: COLON@591..593 ":" [] [Whitespace(" ")], + value: ScssExpression { + items: ScssExpressionItemList [ + CssIdentifier { + value_token: IDENT@593..596 "red" [] [], + }, + ], + }, + }, + important: missing (optional), + }, + r_paren_token: R_PAREN@596..597 ")" [] [], + }, + }, + r_paren_token: R_PAREN@597..599 ")" [] [Whitespace(" ")], + }, + }, + block: CssDeclarationOrRuleBlock { + l_curly_token: L_CURLY@599..600 "{" [] [], + items: CssDeclarationOrRuleList [ + CssDeclarationWithSemicolon { + declaration: CssDeclaration { + property: CssGenericProperty { + name: CssIdentifier { + value_token: IDENT@600..610 "color" [Newline("\n"), Whitespace(" ")] [], + }, + colon_token: COLON@610..612 ":" [] [Whitespace(" ")], + value: ScssExpression { + items: ScssExpressionItemList [ + CssIdentifier { + value_token: IDENT@612..615 "red" [] [], + }, + ], + }, + }, + important: missing (optional), + }, + semicolon_token: SEMICOLON@615..616 ";" [] [], + }, + ], + r_curly_token: R_CURLY@616..620 "}" [Newline("\n"), Whitespace(" ")] [], + }, + }, + }, + CssAtRule { + at_token: AT@620..625 "@" [Newline("\n"), Newline("\n"), Whitespace(" ")] [], + rule: CssSupportsAtRule { + declarator: CssSupportsAtRuleDeclarator { + supports_token: SUPPORTS_KW@625..634 "supports" [] [Whitespace(" ")], + condition: CssSupportsFeatureDeclaration { + l_paren_token: L_PAREN@634..635 "(" [] [], + declaration: CssDeclaration { + property: CssGenericProperty { + name: ScssInterpolatedIdentifier { + items: ScssInterpolatedIdentifierPartList [ + CssIdentifier { + value_token: IDENT@635..642 "margin-" [] [], + }, + ScssInterpolation { + hash_token: HASH@642..643 "#" [] [], + l_curly_token: L_CURLY@643..644 "{" [] [], + value: ScssExpression { + items: ScssExpressionItemList [ + ScssIdentifier { + dollar_token: DOLLAR@644..645 "$" [] [], + name: CssIdentifier { + value_token: IDENT@645..649 "side" [] [], + }, + }, + ], + }, + r_curly_token: R_CURLY@649..650 "}" [] [], + }, + ], + }, + colon_token: COLON@650..652 ":" [] [Whitespace(" ")], + value: ScssExpression { + items: ScssExpressionItemList [ + ScssBinaryExpression { + left: ScssIdentifier { + dollar_token: DOLLAR@652..653 "$" [] [], + name: CssIdentifier { + value_token: IDENT@653..658 "base" [] [Whitespace(" ")], + }, + }, + operator: PLUS@658..660 "+" [] [Whitespace(" ")], + right: CssNumber { + value_token: CSS_NUMBER_LITERAL@660..661 "1" [] [], + }, + }, + ], + }, + }, + important: missing (optional), + }, + r_paren_token: R_PAREN@661..663 ")" [] [Whitespace(" ")], + }, + }, + block: CssDeclarationOrRuleBlock { + l_curly_token: L_CURLY@663..664 "{" [] [], + items: CssDeclarationOrRuleList [ + CssDeclarationWithSemicolon { + declaration: CssDeclaration { + property: CssGenericProperty { + name: CssIdentifier { + value_token: IDENT@664..674 "width" [Newline("\n"), Whitespace(" ")] [], + }, + colon_token: COLON@674..676 ":" [] [Whitespace(" ")], + value: ScssExpression { + items: ScssExpressionItemList [ + ScssBinaryExpression { + left: ScssIdentifier { + dollar_token: DOLLAR@676..677 "$" [] [], + name: CssIdentifier { + value_token: IDENT@677..682 "base" [] [Whitespace(" ")], + }, + }, + operator: PLUS@682..684 "+" [] [Whitespace(" ")], + right: CssNumber { + value_token: CSS_NUMBER_LITERAL@684..685 "1" [] [], + }, + }, + ], + }, + }, + important: missing (optional), + }, + semicolon_token: SEMICOLON@685..686 ";" [] [], + }, + ], + r_curly_token: R_CURLY@686..690 "}" [Newline("\n"), Whitespace(" ")] [], + }, + }, + }, + CssAtRule { + at_token: AT@690..695 "@" [Newline("\n"), Newline("\n"), Whitespace(" ")] [], + rule: CssSupportsAtRule { + declarator: CssSupportsAtRuleDeclarator { + supports_token: SUPPORTS_KW@695..704 "supports" [] [Whitespace(" ")], + condition: CssSupportsFeatureDeclaration { + l_paren_token: L_PAREN@704..705 "(" [] [], + declaration: CssDeclaration { + property: CssGenericProperty { + name: ScssInterpolatedIdentifier { + items: ScssInterpolatedIdentifierPartList [ + CssIdentifier { + value_token: IDENT@705..713 "--theme-" [] [], + }, + ScssInterpolation { + hash_token: HASH@713..714 "#" [] [], + l_curly_token: L_CURLY@714..715 "{" [] [], + value: ScssExpression { + items: ScssExpressionItemList [ + ScssIdentifier { + dollar_token: DOLLAR@715..716 "$" [] [], + name: CssIdentifier { + value_token: IDENT@716..720 "slot" [] [], + }, + }, + ], + }, + r_curly_token: R_CURLY@720..721 "}" [] [], + }, + ], + }, + colon_token: COLON@721..723 ":" [] [Whitespace(" ")], + value: ScssExpression { + items: ScssExpressionItemList [ + CssIdentifier { + value_token: IDENT@723..726 "red" [] [], + }, + ], + }, + }, + important: missing (optional), + }, + r_paren_token: R_PAREN@726..728 ")" [] [Whitespace(" ")], + }, + }, + block: CssDeclarationOrRuleBlock { + l_curly_token: L_CURLY@728..729 "{" [] [], + items: CssDeclarationOrRuleList [ + CssDeclarationWithSemicolon { + declaration: CssDeclaration { + property: CssGenericProperty { + name: CssIdentifier { + value_token: IDENT@729..739 "color" [Newline("\n"), Whitespace(" ")] [], + }, + colon_token: COLON@739..741 ":" [] [Whitespace(" ")], + value: ScssExpression { + items: ScssExpressionItemList [ + CssIdentifier { + value_token: IDENT@741..744 "red" [] [], + }, + ], + }, + }, + important: missing (optional), + }, + semicolon_token: SEMICOLON@744..745 ";" [] [], + }, + ], + r_curly_token: R_CURLY@745..749 "}" [Newline("\n"), Whitespace(" ")] [], + }, + }, + }, + CssAtRule { + at_token: AT@749..754 "@" [Newline("\n"), Newline("\n"), Whitespace(" ")] [], + rule: CssSupportsAtRule { + declarator: CssSupportsAtRuleDeclarator { + supports_token: SUPPORTS_KW@754..763 "supports" [] [Whitespace(" ")], + condition: CssSupportsFeatureSelector { + selector_token: SELECTOR_KW@763..771 "selector" [] [], + l_paren_token: L_PAREN@771..772 "(" [] [], + selector: CssCompoundSelector { + nesting_selectors: CssNestedSelectorList [], + simple_selector: missing (optional), + sub_selectors: CssSubSelectorList [ + CssClassSelector { + dot_token: DOT@772..773 "." [] [], + name: ScssInterpolatedIdentifier { + items: ScssInterpolatedIdentifierPartList [ + CssCustomIdentifier { + value_token: IDENT@773..777 "foo-" [] [], + }, + ScssInterpolation { + hash_token: HASH@777..778 "#" [] [], + l_curly_token: L_CURLY@778..779 "{" [] [], + value: ScssExpression { + items: ScssExpressionItemList [ + ScssIdentifier { + dollar_token: DOLLAR@779..780 "$" [] [], + name: CssIdentifier { + value_token: IDENT@780..784 "name" [] [], + }, + }, + ], + }, + r_curly_token: R_CURLY@784..785 "}" [] [], + }, + ], + }, + }, + ], + }, + r_paren_token: R_PAREN@785..787 ")" [] [Whitespace(" ")], + }, + }, + block: CssDeclarationOrRuleBlock { + l_curly_token: L_CURLY@787..788 "{" [] [], + items: CssDeclarationOrRuleList [ + CssDeclarationWithSemicolon { + declaration: CssDeclaration { + property: CssGenericProperty { + name: CssIdentifier { + value_token: IDENT@788..798 "color" [Newline("\n"), Whitespace(" ")] [], + }, + colon_token: COLON@798..800 ":" [] [Whitespace(" ")], + value: ScssExpression { + items: ScssExpressionItemList [ + CssIdentifier { + value_token: IDENT@800..803 "red" [] [], + }, + ], + }, + }, + important: missing (optional), + }, + semicolon_token: SEMICOLON@803..804 ";" [] [], + }, + ], + r_curly_token: R_CURLY@804..808 "}" [Newline("\n"), Whitespace(" ")] [], + }, + }, + }, + ], + r_curly_token: R_CURLY@808..810 "}" [Newline("\n")] [], + }, + }, + ], + eof_token: EOF@810..811 "" [Newline("\n")] [], +} +``` + +## CST + +``` +0: CSS_ROOT@0..811 + 0: (empty) + 1: CSS_ROOT_ITEM_LIST@0..810 + 0: SCSS_DECLARATION@0..10 + 0: SCSS_IDENTIFIER@0..5 + 0: DOLLAR@0..1 "$" [] [] + 1: CSS_IDENTIFIER@1..5 + 0: IDENT@1..5 "base" [] [] + 1: COLON@5..7 ":" [] [Whitespace(" ")] + 2: SCSS_EXPRESSION@7..9 + 0: SCSS_EXPRESSION_ITEM_LIST@7..9 + 0: CSS_NUMBER@7..9 + 0: CSS_NUMBER_LITERAL@7..9 "10" [] [] + 3: SCSS_VARIABLE_MODIFIER_LIST@9..9 + 4: SEMICOLON@9..10 ";" [] [] + 1: CSS_AT_RULE@10..59 + 0: AT@10..13 "@" [Newline("\n"), Newline("\n")] [] + 1: SCSS_IMPORT_AT_RULE@13..59 + 0: IMPORT_KW@13..20 "import" [] [Whitespace(" ")] + 1: SCSS_IMPORT_ITEM_LIST@20..58 + 0: SCSS_PLAIN_IMPORT@20..58 + 0: CSS_STRING@20..32 + 0: CSS_STRING_LITERAL@20..32 "\"theme.css\"" [] [Whitespace(" ")] + 1: (empty) + 2: CSS_IMPORT_SUPPORTS@32..58 + 0: SUPPORTS_KW@32..40 "supports" [] [] + 1: L_PAREN@40..41 "(" [] [] + 2: CSS_DECLARATION@41..57 + 0: CSS_GENERIC_PROPERTY@41..57 + 0: CSS_IDENTIFIER@41..46 + 0: IDENT@41..46 "width" [] [] + 1: COLON@46..48 ":" [] [Whitespace(" ")] + 2: SCSS_EXPRESSION@48..57 + 0: SCSS_EXPRESSION_ITEM_LIST@48..57 + 0: SCSS_BINARY_EXPRESSION@48..57 + 0: SCSS_IDENTIFIER@48..54 + 0: DOLLAR@48..49 "$" [] [] + 1: CSS_IDENTIFIER@49..54 + 0: IDENT@49..54 "base" [] [Whitespace(" ")] + 1: PLUS@54..56 "+" [] [Whitespace(" ")] + 2: CSS_NUMBER@56..57 + 0: CSS_NUMBER_LITERAL@56..57 "1" [] [] + 1: (empty) + 3: R_PAREN@57..58 ")" [] [] + 3: CSS_MEDIA_QUERY_LIST@58..58 + 2: SEMICOLON@58..59 ";" [] [] + 2: CSS_AT_RULE@59..110 + 0: AT@59..61 "@" [Newline("\n")] [] + 1: SCSS_IMPORT_AT_RULE@61..110 + 0: IMPORT_KW@61..68 "import" [] [Whitespace(" ")] + 1: SCSS_IMPORT_ITEM_LIST@68..109 + 0: SCSS_PLAIN_IMPORT@68..109 + 0: CSS_STRING@68..80 + 0: CSS_STRING_LITERAL@68..80 "\"theme.css\"" [] [Whitespace(" ")] + 1: (empty) + 2: CSS_IMPORT_SUPPORTS@80..109 + 0: SUPPORTS_KW@80..88 "supports" [] [] + 1: L_PAREN@88..89 "(" [] [] + 2: CSS_DECLARATION@89..108 + 0: CSS_GENERIC_PROPERTY@89..108 + 0: SCSS_INTERPOLATED_IDENTIFIER@89..97 + 0: SCSS_INTERPOLATED_IDENTIFIER_PART_LIST@89..97 + 0: SCSS_INTERPOLATION@89..97 + 0: HASH@89..90 "#" [] [] + 1: L_CURLY@90..91 "{" [] [] + 2: SCSS_EXPRESSION@91..96 + 0: SCSS_EXPRESSION_ITEM_LIST@91..96 + 0: SCSS_IDENTIFIER@91..96 + 0: DOLLAR@91..92 "$" [] [] + 1: CSS_IDENTIFIER@92..96 + 0: IDENT@92..96 "name" [] [] + 3: R_CURLY@96..97 "}" [] [] + 1: COLON@97..99 ":" [] [Whitespace(" ")] + 2: SCSS_EXPRESSION@99..108 + 0: SCSS_EXPRESSION_ITEM_LIST@99..108 + 0: SCSS_BINARY_EXPRESSION@99..108 + 0: SCSS_IDENTIFIER@99..105 + 0: DOLLAR@99..100 "$" [] [] + 1: CSS_IDENTIFIER@100..105 + 0: IDENT@100..105 "base" [] [Whitespace(" ")] + 1: PLUS@105..107 "+" [] [Whitespace(" ")] + 2: CSS_NUMBER@107..108 + 0: CSS_NUMBER_LITERAL@107..108 "1" [] [] + 1: (empty) + 3: R_PAREN@108..109 ")" [] [] + 3: CSS_MEDIA_QUERY_LIST@109..109 + 2: SEMICOLON@109..110 ";" [] [] + 3: CSS_AT_RULE@110..168 + 0: AT@110..112 "@" [Newline("\n")] [] + 1: SCSS_IMPORT_AT_RULE@112..168 + 0: IMPORT_KW@112..119 "import" [] [Whitespace(" ")] + 1: SCSS_IMPORT_ITEM_LIST@119..167 + 0: SCSS_PLAIN_IMPORT@119..167 + 0: CSS_STRING@119..131 + 0: CSS_STRING_LITERAL@119..131 "\"theme.css\"" [] [Whitespace(" ")] + 1: (empty) + 2: CSS_IMPORT_SUPPORTS@131..167 + 0: SUPPORTS_KW@131..139 "supports" [] [] + 1: L_PAREN@139..140 "(" [] [] + 2: CSS_DECLARATION@140..166 + 0: CSS_GENERIC_PROPERTY@140..166 + 0: SCSS_INTERPOLATED_IDENTIFIER@140..155 + 0: SCSS_INTERPOLATED_IDENTIFIER_PART_LIST@140..155 + 0: CSS_IDENTIFIER@140..147 + 0: IDENT@140..147 "margin-" [] [] + 1: SCSS_INTERPOLATION@147..155 + 0: HASH@147..148 "#" [] [] + 1: L_CURLY@148..149 "{" [] [] + 2: SCSS_EXPRESSION@149..154 + 0: SCSS_EXPRESSION_ITEM_LIST@149..154 + 0: SCSS_IDENTIFIER@149..154 + 0: DOLLAR@149..150 "$" [] [] + 1: CSS_IDENTIFIER@150..154 + 0: IDENT@150..154 "side" [] [] + 3: R_CURLY@154..155 "}" [] [] + 1: COLON@155..157 ":" [] [Whitespace(" ")] + 2: SCSS_EXPRESSION@157..166 + 0: SCSS_EXPRESSION_ITEM_LIST@157..166 + 0: SCSS_BINARY_EXPRESSION@157..166 + 0: SCSS_IDENTIFIER@157..163 + 0: DOLLAR@157..158 "$" [] [] + 1: CSS_IDENTIFIER@158..163 + 0: IDENT@158..163 "base" [] [Whitespace(" ")] + 1: PLUS@163..165 "+" [] [Whitespace(" ")] + 2: CSS_NUMBER@165..166 + 0: CSS_NUMBER_LITERAL@165..166 "1" [] [] + 1: (empty) + 3: R_PAREN@166..167 ")" [] [] + 3: CSS_MEDIA_QUERY_LIST@167..167 + 2: SEMICOLON@167..168 ";" [] [] + 4: CSS_AT_RULE@168..221 + 0: AT@168..170 "@" [Newline("\n")] [] + 1: SCSS_IMPORT_AT_RULE@170..221 + 0: IMPORT_KW@170..177 "import" [] [Whitespace(" ")] + 1: SCSS_IMPORT_ITEM_LIST@177..220 + 0: SCSS_PLAIN_IMPORT@177..220 + 0: CSS_STRING@177..189 + 0: CSS_STRING_LITERAL@177..189 "\"theme.css\"" [] [Whitespace(" ")] + 1: (empty) + 2: CSS_IMPORT_SUPPORTS@189..220 + 0: SUPPORTS_KW@189..197 "supports" [] [] + 1: L_PAREN@197..198 "(" [] [] + 2: CSS_DECLARATION@198..219 + 0: CSS_GENERIC_PROPERTY@198..219 + 0: SCSS_INTERPOLATED_IDENTIFIER@198..214 + 0: SCSS_INTERPOLATED_IDENTIFIER_PART_LIST@198..214 + 0: CSS_IDENTIFIER@198..206 + 0: IDENT@198..206 "--theme-" [] [] + 1: SCSS_INTERPOLATION@206..214 + 0: HASH@206..207 "#" [] [] + 1: L_CURLY@207..208 "{" [] [] + 2: SCSS_EXPRESSION@208..213 + 0: SCSS_EXPRESSION_ITEM_LIST@208..213 + 0: SCSS_IDENTIFIER@208..213 + 0: DOLLAR@208..209 "$" [] [] + 1: CSS_IDENTIFIER@209..213 + 0: IDENT@209..213 "slot" [] [] + 3: R_CURLY@213..214 "}" [] [] + 1: COLON@214..216 ":" [] [Whitespace(" ")] + 2: SCSS_EXPRESSION@216..219 + 0: SCSS_EXPRESSION_ITEM_LIST@216..219 + 0: CSS_IDENTIFIER@216..219 + 0: IDENT@216..219 "red" [] [] + 1: (empty) + 3: R_PAREN@219..220 ")" [] [] + 3: CSS_MEDIA_QUERY_LIST@220..220 + 2: SEMICOLON@220..221 ";" [] [] + 5: CSS_QUALIFIED_RULE@221..810 + 0: CSS_SELECTOR_LIST@221..228 + 0: CSS_COMPOUND_SELECTOR@221..228 + 0: CSS_NESTED_SELECTOR_LIST@221..221 + 1: (empty) + 2: CSS_SUB_SELECTOR_LIST@221..228 + 0: CSS_CLASS_SELECTOR@221..228 + 0: DOT@221..224 "." [Newline("\n"), Newline("\n")] [] + 1: CSS_CUSTOM_IDENTIFIER@224..228 + 0: IDENT@224..228 "box" [] [Whitespace(" ")] + 1: CSS_DECLARATION_OR_RULE_BLOCK@228..810 + 0: L_CURLY@228..229 "{" [] [] + 1: CSS_DECLARATION_OR_RULE_LIST@229..808 + 0: CSS_AT_RULE@229..288 + 0: AT@229..233 "@" [Newline("\n"), Whitespace(" ")] [] + 1: CSS_SUPPORTS_AT_RULE@233..288 + 0: CSS_SUPPORTS_AT_RULE_DECLARATOR@233..261 + 0: SUPPORTS_KW@233..242 "supports" [] [Whitespace(" ")] + 1: CSS_SUPPORTS_FEATURE_DECLARATION@242..261 + 0: L_PAREN@242..243 "(" [] [] + 1: CSS_DECLARATION@243..259 + 0: CSS_GENERIC_PROPERTY@243..259 + 0: CSS_IDENTIFIER@243..248 + 0: IDENT@243..248 "width" [] [] + 1: COLON@248..250 ":" [] [Whitespace(" ")] + 2: SCSS_EXPRESSION@250..259 + 0: SCSS_EXPRESSION_ITEM_LIST@250..259 + 0: SCSS_BINARY_EXPRESSION@250..259 + 0: SCSS_IDENTIFIER@250..256 + 0: DOLLAR@250..251 "$" [] [] + 1: CSS_IDENTIFIER@251..256 + 0: IDENT@251..256 "base" [] [Whitespace(" ")] + 1: PLUS@256..258 "+" [] [Whitespace(" ")] + 2: CSS_NUMBER@258..259 + 0: CSS_NUMBER_LITERAL@258..259 "1" [] [] + 1: (empty) + 2: R_PAREN@259..261 ")" [] [Whitespace(" ")] + 1: CSS_DECLARATION_OR_RULE_BLOCK@261..288 + 0: L_CURLY@261..262 "{" [] [] + 1: CSS_DECLARATION_OR_RULE_LIST@262..284 + 0: CSS_DECLARATION_WITH_SEMICOLON@262..284 + 0: CSS_DECLARATION@262..283 + 0: CSS_GENERIC_PROPERTY@262..283 + 0: CSS_IDENTIFIER@262..272 + 0: IDENT@262..272 "width" [Newline("\n"), Whitespace(" ")] [] + 1: COLON@272..274 ":" [] [Whitespace(" ")] + 2: SCSS_EXPRESSION@274..283 + 0: SCSS_EXPRESSION_ITEM_LIST@274..283 + 0: SCSS_BINARY_EXPRESSION@274..283 + 0: SCSS_IDENTIFIER@274..280 + 0: DOLLAR@274..275 "$" [] [] + 1: CSS_IDENTIFIER@275..280 + 0: IDENT@275..280 "base" [] [Whitespace(" ")] + 1: PLUS@280..282 "+" [] [Whitespace(" ")] + 2: CSS_NUMBER@282..283 + 0: CSS_NUMBER_LITERAL@282..283 "1" [] [] + 1: (empty) + 1: SEMICOLON@283..284 ";" [] [] + 2: R_CURLY@284..288 "}" [Newline("\n"), Whitespace(" ")] [] + 1: CSS_AT_RULE@288..355 + 0: AT@288..293 "@" [Newline("\n"), Newline("\n"), Whitespace(" ")] [] + 1: CSS_SUPPORTS_AT_RULE@293..355 + 0: CSS_SUPPORTS_AT_RULE_DECLARATOR@293..328 + 0: SUPPORTS_KW@293..302 "supports" [] [Whitespace(" ")] + 1: CSS_SUPPORTS_NOT_CONDITION@302..328 + 0: NOT_KW@302..306 "not" [] [Whitespace(" ")] + 1: CSS_SUPPORTS_FEATURE_DECLARATION@306..328 + 0: L_PAREN@306..307 "(" [] [] + 1: CSS_DECLARATION@307..326 + 0: CSS_GENERIC_PROPERTY@307..326 + 0: SCSS_INTERPOLATED_IDENTIFIER@307..315 + 0: SCSS_INTERPOLATED_IDENTIFIER_PART_LIST@307..315 + 0: SCSS_INTERPOLATION@307..315 + 0: HASH@307..308 "#" [] [] + 1: L_CURLY@308..309 "{" [] [] + 2: SCSS_EXPRESSION@309..314 + 0: SCSS_EXPRESSION_ITEM_LIST@309..314 + 0: SCSS_IDENTIFIER@309..314 + 0: DOLLAR@309..310 "$" [] [] + 1: CSS_IDENTIFIER@310..314 + 0: IDENT@310..314 "name" [] [] + 3: R_CURLY@314..315 "}" [] [] + 1: COLON@315..317 ":" [] [Whitespace(" ")] + 2: SCSS_EXPRESSION@317..326 + 0: SCSS_EXPRESSION_ITEM_LIST@317..326 + 0: SCSS_BINARY_EXPRESSION@317..326 + 0: SCSS_IDENTIFIER@317..323 + 0: DOLLAR@317..318 "$" [] [] + 1: CSS_IDENTIFIER@318..323 + 0: IDENT@318..323 "base" [] [Whitespace(" ")] + 1: PLUS@323..325 "+" [] [Whitespace(" ")] + 2: CSS_NUMBER@325..326 + 0: CSS_NUMBER_LITERAL@325..326 "1" [] [] + 1: (empty) + 2: R_PAREN@326..328 ")" [] [Whitespace(" ")] + 1: CSS_DECLARATION_OR_RULE_BLOCK@328..355 + 0: L_CURLY@328..329 "{" [] [] + 1: CSS_DECLARATION_OR_RULE_LIST@329..351 + 0: CSS_DECLARATION_WITH_SEMICOLON@329..351 + 0: CSS_DECLARATION@329..350 + 0: CSS_GENERIC_PROPERTY@329..350 + 0: CSS_IDENTIFIER@329..339 + 0: IDENT@329..339 "width" [Newline("\n"), Whitespace(" ")] [] + 1: COLON@339..341 ":" [] [Whitespace(" ")] + 2: SCSS_EXPRESSION@341..350 + 0: SCSS_EXPRESSION_ITEM_LIST@341..350 + 0: SCSS_BINARY_EXPRESSION@341..350 + 0: SCSS_IDENTIFIER@341..347 + 0: DOLLAR@341..342 "$" [] [] + 1: CSS_IDENTIFIER@342..347 + 0: IDENT@342..347 "base" [] [Whitespace(" ")] + 1: PLUS@347..349 "+" [] [Whitespace(" ")] + 2: CSS_NUMBER@349..350 + 0: CSS_NUMBER_LITERAL@349..350 "1" [] [] + 1: (empty) + 1: SEMICOLON@350..351 ";" [] [] + 2: R_CURLY@351..355 "}" [Newline("\n"), Whitespace(" ")] [] + 2: CSS_AT_RULE@355..438 + 0: AT@355..360 "@" [Newline("\n"), Newline("\n"), Whitespace(" ")] [] + 1: CSS_SUPPORTS_AT_RULE@360..438 + 0: CSS_SUPPORTS_AT_RULE_DECLARATOR@360..411 + 0: SUPPORTS_KW@360..369 "supports" [] [Whitespace(" ")] + 1: CSS_SUPPORTS_AND_CONDITION@369..411 + 0: CSS_SUPPORTS_FEATURE_DECLARATION@369..391 + 0: L_PAREN@369..370 "(" [] [] + 1: CSS_DECLARATION@370..389 + 0: CSS_GENERIC_PROPERTY@370..389 + 0: SCSS_INTERPOLATED_IDENTIFIER@370..378 + 0: SCSS_INTERPOLATED_IDENTIFIER_PART_LIST@370..378 + 0: SCSS_INTERPOLATION@370..378 + 0: HASH@370..371 "#" [] [] + 1: L_CURLY@371..372 "{" [] [] + 2: SCSS_EXPRESSION@372..377 + 0: SCSS_EXPRESSION_ITEM_LIST@372..377 + 0: SCSS_IDENTIFIER@372..377 + 0: DOLLAR@372..373 "$" [] [] + 1: CSS_IDENTIFIER@373..377 + 0: IDENT@373..377 "name" [] [] + 3: R_CURLY@377..378 "}" [] [] + 1: COLON@378..380 ":" [] [Whitespace(" ")] + 2: SCSS_EXPRESSION@380..389 + 0: SCSS_EXPRESSION_ITEM_LIST@380..389 + 0: SCSS_BINARY_EXPRESSION@380..389 + 0: SCSS_IDENTIFIER@380..386 + 0: DOLLAR@380..381 "$" [] [] + 1: CSS_IDENTIFIER@381..386 + 0: IDENT@381..386 "base" [] [Whitespace(" ")] + 1: PLUS@386..388 "+" [] [Whitespace(" ")] + 2: CSS_NUMBER@388..389 + 0: CSS_NUMBER_LITERAL@388..389 "1" [] [] + 1: (empty) + 2: R_PAREN@389..391 ")" [] [Whitespace(" ")] + 1: AND_KW@391..395 "and" [] [Whitespace(" ")] + 2: CSS_SUPPORTS_FEATURE_DECLARATION@395..411 + 0: L_PAREN@395..396 "(" [] [] + 1: CSS_DECLARATION@396..409 + 0: CSS_GENERIC_PROPERTY@396..409 + 0: CSS_IDENTIFIER@396..403 + 0: IDENT@396..403 "display" [] [] + 1: COLON@403..405 ":" [] [Whitespace(" ")] + 2: SCSS_EXPRESSION@405..409 + 0: SCSS_EXPRESSION_ITEM_LIST@405..409 + 0: CSS_IDENTIFIER@405..409 + 0: IDENT@405..409 "grid" [] [] + 1: (empty) + 2: R_PAREN@409..411 ")" [] [Whitespace(" ")] + 1: CSS_DECLARATION_OR_RULE_BLOCK@411..438 + 0: L_CURLY@411..412 "{" [] [] + 1: CSS_DECLARATION_OR_RULE_LIST@412..434 + 0: CSS_DECLARATION_WITH_SEMICOLON@412..434 + 0: CSS_DECLARATION@412..433 + 0: CSS_GENERIC_PROPERTY@412..433 + 0: CSS_IDENTIFIER@412..422 + 0: IDENT@412..422 "width" [Newline("\n"), Whitespace(" ")] [] + 1: COLON@422..424 ":" [] [Whitespace(" ")] + 2: SCSS_EXPRESSION@424..433 + 0: SCSS_EXPRESSION_ITEM_LIST@424..433 + 0: SCSS_BINARY_EXPRESSION@424..433 + 0: SCSS_IDENTIFIER@424..430 + 0: DOLLAR@424..425 "$" [] [] + 1: CSS_IDENTIFIER@425..430 + 0: IDENT@425..430 "base" [] [Whitespace(" ")] + 1: PLUS@430..432 "+" [] [Whitespace(" ")] + 2: CSS_NUMBER@432..433 + 0: CSS_NUMBER_LITERAL@432..433 "1" [] [] + 1: (empty) + 1: SEMICOLON@433..434 ";" [] [] + 2: R_CURLY@434..438 "}" [Newline("\n"), Whitespace(" ")] [] + 3: CSS_AT_RULE@438..533 + 0: AT@438..443 "@" [Newline("\n"), Newline("\n"), Whitespace(" ")] [] + 1: CSS_SUPPORTS_AT_RULE@443..533 + 0: CSS_SUPPORTS_AT_RULE_DECLARATOR@443..506 + 0: SUPPORTS_KW@443..452 "supports" [] [Whitespace(" ")] + 1: CSS_SUPPORTS_OR_CONDITION@452..506 + 0: CSS_SUPPORTS_FEATURE_DECLARATION@452..474 + 0: L_PAREN@452..453 "(" [] [] + 1: CSS_DECLARATION@453..472 + 0: CSS_GENERIC_PROPERTY@453..472 + 0: SCSS_INTERPOLATED_IDENTIFIER@453..461 + 0: SCSS_INTERPOLATED_IDENTIFIER_PART_LIST@453..461 + 0: SCSS_INTERPOLATION@453..461 + 0: HASH@453..454 "#" [] [] + 1: L_CURLY@454..455 "{" [] [] + 2: SCSS_EXPRESSION@455..460 + 0: SCSS_EXPRESSION_ITEM_LIST@455..460 + 0: SCSS_IDENTIFIER@455..460 + 0: DOLLAR@455..456 "$" [] [] + 1: CSS_IDENTIFIER@456..460 + 0: IDENT@456..460 "name" [] [] + 3: R_CURLY@460..461 "}" [] [] + 1: COLON@461..463 ":" [] [Whitespace(" ")] + 2: SCSS_EXPRESSION@463..472 + 0: SCSS_EXPRESSION_ITEM_LIST@463..472 + 0: SCSS_BINARY_EXPRESSION@463..472 + 0: SCSS_IDENTIFIER@463..469 + 0: DOLLAR@463..464 "$" [] [] + 1: CSS_IDENTIFIER@464..469 + 0: IDENT@464..469 "base" [] [Whitespace(" ")] + 1: PLUS@469..471 "+" [] [Whitespace(" ")] + 2: CSS_NUMBER@471..472 + 0: CSS_NUMBER_LITERAL@471..472 "1" [] [] + 1: (empty) + 2: R_PAREN@472..474 ")" [] [Whitespace(" ")] + 1: OR_KW@474..477 "or" [] [Whitespace(" ")] + 2: CSS_SUPPORTS_FEATURE_DECLARATION@477..506 + 0: L_PAREN@477..478 "(" [] [] + 1: CSS_DECLARATION@478..504 + 0: CSS_GENERIC_PROPERTY@478..504 + 0: SCSS_INTERPOLATED_IDENTIFIER@478..493 + 0: SCSS_INTERPOLATED_IDENTIFIER_PART_LIST@478..493 + 0: CSS_IDENTIFIER@478..485 + 0: IDENT@478..485 "margin-" [] [] + 1: SCSS_INTERPOLATION@485..493 + 0: HASH@485..486 "#" [] [] + 1: L_CURLY@486..487 "{" [] [] + 2: SCSS_EXPRESSION@487..492 + 0: SCSS_EXPRESSION_ITEM_LIST@487..492 + 0: SCSS_IDENTIFIER@487..492 + 0: DOLLAR@487..488 "$" [] [] + 1: CSS_IDENTIFIER@488..492 + 0: IDENT@488..492 "side" [] [] + 3: R_CURLY@492..493 "}" [] [] + 1: COLON@493..495 ":" [] [Whitespace(" ")] + 2: SCSS_EXPRESSION@495..504 + 0: SCSS_EXPRESSION_ITEM_LIST@495..504 + 0: SCSS_BINARY_EXPRESSION@495..504 + 0: SCSS_IDENTIFIER@495..501 + 0: DOLLAR@495..496 "$" [] [] + 1: CSS_IDENTIFIER@496..501 + 0: IDENT@496..501 "base" [] [Whitespace(" ")] + 1: PLUS@501..503 "+" [] [Whitespace(" ")] + 2: CSS_NUMBER@503..504 + 0: CSS_NUMBER_LITERAL@503..504 "1" [] [] + 1: (empty) + 2: R_PAREN@504..506 ")" [] [Whitespace(" ")] + 1: CSS_DECLARATION_OR_RULE_BLOCK@506..533 + 0: L_CURLY@506..507 "{" [] [] + 1: CSS_DECLARATION_OR_RULE_LIST@507..529 + 0: CSS_DECLARATION_WITH_SEMICOLON@507..529 + 0: CSS_DECLARATION@507..528 + 0: CSS_GENERIC_PROPERTY@507..528 + 0: CSS_IDENTIFIER@507..517 + 0: IDENT@507..517 "width" [Newline("\n"), Whitespace(" ")] [] + 1: COLON@517..519 ":" [] [Whitespace(" ")] + 2: SCSS_EXPRESSION@519..528 + 0: SCSS_EXPRESSION_ITEM_LIST@519..528 + 0: SCSS_BINARY_EXPRESSION@519..528 + 0: SCSS_IDENTIFIER@519..525 + 0: DOLLAR@519..520 "$" [] [] + 1: CSS_IDENTIFIER@520..525 + 0: IDENT@520..525 "base" [] [Whitespace(" ")] + 1: PLUS@525..527 "+" [] [Whitespace(" ")] + 2: CSS_NUMBER@527..528 + 0: CSS_NUMBER_LITERAL@527..528 "1" [] [] + 1: (empty) + 1: SEMICOLON@528..529 ";" [] [] + 2: R_CURLY@529..533 "}" [Newline("\n"), Whitespace(" ")] [] + 4: CSS_AT_RULE@533..620 + 0: AT@533..538 "@" [Newline("\n"), Newline("\n"), Whitespace(" ")] [] + 1: CSS_SUPPORTS_AT_RULE@538..620 + 0: CSS_SUPPORTS_AT_RULE_DECLARATOR@538..599 + 0: SUPPORTS_KW@538..547 "supports" [] [Whitespace(" ")] + 1: CSS_SUPPORTS_CONDITION_IN_PARENS@547..599 + 0: L_PAREN@547..548 "(" [] [] + 1: CSS_SUPPORTS_AND_CONDITION@548..597 + 0: CSS_SUPPORTS_FEATURE_DECLARATION@548..570 + 0: L_PAREN@548..549 "(" [] [] + 1: CSS_DECLARATION@549..568 + 0: CSS_GENERIC_PROPERTY@549..568 + 0: SCSS_INTERPOLATED_IDENTIFIER@549..557 + 0: SCSS_INTERPOLATED_IDENTIFIER_PART_LIST@549..557 + 0: SCSS_INTERPOLATION@549..557 + 0: HASH@549..550 "#" [] [] + 1: L_CURLY@550..551 "{" [] [] + 2: SCSS_EXPRESSION@551..556 + 0: SCSS_EXPRESSION_ITEM_LIST@551..556 + 0: SCSS_IDENTIFIER@551..556 + 0: DOLLAR@551..552 "$" [] [] + 1: CSS_IDENTIFIER@552..556 + 0: IDENT@552..556 "name" [] [] + 3: R_CURLY@556..557 "}" [] [] + 1: COLON@557..559 ":" [] [Whitespace(" ")] + 2: SCSS_EXPRESSION@559..568 + 0: SCSS_EXPRESSION_ITEM_LIST@559..568 + 0: SCSS_BINARY_EXPRESSION@559..568 + 0: SCSS_IDENTIFIER@559..565 + 0: DOLLAR@559..560 "$" [] [] + 1: CSS_IDENTIFIER@560..565 + 0: IDENT@560..565 "base" [] [Whitespace(" ")] + 1: PLUS@565..567 "+" [] [Whitespace(" ")] + 2: CSS_NUMBER@567..568 + 0: CSS_NUMBER_LITERAL@567..568 "1" [] [] + 1: (empty) + 2: R_PAREN@568..570 ")" [] [Whitespace(" ")] + 1: AND_KW@570..574 "and" [] [Whitespace(" ")] + 2: CSS_SUPPORTS_FEATURE_DECLARATION@574..597 + 0: L_PAREN@574..575 "(" [] [] + 1: CSS_DECLARATION@575..596 + 0: CSS_GENERIC_PROPERTY@575..596 + 0: SCSS_INTERPOLATED_IDENTIFIER@575..591 + 0: SCSS_INTERPOLATED_IDENTIFIER_PART_LIST@575..591 + 0: CSS_IDENTIFIER@575..583 + 0: IDENT@575..583 "--theme-" [] [] + 1: SCSS_INTERPOLATION@583..591 + 0: HASH@583..584 "#" [] [] + 1: L_CURLY@584..585 "{" [] [] + 2: SCSS_EXPRESSION@585..590 + 0: SCSS_EXPRESSION_ITEM_LIST@585..590 + 0: SCSS_IDENTIFIER@585..590 + 0: DOLLAR@585..586 "$" [] [] + 1: CSS_IDENTIFIER@586..590 + 0: IDENT@586..590 "slot" [] [] + 3: R_CURLY@590..591 "}" [] [] + 1: COLON@591..593 ":" [] [Whitespace(" ")] + 2: SCSS_EXPRESSION@593..596 + 0: SCSS_EXPRESSION_ITEM_LIST@593..596 + 0: CSS_IDENTIFIER@593..596 + 0: IDENT@593..596 "red" [] [] + 1: (empty) + 2: R_PAREN@596..597 ")" [] [] + 2: R_PAREN@597..599 ")" [] [Whitespace(" ")] + 1: CSS_DECLARATION_OR_RULE_BLOCK@599..620 + 0: L_CURLY@599..600 "{" [] [] + 1: CSS_DECLARATION_OR_RULE_LIST@600..616 + 0: CSS_DECLARATION_WITH_SEMICOLON@600..616 + 0: CSS_DECLARATION@600..615 + 0: CSS_GENERIC_PROPERTY@600..615 + 0: CSS_IDENTIFIER@600..610 + 0: IDENT@600..610 "color" [Newline("\n"), Whitespace(" ")] [] + 1: COLON@610..612 ":" [] [Whitespace(" ")] + 2: SCSS_EXPRESSION@612..615 + 0: SCSS_EXPRESSION_ITEM_LIST@612..615 + 0: CSS_IDENTIFIER@612..615 + 0: IDENT@612..615 "red" [] [] + 1: (empty) + 1: SEMICOLON@615..616 ";" [] [] + 2: R_CURLY@616..620 "}" [Newline("\n"), Whitespace(" ")] [] + 5: CSS_AT_RULE@620..690 + 0: AT@620..625 "@" [Newline("\n"), Newline("\n"), Whitespace(" ")] [] + 1: CSS_SUPPORTS_AT_RULE@625..690 + 0: CSS_SUPPORTS_AT_RULE_DECLARATOR@625..663 + 0: SUPPORTS_KW@625..634 "supports" [] [Whitespace(" ")] + 1: CSS_SUPPORTS_FEATURE_DECLARATION@634..663 + 0: L_PAREN@634..635 "(" [] [] + 1: CSS_DECLARATION@635..661 + 0: CSS_GENERIC_PROPERTY@635..661 + 0: SCSS_INTERPOLATED_IDENTIFIER@635..650 + 0: SCSS_INTERPOLATED_IDENTIFIER_PART_LIST@635..650 + 0: CSS_IDENTIFIER@635..642 + 0: IDENT@635..642 "margin-" [] [] + 1: SCSS_INTERPOLATION@642..650 + 0: HASH@642..643 "#" [] [] + 1: L_CURLY@643..644 "{" [] [] + 2: SCSS_EXPRESSION@644..649 + 0: SCSS_EXPRESSION_ITEM_LIST@644..649 + 0: SCSS_IDENTIFIER@644..649 + 0: DOLLAR@644..645 "$" [] [] + 1: CSS_IDENTIFIER@645..649 + 0: IDENT@645..649 "side" [] [] + 3: R_CURLY@649..650 "}" [] [] + 1: COLON@650..652 ":" [] [Whitespace(" ")] + 2: SCSS_EXPRESSION@652..661 + 0: SCSS_EXPRESSION_ITEM_LIST@652..661 + 0: SCSS_BINARY_EXPRESSION@652..661 + 0: SCSS_IDENTIFIER@652..658 + 0: DOLLAR@652..653 "$" [] [] + 1: CSS_IDENTIFIER@653..658 + 0: IDENT@653..658 "base" [] [Whitespace(" ")] + 1: PLUS@658..660 "+" [] [Whitespace(" ")] + 2: CSS_NUMBER@660..661 + 0: CSS_NUMBER_LITERAL@660..661 "1" [] [] + 1: (empty) + 2: R_PAREN@661..663 ")" [] [Whitespace(" ")] + 1: CSS_DECLARATION_OR_RULE_BLOCK@663..690 + 0: L_CURLY@663..664 "{" [] [] + 1: CSS_DECLARATION_OR_RULE_LIST@664..686 + 0: CSS_DECLARATION_WITH_SEMICOLON@664..686 + 0: CSS_DECLARATION@664..685 + 0: CSS_GENERIC_PROPERTY@664..685 + 0: CSS_IDENTIFIER@664..674 + 0: IDENT@664..674 "width" [Newline("\n"), Whitespace(" ")] [] + 1: COLON@674..676 ":" [] [Whitespace(" ")] + 2: SCSS_EXPRESSION@676..685 + 0: SCSS_EXPRESSION_ITEM_LIST@676..685 + 0: SCSS_BINARY_EXPRESSION@676..685 + 0: SCSS_IDENTIFIER@676..682 + 0: DOLLAR@676..677 "$" [] [] + 1: CSS_IDENTIFIER@677..682 + 0: IDENT@677..682 "base" [] [Whitespace(" ")] + 1: PLUS@682..684 "+" [] [Whitespace(" ")] + 2: CSS_NUMBER@684..685 + 0: CSS_NUMBER_LITERAL@684..685 "1" [] [] + 1: (empty) + 1: SEMICOLON@685..686 ";" [] [] + 2: R_CURLY@686..690 "}" [Newline("\n"), Whitespace(" ")] [] + 6: CSS_AT_RULE@690..749 + 0: AT@690..695 "@" [Newline("\n"), Newline("\n"), Whitespace(" ")] [] + 1: CSS_SUPPORTS_AT_RULE@695..749 + 0: CSS_SUPPORTS_AT_RULE_DECLARATOR@695..728 + 0: SUPPORTS_KW@695..704 "supports" [] [Whitespace(" ")] + 1: CSS_SUPPORTS_FEATURE_DECLARATION@704..728 + 0: L_PAREN@704..705 "(" [] [] + 1: CSS_DECLARATION@705..726 + 0: CSS_GENERIC_PROPERTY@705..726 + 0: SCSS_INTERPOLATED_IDENTIFIER@705..721 + 0: SCSS_INTERPOLATED_IDENTIFIER_PART_LIST@705..721 + 0: CSS_IDENTIFIER@705..713 + 0: IDENT@705..713 "--theme-" [] [] + 1: SCSS_INTERPOLATION@713..721 + 0: HASH@713..714 "#" [] [] + 1: L_CURLY@714..715 "{" [] [] + 2: SCSS_EXPRESSION@715..720 + 0: SCSS_EXPRESSION_ITEM_LIST@715..720 + 0: SCSS_IDENTIFIER@715..720 + 0: DOLLAR@715..716 "$" [] [] + 1: CSS_IDENTIFIER@716..720 + 0: IDENT@716..720 "slot" [] [] + 3: R_CURLY@720..721 "}" [] [] + 1: COLON@721..723 ":" [] [Whitespace(" ")] + 2: SCSS_EXPRESSION@723..726 + 0: SCSS_EXPRESSION_ITEM_LIST@723..726 + 0: CSS_IDENTIFIER@723..726 + 0: IDENT@723..726 "red" [] [] + 1: (empty) + 2: R_PAREN@726..728 ")" [] [Whitespace(" ")] + 1: CSS_DECLARATION_OR_RULE_BLOCK@728..749 + 0: L_CURLY@728..729 "{" [] [] + 1: CSS_DECLARATION_OR_RULE_LIST@729..745 + 0: CSS_DECLARATION_WITH_SEMICOLON@729..745 + 0: CSS_DECLARATION@729..744 + 0: CSS_GENERIC_PROPERTY@729..744 + 0: CSS_IDENTIFIER@729..739 + 0: IDENT@729..739 "color" [Newline("\n"), Whitespace(" ")] [] + 1: COLON@739..741 ":" [] [Whitespace(" ")] + 2: SCSS_EXPRESSION@741..744 + 0: SCSS_EXPRESSION_ITEM_LIST@741..744 + 0: CSS_IDENTIFIER@741..744 + 0: IDENT@741..744 "red" [] [] + 1: (empty) + 1: SEMICOLON@744..745 ";" [] [] + 2: R_CURLY@745..749 "}" [Newline("\n"), Whitespace(" ")] [] + 7: CSS_AT_RULE@749..808 + 0: AT@749..754 "@" [Newline("\n"), Newline("\n"), Whitespace(" ")] [] + 1: CSS_SUPPORTS_AT_RULE@754..808 + 0: CSS_SUPPORTS_AT_RULE_DECLARATOR@754..787 + 0: SUPPORTS_KW@754..763 "supports" [] [Whitespace(" ")] + 1: CSS_SUPPORTS_FEATURE_SELECTOR@763..787 + 0: SELECTOR_KW@763..771 "selector" [] [] + 1: L_PAREN@771..772 "(" [] [] + 2: CSS_COMPOUND_SELECTOR@772..785 + 0: CSS_NESTED_SELECTOR_LIST@772..772 1: (empty) - 2: R_PAREN@97..99 ")" [] [Whitespace(" ")] - 1: CSS_DECLARATION_OR_RULE_BLOCK@99..126 - 0: L_CURLY@99..100 "{" [] [] - 1: CSS_DECLARATION_OR_RULE_LIST@100..122 - 0: CSS_DECLARATION_WITH_SEMICOLON@100..122 - 0: CSS_DECLARATION@100..121 - 0: CSS_GENERIC_PROPERTY@100..121 - 0: CSS_IDENTIFIER@100..110 - 0: IDENT@100..110 "width" [Newline("\n"), Whitespace(" ")] [] - 1: COLON@110..112 ":" [] [Whitespace(" ")] - 2: SCSS_EXPRESSION@112..121 - 0: SCSS_EXPRESSION_ITEM_LIST@112..121 - 0: SCSS_BINARY_EXPRESSION@112..121 - 0: SCSS_IDENTIFIER@112..118 - 0: DOLLAR@112..113 "$" [] [] - 1: CSS_IDENTIFIER@113..118 - 0: IDENT@113..118 "base" [] [Whitespace(" ")] - 1: PLUS@118..120 "+" [] [Whitespace(" ")] - 2: CSS_NUMBER@120..121 - 0: CSS_NUMBER_LITERAL@120..121 "1" [] [] + 2: CSS_SUB_SELECTOR_LIST@772..785 + 0: CSS_CLASS_SELECTOR@772..785 + 0: DOT@772..773 "." [] [] + 1: SCSS_INTERPOLATED_IDENTIFIER@773..785 + 0: SCSS_INTERPOLATED_IDENTIFIER_PART_LIST@773..785 + 0: CSS_CUSTOM_IDENTIFIER@773..777 + 0: IDENT@773..777 "foo-" [] [] + 1: SCSS_INTERPOLATION@777..785 + 0: HASH@777..778 "#" [] [] + 1: L_CURLY@778..779 "{" [] [] + 2: SCSS_EXPRESSION@779..784 + 0: SCSS_EXPRESSION_ITEM_LIST@779..784 + 0: SCSS_IDENTIFIER@779..784 + 0: DOLLAR@779..780 "$" [] [] + 1: CSS_IDENTIFIER@780..784 + 0: IDENT@780..784 "name" [] [] + 3: R_CURLY@784..785 "}" [] [] + 3: R_PAREN@785..787 ")" [] [Whitespace(" ")] + 1: CSS_DECLARATION_OR_RULE_BLOCK@787..808 + 0: L_CURLY@787..788 "{" [] [] + 1: CSS_DECLARATION_OR_RULE_LIST@788..804 + 0: CSS_DECLARATION_WITH_SEMICOLON@788..804 + 0: CSS_DECLARATION@788..803 + 0: CSS_GENERIC_PROPERTY@788..803 + 0: CSS_IDENTIFIER@788..798 + 0: IDENT@788..798 "color" [Newline("\n"), Whitespace(" ")] [] + 1: COLON@798..800 ":" [] [Whitespace(" ")] + 2: SCSS_EXPRESSION@800..803 + 0: SCSS_EXPRESSION_ITEM_LIST@800..803 + 0: CSS_IDENTIFIER@800..803 + 0: IDENT@800..803 "red" [] [] 1: (empty) - 1: SEMICOLON@121..122 ";" [] [] - 2: R_CURLY@122..126 "}" [Newline("\n"), Whitespace(" ")] [] - 2: R_CURLY@126..128 "}" [Newline("\n")] [] - 2: EOF@128..129 "" [Newline("\n")] [] + 1: SEMICOLON@803..804 ";" [] [] + 2: R_CURLY@804..808 "}" [Newline("\n"), Whitespace(" ")] [] + 2: R_CURLY@808..810 "}" [Newline("\n")] [] + 2: EOF@810..811 "" [Newline("\n")] [] ``` diff --git a/crates/biome_css_parser/tests/css_test_suite/ok/scss/declaration/ambiguous-selector-vs-nesting.scss b/crates/biome_css_parser/tests/css_test_suite/ok/scss/declaration/ambiguous-selector-vs-nesting.scss index 4e52a603bb06..9193a401fdce 100644 --- a/crates/biome_css_parser/tests/css_test_suite/ok/scss/declaration/ambiguous-selector-vs-nesting.scss +++ b/crates/biome_css_parser/tests/css_test_suite/ok/scss/declaration/ambiguous-selector-vs-nesting.scss @@ -14,4 +14,20 @@ font: bold { family: serif; } + + #{$name}:hover { + color: green; + } + + a:#{$pseudo} { + color: teal; + } + + font-#{$weight}:bold { + color: purple; + } + + font-#{$weight}::before { + color: orange; + } } diff --git a/crates/biome_css_parser/tests/css_test_suite/ok/scss/declaration/ambiguous-selector-vs-nesting.scss.snap b/crates/biome_css_parser/tests/css_test_suite/ok/scss/declaration/ambiguous-selector-vs-nesting.scss.snap index a953814b5b60..d946e4738224 100644 --- a/crates/biome_css_parser/tests/css_test_suite/ok/scss/declaration/ambiguous-selector-vs-nesting.scss.snap +++ b/crates/biome_css_parser/tests/css_test_suite/ok/scss/declaration/ambiguous-selector-vs-nesting.scss.snap @@ -22,6 +22,22 @@ expression: snapshot font: bold { family: serif; } + + #{$name}:hover { + color: green; + } + + a:#{$pseudo} { + color: teal; + } + + font-#{$weight}:bold { + color: purple; + } + + font-#{$weight}::before { + color: orange; + } } ``` @@ -230,22 +246,279 @@ CssRoot { r_curly_token: R_CURLY@157..161 "}" [Newline("\n"), Whitespace(" ")] [], }, }, + CssNestedQualifiedRule { + prelude: CssRelativeSelectorList [ + CssRelativeSelector { + combinator: missing (optional), + selector: CssCompoundSelector { + nesting_selectors: CssNestedSelectorList [], + simple_selector: CssTypeSelector { + namespace: missing (optional), + ident: ScssInterpolatedIdentifier { + items: ScssInterpolatedIdentifierPartList [ + ScssInterpolation { + hash_token: HASH@161..166 "#" [Newline("\n"), Newline("\n"), Whitespace(" ")] [], + l_curly_token: L_CURLY@166..167 "{" [] [], + value: ScssExpression { + items: ScssExpressionItemList [ + ScssIdentifier { + dollar_token: DOLLAR@167..168 "$" [] [], + name: CssIdentifier { + value_token: IDENT@168..172 "name" [] [], + }, + }, + ], + }, + r_curly_token: R_CURLY@172..173 "}" [] [], + }, + ], + }, + }, + sub_selectors: CssSubSelectorList [ + CssPseudoClassSelector { + colon_token: COLON@173..174 ":" [] [], + class: CssPseudoClassIdentifier { + name: CssIdentifier { + value_token: IDENT@174..180 "hover" [] [Whitespace(" ")], + }, + }, + }, + ], + }, + }, + ], + block: CssDeclarationOrRuleBlock { + l_curly_token: L_CURLY@180..181 "{" [] [], + items: CssDeclarationOrRuleList [ + CssDeclarationWithSemicolon { + declaration: CssDeclaration { + property: CssGenericProperty { + name: CssIdentifier { + value_token: IDENT@181..191 "color" [Newline("\n"), Whitespace(" ")] [], + }, + colon_token: COLON@191..193 ":" [] [Whitespace(" ")], + value: ScssExpression { + items: ScssExpressionItemList [ + CssIdentifier { + value_token: IDENT@193..198 "green" [] [], + }, + ], + }, + }, + important: missing (optional), + }, + semicolon_token: SEMICOLON@198..199 ";" [] [], + }, + ], + r_curly_token: R_CURLY@199..203 "}" [Newline("\n"), Whitespace(" ")] [], + }, + }, + ScssNestingDeclaration { + name: CssIdentifier { + value_token: IDENT@203..208 "a" [Newline("\n"), Newline("\n"), Whitespace(" ")] [], + }, + colon_token: COLON@208..209 ":" [] [], + value: ScssExpression { + items: ScssExpressionItemList [ + ScssInterpolation { + hash_token: HASH@209..210 "#" [] [], + l_curly_token: L_CURLY@210..211 "{" [] [], + value: ScssExpression { + items: ScssExpressionItemList [ + ScssIdentifier { + dollar_token: DOLLAR@211..212 "$" [] [], + name: CssIdentifier { + value_token: IDENT@212..218 "pseudo" [] [], + }, + }, + ], + }, + r_curly_token: R_CURLY@218..220 "}" [] [Whitespace(" ")], + }, + ], + }, + block: CssDeclarationOrRuleBlock { + l_curly_token: L_CURLY@220..221 "{" [] [], + items: CssDeclarationOrRuleList [ + CssDeclarationWithSemicolon { + declaration: CssDeclaration { + property: CssGenericProperty { + name: CssIdentifier { + value_token: IDENT@221..231 "color" [Newline("\n"), Whitespace(" ")] [], + }, + colon_token: COLON@231..233 ":" [] [Whitespace(" ")], + value: ScssExpression { + items: ScssExpressionItemList [ + CssIdentifier { + value_token: IDENT@233..237 "teal" [] [], + }, + ], + }, + }, + important: missing (optional), + }, + semicolon_token: SEMICOLON@237..238 ";" [] [], + }, + ], + r_curly_token: R_CURLY@238..242 "}" [Newline("\n"), Whitespace(" ")] [], + }, + }, + CssNestedQualifiedRule { + prelude: CssRelativeSelectorList [ + CssRelativeSelector { + combinator: missing (optional), + selector: CssCompoundSelector { + nesting_selectors: CssNestedSelectorList [], + simple_selector: CssTypeSelector { + namespace: missing (optional), + ident: ScssInterpolatedIdentifier { + items: ScssInterpolatedIdentifierPartList [ + CssIdentifier { + value_token: IDENT@242..251 "font-" [Newline("\n"), Newline("\n"), Whitespace(" ")] [], + }, + ScssInterpolation { + hash_token: HASH@251..252 "#" [] [], + l_curly_token: L_CURLY@252..253 "{" [] [], + value: ScssExpression { + items: ScssExpressionItemList [ + ScssIdentifier { + dollar_token: DOLLAR@253..254 "$" [] [], + name: CssIdentifier { + value_token: IDENT@254..260 "weight" [] [], + }, + }, + ], + }, + r_curly_token: R_CURLY@260..261 "}" [] [], + }, + ], + }, + }, + sub_selectors: CssSubSelectorList [ + CssPseudoClassSelector { + colon_token: COLON@261..262 ":" [] [], + class: CssPseudoClassIdentifier { + name: CssIdentifier { + value_token: IDENT@262..267 "bold" [] [Whitespace(" ")], + }, + }, + }, + ], + }, + }, + ], + block: CssDeclarationOrRuleBlock { + l_curly_token: L_CURLY@267..268 "{" [] [], + items: CssDeclarationOrRuleList [ + CssDeclarationWithSemicolon { + declaration: CssDeclaration { + property: CssGenericProperty { + name: CssIdentifier { + value_token: IDENT@268..278 "color" [Newline("\n"), Whitespace(" ")] [], + }, + colon_token: COLON@278..280 ":" [] [Whitespace(" ")], + value: ScssExpression { + items: ScssExpressionItemList [ + CssIdentifier { + value_token: IDENT@280..286 "purple" [] [], + }, + ], + }, + }, + important: missing (optional), + }, + semicolon_token: SEMICOLON@286..287 ";" [] [], + }, + ], + r_curly_token: R_CURLY@287..291 "}" [Newline("\n"), Whitespace(" ")] [], + }, + }, + CssNestedQualifiedRule { + prelude: CssRelativeSelectorList [ + CssRelativeSelector { + combinator: missing (optional), + selector: CssCompoundSelector { + nesting_selectors: CssNestedSelectorList [], + simple_selector: CssTypeSelector { + namespace: missing (optional), + ident: ScssInterpolatedIdentifier { + items: ScssInterpolatedIdentifierPartList [ + CssIdentifier { + value_token: IDENT@291..300 "font-" [Newline("\n"), Newline("\n"), Whitespace(" ")] [], + }, + ScssInterpolation { + hash_token: HASH@300..301 "#" [] [], + l_curly_token: L_CURLY@301..302 "{" [] [], + value: ScssExpression { + items: ScssExpressionItemList [ + ScssIdentifier { + dollar_token: DOLLAR@302..303 "$" [] [], + name: CssIdentifier { + value_token: IDENT@303..309 "weight" [] [], + }, + }, + ], + }, + r_curly_token: R_CURLY@309..310 "}" [] [], + }, + ], + }, + }, + sub_selectors: CssSubSelectorList [ + CssPseudoElementSelector { + double_colon_token: COLON2@310..312 "::" [] [], + element: CssPseudoElementIdentifier { + name: CssIdentifier { + value_token: IDENT@312..319 "before" [] [Whitespace(" ")], + }, + }, + }, + ], + }, + }, + ], + block: CssDeclarationOrRuleBlock { + l_curly_token: L_CURLY@319..320 "{" [] [], + items: CssDeclarationOrRuleList [ + CssDeclarationWithSemicolon { + declaration: CssDeclaration { + property: CssGenericProperty { + name: CssIdentifier { + value_token: IDENT@320..330 "color" [Newline("\n"), Whitespace(" ")] [], + }, + colon_token: COLON@330..332 ":" [] [Whitespace(" ")], + value: ScssExpression { + items: ScssExpressionItemList [ + CssIdentifier { + value_token: IDENT@332..338 "orange" [] [], + }, + ], + }, + }, + important: missing (optional), + }, + semicolon_token: SEMICOLON@338..339 ";" [] [], + }, + ], + r_curly_token: R_CURLY@339..343 "}" [Newline("\n"), Whitespace(" ")] [], + }, + }, ], - r_curly_token: R_CURLY@161..163 "}" [Newline("\n")] [], + r_curly_token: R_CURLY@343..345 "}" [Newline("\n")] [], }, }, ], - eof_token: EOF@163..164 "" [Newline("\n")] [], + eof_token: EOF@345..346 "" [Newline("\n")] [], } ``` ## CST ``` -0: CSS_ROOT@0..164 +0: CSS_ROOT@0..346 0: (empty) - 1: CSS_ROOT_ITEM_LIST@0..163 - 0: CSS_QUALIFIED_RULE@0..163 + 1: CSS_ROOT_ITEM_LIST@0..345 + 0: CSS_QUALIFIED_RULE@0..345 0: CSS_SELECTOR_LIST@0..6 0: CSS_COMPOUND_SELECTOR@0..6 0: CSS_NESTED_SELECTOR_LIST@0..0 @@ -255,9 +528,9 @@ CssRoot { 0: DOT@0..1 "." [] [] 1: CSS_CUSTOM_IDENTIFIER@1..6 0: IDENT@1..6 "test" [] [Whitespace(" ")] - 1: CSS_DECLARATION_OR_RULE_BLOCK@6..163 + 1: CSS_DECLARATION_OR_RULE_BLOCK@6..345 0: L_CURLY@6..7 "{" [] [] - 1: CSS_DECLARATION_OR_RULE_LIST@7..161 + 1: CSS_DECLARATION_OR_RULE_LIST@7..343 0: CSS_NESTED_QUALIFIED_RULE@7..43 0: CSS_RELATIVE_SELECTOR_LIST@7..22 0: CSS_RELATIVE_SELECTOR@7..22 @@ -371,7 +644,169 @@ CssRoot { 1: (empty) 1: SEMICOLON@156..157 ";" [] [] 2: R_CURLY@157..161 "}" [Newline("\n"), Whitespace(" ")] [] - 2: R_CURLY@161..163 "}" [Newline("\n")] [] - 2: EOF@163..164 "" [Newline("\n")] [] + 4: CSS_NESTED_QUALIFIED_RULE@161..203 + 0: CSS_RELATIVE_SELECTOR_LIST@161..180 + 0: CSS_RELATIVE_SELECTOR@161..180 + 0: (empty) + 1: CSS_COMPOUND_SELECTOR@161..180 + 0: CSS_NESTED_SELECTOR_LIST@161..161 + 1: CSS_TYPE_SELECTOR@161..173 + 0: (empty) + 1: SCSS_INTERPOLATED_IDENTIFIER@161..173 + 0: SCSS_INTERPOLATED_IDENTIFIER_PART_LIST@161..173 + 0: SCSS_INTERPOLATION@161..173 + 0: HASH@161..166 "#" [Newline("\n"), Newline("\n"), Whitespace(" ")] [] + 1: L_CURLY@166..167 "{" [] [] + 2: SCSS_EXPRESSION@167..172 + 0: SCSS_EXPRESSION_ITEM_LIST@167..172 + 0: SCSS_IDENTIFIER@167..172 + 0: DOLLAR@167..168 "$" [] [] + 1: CSS_IDENTIFIER@168..172 + 0: IDENT@168..172 "name" [] [] + 3: R_CURLY@172..173 "}" [] [] + 2: CSS_SUB_SELECTOR_LIST@173..180 + 0: CSS_PSEUDO_CLASS_SELECTOR@173..180 + 0: COLON@173..174 ":" [] [] + 1: CSS_PSEUDO_CLASS_IDENTIFIER@174..180 + 0: CSS_IDENTIFIER@174..180 + 0: IDENT@174..180 "hover" [] [Whitespace(" ")] + 1: CSS_DECLARATION_OR_RULE_BLOCK@180..203 + 0: L_CURLY@180..181 "{" [] [] + 1: CSS_DECLARATION_OR_RULE_LIST@181..199 + 0: CSS_DECLARATION_WITH_SEMICOLON@181..199 + 0: CSS_DECLARATION@181..198 + 0: CSS_GENERIC_PROPERTY@181..198 + 0: CSS_IDENTIFIER@181..191 + 0: IDENT@181..191 "color" [Newline("\n"), Whitespace(" ")] [] + 1: COLON@191..193 ":" [] [Whitespace(" ")] + 2: SCSS_EXPRESSION@193..198 + 0: SCSS_EXPRESSION_ITEM_LIST@193..198 + 0: CSS_IDENTIFIER@193..198 + 0: IDENT@193..198 "green" [] [] + 1: (empty) + 1: SEMICOLON@198..199 ";" [] [] + 2: R_CURLY@199..203 "}" [Newline("\n"), Whitespace(" ")] [] + 5: SCSS_NESTING_DECLARATION@203..242 + 0: CSS_IDENTIFIER@203..208 + 0: IDENT@203..208 "a" [Newline("\n"), Newline("\n"), Whitespace(" ")] [] + 1: COLON@208..209 ":" [] [] + 2: SCSS_EXPRESSION@209..220 + 0: SCSS_EXPRESSION_ITEM_LIST@209..220 + 0: SCSS_INTERPOLATION@209..220 + 0: HASH@209..210 "#" [] [] + 1: L_CURLY@210..211 "{" [] [] + 2: SCSS_EXPRESSION@211..218 + 0: SCSS_EXPRESSION_ITEM_LIST@211..218 + 0: SCSS_IDENTIFIER@211..218 + 0: DOLLAR@211..212 "$" [] [] + 1: CSS_IDENTIFIER@212..218 + 0: IDENT@212..218 "pseudo" [] [] + 3: R_CURLY@218..220 "}" [] [Whitespace(" ")] + 3: CSS_DECLARATION_OR_RULE_BLOCK@220..242 + 0: L_CURLY@220..221 "{" [] [] + 1: CSS_DECLARATION_OR_RULE_LIST@221..238 + 0: CSS_DECLARATION_WITH_SEMICOLON@221..238 + 0: CSS_DECLARATION@221..237 + 0: CSS_GENERIC_PROPERTY@221..237 + 0: CSS_IDENTIFIER@221..231 + 0: IDENT@221..231 "color" [Newline("\n"), Whitespace(" ")] [] + 1: COLON@231..233 ":" [] [Whitespace(" ")] + 2: SCSS_EXPRESSION@233..237 + 0: SCSS_EXPRESSION_ITEM_LIST@233..237 + 0: CSS_IDENTIFIER@233..237 + 0: IDENT@233..237 "teal" [] [] + 1: (empty) + 1: SEMICOLON@237..238 ";" [] [] + 2: R_CURLY@238..242 "}" [Newline("\n"), Whitespace(" ")] [] + 6: CSS_NESTED_QUALIFIED_RULE@242..291 + 0: CSS_RELATIVE_SELECTOR_LIST@242..267 + 0: CSS_RELATIVE_SELECTOR@242..267 + 0: (empty) + 1: CSS_COMPOUND_SELECTOR@242..267 + 0: CSS_NESTED_SELECTOR_LIST@242..242 + 1: CSS_TYPE_SELECTOR@242..261 + 0: (empty) + 1: SCSS_INTERPOLATED_IDENTIFIER@242..261 + 0: SCSS_INTERPOLATED_IDENTIFIER_PART_LIST@242..261 + 0: CSS_IDENTIFIER@242..251 + 0: IDENT@242..251 "font-" [Newline("\n"), Newline("\n"), Whitespace(" ")] [] + 1: SCSS_INTERPOLATION@251..261 + 0: HASH@251..252 "#" [] [] + 1: L_CURLY@252..253 "{" [] [] + 2: SCSS_EXPRESSION@253..260 + 0: SCSS_EXPRESSION_ITEM_LIST@253..260 + 0: SCSS_IDENTIFIER@253..260 + 0: DOLLAR@253..254 "$" [] [] + 1: CSS_IDENTIFIER@254..260 + 0: IDENT@254..260 "weight" [] [] + 3: R_CURLY@260..261 "}" [] [] + 2: CSS_SUB_SELECTOR_LIST@261..267 + 0: CSS_PSEUDO_CLASS_SELECTOR@261..267 + 0: COLON@261..262 ":" [] [] + 1: CSS_PSEUDO_CLASS_IDENTIFIER@262..267 + 0: CSS_IDENTIFIER@262..267 + 0: IDENT@262..267 "bold" [] [Whitespace(" ")] + 1: CSS_DECLARATION_OR_RULE_BLOCK@267..291 + 0: L_CURLY@267..268 "{" [] [] + 1: CSS_DECLARATION_OR_RULE_LIST@268..287 + 0: CSS_DECLARATION_WITH_SEMICOLON@268..287 + 0: CSS_DECLARATION@268..286 + 0: CSS_GENERIC_PROPERTY@268..286 + 0: CSS_IDENTIFIER@268..278 + 0: IDENT@268..278 "color" [Newline("\n"), Whitespace(" ")] [] + 1: COLON@278..280 ":" [] [Whitespace(" ")] + 2: SCSS_EXPRESSION@280..286 + 0: SCSS_EXPRESSION_ITEM_LIST@280..286 + 0: CSS_IDENTIFIER@280..286 + 0: IDENT@280..286 "purple" [] [] + 1: (empty) + 1: SEMICOLON@286..287 ";" [] [] + 2: R_CURLY@287..291 "}" [Newline("\n"), Whitespace(" ")] [] + 7: CSS_NESTED_QUALIFIED_RULE@291..343 + 0: CSS_RELATIVE_SELECTOR_LIST@291..319 + 0: CSS_RELATIVE_SELECTOR@291..319 + 0: (empty) + 1: CSS_COMPOUND_SELECTOR@291..319 + 0: CSS_NESTED_SELECTOR_LIST@291..291 + 1: CSS_TYPE_SELECTOR@291..310 + 0: (empty) + 1: SCSS_INTERPOLATED_IDENTIFIER@291..310 + 0: SCSS_INTERPOLATED_IDENTIFIER_PART_LIST@291..310 + 0: CSS_IDENTIFIER@291..300 + 0: IDENT@291..300 "font-" [Newline("\n"), Newline("\n"), Whitespace(" ")] [] + 1: SCSS_INTERPOLATION@300..310 + 0: HASH@300..301 "#" [] [] + 1: L_CURLY@301..302 "{" [] [] + 2: SCSS_EXPRESSION@302..309 + 0: SCSS_EXPRESSION_ITEM_LIST@302..309 + 0: SCSS_IDENTIFIER@302..309 + 0: DOLLAR@302..303 "$" [] [] + 1: CSS_IDENTIFIER@303..309 + 0: IDENT@303..309 "weight" [] [] + 3: R_CURLY@309..310 "}" [] [] + 2: CSS_SUB_SELECTOR_LIST@310..319 + 0: CSS_PSEUDO_ELEMENT_SELECTOR@310..319 + 0: COLON2@310..312 "::" [] [] + 1: CSS_PSEUDO_ELEMENT_IDENTIFIER@312..319 + 0: CSS_IDENTIFIER@312..319 + 0: IDENT@312..319 "before" [] [Whitespace(" ")] + 1: CSS_DECLARATION_OR_RULE_BLOCK@319..343 + 0: L_CURLY@319..320 "{" [] [] + 1: CSS_DECLARATION_OR_RULE_LIST@320..339 + 0: CSS_DECLARATION_WITH_SEMICOLON@320..339 + 0: CSS_DECLARATION@320..338 + 0: CSS_GENERIC_PROPERTY@320..338 + 0: CSS_IDENTIFIER@320..330 + 0: IDENT@320..330 "color" [Newline("\n"), Whitespace(" ")] [] + 1: COLON@330..332 ":" [] [Whitespace(" ")] + 2: SCSS_EXPRESSION@332..338 + 0: SCSS_EXPRESSION_ITEM_LIST@332..338 + 0: CSS_IDENTIFIER@332..338 + 0: IDENT@332..338 "orange" [] [] + 1: (empty) + 1: SEMICOLON@338..339 ";" [] [] + 2: R_CURLY@339..343 "}" [Newline("\n"), Whitespace(" ")] [] + 2: R_CURLY@343..345 "}" [Newline("\n")] [] + 2: EOF@345..346 "" [Newline("\n")] [] ``` diff --git a/crates/biome_css_parser/tests/css_test_suite/ok/scss/declaration/interpolation.scss b/crates/biome_css_parser/tests/css_test_suite/ok/scss/declaration/interpolation.scss new file mode 100644 index 000000000000..c4c62d4b0433 --- /dev/null +++ b/crates/biome_css_parser/tests/css_test_suite/ok/scss/declaration/interpolation.scss @@ -0,0 +1,5 @@ +.box { + #{$name}: 1px; + margin-#{$side}: 1px; + --theme-#{$slot}: red; +} diff --git a/crates/biome_css_parser/tests/css_test_suite/ok/scss/declaration/interpolation.scss.snap b/crates/biome_css_parser/tests/css_test_suite/ok/scss/declaration/interpolation.scss.snap new file mode 100644 index 000000000000..74498e2253ac --- /dev/null +++ b/crates/biome_css_parser/tests/css_test_suite/ok/scss/declaration/interpolation.scss.snap @@ -0,0 +1,259 @@ +--- +source: crates/biome_css_parser/tests/spec_test.rs +assertion_line: 208 +expression: snapshot +--- + +## Input + +```css +.box { + #{$name}: 1px; + margin-#{$side}: 1px; + --theme-#{$slot}: 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..5 "box" [] [Whitespace(" ")], + }, + }, + ], + }, + ], + block: CssDeclarationOrRuleBlock { + l_curly_token: L_CURLY@5..6 "{" [] [], + items: CssDeclarationOrRuleList [ + CssDeclarationWithSemicolon { + declaration: CssDeclaration { + property: CssGenericProperty { + name: ScssInterpolatedIdentifier { + items: ScssInterpolatedIdentifierPartList [ + ScssInterpolation { + hash_token: HASH@6..10 "#" [Newline("\n"), Whitespace(" ")] [], + l_curly_token: L_CURLY@10..11 "{" [] [], + value: ScssExpression { + items: ScssExpressionItemList [ + ScssIdentifier { + dollar_token: DOLLAR@11..12 "$" [] [], + name: CssIdentifier { + value_token: IDENT@12..16 "name" [] [], + }, + }, + ], + }, + r_curly_token: R_CURLY@16..17 "}" [] [], + }, + ], + }, + colon_token: COLON@17..19 ":" [] [Whitespace(" ")], + value: ScssExpression { + items: ScssExpressionItemList [ + CssRegularDimension { + value_token: CSS_NUMBER_LITERAL@19..20 "1" [] [], + unit_token: IDENT@20..22 "px" [] [], + }, + ], + }, + }, + important: missing (optional), + }, + semicolon_token: SEMICOLON@22..23 ";" [] [], + }, + CssDeclarationWithSemicolon { + declaration: CssDeclaration { + property: CssGenericProperty { + name: ScssInterpolatedIdentifier { + items: ScssInterpolatedIdentifierPartList [ + CssIdentifier { + value_token: IDENT@23..33 "margin-" [Newline("\n"), Whitespace(" ")] [], + }, + ScssInterpolation { + hash_token: HASH@33..34 "#" [] [], + l_curly_token: L_CURLY@34..35 "{" [] [], + value: ScssExpression { + items: ScssExpressionItemList [ + ScssIdentifier { + dollar_token: DOLLAR@35..36 "$" [] [], + name: CssIdentifier { + value_token: IDENT@36..40 "side" [] [], + }, + }, + ], + }, + r_curly_token: R_CURLY@40..41 "}" [] [], + }, + ], + }, + colon_token: COLON@41..43 ":" [] [Whitespace(" ")], + value: ScssExpression { + items: ScssExpressionItemList [ + CssRegularDimension { + value_token: CSS_NUMBER_LITERAL@43..44 "1" [] [], + unit_token: IDENT@44..46 "px" [] [], + }, + ], + }, + }, + important: missing (optional), + }, + semicolon_token: SEMICOLON@46..47 ";" [] [], + }, + CssDeclarationWithSemicolon { + declaration: CssDeclaration { + property: CssGenericProperty { + name: ScssInterpolatedIdentifier { + items: ScssInterpolatedIdentifierPartList [ + CssIdentifier { + value_token: IDENT@47..58 "--theme-" [Newline("\n"), Whitespace(" ")] [], + }, + ScssInterpolation { + hash_token: HASH@58..59 "#" [] [], + l_curly_token: L_CURLY@59..60 "{" [] [], + value: ScssExpression { + items: ScssExpressionItemList [ + ScssIdentifier { + dollar_token: DOLLAR@60..61 "$" [] [], + name: CssIdentifier { + value_token: IDENT@61..65 "slot" [] [], + }, + }, + ], + }, + r_curly_token: R_CURLY@65..66 "}" [] [], + }, + ], + }, + colon_token: COLON@66..68 ":" [] [Whitespace(" ")], + value: ScssExpression { + items: ScssExpressionItemList [ + CssIdentifier { + value_token: IDENT@68..71 "red" [] [], + }, + ], + }, + }, + important: missing (optional), + }, + semicolon_token: SEMICOLON@71..72 ";" [] [], + }, + ], + r_curly_token: R_CURLY@72..74 "}" [Newline("\n")] [], + }, + }, + ], + eof_token: EOF@74..75 "" [Newline("\n")] [], +} +``` + +## CST + +``` +0: CSS_ROOT@0..75 + 0: (empty) + 1: CSS_ROOT_ITEM_LIST@0..74 + 0: CSS_QUALIFIED_RULE@0..74 + 0: CSS_SELECTOR_LIST@0..5 + 0: CSS_COMPOUND_SELECTOR@0..5 + 0: CSS_NESTED_SELECTOR_LIST@0..0 + 1: (empty) + 2: CSS_SUB_SELECTOR_LIST@0..5 + 0: CSS_CLASS_SELECTOR@0..5 + 0: DOT@0..1 "." [] [] + 1: CSS_CUSTOM_IDENTIFIER@1..5 + 0: IDENT@1..5 "box" [] [Whitespace(" ")] + 1: CSS_DECLARATION_OR_RULE_BLOCK@5..74 + 0: L_CURLY@5..6 "{" [] [] + 1: CSS_DECLARATION_OR_RULE_LIST@6..72 + 0: CSS_DECLARATION_WITH_SEMICOLON@6..23 + 0: CSS_DECLARATION@6..22 + 0: CSS_GENERIC_PROPERTY@6..22 + 0: SCSS_INTERPOLATED_IDENTIFIER@6..17 + 0: SCSS_INTERPOLATED_IDENTIFIER_PART_LIST@6..17 + 0: SCSS_INTERPOLATION@6..17 + 0: HASH@6..10 "#" [Newline("\n"), Whitespace(" ")] [] + 1: L_CURLY@10..11 "{" [] [] + 2: SCSS_EXPRESSION@11..16 + 0: SCSS_EXPRESSION_ITEM_LIST@11..16 + 0: SCSS_IDENTIFIER@11..16 + 0: DOLLAR@11..12 "$" [] [] + 1: CSS_IDENTIFIER@12..16 + 0: IDENT@12..16 "name" [] [] + 3: R_CURLY@16..17 "}" [] [] + 1: COLON@17..19 ":" [] [Whitespace(" ")] + 2: SCSS_EXPRESSION@19..22 + 0: SCSS_EXPRESSION_ITEM_LIST@19..22 + 0: CSS_REGULAR_DIMENSION@19..22 + 0: CSS_NUMBER_LITERAL@19..20 "1" [] [] + 1: IDENT@20..22 "px" [] [] + 1: (empty) + 1: SEMICOLON@22..23 ";" [] [] + 1: CSS_DECLARATION_WITH_SEMICOLON@23..47 + 0: CSS_DECLARATION@23..46 + 0: CSS_GENERIC_PROPERTY@23..46 + 0: SCSS_INTERPOLATED_IDENTIFIER@23..41 + 0: SCSS_INTERPOLATED_IDENTIFIER_PART_LIST@23..41 + 0: CSS_IDENTIFIER@23..33 + 0: IDENT@23..33 "margin-" [Newline("\n"), Whitespace(" ")] [] + 1: SCSS_INTERPOLATION@33..41 + 0: HASH@33..34 "#" [] [] + 1: L_CURLY@34..35 "{" [] [] + 2: SCSS_EXPRESSION@35..40 + 0: SCSS_EXPRESSION_ITEM_LIST@35..40 + 0: SCSS_IDENTIFIER@35..40 + 0: DOLLAR@35..36 "$" [] [] + 1: CSS_IDENTIFIER@36..40 + 0: IDENT@36..40 "side" [] [] + 3: R_CURLY@40..41 "}" [] [] + 1: COLON@41..43 ":" [] [Whitespace(" ")] + 2: SCSS_EXPRESSION@43..46 + 0: SCSS_EXPRESSION_ITEM_LIST@43..46 + 0: CSS_REGULAR_DIMENSION@43..46 + 0: CSS_NUMBER_LITERAL@43..44 "1" [] [] + 1: IDENT@44..46 "px" [] [] + 1: (empty) + 1: SEMICOLON@46..47 ";" [] [] + 2: CSS_DECLARATION_WITH_SEMICOLON@47..72 + 0: CSS_DECLARATION@47..71 + 0: CSS_GENERIC_PROPERTY@47..71 + 0: SCSS_INTERPOLATED_IDENTIFIER@47..66 + 0: SCSS_INTERPOLATED_IDENTIFIER_PART_LIST@47..66 + 0: CSS_IDENTIFIER@47..58 + 0: IDENT@47..58 "--theme-" [Newline("\n"), Whitespace(" ")] [] + 1: SCSS_INTERPOLATION@58..66 + 0: HASH@58..59 "#" [] [] + 1: L_CURLY@59..60 "{" [] [] + 2: SCSS_EXPRESSION@60..65 + 0: SCSS_EXPRESSION_ITEM_LIST@60..65 + 0: SCSS_IDENTIFIER@60..65 + 0: DOLLAR@60..61 "$" [] [] + 1: CSS_IDENTIFIER@61..65 + 0: IDENT@61..65 "slot" [] [] + 3: R_CURLY@65..66 "}" [] [] + 1: COLON@66..68 ":" [] [Whitespace(" ")] + 2: SCSS_EXPRESSION@68..71 + 0: SCSS_EXPRESSION_ITEM_LIST@68..71 + 0: CSS_IDENTIFIER@68..71 + 0: IDENT@68..71 "red" [] [] + 1: (empty) + 1: SEMICOLON@71..72 ";" [] [] + 2: R_CURLY@72..74 "}" [Newline("\n")] [] + 2: EOF@74..75 "" [Newline("\n")] [] + +``` diff --git a/crates/biome_css_parser/tests/css_test_suite/ok/tailwind/supports.css b/crates/biome_css_parser/tests/css_test_suite/ok/tailwind/supports.css new file mode 100644 index 000000000000..debcbe67dcc0 --- /dev/null +++ b/crates/biome_css_parser/tests/css_test_suite/ok/tailwind/supports.css @@ -0,0 +1,7 @@ +@import "tailwindcss/theme.css" supports(--color-*: initial); + +@supports (--color-*: initial) { + .box { + color: red; + } +} diff --git a/crates/biome_css_parser/tests/css_test_suite/ok/tailwind/supports.css.snap b/crates/biome_css_parser/tests/css_test_suite/ok/tailwind/supports.css.snap new file mode 100644 index 000000000000..0e1e5d409e2f --- /dev/null +++ b/crates/biome_css_parser/tests/css_test_suite/ok/tailwind/supports.css.snap @@ -0,0 +1,223 @@ +--- +source: crates/biome_css_parser/tests/spec_test.rs +expression: snapshot +--- + +## Input + +```css +@import "tailwindcss/theme.css" supports(--color-*: initial); + +@supports (--color-*: initial) { + .box { + color: red; + } +} + +``` + + +## AST + +``` +CssRoot { + bom_token: missing (optional), + items: CssRootItemList [ + CssAtRule { + at_token: AT@0..1 "@" [] [], + rule: CssImportAtRule { + import_token: IMPORT_KW@1..8 "import" [] [Whitespace(" ")], + url: CssString { + value_token: CSS_STRING_LITERAL@8..32 "\"tailwindcss/theme.css\"" [] [Whitespace(" ")], + }, + layer: missing (optional), + supports: CssImportSupports { + supports_token: SUPPORTS_KW@32..40 "supports" [] [], + l_paren_token: L_PAREN@40..41 "(" [] [], + condition: CssDeclaration { + property: CssGenericProperty { + name: TwValueThemeReference { + reference: CssDashedIdentifier { + value_token: IDENT@41..48 "--color" [] [], + }, + minus_token: MINUS@48..49 "-" [] [], + star_token: STAR@49..50 "*" [] [], + }, + colon_token: COLON@50..52 ":" [] [Whitespace(" ")], + value: CssGenericComponentValueList [ + CssIdentifier { + value_token: IDENT@52..59 "initial" [] [], + }, + ], + }, + important: missing (optional), + }, + r_paren_token: R_PAREN@59..60 ")" [] [], + }, + media: CssMediaQueryList [], + semicolon_token: SEMICOLON@60..61 ";" [] [], + }, + }, + CssAtRule { + at_token: AT@61..64 "@" [Newline("\n"), Newline("\n")] [], + rule: CssSupportsAtRule { + declarator: CssSupportsAtRuleDeclarator { + supports_token: SUPPORTS_KW@64..73 "supports" [] [Whitespace(" ")], + condition: CssSupportsFeatureDeclaration { + l_paren_token: L_PAREN@73..74 "(" [] [], + declaration: CssDeclaration { + property: CssGenericProperty { + name: TwValueThemeReference { + reference: CssDashedIdentifier { + value_token: IDENT@74..81 "--color" [] [], + }, + minus_token: MINUS@81..82 "-" [] [], + star_token: STAR@82..83 "*" [] [], + }, + colon_token: COLON@83..85 ":" [] [Whitespace(" ")], + value: CssGenericComponentValueList [ + CssIdentifier { + value_token: IDENT@85..92 "initial" [] [], + }, + ], + }, + important: missing (optional), + }, + r_paren_token: R_PAREN@92..94 ")" [] [Whitespace(" ")], + }, + }, + block: CssRuleBlock { + l_curly_token: L_CURLY@94..95 "{" [] [], + rules: CssRuleList [ + CssQualifiedRule { + prelude: CssSelectorList [ + CssCompoundSelector { + nesting_selectors: CssNestedSelectorList [], + simple_selector: missing (optional), + sub_selectors: CssSubSelectorList [ + CssClassSelector { + dot_token: DOT@95..99 "." [Newline("\n"), Whitespace(" ")] [], + name: CssCustomIdentifier { + value_token: IDENT@99..103 "box" [] [Whitespace(" ")], + }, + }, + ], + }, + ], + block: CssDeclarationOrRuleBlock { + l_curly_token: L_CURLY@103..104 "{" [] [], + items: CssDeclarationOrRuleList [ + CssDeclarationWithSemicolon { + declaration: CssDeclaration { + property: CssGenericProperty { + name: CssIdentifier { + value_token: IDENT@104..114 "color" [Newline("\n"), Whitespace(" ")] [], + }, + colon_token: COLON@114..116 ":" [] [Whitespace(" ")], + value: CssGenericComponentValueList [ + CssIdentifier { + value_token: IDENT@116..119 "red" [] [], + }, + ], + }, + important: missing (optional), + }, + semicolon_token: SEMICOLON@119..120 ";" [] [], + }, + ], + r_curly_token: R_CURLY@120..124 "}" [Newline("\n"), Whitespace(" ")] [], + }, + }, + ], + r_curly_token: R_CURLY@124..126 "}" [Newline("\n")] [], + }, + }, + }, + ], + eof_token: EOF@126..127 "" [Newline("\n")] [], +} +``` + +## CST + +``` +0: CSS_ROOT@0..127 + 0: (empty) + 1: CSS_ROOT_ITEM_LIST@0..126 + 0: CSS_AT_RULE@0..61 + 0: AT@0..1 "@" [] [] + 1: CSS_IMPORT_AT_RULE@1..61 + 0: IMPORT_KW@1..8 "import" [] [Whitespace(" ")] + 1: CSS_STRING@8..32 + 0: CSS_STRING_LITERAL@8..32 "\"tailwindcss/theme.css\"" [] [Whitespace(" ")] + 2: (empty) + 3: CSS_IMPORT_SUPPORTS@32..60 + 0: SUPPORTS_KW@32..40 "supports" [] [] + 1: L_PAREN@40..41 "(" [] [] + 2: CSS_DECLARATION@41..59 + 0: CSS_GENERIC_PROPERTY@41..59 + 0: TW_VALUE_THEME_REFERENCE@41..50 + 0: CSS_DASHED_IDENTIFIER@41..48 + 0: IDENT@41..48 "--color" [] [] + 1: MINUS@48..49 "-" [] [] + 2: STAR@49..50 "*" [] [] + 1: COLON@50..52 ":" [] [Whitespace(" ")] + 2: CSS_GENERIC_COMPONENT_VALUE_LIST@52..59 + 0: CSS_IDENTIFIER@52..59 + 0: IDENT@52..59 "initial" [] [] + 1: (empty) + 3: R_PAREN@59..60 ")" [] [] + 4: CSS_MEDIA_QUERY_LIST@60..60 + 5: SEMICOLON@60..61 ";" [] [] + 1: CSS_AT_RULE@61..126 + 0: AT@61..64 "@" [Newline("\n"), Newline("\n")] [] + 1: CSS_SUPPORTS_AT_RULE@64..126 + 0: CSS_SUPPORTS_AT_RULE_DECLARATOR@64..94 + 0: SUPPORTS_KW@64..73 "supports" [] [Whitespace(" ")] + 1: CSS_SUPPORTS_FEATURE_DECLARATION@73..94 + 0: L_PAREN@73..74 "(" [] [] + 1: CSS_DECLARATION@74..92 + 0: CSS_GENERIC_PROPERTY@74..92 + 0: TW_VALUE_THEME_REFERENCE@74..83 + 0: CSS_DASHED_IDENTIFIER@74..81 + 0: IDENT@74..81 "--color" [] [] + 1: MINUS@81..82 "-" [] [] + 2: STAR@82..83 "*" [] [] + 1: COLON@83..85 ":" [] [Whitespace(" ")] + 2: CSS_GENERIC_COMPONENT_VALUE_LIST@85..92 + 0: CSS_IDENTIFIER@85..92 + 0: IDENT@85..92 "initial" [] [] + 1: (empty) + 2: R_PAREN@92..94 ")" [] [Whitespace(" ")] + 1: CSS_RULE_BLOCK@94..126 + 0: L_CURLY@94..95 "{" [] [] + 1: CSS_RULE_LIST@95..124 + 0: CSS_QUALIFIED_RULE@95..124 + 0: CSS_SELECTOR_LIST@95..103 + 0: CSS_COMPOUND_SELECTOR@95..103 + 0: CSS_NESTED_SELECTOR_LIST@95..95 + 1: (empty) + 2: CSS_SUB_SELECTOR_LIST@95..103 + 0: CSS_CLASS_SELECTOR@95..103 + 0: DOT@95..99 "." [Newline("\n"), Whitespace(" ")] [] + 1: CSS_CUSTOM_IDENTIFIER@99..103 + 0: IDENT@99..103 "box" [] [Whitespace(" ")] + 1: CSS_DECLARATION_OR_RULE_BLOCK@103..124 + 0: L_CURLY@103..104 "{" [] [] + 1: CSS_DECLARATION_OR_RULE_LIST@104..120 + 0: CSS_DECLARATION_WITH_SEMICOLON@104..120 + 0: CSS_DECLARATION@104..119 + 0: CSS_GENERIC_PROPERTY@104..119 + 0: CSS_IDENTIFIER@104..114 + 0: IDENT@104..114 "color" [Newline("\n"), Whitespace(" ")] [] + 1: COLON@114..116 ":" [] [Whitespace(" ")] + 2: CSS_GENERIC_COMPONENT_VALUE_LIST@116..119 + 0: CSS_IDENTIFIER@116..119 + 0: IDENT@116..119 "red" [] [] + 1: (empty) + 1: SEMICOLON@119..120 ";" [] [] + 2: R_CURLY@120..124 "}" [Newline("\n"), Whitespace(" ")] [] + 2: R_CURLY@124..126 "}" [Newline("\n")] [] + 2: EOF@126..127 "" [Newline("\n")] [] + +``` diff --git a/crates/biome_css_semantic/src/events.rs b/crates/biome_css_semantic/src/events.rs index 4191e81c598f..0165c761afa0 100644 --- a/crates/biome_css_semantic/src/events.rs +++ b/crates/biome_css_semantic/src/events.rs @@ -146,6 +146,9 @@ impl SemanticEventExtractor { }; CssProperty::from(ident) } + AnyCssDeclarationName::ScssInterpolatedIdentifier(_) => { + return; + } }; self.stash.push_back(SemanticEvent::PropertyDeclaration { diff --git a/crates/biome_css_syntax/src/generated/nodes.rs b/crates/biome_css_syntax/src/generated/nodes.rs index 021bb73ad2bd..548b346bf7a6 100644 --- a/crates/biome_css_syntax/src/generated/nodes.rs +++ b/crates/biome_css_syntax/src/generated/nodes.rs @@ -13126,6 +13126,7 @@ impl AnyCssDeclarationBlock { pub enum AnyCssDeclarationName { CssDashedIdentifier(CssDashedIdentifier), CssIdentifier(CssIdentifier), + ScssInterpolatedIdentifier(ScssInterpolatedIdentifier), TwValueThemeReference(TwValueThemeReference), } impl AnyCssDeclarationName { @@ -13141,6 +13142,12 @@ impl AnyCssDeclarationName { _ => None, } } + pub fn as_scss_interpolated_identifier(&self) -> Option<&ScssInterpolatedIdentifier> { + match &self { + Self::ScssInterpolatedIdentifier(item) => Some(item), + _ => None, + } + } pub fn as_tw_value_theme_reference(&self) -> Option<&TwValueThemeReference> { match &self { Self::TwValueThemeReference(item) => Some(item), @@ -32947,6 +32954,11 @@ impl From for AnyCssDeclarationName { Self::CssIdentifier(node) } } +impl From for AnyCssDeclarationName { + fn from(node: ScssInterpolatedIdentifier) -> Self { + Self::ScssInterpolatedIdentifier(node) + } +} impl From for AnyCssDeclarationName { fn from(node: TwValueThemeReference) -> Self { Self::TwValueThemeReference(node) @@ -32956,17 +32968,24 @@ impl AstNode for AnyCssDeclarationName { type Language = Language; const KIND_SET: SyntaxKindSet = CssDashedIdentifier::KIND_SET .union(CssIdentifier::KIND_SET) + .union(ScssInterpolatedIdentifier::KIND_SET) .union(TwValueThemeReference::KIND_SET); fn can_cast(kind: SyntaxKind) -> bool { matches!( kind, - CSS_DASHED_IDENTIFIER | CSS_IDENTIFIER | TW_VALUE_THEME_REFERENCE + CSS_DASHED_IDENTIFIER + | CSS_IDENTIFIER + | SCSS_INTERPOLATED_IDENTIFIER + | TW_VALUE_THEME_REFERENCE ) } fn cast(syntax: SyntaxNode) -> Option { let res = match syntax.kind() { CSS_DASHED_IDENTIFIER => Self::CssDashedIdentifier(CssDashedIdentifier { syntax }), CSS_IDENTIFIER => Self::CssIdentifier(CssIdentifier { syntax }), + SCSS_INTERPOLATED_IDENTIFIER => { + Self::ScssInterpolatedIdentifier(ScssInterpolatedIdentifier { syntax }) + } TW_VALUE_THEME_REFERENCE => { Self::TwValueThemeReference(TwValueThemeReference { syntax }) } @@ -32978,6 +32997,7 @@ impl AstNode for AnyCssDeclarationName { match self { Self::CssDashedIdentifier(it) => it.syntax(), Self::CssIdentifier(it) => it.syntax(), + Self::ScssInterpolatedIdentifier(it) => it.syntax(), Self::TwValueThemeReference(it) => it.syntax(), } } @@ -32985,6 +33005,7 @@ impl AstNode for AnyCssDeclarationName { match self { Self::CssDashedIdentifier(it) => it.into_syntax(), Self::CssIdentifier(it) => it.into_syntax(), + Self::ScssInterpolatedIdentifier(it) => it.into_syntax(), Self::TwValueThemeReference(it) => it.into_syntax(), } } @@ -32994,6 +33015,7 @@ impl std::fmt::Debug for AnyCssDeclarationName { match self { Self::CssDashedIdentifier(it) => std::fmt::Debug::fmt(it, f), Self::CssIdentifier(it) => std::fmt::Debug::fmt(it, f), + Self::ScssInterpolatedIdentifier(it) => std::fmt::Debug::fmt(it, f), Self::TwValueThemeReference(it) => std::fmt::Debug::fmt(it, f), } } @@ -33003,6 +33025,7 @@ impl From for SyntaxNode { match n { AnyCssDeclarationName::CssDashedIdentifier(it) => it.into_syntax(), AnyCssDeclarationName::CssIdentifier(it) => it.into_syntax(), + AnyCssDeclarationName::ScssInterpolatedIdentifier(it) => it.into_syntax(), AnyCssDeclarationName::TwValueThemeReference(it) => it.into_syntax(), } } diff --git a/crates/biome_parser/src/lexer.rs b/crates/biome_parser/src/lexer.rs index c6c37df10b07..6b3ca407f645 100644 --- a/crates/biome_parser/src/lexer.rs +++ b/crates/biome_parser/src/lexer.rs @@ -44,6 +44,11 @@ pub trait Lexer<'src> { /// Returns `true` if the current kind is preceded by a line break. fn has_preceding_line_break(&self) -> bool; + /// Returns `true` if the current kind is preceded by whitespace trivia. + fn has_preceding_whitespace(&self) -> bool { + false + } + /// Returns if the current kind is an identifier that includes a unicode escape sequence (`\u...`). fn has_unicode_escape(&self) -> bool; @@ -565,6 +570,16 @@ impl<'l, Lex: Lexer<'l>> BufferedLexer { } } + /// Tests if there's whitespace before the current token. + #[inline(always)] + pub fn has_preceding_whitespace(&self) -> bool { + if let Some(current) = &self.current { + current.has_preceding_whitespace() + } else { + self.inner.has_preceding_whitespace() + } + } + /// Returns true if the current token is an identifier, and it contains any unicode escape sequences #[inline] pub fn has_unicode_escape(&self) -> bool { @@ -823,6 +838,10 @@ impl LexerCheckpoint { self.current_flags.has_preceding_line_break() } + pub(crate) fn has_preceding_whitespace(&self) -> bool { + self.current_flags.has_preceding_whitespace() + } + pub(crate) fn has_unicode_escape(&self) -> bool { self.current_flags.has_unicode_escape() } diff --git a/crates/biome_parser/src/lib.rs b/crates/biome_parser/src/lib.rs index 6ab1a00bdf98..cb9abeca01ae 100644 --- a/crates/biome_parser/src/lib.rs +++ b/crates/biome_parser/src/lib.rs @@ -194,6 +194,11 @@ pub trait Parser: Sized { self.source().has_preceding_line_break() } + /// Tests if there's whitespace trivia before the current token. + fn has_preceding_whitespace(&self) -> bool { + self.source().has_preceding_whitespace() + } + /// Get the source code of the parser's current token. fn cur_text(&self) -> &str { &self.source().text()[self.cur_range()] diff --git a/crates/biome_parser/src/token_source.rs b/crates/biome_parser/src/token_source.rs index 99fe8ee69a9d..63ac2a0d0b26 100644 --- a/crates/biome_parser/src/token_source.rs +++ b/crates/biome_parser/src/token_source.rs @@ -74,6 +74,11 @@ pub trait TokenSource { /// Returns true if the current token is preceded by a line break fn has_preceding_line_break(&self) -> bool; + /// Returns true if the current token is preceded by whitespace trivia. + fn has_preceding_whitespace(&self) -> bool { + false + } + fn bump(&mut self); fn skip_as_trivia(&mut self); diff --git a/xtask/codegen/css.ungram b/xtask/codegen/css.ungram index 5b04fe7ed31a..660a3e30ed24 100644 --- a/xtask/codegen/css.ungram +++ b/xtask/codegen/css.ungram @@ -653,7 +653,7 @@ CssGenericDelimiter = CssComponentValueList = AnyCssValue* -AnyCssDeclarationName = CssIdentifier | CssDashedIdentifier | TwValueThemeReference +AnyCssDeclarationName = CssIdentifier | CssDashedIdentifier | ScssInterpolatedIdentifier | TwValueThemeReference CssDeclarationImportant = '!'