Skip to main content

omena_parser/
parse.rs

1//! Recursive-descent parser entry points and result types.
2//!
3//! This module owns the concrete parser loop while exporting stable parse,
4//! lex, and fact-collection functions for the crate's public API.
5
6use cstree::{
7    build::{GreenNodeBuilder, NodeCache},
8    green::GreenNode,
9    interning::{Resolver, TokenInterner, TokenKey},
10    syntax::SyntaxNode,
11    text::{TextRange, TextSize},
12    util::NodeOrToken,
13};
14use omena_syntax::{StyleDialect, SyntaxKind, css_keyword};
15use std::collections::HashMap;
16use std::sync::{Arc, OnceLock};
17
18use crate::extension::{AtRuleBlockKind, AtRuleSpec, at_rule_spec, scss_at_rule_spec};
19use crate::facts::{
20    collect_style_fact_collection_with_extension, collect_style_facts_with_extension,
21};
22use crate::{
23    BuiltinDialectExtension, DialectExtension, LexResult, LexedToken, ParsedCst,
24    ParsedStyleFactCollectionV0, ParsedStyleFacts, Token, Tokenizer,
25    UNARY_PREFIX_RIGHT_BINDING_POWER, at_rule_prelude_head_is_custom_ident,
26    at_rule_prelude_head_is_custom_property_name, attribute_name_token_can_continue,
27    attribute_name_token_can_start, attribute_value_token_can_start, bracketed_value_recovery,
28    comma_separated_component_value_list_item_recovery, css_module_block_scope_marker_in_header,
29    css_module_header_is_global_only, css_module_scope_function_kind,
30    dialect_allows_value_logical_operators, function_argument_count_is_valid,
31    function_argument_recovery, function_requires_filled_top_level_arguments,
32    interpolation_end_kind, is_at_rule_prelude_boundary, is_attribute_matcher, is_combinator,
33    is_component_value_atom_start, is_css_module_from_source_token,
34    is_dynamic_function_argument_head, is_interpolation_start, is_nth_pseudo_class,
35    is_scss_control_rule_kind, is_scss_module_namespace_token, is_scss_module_source_token,
36    is_scss_module_visibility_name_token, is_selector_boundary, is_selector_boundary_until,
37    is_selector_list_pseudo_class, is_statement_end, keyframe_selector_token_is_valid,
38    language_tag_token_can_start, matches_ignore_ascii_case, matching_simple_block_close,
39    namespace_selector_target_can_start, public_token_text, selector_component_can_start,
40    selector_item_token_is_recoverable, simple_block_recovery, specialized_function_kind,
41    value_infix_operator_binding, value_list_item_recovery, variable_declaration_node_kind,
42};
43
44#[derive(Debug)]
45pub struct ParseResult {
46    green: GreenNode,
47    resolver: Option<ParseTokenResolver>,
48    errors: Vec<ParseError>,
49    token_count: usize,
50    dialect: StyleDialect,
51    syntax_root: OnceLock<SyntaxNode<SyntaxKind>>,
52    syntax_tokens: OnceLock<Vec<SyntaxTokenView>>,
53}
54
55#[derive(Debug, Clone, Copy, PartialEq, Eq)]
56pub(crate) struct SyntaxTokenView {
57    pub(crate) kind: SyntaxKind,
58    pub(crate) range: TextRange,
59}
60
61impl ParseResult {
62    pub(crate) fn new(
63        green: GreenNode,
64        interner: Option<Arc<TokenInterner>>,
65        errors: Vec<ParseError>,
66        token_count: usize,
67        dialect: StyleDialect,
68    ) -> Self {
69        Self::new_with_resolver(
70            green,
71            interner.map(ParseTokenResolver::Cstree),
72            errors,
73            token_count,
74            dialect,
75        )
76    }
77
78    fn new_with_resolver(
79        green: GreenNode,
80        resolver: Option<ParseTokenResolver>,
81        errors: Vec<ParseError>,
82        token_count: usize,
83        dialect: StyleDialect,
84    ) -> Self {
85        Self {
86            green,
87            resolver,
88            errors,
89            token_count,
90            dialect,
91            syntax_root: OnceLock::new(),
92            syntax_tokens: OnceLock::new(),
93        }
94    }
95
96    fn materialize_syntax_root(&self) -> SyntaxNode<SyntaxKind> {
97        crate::record_omena_parser_syntax_root_materialization();
98        if let Some(resolver) = &self.resolver {
99            return SyntaxNode::new_root_with_resolver(self.green.clone(), resolver.clone())
100                .syntax()
101                .clone();
102        }
103        SyntaxNode::new_root(self.green.clone())
104    }
105}
106
107impl Clone for ParseResult {
108    fn clone(&self) -> Self {
109        let syntax_root = OnceLock::new();
110        if let Some(root) = self.syntax_root.get() {
111            let _ = syntax_root.set(root.clone());
112        }
113        let syntax_tokens = OnceLock::new();
114        if let Some(tokens) = self.syntax_tokens.get() {
115            let _ = syntax_tokens.set(tokens.clone());
116        }
117        Self {
118            green: self.green.clone(),
119            resolver: self.resolver.clone(),
120            errors: self.errors.clone(),
121            token_count: self.token_count,
122            dialect: self.dialect,
123            syntax_root,
124            syntax_tokens,
125        }
126    }
127}
128
129#[derive(Debug, Clone)]
130enum ParseTokenResolver {
131    Cstree(Arc<TokenInterner>),
132    Snapshot(Arc<TokenTextSnapshotResolver>),
133}
134
135impl Resolver<TokenKey> for ParseTokenResolver {
136    fn try_resolve(&self, key: TokenKey) -> Option<&str> {
137        match self {
138            Self::Cstree(interner) => interner.try_resolve(key),
139            Self::Snapshot(snapshot) => snapshot.try_resolve(key),
140        }
141    }
142}
143
144#[derive(Debug)]
145struct TokenTextSnapshotResolver {
146    texts: HashMap<TokenKey, String>,
147}
148
149impl Resolver<TokenKey> for TokenTextSnapshotResolver {
150    fn try_resolve(&self, key: TokenKey) -> Option<&str> {
151        self.texts.get(&key).map(String::as_str)
152    }
153}
154
155impl PartialEq for ParseResult {
156    fn eq(&self, other: &Self) -> bool {
157        self.green == other.green
158            && self.errors == other.errors
159            && self.token_count == other.token_count
160            && self.dialect == other.dialect
161    }
162}
163
164impl Eq for ParseResult {}
165
166impl ParseResult {
167    pub fn green(&self) -> &GreenNode {
168        &self.green
169    }
170
171    pub fn syntax(&self) -> SyntaxNode<SyntaxKind> {
172        self.syntax_root
173            .get_or_init(|| self.materialize_syntax_root())
174            .clone()
175    }
176
177    pub fn source_text(&self) -> Option<String> {
178        let syntax = self.syntax();
179        syntax
180            .try_resolved()
181            .map(|resolved| resolved.text().to_string())
182    }
183
184    pub fn errors(&self) -> &[ParseError] {
185        &self.errors
186    }
187
188    pub fn token_count(&self) -> usize {
189        self.token_count
190    }
191
192    pub fn dialect(&self) -> StyleDialect {
193        self.dialect
194    }
195
196    pub fn cst(&self) -> ParsedCst {
197        ParsedCst::new(self.syntax())
198    }
199
200    pub(crate) fn syntax_token_views(&self) -> &[SyntaxTokenView] {
201        self.syntax_tokens
202            .get_or_init(|| green_syntax_token_views(&self.green, self.token_count))
203            .as_slice()
204    }
205}
206
207fn green_syntax_token_views(green: &GreenNode, token_count: usize) -> Vec<SyntaxTokenView> {
208    let mut views = Vec::with_capacity(token_count);
209    collect_green_syntax_token_views(green, TextSize::from(0), &mut views);
210    views
211}
212
213fn collect_green_syntax_token_views(
214    node: &GreenNode,
215    start: TextSize,
216    views: &mut Vec<SyntaxTokenView>,
217) {
218    let mut offset = start;
219    for child in node.children() {
220        match child {
221            NodeOrToken::Node(child_node) => {
222                collect_green_syntax_token_views(child_node, offset, views);
223                offset += child_node.text_len();
224            }
225            NodeOrToken::Token(token) => {
226                views.push(SyntaxTokenView {
227                    kind: SyntaxKind::from_raw_kind(token.kind().0).unwrap_or(SyntaxKind::Unknown),
228                    range: TextRange::at(offset, token.text_len()),
229                });
230                offset += token.text_len();
231            }
232        }
233    }
234}
235
236fn snapshot_token_text_resolver_from_green(
237    green: &GreenNode,
238    tokens: &[Token<'_>],
239) -> ParseTokenResolver {
240    let mut texts = HashMap::new();
241    let mut token_index = 0usize;
242    collect_green_token_text_keys(green, tokens, &mut token_index, &mut texts);
243    debug_assert_eq!(token_index, tokens.len());
244    ParseTokenResolver::Snapshot(Arc::new(TokenTextSnapshotResolver { texts }))
245}
246
247fn collect_green_token_text_keys(
248    node: &GreenNode,
249    tokens: &[Token<'_>],
250    token_index: &mut usize,
251    texts: &mut HashMap<TokenKey, String>,
252) {
253    for child in node.children() {
254        match child {
255            NodeOrToken::Node(child_node) => {
256                collect_green_token_text_keys(child_node, tokens, token_index, texts);
257            }
258            NodeOrToken::Token(token) => {
259                if let Some(source_token) = tokens.get(*token_index) {
260                    if let Some(key) = token.text_key() {
261                        let previous = texts.insert(key, source_token.text.to_string());
262                        if let Some(previous) = previous {
263                            debug_assert_eq!(previous, source_token.text);
264                        }
265                    }
266                } else {
267                    debug_assert!(false, "green token count exceeded token stream");
268                }
269                *token_index += 1;
270            }
271        }
272    }
273}
274
275#[derive(Debug, Clone, PartialEq, Eq)]
276pub struct ParseError {
277    pub code: ParseErrorCode,
278    pub range: TextRange,
279    pub message: &'static str,
280}
281
282#[derive(Debug, Clone, Copy, PartialEq, Eq)]
283pub enum ParseErrorCode {
284    UnterminatedBlockComment,
285    UnterminatedString,
286    UnexpectedCharacter,
287    ExpectedSelectorName,
288    UnterminatedAttributeSelector,
289    ExpectedValue,
290}
291
292#[derive(Debug, Clone, Copy, PartialEq, Eq)]
293pub enum ParseEntryPoint {
294    Stylesheet,
295    RuleList,
296    Rule,
297    DeclarationList,
298    Declaration,
299    Value,
300    ComponentValue,
301    ComponentValueList,
302    CommaSeparatedComponentValueList,
303    SimpleBlock,
304}
305
306#[derive(Debug, Default)]
307pub struct ParseReuseCache {
308    node_cache: NodeCache<'static>,
309}
310
311#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
312pub struct SyntaxNodeId {
313    value: String,
314}
315
316impl SyntaxNodeId {
317    pub fn as_str(&self) -> &str {
318        self.value.as_str()
319    }
320}
321
322#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
323pub struct HirId {
324    value: String,
325}
326
327impl HirId {
328    pub fn as_str(&self) -> &str {
329        self.value.as_str()
330    }
331}
332
333pub fn syntax_node_id(node: &SyntaxNode<SyntaxKind>) -> SyntaxNodeId {
334    let path = syntax_node_child_path(node)
335        .into_iter()
336        .map(|index| index.to_string())
337        .collect::<Vec<_>>()
338        .join(".");
339    let text = node
340        .try_resolved()
341        .map(|resolved| resolved.text().to_string())
342        .unwrap_or_default();
343    let text_hash = stable_parser_identity_hash(text.as_bytes());
344    SyntaxNodeId {
345        value: format!(
346            "syntax:v0:kind={}:path={}:len={}:text={text_hash:016x}",
347            node.kind().as_u32(),
348            path,
349            u32::from(node.text_range().len())
350        ),
351    }
352}
353
354pub fn hir_id_for_syntax_node(node: &SyntaxNode<SyntaxKind>) -> HirId {
355    let syntax_id = syntax_node_id(node);
356    HirId {
357        value: format!("hir:v0:{}", syntax_id.as_str()),
358    }
359}
360
361pub fn parse(text: &str, dialect: StyleDialect) -> ParseResult {
362    parse_entry_point(text, dialect, ParseEntryPoint::Stylesheet)
363}
364
365/// Parses a stylesheet without collecting parser facts.
366pub fn parse_only(text: &str, dialect: StyleDialect) -> ParseResult {
367    parse(text, dialect)
368}
369
370pub fn parse_entry_point(
371    text: &str,
372    dialect: StyleDialect,
373    entry_point: ParseEntryPoint,
374) -> ParseResult {
375    let extension = BuiltinDialectExtension::new(dialect);
376    parse_entry_point_with_extension(text, &extension, entry_point)
377}
378
379pub fn lex(text: &str, dialect: StyleDialect) -> LexResult {
380    let extension = BuiltinDialectExtension::new(dialect);
381    lex_with_extension(text, &extension)
382}
383
384pub fn lex_with_extension(text: &str, extension: &impl DialectExtension) -> LexResult {
385    let (tokens, errors) = tokenize(text, extension);
386    let token_count = tokens.len();
387    crate::record_omena_parser_lex_materialization(token_count);
388    LexResult::new(
389        tokens
390            .into_iter()
391            .map(|token| LexedToken {
392                kind: token.kind,
393                range: token.range,
394                text: public_token_text(token.text),
395            })
396            .collect(),
397        errors,
398        extension.dialect(),
399    )
400}
401
402pub fn parse_with_extension(text: &str, extension: &impl DialectExtension) -> ParseResult {
403    parse_entry_point_with_extension(text, extension, ParseEntryPoint::Stylesheet)
404}
405
406pub fn parse_entry_point_with_extension(
407    text: &str,
408    extension: &impl DialectExtension,
409    entry_point: ParseEntryPoint,
410) -> ParseResult {
411    let (tokens, errors) = tokenize(text, extension);
412    let token_count = tokens.len();
413    let mut parser = Parser::new(tokens, errors, extension.dialect());
414    crate::record_omena_parser_parse_materialization(token_count);
415    let (green, interner) = parser.parse_entry_point(entry_point);
416
417    ParseResult::new(
418        green,
419        interner,
420        parser.into_errors(),
421        token_count,
422        extension.dialect(),
423    )
424}
425
426pub fn parse_with_reuse_cache(
427    text: &str,
428    dialect: StyleDialect,
429    cache: &mut ParseReuseCache,
430) -> ParseResult {
431    parse_entry_point_with_reuse_cache(text, dialect, ParseEntryPoint::Stylesheet, cache)
432}
433
434pub fn parse_entry_point_with_reuse_cache(
435    text: &str,
436    dialect: StyleDialect,
437    entry_point: ParseEntryPoint,
438    cache: &mut ParseReuseCache,
439) -> ParseResult {
440    let extension = BuiltinDialectExtension::new(dialect);
441    parse_entry_point_with_extension_and_reuse_cache(text, &extension, entry_point, cache)
442}
443
444pub fn parse_entry_point_with_extension_and_reuse_cache(
445    text: &str,
446    extension: &impl DialectExtension,
447    entry_point: ParseEntryPoint,
448    cache: &mut ParseReuseCache,
449) -> ParseResult {
450    let (tokens, errors) = tokenize(text, extension);
451    let token_count = tokens.len();
452    let token_snapshot = tokens.clone();
453    let node_cache = std::mem::take(&mut cache.node_cache);
454    let mut parser = Parser::new_with_node_cache(tokens, errors, extension.dialect(), node_cache);
455    crate::record_omena_parser_parse_materialization(token_count);
456    let (green, node_cache) = parser.parse_entry_point_reusing_cache(entry_point);
457    let resolver = snapshot_token_text_resolver_from_green(&green, &token_snapshot);
458    cache.node_cache = node_cache.unwrap_or_default();
459
460    ParseResult::new_with_resolver(
461        green,
462        Some(resolver),
463        parser.into_errors(),
464        token_count,
465        extension.dialect(),
466    )
467}
468
469pub fn collect_style_facts(text: &str, dialect: StyleDialect) -> ParsedStyleFacts {
470    let extension = BuiltinDialectExtension::new(dialect);
471    collect_style_facts_with_extension(text, &extension)
472}
473
474pub fn collect_style_fact_collection(
475    text: &str,
476    dialect: StyleDialect,
477) -> ParsedStyleFactCollectionV0 {
478    let extension = BuiltinDialectExtension::new(dialect);
479    collect_style_fact_collection_with_extension(text, &extension)
480}
481
482pub(crate) fn tokenize<'text>(
483    text: &'text str,
484    extension: &impl DialectExtension,
485) -> (Vec<Token<'text>>, Vec<ParseError>) {
486    let mut tokenizer = Tokenizer::new(text, extension);
487    tokenizer.tokenize();
488    (tokenizer.tokens, tokenizer.errors)
489}
490
491fn syntax_node_child_path(node: &SyntaxNode<SyntaxKind>) -> Vec<usize> {
492    let mut ancestors = node.ancestors().collect::<Vec<_>>();
493    ancestors.reverse();
494    ancestors
495        .windows(2)
496        .map(|pair| {
497            let parent = pair[0];
498            let child = pair[1];
499            parent
500                .children()
501                .position(|candidate| candidate == child)
502                .unwrap_or(0)
503        })
504        .collect()
505}
506
507fn stable_parser_identity_hash(bytes: &[u8]) -> u64 {
508    const FNV_OFFSET: u64 = 0xcbf29ce484222325;
509    const FNV_PRIME: u64 = 0x00000100000001b3;
510
511    bytes.iter().fold(FNV_OFFSET, |hash, byte| {
512        (hash ^ u64::from(*byte)).wrapping_mul(FNV_PRIME)
513    })
514}
515
516pub(crate) struct Parser<'text> {
517    tokens: Vec<Token<'text>>,
518    position: usize,
519    dialect: StyleDialect,
520    builder: GreenNodeBuilder<'static, 'static, SyntaxKind>,
521    errors: Vec<ParseError>,
522}
523
524impl<'text> Parser<'text> {
525    pub(crate) fn new(
526        tokens: Vec<Token<'text>>,
527        errors: Vec<ParseError>,
528        dialect: StyleDialect,
529    ) -> Self {
530        Self::new_with_node_cache(tokens, errors, dialect, NodeCache::new())
531    }
532
533    pub(crate) fn new_with_node_cache(
534        tokens: Vec<Token<'text>>,
535        errors: Vec<ParseError>,
536        dialect: StyleDialect,
537        node_cache: NodeCache<'static>,
538    ) -> Self {
539        Self {
540            tokens,
541            position: 0,
542            dialect,
543            builder: GreenNodeBuilder::from_cache(node_cache),
544            errors,
545        }
546    }
547
548    pub(crate) fn parse(&mut self) -> (GreenNode, Option<Arc<TokenInterner>>) {
549        self.parse_entry_point(ParseEntryPoint::Stylesheet)
550    }
551
552    fn parse_entry_point(
553        &mut self,
554        entry_point: ParseEntryPoint,
555    ) -> (GreenNode, Option<Arc<TokenInterner>>) {
556        let (green, cache) = self.parse_entry_point_reusing_cache(entry_point);
557        let interner = cache.and_then(|cache| cache.into_interner()).map(Arc::new);
558        (green, interner)
559    }
560
561    fn parse_entry_point_reusing_cache(
562        &mut self,
563        entry_point: ParseEntryPoint,
564    ) -> (GreenNode, Option<NodeCache<'static>>) {
565        self.builder.start_node(SyntaxKind::Root);
566        match entry_point {
567            ParseEntryPoint::Stylesheet => {
568                self.builder.start_node(SyntaxKind::Stylesheet);
569                self.parse_stylesheet_items();
570                self.builder.finish_node();
571            }
572            ParseEntryPoint::RuleList => {
573                self.builder.start_node(SyntaxKind::RuleList);
574                self.parse_rule_list_items();
575                self.builder.finish_node();
576            }
577            ParseEntryPoint::Rule => self.parse_rule(),
578            ParseEntryPoint::DeclarationList => {
579                self.builder.start_node(SyntaxKind::DeclarationList);
580                self.parse_declaration_list();
581                self.builder.finish_node();
582            }
583            ParseEntryPoint::Declaration => self.parse_declaration(),
584            ParseEntryPoint::Value => {
585                self.builder.start_node(SyntaxKind::Value);
586                self.parse_value_or_value_list_until(&[]);
587                self.builder.finish_node();
588            }
589            ParseEntryPoint::ComponentValue => self.parse_component_value(&[]),
590            ParseEntryPoint::ComponentValueList => self.parse_component_value_list_until(&[]),
591            ParseEntryPoint::CommaSeparatedComponentValueList => {
592                self.parse_comma_separated_component_value_list_until(&[])
593            }
594            ParseEntryPoint::SimpleBlock => self.parse_simple_block_entry_point(&[]),
595        }
596        self.parse_sass_indentation_bogus();
597        self.parse_entry_point_trailing_bogus();
598        self.builder.finish_node();
599
600        let builder = std::mem::take(&mut self.builder);
601        builder.finish()
602    }
603
604    fn parse_sass_indentation_bogus(&mut self) {
605        if self.dialect != StyleDialect::Sass
606            || !self
607                .errors
608                .iter()
609                .any(|error| error.message == "inconsistent Sass indentation")
610        {
611            return;
612        }
613        self.builder.start_node(SyntaxKind::BogusSassIndentation);
614        self.builder.finish_node();
615    }
616
617    fn parse_entry_point_trailing_bogus(&mut self) {
618        self.eat_trivia();
619        if self.at_end() {
620            return;
621        }
622        self.builder.start_node(SyntaxKind::BogusRecovery);
623        while !self.at_end() {
624            self.token_current();
625        }
626        self.builder.finish_node();
627    }
628
629    pub(crate) fn into_errors(self) -> Vec<ParseError> {
630        self.errors
631    }
632
633    fn parse_stylesheet_items(&mut self) {
634        while !self.at_end() {
635            self.eat_trivia();
636            if self.at_end() {
637                break;
638            }
639            match self.current_kind() {
640                Some(SyntaxKind::AtKeyword) if self.current_is_css_module_value_rule() => {
641                    self.parse_css_module_value_rule()
642                }
643                Some(SyntaxKind::AtKeyword) if self.current_dialect_at_rule_spec().is_some() => {
644                    self.parse_dialect_at_rule()
645                }
646                Some(SyntaxKind::AtKeyword) => self.parse_at_rule(),
647                Some(SyntaxKind::ScssVariable)
648                    if matches!(self.dialect, StyleDialect::Scss | StyleDialect::Sass) =>
649                {
650                    self.parse_variable_declaration(SyntaxKind::ScssVariableDeclaration)
651                }
652                Some(SyntaxKind::LessVariable) if self.dialect == StyleDialect::Less => {
653                    self.parse_variable_declaration(SyntaxKind::LessVariableDeclaration)
654                }
655                Some(SyntaxKind::Cdo | SyntaxKind::Cdc) => self.token_current(),
656                Some(SyntaxKind::RightBrace | SyntaxKind::SassDedent) => self.token_current(),
657                Some(SyntaxKind::Semicolon | SyntaxKind::SassOptionalSemicolon) => {
658                    self.token_current()
659                }
660                Some(_) => self.parse_rule(),
661                None => break,
662            }
663        }
664    }
665
666    fn parse_rule(&mut self) {
667        let starts_less_mixin =
668            self.dialect == StyleDialect::Less && self.current_starts_less_callable_signature();
669        let has_rule_block = self.find_rule_block_open_before_recovery(&[
670            SyntaxKind::Semicolon,
671            SyntaxKind::SassOptionalSemicolon,
672            SyntaxKind::RightBrace,
673            SyntaxKind::SassDedent,
674        ]);
675        let kind = if let Some(kind) = self
676            .current_icss_module_rule_kind()
677            .filter(|_| has_rule_block)
678        {
679            kind
680        } else if self.current_starts_less_mixin_declaration() {
681            SyntaxKind::LessMixinDeclaration
682        } else if starts_less_mixin {
683            SyntaxKind::BogusLessMixin
684        } else if has_rule_block {
685            SyntaxKind::Rule
686        } else {
687            SyntaxKind::BogusRule
688        };
689
690        self.builder.start_node(kind);
691        if kind == SyntaxKind::CssModuleImportBlock && !self.current_icss_import_has_source() {
692            self.error_at_current(ParseErrorCode::ExpectedValue, "expected ICSS import source");
693        }
694        if kind == SyntaxKind::LessMixinDeclaration {
695            self.parse_less_mixin_header();
696        } else if kind == SyntaxKind::BogusLessMixin {
697            self.parse_until_recovery_with_optional_less_guard(&[
698                SyntaxKind::Semicolon,
699                SyntaxKind::RightBrace,
700                SyntaxKind::SassDedent,
701            ]);
702            self.error_at_current(
703                ParseErrorCode::UnexpectedCharacter,
704                "expected Less mixin block",
705            );
706        } else {
707            self.parse_selector_list();
708        }
709        if self.current_kind() == Some(SyntaxKind::LeftBrace) {
710            self.token_current();
711            self.builder
712                .start_node(if self.previous_left_brace_has_match() {
713                    SyntaxKind::DeclarationList
714                } else {
715                    SyntaxKind::BogusDeclarationList
716                });
717            self.parse_declaration_list();
718            self.builder.finish_node();
719            if self.current_kind() == Some(SyntaxKind::RightBrace) {
720                self.token_current();
721            } else {
722                self.missing_token_bogus_trivia(
723                    ParseErrorCode::UnexpectedCharacter,
724                    "unterminated declaration block",
725                );
726            }
727        } else if self.current_kind() == Some(SyntaxKind::SassIndent) {
728            self.builder.start_node(SyntaxKind::SassIndentedBlock);
729            self.token_current();
730            self.builder.start_node(SyntaxKind::DeclarationList);
731            self.parse_declaration_list();
732            self.builder.finish_node();
733            if self.current_kind() == Some(SyntaxKind::SassDedent) {
734                self.token_current();
735            } else {
736                self.missing_token_bogus_trivia(
737                    ParseErrorCode::UnexpectedCharacter,
738                    "unterminated Sass indented declaration block",
739                );
740            }
741            self.builder.finish_node();
742        } else {
743            self.consume_until_recovery(&[
744                SyntaxKind::Semicolon,
745                SyntaxKind::SassOptionalSemicolon,
746                SyntaxKind::RightBrace,
747                SyntaxKind::SassDedent,
748            ]);
749            if self.current_kind().is_some_and(is_statement_end) {
750                self.token_current();
751            }
752        }
753        self.builder.finish_node();
754    }
755
756    fn current_icss_module_rule_kind(&self) -> Option<SyntaxKind> {
757        if self.current_kind() != Some(SyntaxKind::Colon) {
758            return None;
759        }
760        let (name_index, name_kind) = self.non_trivia_token_from(self.position + 1)?;
761        if name_kind != SyntaxKind::Ident {
762            return None;
763        }
764        match self.tokens.get(name_index)?.text {
765            "export" => Some(SyntaxKind::CssModuleExportBlock),
766            "import" => Some(SyntaxKind::CssModuleImportBlock),
767            _ => None,
768        }
769    }
770
771    fn current_icss_import_has_source(&self) -> bool {
772        let Some((name_index, SyntaxKind::Ident)) = self.non_trivia_token_from(self.position + 1)
773        else {
774            return false;
775        };
776        if self
777            .tokens
778            .get(name_index)
779            .is_none_or(|token| token.text != "import")
780        {
781            return false;
782        }
783        let Some((open_index, SyntaxKind::LeftParen)) = self.non_trivia_token_from(name_index + 1)
784        else {
785            return false;
786        };
787        let Some((_, source_kind)) = self.non_trivia_token_from(open_index + 1) else {
788            return false;
789        };
790        matches!(
791            source_kind,
792            SyntaxKind::String | SyntaxKind::Url | SyntaxKind::ScssInterpolationStart
793        )
794    }
795
796    fn parse_selector_list(&mut self) {
797        self.parse_selector_list_until(&[]);
798    }
799
800    fn parse_selector_list_until(&mut self, recovery: &[SyntaxKind]) {
801        let kind = if self.current_kind() == Some(SyntaxKind::LeftBrace) {
802            SyntaxKind::BogusSelectorList
803        } else {
804            SyntaxKind::SelectorList
805        };
806        self.builder.start_node(kind);
807        while !self.at_end() {
808            match self.current_kind() {
809                Some(SyntaxKind::Comma) => self.token_current(),
810                Some(kind) if is_selector_boundary_until(kind, recovery) => break,
811                Some(SyntaxKind::SassIndentedNewline) => self.token_current(),
812                Some(_)
813                    if recovery.contains(&SyntaxKind::RightParen)
814                        && self.current_selector_item_is_bogus(recovery) =>
815                {
816                    self.parse_bogus_selector_until(recovery)
817                }
818                Some(_) => self.parse_selector_until(recovery),
819                None => break,
820            }
821        }
822        self.builder.finish_node();
823    }
824
825    fn parse_strict_selector_list_until(&mut self, recovery: &[SyntaxKind]) {
826        self.builder.start_node(
827            if self.selector_list_contains_bogus_item_until(recovery)
828                && self.current_kind() != Some(SyntaxKind::RightParen)
829            {
830                SyntaxKind::BogusSelectorList
831            } else {
832                SyntaxKind::SelectorList
833            },
834        );
835        while !self.at_end() {
836            match self.current_kind() {
837                Some(SyntaxKind::Comma) => self.token_current(),
838                Some(kind) if is_selector_boundary_until(kind, recovery) => break,
839                Some(SyntaxKind::SassIndentedNewline) => self.token_current(),
840                Some(_)
841                    if self.current_selector_item_is_bogus(recovery)
842                        && self.current_kind() != Some(SyntaxKind::RightParen) =>
843                {
844                    self.parse_bogus_selector_until(recovery)
845                }
846                Some(_) => self.parse_selector_until(recovery),
847                None => break,
848            }
849        }
850        self.builder.finish_node();
851    }
852
853    fn parse_relative_selector_list_until(&mut self, recovery: &[SyntaxKind]) {
854        self.builder.start_node(
855            if self.current_selector_item_is_bogus(recovery)
856                && self.current_kind() != Some(SyntaxKind::RightParen)
857            {
858                SyntaxKind::BogusSelectorList
859            } else {
860                SyntaxKind::RelativeSelectorList
861            },
862        );
863        while !self.at_end() {
864            match self.current_kind() {
865                Some(SyntaxKind::Comma) => self.token_current(),
866                Some(kind) if is_selector_boundary_until(kind, recovery) => break,
867                Some(SyntaxKind::SassIndentedNewline) => self.token_current(),
868                Some(_)
869                    if self.current_selector_item_is_bogus(recovery)
870                        && self.current_kind() != Some(SyntaxKind::RightParen) =>
871                {
872                    self.parse_bogus_selector_until(recovery)
873                }
874                Some(_) => self.parse_relative_selector_until(recovery),
875                None => break,
876            }
877        }
878        self.builder.finish_node();
879    }
880
881    fn parse_relative_selector_until(&mut self, recovery: &[SyntaxKind]) {
882        self.builder.start_node(SyntaxKind::RelativeSelector);
883        self.builder.start_node(SyntaxKind::ComplexSelector);
884        self.parse_complex_selector_until(recovery);
885        self.builder.finish_node();
886        self.builder.finish_node();
887    }
888
889    fn parse_bogus_selector_until(&mut self, recovery: &[SyntaxKind]) {
890        self.builder.start_node(SyntaxKind::BogusSelector);
891        self.error_at_current(
892            ParseErrorCode::UnexpectedCharacter,
893            "invalid selector in selector list",
894        );
895        let mut paren_depth = 0usize;
896        let mut bracket_depth = 0usize;
897        while !self.at_end() {
898            let Some(kind) = self.current_kind() else {
899                break;
900            };
901            if paren_depth == 0
902                && bracket_depth == 0
903                && (kind == SyntaxKind::Comma || is_selector_boundary_until(kind, recovery))
904            {
905                break;
906            }
907            match kind {
908                SyntaxKind::LeftParen => paren_depth += 1,
909                SyntaxKind::RightParen => paren_depth = paren_depth.saturating_sub(1),
910                SyntaxKind::LeftBracket => bracket_depth += 1,
911                SyntaxKind::RightBracket => bracket_depth = bracket_depth.saturating_sub(1),
912                _ => {}
913            }
914            self.token_current();
915        }
916        self.builder.finish_node();
917    }
918
919    fn parse_selector_until(&mut self, recovery: &[SyntaxKind]) {
920        self.builder.start_node(SyntaxKind::Selector);
921        self.builder.start_node(SyntaxKind::ComplexSelector);
922        self.parse_complex_selector_until(recovery);
923        self.builder.finish_node();
924        self.builder.finish_node();
925    }
926
927    fn parse_complex_selector_until(&mut self, recovery: &[SyntaxKind]) {
928        let mut has_component = false;
929        while !self.at_end() {
930            match self.current_kind() {
931                Some(kind) if is_selector_boundary_until(kind, recovery) => break,
932                Some(SyntaxKind::Whitespace) => {
933                    if has_component
934                        && self.next_non_trivia_kind().is_some_and(|kind| {
935                            !is_selector_boundary_until(kind, recovery) && !is_combinator(kind)
936                        })
937                    {
938                        self.parse_whitespace_combinator();
939                        has_component = false;
940                    } else {
941                        self.token_current();
942                    }
943                }
944                Some(SyntaxKind::SassIndentedNewline) => self.token_current(),
945                Some(kind) if is_combinator(kind) => {
946                    self.parse_combinator();
947                    has_component = false;
948                }
949                Some(_) => {
950                    self.parse_compound_selector_until(recovery);
951                    has_component = true;
952                }
953                None => break,
954            }
955        }
956    }
957
958    fn parse_compound_selector_until(&mut self, recovery: &[SyntaxKind]) {
959        let starts_valid = self.current_kind().is_some_and(|kind| {
960            selector_component_can_start(kind)
961                || self.current_starts_namespace_qualified_selector(kind)
962                || is_interpolation_start(kind)
963        });
964        self.builder.start_node(if starts_valid {
965            SyntaxKind::CompoundSelector
966        } else {
967            SyntaxKind::BogusCompoundSelector
968        });
969        let start = self.position;
970        while !self.at_end() {
971            match self.current_kind() {
972                Some(kind)
973                    if is_selector_boundary_until(kind, recovery)
974                        || kind == SyntaxKind::Whitespace
975                        || kind == SyntaxKind::SassIndentedNewline
976                        || is_combinator(kind) =>
977                {
978                    break;
979                }
980                Some(SyntaxKind::Dot) => self.parse_class_selector(),
981                Some(SyntaxKind::Hash) => self.parse_id_selector(),
982                Some(kind) if self.current_starts_namespace_qualified_selector(kind) => {
983                    self.parse_namespace_qualified_selector()
984                }
985                Some(SyntaxKind::Ident) => self.parse_type_selector(),
986                Some(SyntaxKind::Star) => self.parse_universal_selector(),
987                Some(SyntaxKind::Ampersand) => self.parse_nesting_selector(),
988                Some(SyntaxKind::ScssPlaceholder) => self.parse_scss_placeholder_selector(),
989                Some(kind) if is_interpolation_start(kind) => self.parse_interpolation(
990                    kind,
991                    &[
992                        SyntaxKind::Comma,
993                        SyntaxKind::LeftBrace,
994                        SyntaxKind::SassIndent,
995                        SyntaxKind::RightBrace,
996                        SyntaxKind::SassDedent,
997                        SyntaxKind::RightParen,
998                        SyntaxKind::Semicolon,
999                        SyntaxKind::SassOptionalSemicolon,
1000                    ],
1001                ),
1002                Some(SyntaxKind::LeftBracket) => self.parse_attribute_selector(),
1003                Some(SyntaxKind::Colon) if self.current_starts_less_extend_rule() => {
1004                    self.parse_less_extend_rule()
1005                }
1006                Some(SyntaxKind::Colon) => {
1007                    self.parse_pseudo_selector(SyntaxKind::PseudoClassSelector)
1008                }
1009                Some(SyntaxKind::DoubleColon) => {
1010                    self.parse_pseudo_selector(SyntaxKind::PseudoElementSelector)
1011                }
1012                Some(_) => self.token_current(),
1013                None => break,
1014            }
1015        }
1016        if self.position == start {
1017            self.token_current();
1018        }
1019        if !starts_valid {
1020            self.error_at_current(
1021                ParseErrorCode::UnexpectedCharacter,
1022                "expected selector component",
1023            );
1024        }
1025        self.builder.finish_node();
1026    }
1027
1028    fn parse_class_selector(&mut self) {
1029        self.builder.start_node(SyntaxKind::ClassSelector);
1030        self.token_current();
1031        if matches!(
1032            self.current_kind(),
1033            Some(SyntaxKind::Ident | SyntaxKind::CustomPropertyName)
1034        ) {
1035            self.token_current();
1036        } else {
1037            self.empty_bogus_node(
1038                SyntaxKind::BogusSelector,
1039                ParseErrorCode::ExpectedSelectorName,
1040                "expected class selector name",
1041            );
1042        }
1043        self.builder.finish_node();
1044    }
1045
1046    fn parse_id_selector(&mut self) {
1047        self.builder.start_node(SyntaxKind::IdSelector);
1048        self.token_current();
1049        self.builder.finish_node();
1050    }
1051
1052    fn parse_type_selector(&mut self) {
1053        self.builder.start_node(SyntaxKind::TypeSelector);
1054        self.token_current();
1055        self.builder.finish_node();
1056    }
1057
1058    fn parse_universal_selector(&mut self) {
1059        self.builder.start_node(SyntaxKind::UniversalSelector);
1060        self.token_current();
1061        self.builder.finish_node();
1062    }
1063
1064    fn parse_namespace_qualified_selector(&mut self) {
1065        let selector_kind =
1066            if self.namespace_qualified_selector_target_kind() == Some(SyntaxKind::Star) {
1067                SyntaxKind::UniversalSelector
1068            } else {
1069                SyntaxKind::TypeSelector
1070            };
1071        self.builder.start_node(selector_kind);
1072        self.builder.start_node(SyntaxKind::NamespacePrefix);
1073        if self.current_kind() != Some(SyntaxKind::Pipe) {
1074            self.token_current();
1075        }
1076        self.token_current();
1077        self.builder.finish_node();
1078        if matches!(
1079            self.current_kind(),
1080            Some(SyntaxKind::Ident | SyntaxKind::CustomPropertyName | SyntaxKind::Star)
1081        ) {
1082            self.token_current();
1083        } else {
1084            self.empty_bogus_node(
1085                SyntaxKind::BogusSelector,
1086                ParseErrorCode::ExpectedSelectorName,
1087                "expected namespace-qualified selector name",
1088            );
1089        }
1090        self.builder.finish_node();
1091    }
1092
1093    fn parse_nesting_selector(&mut self) {
1094        self.builder.start_node(SyntaxKind::NestingSelectorNode);
1095        self.token_current();
1096        self.builder.finish_node();
1097    }
1098
1099    fn parse_scss_placeholder_selector(&mut self) {
1100        self.builder.start_node(SyntaxKind::ScssPlaceholderSelector);
1101        self.token_current();
1102        self.builder.finish_node();
1103    }
1104
1105    fn parse_attribute_selector(&mut self) {
1106        let kind = if self.find_before_recovery(
1107            SyntaxKind::RightBracket,
1108            &[
1109                SyntaxKind::Comma,
1110                SyntaxKind::LeftBrace,
1111                SyntaxKind::RightBrace,
1112                SyntaxKind::Semicolon,
1113            ],
1114        ) {
1115            SyntaxKind::AttributeSelector
1116        } else {
1117            SyntaxKind::BogusSelector
1118        };
1119        self.builder.start_node(kind);
1120        self.token_current();
1121        let mut saw_matcher = false;
1122        let mut saw_value = false;
1123        let mut closed = false;
1124        while !self.at_end() {
1125            match self.current_kind() {
1126                Some(SyntaxKind::RightBracket) => {
1127                    self.token_current();
1128                    closed = true;
1129                    break;
1130                }
1131                Some(kind) if is_attribute_matcher(kind) => {
1132                    self.parse_attribute_matcher();
1133                    saw_matcher = true;
1134                }
1135                Some(kind) if is_selector_boundary(kind) => break,
1136                Some(kind) if !saw_matcher && attribute_name_token_can_start(kind) => {
1137                    self.parse_attribute_name()
1138                }
1139                Some(kind)
1140                    if saw_matcher && !saw_value && attribute_value_token_can_start(kind) =>
1141                {
1142                    self.parse_attribute_value();
1143                    saw_value = true;
1144                }
1145                Some(SyntaxKind::Ident | SyntaxKind::CustomPropertyName) if saw_value => {
1146                    self.parse_attribute_modifier()
1147                }
1148                Some(_) => self.token_current(),
1149                None => break,
1150            }
1151        }
1152        if !closed {
1153            self.error_at_current(
1154                ParseErrorCode::UnterminatedAttributeSelector,
1155                "unterminated attribute selector",
1156            );
1157        }
1158        self.builder.finish_node();
1159    }
1160
1161    fn parse_attribute_matcher(&mut self) {
1162        self.builder.start_node(SyntaxKind::AttributeMatcher);
1163        self.token_current();
1164        self.builder.finish_node();
1165    }
1166
1167    fn parse_attribute_name(&mut self) {
1168        self.builder.start_node(SyntaxKind::AttributeName);
1169        while !self.at_end() {
1170            match self.current_kind() {
1171                Some(SyntaxKind::RightBracket) => break,
1172                Some(kind) if is_attribute_matcher(kind) || is_selector_boundary(kind) => break,
1173                Some(kind) if attribute_name_token_can_continue(kind) => self.token_current(),
1174                Some(_) => break,
1175                None => break,
1176            }
1177        }
1178        self.builder.finish_node();
1179    }
1180
1181    fn parse_attribute_value(&mut self) {
1182        self.builder.start_node(SyntaxKind::AttributeValue);
1183        self.token_current();
1184        self.builder.finish_node();
1185    }
1186
1187    fn parse_attribute_modifier(&mut self) {
1188        self.builder.start_node(SyntaxKind::AttributeModifier);
1189        self.token_current();
1190        self.builder.finish_node();
1191    }
1192
1193    fn parse_pseudo_selector(&mut self, kind: SyntaxKind) {
1194        self.builder.start_node(kind);
1195        self.token_current();
1196        let pseudo_name = self.current_text().map(str::to_owned);
1197        let css_module_scope_kind = if kind == SyntaxKind::PseudoClassSelector {
1198            self.current_text().and_then(css_module_scope_function_kind)
1199        } else {
1200            None
1201        };
1202        if self.current_kind() == Some(SyntaxKind::Ident) {
1203            if let Some(kind) = css_module_scope_kind {
1204                self.builder.start_node(kind);
1205            }
1206            self.token_current();
1207        } else {
1208            self.empty_bogus_node(
1209                SyntaxKind::BogusSelector,
1210                ParseErrorCode::ExpectedSelectorName,
1211                "expected pseudo selector name",
1212            );
1213        }
1214        if self.current_kind() == Some(SyntaxKind::LeftParen) {
1215            self.token_current();
1216            self.builder.start_node(SyntaxKind::PseudoSelectorArgument);
1217            if kind == SyntaxKind::PseudoClassSelector
1218                && pseudo_name
1219                    .as_deref()
1220                    .is_some_and(is_selector_list_pseudo_class)
1221            {
1222                self.parse_selector_list_until(&[SyntaxKind::RightParen]);
1223            } else if kind == SyntaxKind::PseudoClassSelector
1224                && pseudo_name.as_deref() == Some("not")
1225            {
1226                self.parse_strict_selector_list_until(&[SyntaxKind::RightParen]);
1227            } else if kind == SyntaxKind::PseudoClassSelector
1228                && pseudo_name.as_deref() == Some("has")
1229            {
1230                self.parse_relative_selector_list_until(&[SyntaxKind::RightParen]);
1231            } else if kind == SyntaxKind::PseudoClassSelector
1232                && pseudo_name.as_deref().is_some_and(is_nth_pseudo_class)
1233            {
1234                self.parse_nth_selector_argument();
1235            } else if kind == SyntaxKind::PseudoClassSelector
1236                && pseudo_name.as_deref() == Some("lang")
1237            {
1238                self.parse_language_selector_argument();
1239            } else if kind == SyntaxKind::PseudoClassSelector
1240                && pseudo_name.as_deref() == Some("dir")
1241            {
1242                self.parse_directionality_selector_argument();
1243            } else {
1244                while !self.at_end() {
1245                    match self.current_kind() {
1246                        Some(SyntaxKind::RightParen) => break,
1247                        Some(kind) if is_selector_boundary(kind) => break,
1248                        Some(_) => self.token_current(),
1249                        None => break,
1250                    }
1251                }
1252            }
1253            self.builder.finish_node();
1254            if self.current_kind() == Some(SyntaxKind::RightParen) {
1255                self.token_current();
1256            }
1257        }
1258        if css_module_scope_kind.is_some() {
1259            self.builder.finish_node();
1260        }
1261        self.builder.finish_node();
1262    }
1263
1264    fn parse_nth_selector_argument(&mut self) {
1265        self.builder.start_node(SyntaxKind::NthSelectorArgument);
1266        self.builder.start_node(SyntaxKind::NthSelectorFormula);
1267        while !self.at_end() {
1268            match self.current_kind() {
1269                Some(SyntaxKind::RightParen) => break,
1270                Some(kind) if is_selector_boundary(kind) => break,
1271                Some(SyntaxKind::Ident) if self.current_text() == Some("of") => break,
1272                Some(_) => self.token_current(),
1273                None => break,
1274            }
1275        }
1276        self.builder.finish_node();
1277
1278        if self.current_kind() == Some(SyntaxKind::Ident) && self.current_text() == Some("of") {
1279            self.builder
1280                .start_node(SyntaxKind::NthSelectorOfSelectorList);
1281            self.token_current();
1282            self.parse_selector_list_until(&[SyntaxKind::RightParen]);
1283            self.builder.finish_node();
1284        }
1285
1286        self.builder.finish_node();
1287    }
1288
1289    fn parse_language_selector_argument(&mut self) {
1290        self.builder
1291            .start_node(SyntaxKind::LanguageSelectorArgument);
1292        while !self.at_end() {
1293            match self.current_kind() {
1294                Some(SyntaxKind::RightParen) => break,
1295                Some(SyntaxKind::Comma) => self.token_current(),
1296                Some(kind) if is_selector_boundary(kind) => break,
1297                Some(kind) if language_tag_token_can_start(kind) => self.parse_language_tag(),
1298                Some(_) => self.token_current(),
1299                None => break,
1300            }
1301        }
1302        self.builder.finish_node();
1303    }
1304
1305    fn parse_language_tag(&mut self) {
1306        self.builder.start_node(SyntaxKind::LanguageTag);
1307        self.token_current();
1308        self.builder.finish_node();
1309    }
1310
1311    fn parse_directionality_selector_argument(&mut self) {
1312        self.builder
1313            .start_node(SyntaxKind::DirectionalitySelectorArgument);
1314        if self
1315            .current_kind()
1316            .is_some_and(language_tag_token_can_start)
1317        {
1318            self.token_current();
1319        }
1320        while !self.at_end() {
1321            match self.current_kind() {
1322                Some(SyntaxKind::RightParen) => break,
1323                Some(kind) if is_selector_boundary(kind) => break,
1324                Some(_) => self.token_current(),
1325                None => break,
1326            }
1327        }
1328        self.builder.finish_node();
1329    }
1330
1331    fn parse_less_extend_rule(&mut self) {
1332        self.builder.start_node(SyntaxKind::LessExtendRule);
1333        if self.current_kind() == Some(SyntaxKind::Colon) {
1334            self.token_current();
1335        }
1336        if self.current_text() == Some("extend") {
1337            self.token_current();
1338        } else {
1339            self.empty_bogus_node(
1340                SyntaxKind::BogusSelector,
1341                ParseErrorCode::ExpectedSelectorName,
1342                "expected Less extend selector",
1343            );
1344        }
1345        if self.current_kind() == Some(SyntaxKind::LeftParen) {
1346            self.token_current();
1347            self.builder.start_node(SyntaxKind::PseudoSelectorArgument);
1348            while !self.at_end() {
1349                match self.current_kind() {
1350                    Some(SyntaxKind::RightParen) => break,
1351                    Some(kind) if is_selector_boundary(kind) => break,
1352                    Some(kind) if is_interpolation_start(kind) => self.parse_interpolation(
1353                        kind,
1354                        &[
1355                            SyntaxKind::RightParen,
1356                            SyntaxKind::Comma,
1357                            SyntaxKind::LeftBrace,
1358                            SyntaxKind::SassIndent,
1359                            SyntaxKind::Semicolon,
1360                            SyntaxKind::SassOptionalSemicolon,
1361                        ],
1362                    ),
1363                    Some(_) => self.token_current(),
1364                    None => break,
1365                }
1366            }
1367            self.builder.finish_node();
1368            if self.current_kind() == Some(SyntaxKind::RightParen) {
1369                self.token_current();
1370            }
1371        }
1372        self.builder.finish_node();
1373    }
1374
1375    fn parse_combinator(&mut self) {
1376        let has_rhs = self
1377            .next_non_trivia_kind()
1378            .is_some_and(|kind| selector_component_can_start(kind) || is_interpolation_start(kind));
1379        self.builder.start_node(if has_rhs {
1380            SyntaxKind::Combinator
1381        } else {
1382            SyntaxKind::BogusCombinator
1383        });
1384        self.token_current();
1385        if !has_rhs {
1386            self.error_at_current(
1387                ParseErrorCode::UnexpectedCharacter,
1388                "expected selector after combinator",
1389            );
1390        }
1391        self.builder.finish_node();
1392    }
1393
1394    fn parse_whitespace_combinator(&mut self) {
1395        self.builder.start_node(SyntaxKind::Combinator);
1396        while self.current_kind() == Some(SyntaxKind::Whitespace) {
1397            self.token_current();
1398        }
1399        self.builder.finish_node();
1400    }
1401
1402    fn parse_declaration_list(&mut self) {
1403        while !self.at_end() {
1404            self.eat_trivia();
1405            match self.current_kind() {
1406                Some(SyntaxKind::RightBrace | SyntaxKind::SassDedent) | None => break,
1407                Some(SyntaxKind::Semicolon | SyntaxKind::SassOptionalSemicolon) => {
1408                    self.token_current()
1409                }
1410                Some(SyntaxKind::AtKeyword) if self.current_is_css_module_value_rule() => {
1411                    self.parse_css_module_value_rule()
1412                }
1413                Some(SyntaxKind::AtKeyword) if self.current_dialect_at_rule_spec().is_some() => {
1414                    self.parse_dialect_at_rule()
1415                }
1416                Some(SyntaxKind::AtKeyword) => self.parse_at_rule(),
1417                Some(_) if self.current_starts_less_namespace_access() => {
1418                    self.parse_less_namespace_access()
1419                }
1420                Some(_) if self.current_starts_less_mixin_call() => self.parse_less_mixin_call(),
1421                Some(_) if self.current_starts_scss_nested_property() => {
1422                    self.parse_scss_nested_property()
1423                }
1424                Some(_) if self.current_starts_nested_rule() => self.parse_rule(),
1425                Some(SyntaxKind::ScssVariable)
1426                    if matches!(self.dialect, StyleDialect::Scss | StyleDialect::Sass) =>
1427                {
1428                    self.parse_variable_declaration(SyntaxKind::ScssVariableDeclaration)
1429                }
1430                Some(SyntaxKind::LessVariable) if self.dialect == StyleDialect::Less => {
1431                    self.parse_variable_declaration(SyntaxKind::LessVariableDeclaration)
1432                }
1433                Some(SyntaxKind::LeftBrace) => {
1434                    self.builder.start_node(SyntaxKind::BogusDeclaration);
1435                    self.token_current();
1436                    self.builder.finish_node();
1437                }
1438                Some(_) => self.parse_declaration(),
1439            }
1440        }
1441    }
1442
1443    fn parse_scss_nested_property(&mut self) {
1444        self.builder.start_node(SyntaxKind::ScssNestedProperty);
1445        self.builder.start_node(SyntaxKind::PropertyName);
1446        while !self.at_end() {
1447            match self.current_kind() {
1448                Some(SyntaxKind::Colon) => break,
1449                Some(
1450                    SyntaxKind::Semicolon
1451                    | SyntaxKind::SassOptionalSemicolon
1452                    | SyntaxKind::RightBrace
1453                    | SyntaxKind::SassDedent,
1454                ) => break,
1455                Some(kind) if is_interpolation_start(kind) => self.parse_interpolation(
1456                    kind,
1457                    &[
1458                        SyntaxKind::Colon,
1459                        SyntaxKind::Semicolon,
1460                        SyntaxKind::SassOptionalSemicolon,
1461                        SyntaxKind::RightBrace,
1462                        SyntaxKind::SassDedent,
1463                    ],
1464                ),
1465                Some(_) => self.token_current(),
1466                None => break,
1467            }
1468        }
1469        self.builder.finish_node();
1470
1471        if self.current_kind() == Some(SyntaxKind::Colon) {
1472            self.token_current();
1473        }
1474
1475        let block_recovery = [
1476            SyntaxKind::LeftBrace,
1477            SyntaxKind::SassIndent,
1478            SyntaxKind::Semicolon,
1479            SyntaxKind::SassOptionalSemicolon,
1480            SyntaxKind::RightBrace,
1481            SyntaxKind::SassDedent,
1482        ];
1483        if !matches!(
1484            self.current_kind(),
1485            Some(
1486                SyntaxKind::LeftBrace
1487                    | SyntaxKind::SassIndent
1488                    | SyntaxKind::Semicolon
1489                    | SyntaxKind::SassOptionalSemicolon
1490                    | SyntaxKind::RightBrace
1491                    | SyntaxKind::SassDedent
1492            )
1493        ) {
1494            self.builder.start_node(SyntaxKind::Value);
1495            self.parse_value_or_value_list_until(&block_recovery);
1496            self.builder.finish_node();
1497        }
1498
1499        match self.current_kind() {
1500            Some(SyntaxKind::LeftBrace) => self.parse_declaration_block(),
1501            Some(SyntaxKind::SassIndent) => self.parse_sass_indented_nested_property_block(),
1502            Some(_) => self.consume_until_recovery(&[
1503                SyntaxKind::Semicolon,
1504                SyntaxKind::SassOptionalSemicolon,
1505                SyntaxKind::RightBrace,
1506                SyntaxKind::SassDedent,
1507            ]),
1508            None => {}
1509        }
1510
1511        if self.current_kind().is_some_and(is_statement_end) {
1512            self.token_current();
1513        }
1514        self.builder.finish_node();
1515    }
1516
1517    fn parse_sass_indented_nested_property_block(&mut self) {
1518        self.builder.start_node(SyntaxKind::SassIndentedBlock);
1519        if self.current_kind() == Some(SyntaxKind::SassIndent) {
1520            self.token_current();
1521        }
1522        self.builder.start_node(SyntaxKind::DeclarationList);
1523        self.parse_declaration_list();
1524        self.builder.finish_node();
1525        if self.current_kind() == Some(SyntaxKind::SassDedent) {
1526            self.token_current();
1527        } else {
1528            self.error_at_current(
1529                ParseErrorCode::UnexpectedCharacter,
1530                "unterminated Sass indented nested property block",
1531            );
1532        }
1533        self.builder.finish_node();
1534    }
1535
1536    fn parse_variable_declaration(&mut self, kind: SyntaxKind) {
1537        let has_colon = self.find_before_recovery(
1538            SyntaxKind::Colon,
1539            &[
1540                SyntaxKind::Semicolon,
1541                SyntaxKind::SassOptionalSemicolon,
1542                SyntaxKind::RightBrace,
1543                SyntaxKind::SassDedent,
1544            ],
1545        );
1546        self.builder
1547            .start_node(variable_declaration_node_kind(kind, has_colon));
1548        self.token_current();
1549        if self.current_kind() == Some(SyntaxKind::Colon) {
1550            self.token_current();
1551            self.eat_value_trivia();
1552            let value_recovery = [
1553                SyntaxKind::Semicolon,
1554                SyntaxKind::SassOptionalSemicolon,
1555                SyntaxKind::RightBrace,
1556                SyntaxKind::SassDedent,
1557            ];
1558            if kind == SyntaxKind::LessVariableDeclaration
1559                && self.current_kind() == Some(SyntaxKind::LeftBrace)
1560            {
1561                self.parse_less_detached_ruleset();
1562            } else {
1563                let has_value = self
1564                    .non_trivia_token_from(self.position)
1565                    .is_some_and(|(_, kind)| !value_recovery.contains(&kind));
1566                self.builder.start_node(SyntaxKind::Value);
1567                if has_value {
1568                    self.parse_value_or_value_list_until(&value_recovery);
1569                } else {
1570                    self.empty_bogus_node(
1571                        SyntaxKind::BogusValue,
1572                        ParseErrorCode::ExpectedValue,
1573                        "expected variable value",
1574                    );
1575                }
1576                self.builder.finish_node();
1577            }
1578        } else {
1579            self.error_at_current(
1580                ParseErrorCode::UnexpectedCharacter,
1581                "expected variable declaration colon",
1582            );
1583            self.consume_until_recovery(&[
1584                SyntaxKind::Semicolon,
1585                SyntaxKind::SassOptionalSemicolon,
1586                SyntaxKind::RightBrace,
1587                SyntaxKind::SassDedent,
1588            ]);
1589        }
1590        if self.current_kind().is_some_and(is_statement_end) {
1591            self.token_current();
1592        }
1593        self.builder.finish_node();
1594    }
1595
1596    fn parse_less_detached_ruleset(&mut self) {
1597        let closed = self.current_left_brace_has_match();
1598        self.builder.start_node(if closed {
1599            SyntaxKind::LessDetachedRulesetNode
1600        } else {
1601            SyntaxKind::BogusLessDetachedRuleset
1602        });
1603        if self.current_kind() == Some(SyntaxKind::LeftBrace) {
1604            self.token_current();
1605            self.builder.start_node(SyntaxKind::DeclarationList);
1606            self.parse_declaration_list();
1607            self.builder.finish_node();
1608        }
1609        if self.current_kind() == Some(SyntaxKind::RightBrace) {
1610            self.token_current();
1611        } else {
1612            self.error_at_current(
1613                ParseErrorCode::UnexpectedCharacter,
1614                "unterminated Less detached ruleset",
1615            );
1616        }
1617        self.builder.finish_node();
1618    }
1619
1620    fn parse_declaration(&mut self) {
1621        let starts_composes = self
1622            .current_text()
1623            .is_some_and(|text| css_keyword(text).equals("composes"));
1624        let starts_custom_property = self.current_kind() == Some(SyntaxKind::CustomPropertyName);
1625        let has_colon = self.find_before_recovery(
1626            SyntaxKind::Colon,
1627            &[
1628                SyntaxKind::Semicolon,
1629                SyntaxKind::SassOptionalSemicolon,
1630                SyntaxKind::RightBrace,
1631                SyntaxKind::SassDedent,
1632                SyntaxKind::LeftBrace,
1633                SyntaxKind::SassIndent,
1634            ],
1635        );
1636        let kind = if starts_composes && has_colon {
1637            SyntaxKind::CssModuleComposesDeclaration
1638        } else if starts_composes {
1639            SyntaxKind::BogusComposesDeclaration
1640        } else if has_colon {
1641            SyntaxKind::Declaration
1642        } else {
1643            SyntaxKind::BogusDeclaration
1644        };
1645        self.builder.start_node(kind);
1646        if kind == SyntaxKind::CssModuleComposesDeclaration
1647            && self.current_css_module_scope_context() == Some("global")
1648        {
1649            self.error_at_current(
1650                ParseErrorCode::UnexpectedCharacter,
1651                "composes is not allowed inside :global scope",
1652            );
1653        }
1654        let property_kind = if matches!(
1655            self.current_kind(),
1656            Some(
1657                SyntaxKind::Colon
1658                    | SyntaxKind::Semicolon
1659                    | SyntaxKind::SassOptionalSemicolon
1660                    | SyntaxKind::LeftBrace
1661                    | SyntaxKind::SassIndent
1662                    | SyntaxKind::RightBrace
1663                    | SyntaxKind::SassDedent
1664            )
1665        ) {
1666            SyntaxKind::BogusPropertyName
1667        } else {
1668            SyntaxKind::PropertyName
1669        };
1670        self.builder.start_node(property_kind);
1671        while !self.at_end() {
1672            match self.current_kind() {
1673                Some(
1674                    SyntaxKind::Colon
1675                    | SyntaxKind::Semicolon
1676                    | SyntaxKind::SassOptionalSemicolon
1677                    | SyntaxKind::RightBrace
1678                    | SyntaxKind::SassDedent,
1679                ) => break,
1680                Some(kind) if is_interpolation_start(kind) => self.parse_interpolation(
1681                    kind,
1682                    &[
1683                        SyntaxKind::Colon,
1684                        SyntaxKind::Semicolon,
1685                        SyntaxKind::SassOptionalSemicolon,
1686                        SyntaxKind::RightBrace,
1687                        SyntaxKind::SassDedent,
1688                    ],
1689                ),
1690                Some(_) => self.token_current(),
1691                None => break,
1692            }
1693        }
1694        self.builder.finish_node();
1695        if property_kind == SyntaxKind::BogusPropertyName {
1696            self.error_at_current(
1697                ParseErrorCode::UnexpectedCharacter,
1698                "expected declaration property name",
1699            );
1700        }
1701
1702        if self.current_kind() == Some(SyntaxKind::Colon) {
1703            self.token_current();
1704            let value_recovery = [
1705                SyntaxKind::Semicolon,
1706                SyntaxKind::SassOptionalSemicolon,
1707                SyntaxKind::RightBrace,
1708                SyntaxKind::SassDedent,
1709            ];
1710            let has_value = self
1711                .non_trivia_token_from(self.position)
1712                .is_some_and(|(_, kind)| !value_recovery.contains(&kind));
1713            self.builder.start_node(SyntaxKind::Value);
1714            if kind == SyntaxKind::CssModuleComposesDeclaration {
1715                self.parse_composes_value_until(&value_recovery);
1716            } else if starts_custom_property {
1717                self.builder.start_node(SyntaxKind::CustomPropertyValue);
1718                self.parse_component_value_list_until(&value_recovery);
1719                self.builder.finish_node();
1720            } else if !has_value {
1721                self.empty_bogus_node(
1722                    SyntaxKind::BogusValue,
1723                    ParseErrorCode::ExpectedValue,
1724                    "expected declaration value",
1725                );
1726            } else {
1727                self.parse_declaration_value_or_value_list_until(&value_recovery);
1728            }
1729            self.builder.finish_node();
1730        } else {
1731            self.consume_until_recovery(&[
1732                SyntaxKind::Semicolon,
1733                SyntaxKind::SassOptionalSemicolon,
1734                SyntaxKind::RightBrace,
1735                SyntaxKind::SassDedent,
1736            ]);
1737        }
1738
1739        if self.current_kind().is_some_and(is_statement_end) {
1740            self.token_current();
1741        }
1742        self.builder.finish_node();
1743    }
1744
1745    fn parse_composes_value_until(&mut self, recovery: &[SyntaxKind]) {
1746        let mut saw_target = false;
1747        if self.current_composes_value_has_multiple_from_clauses(recovery) {
1748            self.error_at_current(
1749                ParseErrorCode::UnexpectedCharacter,
1750                "multiple composes from clauses are not allowed",
1751            );
1752        }
1753        while !self.at_end() {
1754            self.eat_value_trivia();
1755            match self.current_kind() {
1756                Some(kind) if recovery.contains(&kind) => break,
1757                Some(SyntaxKind::Ident)
1758                    if self
1759                        .current_text()
1760                        .is_some_and(|text| css_keyword(text).equals("from")) =>
1761                {
1762                    if !saw_target {
1763                        self.empty_bogus_node(
1764                            SyntaxKind::BogusComposesTarget,
1765                            ParseErrorCode::UnexpectedCharacter,
1766                            "expected composes target before from clause",
1767                        );
1768                        saw_target = true;
1769                    }
1770                    self.parse_css_module_from_clause(recovery);
1771                }
1772                Some(SyntaxKind::Ident | SyntaxKind::CustomPropertyName) => {
1773                    self.builder.start_node(SyntaxKind::CssModuleComposesTarget);
1774                    self.token_current();
1775                    self.builder.finish_node();
1776                    saw_target = true;
1777                }
1778                Some(kind) if is_interpolation_start(kind) => {
1779                    self.parse_interpolation(kind, recovery)
1780                }
1781                Some(_) => self.token_current(),
1782                None => break,
1783            }
1784        }
1785        if !saw_target {
1786            self.empty_bogus_node(
1787                SyntaxKind::BogusComposesTarget,
1788                ParseErrorCode::UnexpectedCharacter,
1789                "expected composes target",
1790            );
1791        }
1792    }
1793
1794    fn current_composes_value_has_multiple_from_clauses(&self, recovery: &[SyntaxKind]) -> bool {
1795        let mut index = self.position;
1796        let mut paren_depth = 0usize;
1797        let mut bracket_depth = 0usize;
1798        let mut brace_depth = 0usize;
1799        let mut from_count = 0usize;
1800        while let Some(token) = self.tokens.get(index) {
1801            if paren_depth == 0
1802                && bracket_depth == 0
1803                && brace_depth == 0
1804                && recovery.contains(&token.kind)
1805            {
1806                break;
1807            }
1808            match token.kind {
1809                SyntaxKind::LeftParen => paren_depth += 1,
1810                SyntaxKind::RightParen => paren_depth = paren_depth.saturating_sub(1),
1811                SyntaxKind::LeftBracket => bracket_depth += 1,
1812                SyntaxKind::RightBracket => bracket_depth = bracket_depth.saturating_sub(1),
1813                SyntaxKind::LeftBrace => brace_depth += 1,
1814                SyntaxKind::RightBrace => brace_depth = brace_depth.saturating_sub(1),
1815                SyntaxKind::Ident
1816                    if paren_depth == 0
1817                        && bracket_depth == 0
1818                        && brace_depth == 0
1819                        && css_keyword(token.text).equals("from") =>
1820                {
1821                    from_count += 1;
1822                    if from_count > 1 {
1823                        return true;
1824                    }
1825                }
1826                _ => {}
1827            }
1828            index += 1;
1829        }
1830        false
1831    }
1832
1833    fn parse_css_module_from_clause(&mut self, recovery: &[SyntaxKind]) {
1834        let source = self.non_trivia_token_from(self.position + 1);
1835        let has_source = source.is_some_and(|(_, kind)| !recovery.contains(&kind));
1836        let has_valid_source = source.is_some_and(|(index, kind)| {
1837            self.tokens
1838                .get(index)
1839                .is_some_and(|token| is_css_module_from_source_token(kind, token.text))
1840        });
1841        self.builder.start_node(if has_valid_source {
1842            SyntaxKind::CssModuleFromClause
1843        } else {
1844            SyntaxKind::BogusFromClause
1845        });
1846        self.token_current();
1847        while !self.at_end() {
1848            match self.current_kind() {
1849                Some(kind) if recovery.contains(&kind) => break,
1850                Some(_) => self.token_current(),
1851                None => break,
1852            }
1853        }
1854        if !has_source {
1855            self.error_at_current(
1856                ParseErrorCode::UnexpectedCharacter,
1857                "expected CSS Modules from-clause source",
1858            );
1859        } else if !has_valid_source {
1860            self.error_at_current(
1861                ParseErrorCode::ExpectedValue,
1862                "invalid CSS Modules from-clause source",
1863            );
1864        }
1865        self.builder.finish_node();
1866    }
1867
1868    fn current_css_module_scope_context(&self) -> Option<&'static str> {
1869        let mut open_blocks = Vec::new();
1870        for (index, token) in self.tokens.iter().take(self.position).enumerate() {
1871            match token.kind {
1872                SyntaxKind::LeftBrace | SyntaxKind::SassIndent => open_blocks.push(index),
1873                SyntaxKind::RightBrace | SyntaxKind::SassDedent => {
1874                    open_blocks.pop();
1875                }
1876                _ => {}
1877            }
1878        }
1879
1880        if let Some(scope) = open_blocks.iter().copied().find_map(|block_start| {
1881            let header_start = self.header_start_for_block(block_start);
1882            css_module_block_scope_marker_in_header(&self.tokens, header_start, block_start)
1883        }) {
1884            return Some(scope);
1885        }
1886
1887        let block_start = open_blocks.last().copied()?;
1888        let header_start = self.header_start_for_block(block_start);
1889        css_module_header_is_global_only(&self.tokens, header_start, block_start)
1890            .then_some("global")
1891    }
1892
1893    fn header_start_for_block(&self, block_start: usize) -> usize {
1894        let mut index = block_start;
1895        while index > 0 {
1896            let previous = index - 1;
1897            if matches!(
1898                self.tokens[previous].kind,
1899                SyntaxKind::LeftBrace
1900                    | SyntaxKind::RightBrace
1901                    | SyntaxKind::SassIndent
1902                    | SyntaxKind::SassDedent
1903                    | SyntaxKind::Semicolon
1904                    | SyntaxKind::SassOptionalSemicolon
1905            ) {
1906                break;
1907            }
1908            index = previous;
1909        }
1910        index
1911    }
1912
1913    fn parse_dialect_at_rule(&mut self) {
1914        let Some(spec) = self.current_dialect_at_rule_spec() else {
1915            self.parse_at_rule();
1916            return;
1917        };
1918
1919        self.builder
1920            .start_node(self.current_dialect_at_rule_node_kind(spec));
1921        if self.current_kind() == Some(SyntaxKind::AtKeyword) {
1922            self.token_current();
1923        }
1924        if matches!(
1925            spec.node_kind,
1926            SyntaxKind::ScssUseRule | SyntaxKind::ScssForwardRule
1927        ) {
1928            self.parse_scss_module_prelude(spec.node_kind);
1929        }
1930        if is_scss_control_rule_kind(spec.node_kind)
1931            && !self.current_scss_control_prelude_is_valid(spec.node_kind)
1932        {
1933            self.error_at_current(
1934                ParseErrorCode::ExpectedValue,
1935                "invalid SCSS control prelude",
1936            );
1937        }
1938        self.parse_scss_control_condition_prelude(spec.node_kind);
1939        while !self.at_end() {
1940            match self.current_kind() {
1941                Some(kind) if is_statement_end(kind) => {
1942                    self.token_current();
1943                    break;
1944                }
1945                Some(SyntaxKind::LeftBrace) => {
1946                    match spec.block_kind {
1947                        AtRuleBlockKind::GroupRuleList => self.parse_group_at_rule_block(),
1948                        AtRuleBlockKind::DeclarationList => self.parse_declaration_block(),
1949                        AtRuleBlockKind::Keyframes => self.parse_keyframes_block(),
1950                        AtRuleBlockKind::Raw => self.consume_balanced_block(),
1951                    }
1952                    break;
1953                }
1954                Some(SyntaxKind::SassIndent) => {
1955                    self.parse_sass_indented_at_rule_block(spec.block_kind);
1956                    break;
1957                }
1958                Some(_) => self.token_current(),
1959                None => break,
1960            }
1961        }
1962        self.builder.finish_node();
1963    }
1964
1965    fn parse_scss_control_condition_prelude(&mut self, node_kind: SyntaxKind) {
1966        let recovery = [
1967            SyntaxKind::LeftBrace,
1968            SyntaxKind::SassIndent,
1969            SyntaxKind::Semicolon,
1970            SyntaxKind::SassOptionalSemicolon,
1971            SyntaxKind::RightBrace,
1972            SyntaxKind::SassDedent,
1973        ];
1974        match node_kind {
1975            SyntaxKind::ScssControlIf | SyntaxKind::ScssControlWhile => {
1976                self.parse_scss_condition_until(&recovery)
1977            }
1978            SyntaxKind::ScssControlElse
1979                if self
1980                    .current_text()
1981                    .is_some_and(|text| matches_ignore_ascii_case(text, &["if"])) =>
1982            {
1983                self.token_current();
1984                self.parse_scss_condition_until(&recovery);
1985            }
1986            _ => {}
1987        }
1988    }
1989
1990    fn parse_scss_condition_until(&mut self, recovery: &[SyntaxKind]) {
1991        self.parse_dialect_condition_until(
1992            SyntaxKind::ScssCondition,
1993            SyntaxKind::BogusScssCondition,
1994            recovery,
1995        );
1996    }
1997
1998    fn parse_less_condition_until(&mut self, recovery: &[SyntaxKind]) {
1999        self.parse_dialect_condition_until(
2000            SyntaxKind::LessCondition,
2001            SyntaxKind::BogusLessCondition,
2002            recovery,
2003        );
2004    }
2005
2006    fn parse_dialect_condition_until(
2007        &mut self,
2008        condition_kind: SyntaxKind,
2009        bogus_kind: SyntaxKind,
2010        recovery: &[SyntaxKind],
2011    ) {
2012        let has_condition = self
2013            .non_trivia_token_from(self.position)
2014            .is_some_and(|(_, kind)| !recovery.contains(&kind));
2015        self.builder.start_node(if has_condition {
2016            condition_kind
2017        } else {
2018            bogus_kind
2019        });
2020        if has_condition {
2021            self.parse_value_until(recovery);
2022        } else {
2023            self.empty_bogus_node(
2024                SyntaxKind::BogusValue,
2025                ParseErrorCode::ExpectedValue,
2026                "expected condition",
2027            );
2028        }
2029        self.builder.finish_node();
2030    }
2031
2032    fn parse_scss_module_prelude(&mut self, node_kind: SyntaxKind) {
2033        self.validate_scss_module_prelude(node_kind);
2034        while !self.at_end() {
2035            match self.current_kind() {
2036                Some(kind)
2037                    if is_statement_end(kind)
2038                        || kind == SyntaxKind::LeftBrace
2039                        || kind == SyntaxKind::SassIndent =>
2040                {
2041                    break;
2042                }
2043                Some(SyntaxKind::Ident | SyntaxKind::KeywordWith)
2044                    if self.current_text() == Some("with")
2045                        && self
2046                            .non_trivia_token_from(self.position + 1)
2047                            .is_some_and(|(_, kind)| kind == SyntaxKind::LeftParen) =>
2048                {
2049                    self.parse_scss_module_config()
2050                }
2051                Some(kind) if is_interpolation_start(kind) => self.parse_interpolation(
2052                    kind,
2053                    &[
2054                        SyntaxKind::Semicolon,
2055                        SyntaxKind::SassOptionalSemicolon,
2056                        SyntaxKind::LeftBrace,
2057                        SyntaxKind::SassIndent,
2058                    ],
2059                ),
2060                Some(_) => self.token_current(),
2061                None => break,
2062            }
2063        }
2064    }
2065
2066    fn validate_scss_module_prelude(&mut self, node_kind: SyntaxKind) {
2067        let recovery = [
2068            SyntaxKind::Semicolon,
2069            SyntaxKind::SassOptionalSemicolon,
2070            SyntaxKind::LeftBrace,
2071            SyntaxKind::SassIndent,
2072        ];
2073        let Some((source_index, source_kind)) = self.non_trivia_token_from(self.position) else {
2074            self.error_at_current(ParseErrorCode::ExpectedValue, "expected SCSS module source");
2075            return;
2076        };
2077        if recovery.contains(&source_kind) || !is_scss_module_source_token(source_kind) {
2078            let range = self
2079                .tokens
2080                .get(source_index)
2081                .map(|token| token.range)
2082                .unwrap_or_else(|| self.current_range());
2083            self.errors.push(ParseError {
2084                code: ParseErrorCode::ExpectedValue,
2085                range,
2086                message: "expected SCSS module source",
2087            });
2088        }
2089
2090        let mut index = source_index;
2091        while let Some(token) = self.tokens.get(index).copied() {
2092            if recovery.contains(&token.kind) {
2093                break;
2094            }
2095            if token.kind == SyntaxKind::Ident {
2096                if matches_ignore_ascii_case(token.text, &["as"]) {
2097                    let next_kind = self.non_trivia_token_from(index + 1).map(|(_, kind)| kind);
2098                    if next_kind.is_none_or(|kind| {
2099                        recovery.contains(&kind) || !is_scss_module_namespace_token(kind)
2100                    }) {
2101                        self.errors.push(ParseError {
2102                            code: ParseErrorCode::ExpectedValue,
2103                            range: token.range,
2104                            message: "expected SCSS module namespace",
2105                        });
2106                    }
2107                } else if matches_ignore_ascii_case(token.text, &["with"]) {
2108                    let next_kind = self.non_trivia_token_from(index + 1).map(|(_, kind)| kind);
2109                    if next_kind != Some(SyntaxKind::LeftParen) {
2110                        self.errors.push(ParseError {
2111                            code: ParseErrorCode::ExpectedValue,
2112                            range: token.range,
2113                            message: "expected SCSS module configuration",
2114                        });
2115                    }
2116                } else if matches_ignore_ascii_case(token.text, &["show", "hide"]) {
2117                    if node_kind != SyntaxKind::ScssForwardRule {
2118                        self.errors.push(ParseError {
2119                            code: ParseErrorCode::UnexpectedCharacter,
2120                            range: token.range,
2121                            message: "unexpected SCSS module visibility clause",
2122                        });
2123                    }
2124                    let next_kind = self.non_trivia_token_from(index + 1).map(|(_, kind)| kind);
2125                    if next_kind.is_none_or(|kind| {
2126                        recovery.contains(&kind) || !is_scss_module_visibility_name_token(kind)
2127                    }) {
2128                        self.errors.push(ParseError {
2129                            code: ParseErrorCode::ExpectedValue,
2130                            range: token.range,
2131                            message: "expected SCSS module visibility name",
2132                        });
2133                    }
2134                }
2135            }
2136            index += 1;
2137        }
2138    }
2139
2140    fn current_scss_control_prelude_is_valid(&self, node_kind: SyntaxKind) -> bool {
2141        let recovery = [
2142            SyntaxKind::LeftBrace,
2143            SyntaxKind::SassIndent,
2144            SyntaxKind::Semicolon,
2145            SyntaxKind::SassOptionalSemicolon,
2146            SyntaxKind::RightBrace,
2147            SyntaxKind::SassDedent,
2148        ];
2149        match node_kind {
2150            SyntaxKind::ScssControlIf | SyntaxKind::ScssControlWhile => self
2151                .non_trivia_token_from(self.position)
2152                .is_some_and(|(_, kind)| !recovery.contains(&kind)),
2153            SyntaxKind::ScssControlFor => {
2154                self.non_trivia_token_from(self.position)
2155                    .is_some_and(|(_, kind)| kind == SyntaxKind::ScssVariable)
2156                    && self.find_text_before_recovery("from", &recovery)
2157                    && (self.find_text_before_recovery("to", &recovery)
2158                        || self.find_text_before_recovery("through", &recovery))
2159            }
2160            SyntaxKind::ScssControlEach => {
2161                self.non_trivia_token_from(self.position)
2162                    .is_some_and(|(_, kind)| kind == SyntaxKind::ScssVariable)
2163                    && self.find_text_before_recovery("in", &recovery)
2164            }
2165            SyntaxKind::ScssControlElse => true,
2166            _ => true,
2167        }
2168    }
2169
2170    fn parse_scss_module_config(&mut self) {
2171        let has_balanced_config = self.current_scss_module_config_has_balanced_parens();
2172        self.builder.start_node(if has_balanced_config {
2173            SyntaxKind::ScssModuleConfig
2174        } else {
2175            SyntaxKind::BogusScssModuleConfig
2176        });
2177        self.token_current();
2178        self.eat_trivia();
2179        if self.current_kind() == Some(SyntaxKind::LeftParen) {
2180            self.parse_balanced_parenthesized_prelude_until(
2181                None,
2182                &[
2183                    SyntaxKind::LeftBrace,
2184                    SyntaxKind::SassIndent,
2185                    SyntaxKind::Semicolon,
2186                    SyntaxKind::SassOptionalSemicolon,
2187                ],
2188            );
2189        }
2190        self.builder.finish_node();
2191    }
2192
2193    fn parse_css_module_value_rule(&mut self) {
2194        let has_name = self
2195            .non_trivia_token_from(self.position + 1)
2196            .and_then(|(index, kind)| {
2197                self.tokens
2198                    .get(index)
2199                    .map(|token| (kind, !css_keyword(token.text).equals("from")))
2200            })
2201            .is_some_and(|(kind, allowed_name)| {
2202                allowed_name && matches!(kind, SyntaxKind::Ident | SyntaxKind::CustomPropertyName)
2203            });
2204        let has_from = self.find_keyword_before_recovery(
2205            "from",
2206            &[
2207                SyntaxKind::Semicolon,
2208                SyntaxKind::SassOptionalSemicolon,
2209                SyntaxKind::LeftBrace,
2210                SyntaxKind::SassIndent,
2211            ],
2212        );
2213        let has_colon = self.find_before_recovery(
2214            SyntaxKind::Colon,
2215            &[
2216                SyntaxKind::Semicolon,
2217                SyntaxKind::SassOptionalSemicolon,
2218                SyntaxKind::LeftBrace,
2219                SyntaxKind::SassIndent,
2220            ],
2221        );
2222        let kind = if !has_name {
2223            SyntaxKind::BogusCssModuleBlock
2224        } else if has_from && !has_colon {
2225            SyntaxKind::CssModuleImportBlock
2226        } else {
2227            SyntaxKind::CssModuleExportBlock
2228        };
2229
2230        self.builder.start_node(kind);
2231        self.token_current();
2232        if !has_name {
2233            self.error_at_current(
2234                ParseErrorCode::UnexpectedCharacter,
2235                "expected CSS Modules @value name",
2236            );
2237        }
2238        if has_colon {
2239            self.parse_css_module_value_export();
2240        } else {
2241            self.parse_css_module_value_import_or_statement();
2242        }
2243        if self.current_kind().is_some_and(is_statement_end) {
2244            self.token_current();
2245        }
2246        self.builder.finish_node();
2247    }
2248
2249    fn parse_css_module_value_export(&mut self) {
2250        self.parse_css_module_token_definitions_until(&[
2251            SyntaxKind::Colon,
2252            SyntaxKind::Semicolon,
2253            SyntaxKind::SassOptionalSemicolon,
2254        ]);
2255        if self.current_kind() == Some(SyntaxKind::Colon) {
2256            self.token_current();
2257            self.builder.start_node(SyntaxKind::Value);
2258            self.parse_css_module_token_references_until(&[
2259                SyntaxKind::Semicolon,
2260                SyntaxKind::SassOptionalSemicolon,
2261            ]);
2262            self.builder.finish_node();
2263        }
2264    }
2265
2266    fn parse_css_module_value_import_or_statement(&mut self) {
2267        self.parse_css_module_token_definitions_until(&[
2268            SyntaxKind::Semicolon,
2269            SyntaxKind::SassOptionalSemicolon,
2270        ]);
2271    }
2272
2273    fn parse_css_module_token_definitions_until(&mut self, recovery: &[SyntaxKind]) {
2274        while !self.at_end() {
2275            match self.current_kind() {
2276                Some(kind) if recovery.contains(&kind) => break,
2277                Some(SyntaxKind::Ident)
2278                    if self
2279                        .current_text()
2280                        .is_some_and(|text| css_keyword(text).equals("from")) =>
2281                {
2282                    self.parse_css_module_from_clause(recovery);
2283                    break;
2284                }
2285                Some(SyntaxKind::Ident | SyntaxKind::CustomPropertyName) => {
2286                    self.builder.start_node(SyntaxKind::TokenDefinition);
2287                    self.token_current();
2288                    self.builder.finish_node();
2289                }
2290                Some(_) => self.token_current(),
2291                None => break,
2292            }
2293        }
2294    }
2295
2296    fn parse_css_module_token_references_until(&mut self, recovery: &[SyntaxKind]) {
2297        while !self.at_end() {
2298            self.eat_value_trivia();
2299            match self.current_kind() {
2300                Some(kind) if recovery.contains(&kind) => break,
2301                Some(SyntaxKind::Ident | SyntaxKind::CustomPropertyName) => {
2302                    self.builder.start_node(SyntaxKind::TokenReference);
2303                    self.token_current();
2304                    self.builder.finish_node();
2305                }
2306                Some(kind) if is_interpolation_start(kind) => {
2307                    self.parse_interpolation(kind, recovery)
2308                }
2309                Some(_) => self.token_current(),
2310                None => break,
2311            }
2312        }
2313    }
2314
2315    fn parse_less_mixin_header(&mut self) {
2316        self.builder.start_node(SyntaxKind::SelectorList);
2317        self.parse_until_recovery_with_optional_less_guard(&[SyntaxKind::LeftBrace]);
2318        self.builder.finish_node();
2319    }
2320
2321    fn parse_less_mixin_call(&mut self) {
2322        self.builder.start_node(SyntaxKind::LessMixinCall);
2323        self.parse_until_recovery_with_optional_less_guard(&[
2324            SyntaxKind::Semicolon,
2325            SyntaxKind::SassOptionalSemicolon,
2326            SyntaxKind::RightBrace,
2327            SyntaxKind::SassDedent,
2328        ]);
2329        if self.current_kind().is_some_and(is_statement_end) {
2330            self.token_current();
2331        }
2332        self.builder.finish_node();
2333    }
2334
2335    fn parse_less_namespace_access(&mut self) {
2336        self.builder.start_node(SyntaxKind::LessNamespaceAccess);
2337        while !self.at_end() {
2338            match self.current_kind() {
2339                Some(
2340                    SyntaxKind::Semicolon
2341                    | SyntaxKind::SassOptionalSemicolon
2342                    | SyntaxKind::RightBrace
2343                    | SyntaxKind::SassDedent
2344                    | SyntaxKind::LeftBrace
2345                    | SyntaxKind::SassIndent,
2346                ) => break,
2347                Some(_) if self.current_starts_less_mixin_call() => {
2348                    self.parse_less_mixin_call();
2349                    break;
2350                }
2351                Some(_) => self.token_current(),
2352                None => break,
2353            }
2354        }
2355        if self.current_kind().is_some_and(is_statement_end) {
2356            self.token_current();
2357        }
2358        self.builder.finish_node();
2359    }
2360
2361    fn parse_until_recovery_with_optional_less_guard(&mut self, recovery: &[SyntaxKind]) {
2362        let mut guard_open = false;
2363        while !self.at_end() {
2364            match self.current_kind() {
2365                Some(kind) if recovery.contains(&kind) => break,
2366                Some(SyntaxKind::Ident) if self.current_text() == Some("when") && !guard_open => {
2367                    self.builder.start_node(
2368                        if self.current_less_guard_has_condition_before(recovery) {
2369                            SyntaxKind::LessMixinGuard
2370                        } else {
2371                            SyntaxKind::BogusLessGuard
2372                        },
2373                    );
2374                    guard_open = true;
2375                    self.token_current();
2376                    self.parse_less_condition_until(recovery);
2377                }
2378                Some(_) => self.token_current(),
2379                None => break,
2380            }
2381        }
2382        if guard_open {
2383            self.builder.finish_node();
2384        }
2385    }
2386
2387    fn parse_value_until(&mut self, recovery: &[SyntaxKind]) {
2388        if self.current_starts_scss_space_list_before(recovery) {
2389            self.parse_scss_space_list_until(recovery);
2390            return;
2391        }
2392        while !self.at_end() {
2393            self.eat_value_trivia();
2394            if matches!(self.current_kind(), Some(kind) if recovery.contains(&kind)) {
2395                break;
2396            }
2397            if self.at_end() {
2398                break;
2399            }
2400            self.parse_value_expression(0, recovery);
2401        }
2402    }
2403
2404    fn parse_value_or_value_list_until(&mut self, recovery: &[SyntaxKind]) {
2405        if self.current_value_has_top_level_comma_before(recovery) {
2406            self.parse_value_list_until(recovery);
2407        } else {
2408            self.parse_value_until(recovery);
2409        }
2410    }
2411
2412    fn parse_declaration_value_or_value_list_until(&mut self, recovery: &[SyntaxKind]) {
2413        if self.current_value_has_top_level_comma_before(recovery) {
2414            self.parse_declaration_value_list_until(recovery);
2415        } else {
2416            self.parse_declaration_value_until(recovery);
2417        }
2418    }
2419
2420    fn parse_declaration_value_until(&mut self, recovery: &[SyntaxKind]) {
2421        if self.current_starts_scss_space_list_before(recovery) {
2422            self.parse_scss_space_list_until(recovery);
2423            return;
2424        }
2425        let mut saw_value = false;
2426        while !self.at_end() {
2427            self.eat_value_trivia();
2428            if matches!(self.current_kind(), Some(kind) if recovery.contains(&kind)) {
2429                break;
2430            }
2431            if saw_value && self.current_starts_missing_semicolon_declaration(recovery) {
2432                self.error_at_current(
2433                    ParseErrorCode::UnexpectedCharacter,
2434                    "expected semicolon between declarations",
2435                );
2436                break;
2437            }
2438            if self.at_end() {
2439                break;
2440            }
2441            self.parse_value_expression(0, recovery);
2442            saw_value = true;
2443        }
2444    }
2445
2446    fn parse_declaration_value_list_until(&mut self, recovery: &[SyntaxKind]) {
2447        self.builder
2448            .start_node(if self.current_value_list_is_bogus(recovery) {
2449                SyntaxKind::BogusValueList
2450            } else {
2451                SyntaxKind::ValueList
2452            });
2453        let item_recovery = value_list_item_recovery(recovery);
2454        let mut saw_item = false;
2455        while !self.at_end() {
2456            self.eat_value_trivia();
2457            match self.current_kind() {
2458                Some(kind) if recovery.contains(&kind) => break,
2459                Some(SyntaxKind::Comma) => self.token_current(),
2460                Some(_)
2461                    if saw_item && self.current_starts_missing_semicolon_declaration(recovery) =>
2462                {
2463                    self.error_at_current(
2464                        ParseErrorCode::UnexpectedCharacter,
2465                        "expected semicolon between declarations",
2466                    );
2467                    break;
2468                }
2469                Some(_) => {
2470                    self.parse_value_expression(0, &item_recovery);
2471                    saw_item = true;
2472                }
2473                None => break,
2474            }
2475        }
2476        self.builder.finish_node();
2477    }
2478
2479    fn parse_value_list_until(&mut self, recovery: &[SyntaxKind]) {
2480        self.builder
2481            .start_node(if self.current_value_list_is_bogus(recovery) {
2482                SyntaxKind::BogusValueList
2483            } else {
2484                SyntaxKind::ValueList
2485            });
2486        let item_recovery = value_list_item_recovery(recovery);
2487        while !self.at_end() {
2488            self.eat_value_trivia();
2489            match self.current_kind() {
2490                Some(kind) if recovery.contains(&kind) => break,
2491                Some(SyntaxKind::Comma) => self.token_current(),
2492                Some(_) => self.parse_value_expression(0, &item_recovery),
2493                None => break,
2494            }
2495        }
2496        self.builder.finish_node();
2497    }
2498
2499    fn parse_component_value(&mut self, recovery: &[SyntaxKind]) {
2500        self.builder.start_node(SyntaxKind::ComponentValue);
2501        self.parse_component_value_inner(recovery);
2502        self.builder.finish_node();
2503    }
2504
2505    fn parse_component_value_list_until(&mut self, recovery: &[SyntaxKind]) {
2506        self.builder.start_node(SyntaxKind::ComponentValueList);
2507        while !self.at_end() {
2508            self.eat_value_trivia();
2509            match self.current_kind() {
2510                Some(kind) if recovery.contains(&kind) => break,
2511                Some(_) => self.parse_component_value(recovery),
2512                None => break,
2513            }
2514        }
2515        self.builder.finish_node();
2516    }
2517
2518    fn parse_comma_separated_component_value_list_until(&mut self, recovery: &[SyntaxKind]) {
2519        self.builder
2520            .start_node(SyntaxKind::CommaSeparatedComponentValueList);
2521        let item_recovery = comma_separated_component_value_list_item_recovery(recovery);
2522        while !self.at_end() {
2523            self.eat_value_trivia();
2524            match self.current_kind() {
2525                Some(kind) if recovery.contains(&kind) => break,
2526                Some(SyntaxKind::Comma) => self.token_current(),
2527                Some(_) => self.parse_component_value(&item_recovery),
2528                None => break,
2529            }
2530        }
2531        self.builder.finish_node();
2532    }
2533
2534    fn parse_component_value_inner(&mut self, recovery: &[SyntaxKind]) {
2535        self.eat_value_trivia();
2536        match self.current_kind() {
2537            Some(kind) if recovery.contains(&kind) => {
2538                self.empty_bogus_node(
2539                    SyntaxKind::BogusValue,
2540                    ParseErrorCode::ExpectedValue,
2541                    "expected component value",
2542                );
2543            }
2544            Some(SyntaxKind::LeftBrace | SyntaxKind::LeftBracket | SyntaxKind::LeftParen) => {
2545                self.parse_simple_block(recovery)
2546            }
2547            Some(SyntaxKind::Ident) if self.next_kind() == Some(SyntaxKind::LeftParen) => {
2548                self.parse_function_call(recovery)
2549            }
2550            Some(kind) if is_component_value_atom_start(kind) => self.parse_value_prefix(recovery),
2551            Some(_) => self.token_current(),
2552            None => {
2553                self.empty_bogus_node(
2554                    SyntaxKind::BogusValue,
2555                    ParseErrorCode::ExpectedValue,
2556                    "expected component value",
2557                );
2558            }
2559        }
2560    }
2561
2562    fn parse_simple_block_entry_point(&mut self, recovery: &[SyntaxKind]) {
2563        self.eat_value_trivia();
2564        match self.current_kind() {
2565            Some(SyntaxKind::LeftBrace | SyntaxKind::LeftBracket | SyntaxKind::LeftParen) => {
2566                self.parse_simple_block(recovery)
2567            }
2568            Some(_) | None => {
2569                self.empty_bogus_node(
2570                    SyntaxKind::BogusSimpleBlock,
2571                    ParseErrorCode::ExpectedValue,
2572                    "expected simple block",
2573                );
2574            }
2575        }
2576    }
2577
2578    fn parse_simple_block(&mut self, recovery: &[SyntaxKind]) {
2579        let Some(open_kind) = self.current_kind() else {
2580            self.empty_bogus_node(
2581                SyntaxKind::BogusSimpleBlock,
2582                ParseErrorCode::ExpectedValue,
2583                "expected simple block",
2584            );
2585            return;
2586        };
2587        let Some(close_kind) = matching_simple_block_close(open_kind) else {
2588            self.empty_bogus_node(
2589                SyntaxKind::BogusSimpleBlock,
2590                ParseErrorCode::ExpectedValue,
2591                "expected simple block",
2592            );
2593            return;
2594        };
2595
2596        let block_kind = if self.current_simple_block_has_matching_close(recovery) {
2597            SyntaxKind::SimpleBlock
2598        } else {
2599            SyntaxKind::BogusSimpleBlock
2600        };
2601        self.builder.start_node(block_kind);
2602        self.token_current();
2603
2604        let block_recovery = simple_block_recovery(close_kind, recovery);
2605        while !self.at_end() {
2606            self.eat_value_trivia();
2607            match self.current_kind() {
2608                Some(kind) if kind == close_kind => break,
2609                Some(kind) if recovery.contains(&kind) => break,
2610                Some(_) => self.parse_component_value(&block_recovery),
2611                None => break,
2612            }
2613        }
2614
2615        if self.current_kind() == Some(close_kind) {
2616            self.token_current();
2617        } else {
2618            self.error_at_current(
2619                ParseErrorCode::UnexpectedCharacter,
2620                "unterminated simple block",
2621            );
2622        }
2623        self.builder.finish_node();
2624    }
2625
2626    fn parse_value_expression(&mut self, min_binding_power: u8, recovery: &[SyntaxKind]) {
2627        self.eat_value_trivia();
2628        let checkpoint = self.builder.checkpoint();
2629        self.parse_value_prefix(recovery);
2630
2631        loop {
2632            self.eat_value_trivia();
2633            let Some(operator) = self.current_kind() else {
2634                break;
2635            };
2636            if recovery.contains(&operator) {
2637                break;
2638            }
2639            let Some(binding) = self.current_value_infix_operator_binding(operator) else {
2640                break;
2641            };
2642            if binding.left_binding_power < min_binding_power {
2643                break;
2644            }
2645
2646            self.builder
2647                .start_node_at(checkpoint, SyntaxKind::BinaryExpression);
2648            self.consume_current_value_infix_operator(binding.token_count);
2649            self.parse_value_expression(binding.right_binding_power, recovery);
2650            self.builder.finish_node();
2651        }
2652    }
2653
2654    fn parse_value_prefix(&mut self, recovery: &[SyntaxKind]) {
2655        match self.current_kind() {
2656            Some(SyntaxKind::Plus | SyntaxKind::Minus) => {
2657                self.builder.start_node(SyntaxKind::UnaryExpression);
2658                self.token_current();
2659                self.parse_value_expression(UNARY_PREFIX_RIGHT_BINDING_POWER, recovery);
2660                self.builder.finish_node();
2661            }
2662            Some(SyntaxKind::KeywordNot)
2663                if dialect_allows_value_logical_operators(self.dialect) =>
2664            {
2665                self.builder.start_node(SyntaxKind::UnaryExpression);
2666                self.token_current();
2667                self.parse_value_expression(UNARY_PREFIX_RIGHT_BINDING_POWER, recovery);
2668                self.builder.finish_node();
2669            }
2670            Some(SyntaxKind::Ident)
2671                if dialect_allows_value_logical_operators(self.dialect)
2672                    && self
2673                        .current_text()
2674                        .is_some_and(|text| matches_ignore_ascii_case(text, &["not"])) =>
2675            {
2676                self.builder.start_node(SyntaxKind::UnaryExpression);
2677                self.token_current();
2678                self.parse_value_expression(UNARY_PREFIX_RIGHT_BINDING_POWER, recovery);
2679                self.builder.finish_node();
2680            }
2681            Some(SyntaxKind::Ident)
2682                if self
2683                    .current_text()
2684                    .is_some_and(|text| matches_ignore_ascii_case(text, &["url"]))
2685                    && self.next_kind() == Some(SyntaxKind::LeftParen) =>
2686            {
2687                self.builder.start_node(SyntaxKind::UrlValue);
2688                self.parse_function_call(recovery);
2689                self.builder.finish_node();
2690            }
2691            Some(SyntaxKind::Ident) if self.next_kind() == Some(SyntaxKind::LeftParen) => {
2692                self.parse_function_call(recovery)
2693            }
2694            Some(SyntaxKind::Number) => {
2695                self.builder.start_node(SyntaxKind::NumberValue);
2696                self.token_current();
2697                self.builder.finish_node();
2698            }
2699            Some(SyntaxKind::Percentage) => {
2700                self.builder.start_node(SyntaxKind::PercentageValue);
2701                self.token_current();
2702                self.builder.finish_node();
2703            }
2704            Some(SyntaxKind::Dimension) => {
2705                self.builder.start_node(SyntaxKind::DimensionValue);
2706                self.token_current();
2707                self.builder.finish_node();
2708            }
2709            Some(
2710                SyntaxKind::Ident
2711                | SyntaxKind::CustomPropertyName
2712                | SyntaxKind::TemplatePlaceholder,
2713            ) => {
2714                self.builder.start_node(SyntaxKind::IdentifierValue);
2715                self.token_current();
2716                self.builder.finish_node();
2717            }
2718            Some(SyntaxKind::String | SyntaxKind::LessEscapedString) => {
2719                self.builder.start_node(SyntaxKind::StringValue);
2720                self.token_current();
2721                self.builder.finish_node();
2722            }
2723            Some(SyntaxKind::UnicodeRange) => {
2724                self.builder.start_node(SyntaxKind::UnicodeRangeValue);
2725                self.token_current();
2726                self.builder.finish_node();
2727            }
2728            Some(SyntaxKind::Hash) => {
2729                self.builder.start_node(SyntaxKind::ColorValue);
2730                self.token_current();
2731                self.builder.finish_node();
2732            }
2733            Some(SyntaxKind::Url) => {
2734                self.builder.start_node(SyntaxKind::UrlValue);
2735                self.token_current();
2736                self.builder.finish_node();
2737            }
2738            Some(SyntaxKind::BadUrl) => {
2739                self.builder.start_node(SyntaxKind::BogusValue);
2740                self.token_current();
2741                self.builder.finish_node();
2742            }
2743            Some(SyntaxKind::BadString) => {
2744                self.builder.start_node(SyntaxKind::BogusValue);
2745                self.token_current();
2746                self.builder.finish_node();
2747            }
2748            Some(SyntaxKind::Important) => {
2749                self.builder.start_node(SyntaxKind::ImportantAnnotation);
2750                self.token_current();
2751                self.builder.finish_node();
2752            }
2753            Some(SyntaxKind::Delim) if self.current_split_important_annotation() => {
2754                self.parse_split_important_annotation()
2755            }
2756            Some(SyntaxKind::Delim) if self.current_scss_variable_flag_annotation() => {
2757                self.parse_scss_variable_flag_annotation()
2758            }
2759            Some(kind) if is_interpolation_start(kind) => self.parse_interpolation(kind, recovery),
2760            Some(SyntaxKind::ScssVariable) => {
2761                self.builder.start_node(SyntaxKind::ScssVariableReference);
2762                self.token_current();
2763                self.builder.finish_node();
2764            }
2765            Some(SyntaxKind::LessVariable) => {
2766                self.builder.start_node(SyntaxKind::LessVariableReference);
2767                self.token_current();
2768                self.builder.finish_node();
2769            }
2770            Some(SyntaxKind::LessPropertyVariableToken) => {
2771                self.builder.start_node(SyntaxKind::LessPropertyVariable);
2772                self.token_current();
2773                self.builder.finish_node();
2774            }
2775            Some(SyntaxKind::LeftBrace) => self.parse_simple_block(recovery),
2776            Some(SyntaxKind::LeftParen)
2777                if self
2778                    .current_scss_parenthesized_collection_kind(recovery)
2779                    .is_some() =>
2780            {
2781                self.parse_scss_parenthesized_collection(recovery)
2782            }
2783            Some(SyntaxKind::LeftParen) => self.parse_parenthesized_expression(recovery),
2784            Some(SyntaxKind::LeftBracket) => self.parse_bracketed_value(recovery),
2785            Some(kind) if recovery.contains(&kind) => {
2786                self.empty_bogus_node(
2787                    SyntaxKind::BogusValue,
2788                    ParseErrorCode::ExpectedValue,
2789                    "expected value",
2790                );
2791            }
2792            Some(SyntaxKind::Delim) => {
2793                self.builder.start_node(SyntaxKind::BogusToken);
2794                self.token_current();
2795                self.builder.finish_node();
2796            }
2797            Some(_) => {
2798                self.builder.start_node(SyntaxKind::BogusValue);
2799                self.error_at_current(ParseErrorCode::ExpectedValue, "expected value");
2800                self.token_current();
2801                self.builder.finish_node();
2802            }
2803            None => {
2804                self.empty_bogus_node(
2805                    SyntaxKind::BogusValue,
2806                    ParseErrorCode::ExpectedValue,
2807                    "expected value",
2808                );
2809            }
2810        }
2811    }
2812
2813    fn current_value_infix_operator_binding(
2814        &self,
2815        operator: SyntaxKind,
2816    ) -> Option<crate::syntax_helpers::ValueInfixOperatorBinding> {
2817        value_infix_operator_binding(
2818            self.dialect,
2819            operator,
2820            self.current_text(),
2821            self.next_kind(),
2822            self.current_token_is_adjacent_to_next(),
2823        )
2824    }
2825
2826    fn consume_current_value_infix_operator(&mut self, token_count: usize) {
2827        for _ in 0..token_count {
2828            self.token_current();
2829        }
2830    }
2831
2832    fn parse_split_important_annotation(&mut self) {
2833        self.builder.start_node(SyntaxKind::ImportantAnnotation);
2834        self.token_current();
2835        self.eat_value_trivia();
2836        if self
2837            .current_text()
2838            .is_some_and(|text| matches_ignore_ascii_case(text, &["important"]))
2839        {
2840            self.token_current();
2841        }
2842        self.builder.finish_node();
2843    }
2844
2845    fn parse_scss_variable_flag_annotation(&mut self) {
2846        self.builder.start_node(SyntaxKind::ScssVariableFlag);
2847        self.token_current();
2848        self.eat_value_trivia();
2849        self.token_current();
2850        self.builder.finish_node();
2851    }
2852
2853    fn eat_value_trivia(&mut self) {
2854        while matches!(self.current_kind(), Some(kind) if kind.is_trivia()) {
2855            self.token_current();
2856        }
2857    }
2858
2859    fn parse_function_call(&mut self, recovery: &[SyntaxKind]) {
2860        let function_name = self.current_text().map(str::to_owned);
2861        let function_range = self.current_range();
2862        let argument_count = self.current_function_top_level_argument_count_before(recovery);
2863        let has_empty_argument_slot =
2864            self.current_function_has_empty_top_level_argument_slot_before(recovery);
2865        let argument_head = self.current_function_first_argument_token_before(recovery);
2866        let specialized_kind = function_name.as_deref().and_then(specialized_function_kind);
2867        let uses_component_value_arguments = function_name.as_deref().is_some_and(|name| {
2868            matches_ignore_ascii_case(name, &["if", "media", "supports", "style"])
2869        });
2870        let closed = self.current_function_has_closing_paren_before(recovery);
2871        let function_kind = if closed {
2872            SyntaxKind::FunctionCall
2873        } else {
2874            SyntaxKind::BogusFunctionCall
2875        };
2876        let arguments_kind = if closed {
2877            SyntaxKind::FunctionArguments
2878        } else {
2879            SyntaxKind::BogusFunctionArguments
2880        };
2881
2882        self.builder.start_node(function_kind);
2883        if let Some(kind) = specialized_kind {
2884            self.builder.start_node(kind);
2885        }
2886        self.token_current();
2887        if self.current_kind() == Some(SyntaxKind::LeftParen) {
2888            self.token_current();
2889            self.builder.start_node(arguments_kind);
2890            let mut argument_recovery = function_argument_recovery(recovery);
2891            if function_name
2892                .as_deref()
2893                .is_some_and(|name| matches_ignore_ascii_case(name, &["if"]))
2894            {
2895                argument_recovery.retain(|kind| {
2896                    !matches!(
2897                        kind,
2898                        SyntaxKind::Semicolon | SyntaxKind::SassOptionalSemicolon
2899                    )
2900                });
2901            }
2902            if uses_component_value_arguments {
2903                self.parse_component_value_list_until(&argument_recovery);
2904            } else {
2905                self.parse_value_or_value_list_until(&argument_recovery);
2906            }
2907            self.builder.finish_node();
2908            if self.current_kind() == Some(SyntaxKind::RightParen) {
2909                self.token_current();
2910            } else {
2911                self.error_at_current(
2912                    ParseErrorCode::UnexpectedCharacter,
2913                    "unterminated function call",
2914                );
2915            }
2916        }
2917        if let Some(function_name) = function_name {
2918            if let Some(argument_count) = argument_count {
2919                self.validate_function_argument_count(
2920                    &function_name,
2921                    argument_count,
2922                    function_range,
2923                );
2924            }
2925            if let Some(true) = has_empty_argument_slot {
2926                self.validate_function_argument_slots(&function_name, function_range);
2927            }
2928            self.validate_function_argument_head(&function_name, argument_head, function_range);
2929        }
2930        if specialized_kind.is_some() {
2931            self.builder.finish_node();
2932        }
2933        self.builder.finish_node();
2934    }
2935
2936    fn current_function_top_level_argument_count_before(
2937        &self,
2938        recovery: &[SyntaxKind],
2939    ) -> Option<usize> {
2940        if self.next_kind() != Some(SyntaxKind::LeftParen) {
2941            return None;
2942        }
2943
2944        let mut index = self.position + 2;
2945        let mut depth = 0usize;
2946        let mut comma_count = 0usize;
2947        let mut saw_argument = false;
2948        while let Some(token) = self.tokens.get(index) {
2949            match token.kind {
2950                kind if depth == 0 && recovery.contains(&kind) => return None,
2951                SyntaxKind::RightParen if depth == 0 => {
2952                    return Some(if saw_argument { comma_count + 1 } else { 0 });
2953                }
2954                SyntaxKind::Comma if depth == 0 => {
2955                    comma_count += 1;
2956                    saw_argument = false;
2957                }
2958                kind if kind.is_trivia() => {}
2959                SyntaxKind::LeftBrace | SyntaxKind::LeftBracket | SyntaxKind::LeftParen => {
2960                    depth += 1;
2961                    saw_argument = true;
2962                }
2963                SyntaxKind::RightBrace | SyntaxKind::RightBracket | SyntaxKind::RightParen => {
2964                    depth = depth.saturating_sub(1);
2965                    saw_argument = true;
2966                }
2967                _ => saw_argument = true,
2968            }
2969            index += 1;
2970        }
2971        None
2972    }
2973
2974    fn current_function_has_empty_top_level_argument_slot_before(
2975        &self,
2976        recovery: &[SyntaxKind],
2977    ) -> Option<bool> {
2978        if self.next_kind() != Some(SyntaxKind::LeftParen) {
2979            return None;
2980        }
2981
2982        let mut index = self.position + 2;
2983        let mut depth = 0usize;
2984        let mut expecting_argument = true;
2985        let mut saw_argument = false;
2986        while let Some(token) = self.tokens.get(index) {
2987            match token.kind {
2988                kind if depth == 0 && recovery.contains(&kind) => return None,
2989                SyntaxKind::RightParen if depth == 0 => {
2990                    return Some(expecting_argument && saw_argument);
2991                }
2992                SyntaxKind::Comma if depth == 0 => {
2993                    if expecting_argument {
2994                        return Some(true);
2995                    }
2996                    expecting_argument = true;
2997                }
2998                kind if kind.is_trivia() => {}
2999                SyntaxKind::LeftBrace | SyntaxKind::LeftBracket | SyntaxKind::LeftParen => {
3000                    depth += 1;
3001                    expecting_argument = false;
3002                    saw_argument = true;
3003                }
3004                SyntaxKind::RightBrace | SyntaxKind::RightBracket | SyntaxKind::RightParen => {
3005                    depth = depth.saturating_sub(1);
3006                    expecting_argument = false;
3007                    saw_argument = true;
3008                }
3009                _ => {
3010                    expecting_argument = false;
3011                    saw_argument = true;
3012                }
3013            }
3014            index += 1;
3015        }
3016        None
3017    }
3018
3019    fn current_function_first_argument_token_before(
3020        &self,
3021        recovery: &[SyntaxKind],
3022    ) -> Option<Token<'text>> {
3023        if self.next_kind() != Some(SyntaxKind::LeftParen) {
3024            return None;
3025        }
3026
3027        let mut index = self.position + 2;
3028        while let Some(token) = self.tokens.get(index).copied() {
3029            match token.kind {
3030                kind if recovery.contains(&kind) => return None,
3031                SyntaxKind::RightParen => return None,
3032                kind if kind.is_trivia() => {}
3033                _ => return Some(token),
3034            }
3035            index += 1;
3036        }
3037        None
3038    }
3039
3040    fn validate_function_argument_count(
3041        &mut self,
3042        function_name: &str,
3043        argument_count: usize,
3044        range: TextRange,
3045    ) {
3046        if function_argument_count_is_valid(function_name, argument_count) {
3047            return;
3048        }
3049        self.errors.push(ParseError {
3050            code: ParseErrorCode::ExpectedValue,
3051            range,
3052            message: "invalid function argument count",
3053        });
3054    }
3055
3056    fn validate_function_argument_slots(&mut self, function_name: &str, range: TextRange) {
3057        if !function_requires_filled_top_level_arguments(function_name) {
3058            return;
3059        }
3060        self.errors.push(ParseError {
3061            code: ParseErrorCode::ExpectedValue,
3062            range,
3063            message: "empty function argument",
3064        });
3065    }
3066
3067    fn validate_function_argument_head(
3068        &mut self,
3069        function_name: &str,
3070        argument_head: Option<Token<'text>>,
3071        range: TextRange,
3072    ) {
3073        let head_kind = argument_head.map(|token| token.kind);
3074        let valid = if matches_ignore_ascii_case(function_name, &["var"]) {
3075            matches!(head_kind, Some(SyntaxKind::CustomPropertyName))
3076                || head_kind.is_some_and(is_dynamic_function_argument_head)
3077        } else if matches_ignore_ascii_case(function_name, &["env"]) {
3078            matches!(
3079                head_kind,
3080                Some(SyntaxKind::Ident | SyntaxKind::CustomPropertyName)
3081            ) || head_kind.is_some_and(is_dynamic_function_argument_head)
3082        } else if matches_ignore_ascii_case(function_name, &["attr"]) {
3083            matches!(head_kind, Some(SyntaxKind::Ident))
3084                || head_kind.is_some_and(is_dynamic_function_argument_head)
3085        } else if matches_ignore_ascii_case(function_name, &["color-mix"]) {
3086            argument_head.is_some_and(|token| matches_ignore_ascii_case(token.text, &["in"]))
3087                || head_kind.is_some_and(is_dynamic_function_argument_head)
3088        } else {
3089            true
3090        };
3091
3092        if valid {
3093            return;
3094        }
3095        self.errors.push(ParseError {
3096            code: ParseErrorCode::ExpectedValue,
3097            range,
3098            message: "invalid function argument head",
3099        });
3100    }
3101
3102    fn parse_bracketed_value(&mut self, recovery: &[SyntaxKind]) {
3103        let closed = self.current_bracketed_value_has_closing_bracket_before(recovery);
3104        self.builder.start_node(if closed {
3105            SyntaxKind::BracketedValue
3106        } else {
3107            SyntaxKind::BogusBracketedValue
3108        });
3109        self.token_current();
3110        let bracket_recovery = bracketed_value_recovery(recovery);
3111        self.parse_value_until(&bracket_recovery);
3112        if self.current_kind() == Some(SyntaxKind::RightBracket) {
3113            self.token_current();
3114        } else {
3115            self.error_at_current(
3116                ParseErrorCode::UnexpectedCharacter,
3117                "unterminated bracketed value",
3118            );
3119        }
3120        self.builder.finish_node();
3121    }
3122
3123    fn parse_scss_parenthesized_collection(&mut self, recovery: &[SyntaxKind]) {
3124        let Some(collection_kind) = self.current_scss_parenthesized_collection_kind(recovery)
3125        else {
3126            self.parse_parenthesized_expression(recovery);
3127            return;
3128        };
3129        let closed = self.current_parenthesized_collection_has_closing_paren_before(recovery);
3130        self.builder.start_node(match (collection_kind, closed) {
3131            (SyntaxKind::ScssMap, true) => SyntaxKind::ScssMap,
3132            (SyntaxKind::ScssMap, false) => SyntaxKind::BogusScssMap,
3133            (SyntaxKind::ScssList, true) => SyntaxKind::ScssList,
3134            (SyntaxKind::ScssList, false) => SyntaxKind::BogusScssList,
3135            _ => collection_kind,
3136        });
3137        self.token_current();
3138        let paren_recovery = function_argument_recovery(recovery);
3139        match collection_kind {
3140            SyntaxKind::ScssMap => self.parse_scss_map_entries_until(&paren_recovery),
3141            SyntaxKind::ScssList => self.parse_scss_list_items_until(&paren_recovery),
3142            _ => {}
3143        }
3144        if self.current_kind() == Some(SyntaxKind::RightParen) {
3145            self.token_current();
3146        } else {
3147            self.error_at_current(
3148                ParseErrorCode::UnexpectedCharacter,
3149                "unterminated Sass collection",
3150            );
3151        }
3152        self.builder.finish_node();
3153    }
3154
3155    fn parse_scss_map_entries_until(&mut self, recovery: &[SyntaxKind]) {
3156        let mut entry_recovery = vec![SyntaxKind::Comma, SyntaxKind::RightParen];
3157        for kind in recovery {
3158            if !entry_recovery.contains(kind) {
3159                entry_recovery.push(*kind);
3160            }
3161        }
3162        while !self.at_end() {
3163            self.eat_value_trivia();
3164            match self.current_kind() {
3165                Some(kind) if recovery.contains(&kind) => break,
3166                Some(SyntaxKind::Comma) => self.token_current(),
3167                Some(_) => self.parse_scss_map_entry_until(&entry_recovery),
3168                None => break,
3169            }
3170        }
3171    }
3172
3173    fn parse_scss_map_entry_until(&mut self, recovery: &[SyntaxKind]) {
3174        let has_colon = self.current_scss_map_entry_has_colon_before(recovery);
3175        let has_value = self.current_scss_map_entry_has_value_before(recovery);
3176        self.builder.start_node(if has_colon && has_value {
3177            SyntaxKind::ScssMapEntry
3178        } else {
3179            SyntaxKind::BogusScssMapEntry
3180        });
3181
3182        let mut key_recovery = vec![SyntaxKind::Colon];
3183        for kind in recovery {
3184            if !key_recovery.contains(kind) {
3185                key_recovery.push(*kind);
3186            }
3187        }
3188        self.parse_value_until(&key_recovery);
3189        if self.current_kind() == Some(SyntaxKind::Colon) {
3190            self.token_current();
3191        } else {
3192            self.error_at_current(
3193                ParseErrorCode::ExpectedValue,
3194                "expected Sass map entry colon",
3195            );
3196        }
3197
3198        if has_value {
3199            self.parse_value_until(recovery);
3200        } else {
3201            self.empty_bogus_node(
3202                SyntaxKind::BogusValue,
3203                ParseErrorCode::ExpectedValue,
3204                "expected Sass map entry value",
3205            );
3206        }
3207        self.builder.finish_node();
3208    }
3209
3210    fn parse_scss_list_items_until(&mut self, recovery: &[SyntaxKind]) {
3211        let mut item_recovery = vec![SyntaxKind::Comma, SyntaxKind::RightParen];
3212        for kind in recovery {
3213            if !item_recovery.contains(kind) {
3214                item_recovery.push(*kind);
3215            }
3216        }
3217        while !self.at_end() {
3218            self.eat_value_trivia();
3219            match self.current_kind() {
3220                Some(kind) if recovery.contains(&kind) => break,
3221                Some(SyntaxKind::Comma) => self.token_current(),
3222                Some(_) => self.parse_value_expression(0, &item_recovery),
3223                None => break,
3224            }
3225        }
3226    }
3227
3228    fn parse_scss_space_list_until(&mut self, recovery: &[SyntaxKind]) {
3229        self.builder.start_node(SyntaxKind::ScssList);
3230        while !self.at_end() {
3231            self.eat_value_trivia();
3232            match self.current_kind() {
3233                Some(kind) if recovery.contains(&kind) => break,
3234                Some(_) => self.parse_value_expression(0, recovery),
3235                None => break,
3236            }
3237        }
3238        self.builder.finish_node();
3239    }
3240
3241    fn parse_parenthesized_expression(&mut self, recovery: &[SyntaxKind]) {
3242        self.builder.start_node(SyntaxKind::ParenthesizedExpression);
3243        self.token_current();
3244        let paren_recovery = function_argument_recovery(recovery);
3245        self.parse_value_until(&paren_recovery);
3246        if self.current_kind() == Some(SyntaxKind::RightParen) {
3247            self.token_current();
3248        }
3249        self.builder.finish_node();
3250    }
3251
3252    fn parse_at_rule(&mut self) {
3253        let spec = self.current_text().and_then(at_rule_spec);
3254        let at_rule_kind = if spec.is_none() && self.current_text() == Some("@") {
3255            SyntaxKind::BogusAtRule
3256        } else {
3257            SyntaxKind::AtRule
3258        };
3259        self.builder.start_node(at_rule_kind);
3260        if at_rule_kind == SyntaxKind::BogusAtRule {
3261            self.error_at_current(ParseErrorCode::UnexpectedCharacter, "expected at-rule name");
3262        }
3263        if let Some(spec) = spec {
3264            self.builder.start_node(spec.node_kind);
3265        }
3266
3267        if self.current_kind() == Some(SyntaxKind::AtKeyword) {
3268            self.token_current();
3269        }
3270        if let Some(spec) = spec {
3271            self.parse_at_rule_prelude(spec.node_kind);
3272        } else {
3273            self.consume_at_rule_prelude_tokens();
3274        }
3275
3276        while !self.at_end() {
3277            match self.current_kind() {
3278                Some(kind) if is_statement_end(kind) => {
3279                    self.token_current();
3280                    break;
3281                }
3282                Some(SyntaxKind::LeftBrace) => {
3283                    match spec
3284                        .map(|spec| spec.block_kind)
3285                        .unwrap_or(AtRuleBlockKind::Raw)
3286                    {
3287                        AtRuleBlockKind::GroupRuleList => self.parse_group_at_rule_block(),
3288                        AtRuleBlockKind::DeclarationList => self.parse_declaration_block(),
3289                        AtRuleBlockKind::Keyframes => self.parse_keyframes_block(),
3290                        AtRuleBlockKind::Raw => self.consume_balanced_block(),
3291                    }
3292                    break;
3293                }
3294                Some(SyntaxKind::SassIndent) => {
3295                    self.parse_sass_indented_at_rule_block(
3296                        spec.map(|spec| spec.block_kind)
3297                            .unwrap_or(AtRuleBlockKind::Raw),
3298                    );
3299                    break;
3300                }
3301                Some(_) => self.token_current(),
3302                None => break,
3303            }
3304        }
3305
3306        if spec.is_some() {
3307            self.builder.finish_node();
3308        }
3309        self.builder.finish_node();
3310    }
3311
3312    fn parse_at_rule_prelude(&mut self, node_kind: SyntaxKind) {
3313        match node_kind {
3314            SyntaxKind::MediaRule => self.parse_media_query_list(),
3315            SyntaxKind::SupportsRule => self.parse_supports_rule_prelude(),
3316            SyntaxKind::ContainerRule => self.parse_container_rule_prelude(),
3317            SyntaxKind::ImportRule => self.parse_import_prelude(),
3318            SyntaxKind::CharsetRule => self.parse_charset_rule_prelude(),
3319            SyntaxKind::NamespaceRule => self.parse_namespace_rule_prelude(),
3320            SyntaxKind::KeyframesRule => self.parse_keyframes_rule_prelude(),
3321            SyntaxKind::PageRule => self.parse_page_rule_prelude(),
3322            SyntaxKind::FontFaceRule
3323            | SyntaxKind::StartingStyleRule
3324            | SyntaxKind::PageMarginRule
3325            | SyntaxKind::FontFeatureValuesStylisticRule
3326            | SyntaxKind::FontFeatureValuesStylesetRule
3327            | SyntaxKind::FontFeatureValuesCharacterVariantRule
3328            | SyntaxKind::FontFeatureValuesSwashRule
3329            | SyntaxKind::FontFeatureValuesOrnamentsRule
3330            | SyntaxKind::FontFeatureValuesAnnotationRule
3331            | SyntaxKind::FontFeatureValuesHistoricalFormsRule
3332            | SyntaxKind::ViewTransitionRule => {
3333                self.parse_empty_at_rule_prelude("unexpected at-rule prelude")
3334            }
3335            SyntaxKind::PropertyRule => self.parse_named_at_rule_prelude(
3336                at_rule_prelude_head_is_custom_property_name,
3337                "invalid @property name",
3338            ),
3339            SyntaxKind::FontPaletteValuesRule
3340            | SyntaxKind::ColorProfileRule
3341            | SyntaxKind::PositionTryRule => self.parse_named_at_rule_prelude(
3342                at_rule_prelude_head_is_custom_property_name,
3343                "invalid at-rule custom property name",
3344            ),
3345            SyntaxKind::CustomMediaRule => self.parse_custom_media_rule_prelude(),
3346            SyntaxKind::CounterStyleRule => self.parse_named_at_rule_prelude(
3347                at_rule_prelude_head_is_custom_ident,
3348                "invalid @counter-style name",
3349            ),
3350            SyntaxKind::FontFeatureValuesRule => self.parse_font_feature_values_prelude(),
3351            SyntaxKind::LayerRule => self.parse_layer_rule_prelude(),
3352            SyntaxKind::ScopeRule => self.parse_scope_rule_prelude(),
3353            _ => self.consume_at_rule_prelude_tokens(),
3354        }
3355    }
3356
3357    fn parse_media_query_list(&mut self) {
3358        self.builder.start_node(SyntaxKind::MediaQueryList);
3359        let mut saw_query = false;
3360        let mut expecting_query = true;
3361        while !self.at_end() {
3362            match self.current_kind() {
3363                Some(kind) if is_at_rule_prelude_boundary(kind) => break,
3364                Some(SyntaxKind::Comma) => {
3365                    if expecting_query {
3366                        self.error_at_current(
3367                            ParseErrorCode::ExpectedValue,
3368                            "invalid @media prelude",
3369                        );
3370                        self.builder.start_node(SyntaxKind::BogusMediaQuery);
3371                        self.token_current();
3372                        self.builder.finish_node();
3373                    } else {
3374                        self.token_current();
3375                        expecting_query = true;
3376                    }
3377                }
3378                Some(_) => {
3379                    let valid = self.current_media_query_is_valid();
3380                    if !valid {
3381                        self.error_at_current(
3382                            ParseErrorCode::ExpectedValue,
3383                            "invalid @media prelude",
3384                        );
3385                    }
3386                    self.parse_media_query(valid);
3387                    saw_query = true;
3388                    expecting_query = false;
3389                }
3390                None => break,
3391            }
3392        }
3393        if !saw_query || expecting_query {
3394            self.error_at_current(ParseErrorCode::ExpectedValue, "invalid @media prelude");
3395            self.builder.start_node(SyntaxKind::BogusMediaQuery);
3396            self.builder.finish_node();
3397        }
3398        self.builder.finish_node();
3399    }
3400
3401    fn parse_media_query(&mut self, valid: bool) {
3402        self.builder.start_node(if valid {
3403            SyntaxKind::MediaQuery
3404        } else {
3405            SyntaxKind::BogusMediaQuery
3406        });
3407        while !self.at_end() {
3408            match self.current_kind() {
3409                Some(kind) if is_at_rule_prelude_boundary(kind) || kind == SyntaxKind::Comma => {
3410                    break;
3411                }
3412                Some(SyntaxKind::LeftParen) => self.parse_balanced_parenthesized_prelude_until(
3413                    Some(SyntaxKind::MediaFeature),
3414                    &[
3415                        SyntaxKind::Comma,
3416                        SyntaxKind::LeftBrace,
3417                        SyntaxKind::Semicolon,
3418                    ],
3419                ),
3420                Some(kind) if is_interpolation_start(kind) => self.parse_interpolation(
3421                    kind,
3422                    &[
3423                        SyntaxKind::Comma,
3424                        SyntaxKind::LeftBrace,
3425                        SyntaxKind::Semicolon,
3426                    ],
3427                ),
3428                Some(_) => self.token_current(),
3429                None => break,
3430            }
3431        }
3432        self.builder.finish_node();
3433    }
3434
3435    fn current_media_query_is_valid(&self) -> bool {
3436        let Some((first_index, first_kind)) = self.non_trivia_token_from(self.position) else {
3437            return false;
3438        };
3439        if is_at_rule_prelude_boundary(first_kind) || first_kind == SyntaxKind::Comma {
3440            return false;
3441        }
3442        if !self.current_prelude_parentheses_are_balanced_until(&[
3443            SyntaxKind::Comma,
3444            SyntaxKind::LeftBrace,
3445            SyntaxKind::SassIndent,
3446            SyntaxKind::Semicolon,
3447            SyntaxKind::SassOptionalSemicolon,
3448        ]) {
3449            return false;
3450        }
3451        self.media_query_starts_at(first_index, first_kind)
3452    }
3453
3454    fn media_query_starts_at(&self, index: usize, kind: SyntaxKind) -> bool {
3455        match kind {
3456            SyntaxKind::Ident | SyntaxKind::LeftParen => true,
3457            SyntaxKind::KeywordNot | SyntaxKind::KeywordOnly => self
3458                .non_trivia_token_from(index + 1)
3459                .is_some_and(|(_, next_kind)| {
3460                    matches!(next_kind, SyntaxKind::Ident | SyntaxKind::LeftParen)
3461                        || is_interpolation_start(next_kind)
3462                }),
3463            kind if is_interpolation_start(kind) => true,
3464            _ => false,
3465        }
3466    }
3467
3468    fn parse_charset_rule_prelude(&mut self) {
3469        if !self.charset_rule_prelude_is_valid() {
3470            self.error_at_current(ParseErrorCode::ExpectedValue, "invalid @charset prelude");
3471        }
3472        self.consume_at_rule_prelude_tokens();
3473    }
3474
3475    fn charset_rule_prelude_is_valid(&self) -> bool {
3476        let Some((source_index, SyntaxKind::String)) = self.non_trivia_token_from(self.position)
3477        else {
3478            return false;
3479        };
3480        self.non_trivia_token_from(source_index + 1)
3481            .is_none_or(|(_, kind)| is_at_rule_prelude_boundary(kind))
3482    }
3483
3484    fn parse_namespace_rule_prelude(&mut self) {
3485        if !self.namespace_rule_prelude_is_valid() {
3486            self.error_at_current(ParseErrorCode::ExpectedValue, "invalid @namespace prelude");
3487        }
3488        self.consume_at_rule_prelude_tokens();
3489    }
3490
3491    fn parse_custom_media_rule_prelude(&mut self) {
3492        self.eat_trivia();
3493        let valid = self.custom_media_rule_prelude_is_valid();
3494        if !valid {
3495            self.error_at_current(
3496                ParseErrorCode::ExpectedValue,
3497                "invalid @custom-media prelude",
3498            );
3499        }
3500        self.builder.start_node(if valid {
3501            SyntaxKind::AtRulePrelude
3502        } else {
3503            SyntaxKind::BogusAtRulePrelude
3504        });
3505        self.consume_at_rule_prelude_tokens_without_wrapping();
3506        self.builder.finish_node();
3507    }
3508
3509    fn custom_media_rule_prelude_is_valid(&self) -> bool {
3510        let Some((name_index, name_kind)) = self.non_trivia_token_from(self.position) else {
3511            return false;
3512        };
3513        if !self.current_prelude_parentheses_are_balanced_until(&[
3514            SyntaxKind::Semicolon,
3515            SyntaxKind::SassOptionalSemicolon,
3516        ]) {
3517            return false;
3518        }
3519        let tail = if name_kind == SyntaxKind::CustomPropertyName {
3520            self.non_trivia_token_from(name_index + 1)
3521        } else if is_interpolation_start(name_kind) {
3522            self.non_trivia_token_after_interpolation(name_index, name_kind)
3523        } else {
3524            return false;
3525        };
3526        let Some((tail_index, tail_kind)) = tail else {
3527            return false;
3528        };
3529        if is_at_rule_prelude_boundary(tail_kind) {
3530            return false;
3531        }
3532        self.media_query_starts_at(tail_index, tail_kind)
3533    }
3534
3535    fn namespace_rule_prelude_is_valid(&self) -> bool {
3536        let Some((first_index, first_kind)) = self.non_trivia_token_from(self.position) else {
3537            return false;
3538        };
3539
3540        if self.namespace_source_starts_at(first_index, first_kind) {
3541            return true;
3542        }
3543        if !matches!(
3544            first_kind,
3545            SyntaxKind::Ident | SyntaxKind::CustomPropertyName
3546        ) {
3547            return false;
3548        }
3549        self.non_trivia_token_from(first_index + 1)
3550            .is_some_and(|(source_index, source_kind)| {
3551                self.namespace_source_starts_at(source_index, source_kind)
3552            })
3553    }
3554
3555    fn namespace_source_starts_at(&self, index: usize, kind: SyntaxKind) -> bool {
3556        matches!(kind, SyntaxKind::String | SyntaxKind::Url)
3557            || is_interpolation_start(kind)
3558            || self.token_starts_url_function(index, kind)
3559    }
3560
3561    fn token_starts_url_function(&self, index: usize, kind: SyntaxKind) -> bool {
3562        kind == SyntaxKind::Ident
3563            && self
3564                .tokens
3565                .get(index)
3566                .is_some_and(|token| matches_ignore_ascii_case(token.text, &["url"]))
3567            && self
3568                .non_trivia_token_from(index + 1)
3569                .is_some_and(|(_, next_kind)| next_kind == SyntaxKind::LeftParen)
3570    }
3571
3572    fn parse_keyframes_rule_prelude(&mut self) {
3573        if !self.keyframes_rule_prelude_is_valid() {
3574            self.error_at_current(ParseErrorCode::ExpectedValue, "invalid @keyframes name");
3575        }
3576        self.consume_at_rule_prelude_tokens();
3577    }
3578
3579    fn keyframes_rule_prelude_is_valid(&self) -> bool {
3580        let Some((name_index, name_kind)) = self.non_trivia_token_from(self.position) else {
3581            return false;
3582        };
3583        if is_interpolation_start(name_kind) {
3584            return true;
3585        }
3586        if !matches!(name_kind, SyntaxKind::Ident | SyntaxKind::String) {
3587            return false;
3588        }
3589        self.non_trivia_token_from(name_index + 1)
3590            .is_none_or(|(_, kind)| is_at_rule_prelude_boundary(kind))
3591    }
3592
3593    fn parse_empty_at_rule_prelude(&mut self, message: &'static str) {
3594        self.eat_trivia();
3595        if self
3596            .current_kind()
3597            .is_some_and(|kind| !is_at_rule_prelude_boundary(kind))
3598        {
3599            self.error_at_current(ParseErrorCode::ExpectedValue, message);
3600            self.consume_at_rule_prelude_tokens();
3601        }
3602    }
3603
3604    fn parse_font_feature_values_prelude(&mut self) {
3605        if !self.font_feature_values_prelude_is_valid() {
3606            self.error_at_current(
3607                ParseErrorCode::ExpectedValue,
3608                "invalid @font-feature-values family name",
3609            );
3610        }
3611        self.consume_at_rule_prelude_tokens();
3612    }
3613
3614    fn font_feature_values_prelude_is_valid(&self) -> bool {
3615        self.non_trivia_token_from(self.position)
3616            .is_some_and(|(_, kind)| {
3617                matches!(kind, SyntaxKind::Ident | SyntaxKind::String)
3618                    || is_interpolation_start(kind)
3619            })
3620    }
3621
3622    fn parse_layer_rule_prelude(&mut self) {
3623        self.eat_trivia();
3624        match self.current_kind() {
3625            Some(SyntaxKind::LeftBrace | SyntaxKind::SassIndent) => return,
3626            Some(SyntaxKind::Semicolon | SyntaxKind::SassOptionalSemicolon) | None => {
3627                self.empty_bogus_node(
3628                    SyntaxKind::BogusLayerName,
3629                    ParseErrorCode::ExpectedValue,
3630                    "invalid @layer prelude",
3631                );
3632                return;
3633            }
3634            Some(_) => {}
3635        }
3636
3637        let valid = self.layer_rule_prelude_is_valid();
3638        if !valid {
3639            self.error_at_current(ParseErrorCode::ExpectedValue, "invalid @layer prelude");
3640        }
3641        self.builder.start_node(if valid {
3642            SyntaxKind::LayerName
3643        } else {
3644            SyntaxKind::BogusLayerName
3645        });
3646        self.consume_at_rule_prelude_tokens_without_wrapping();
3647        self.builder.finish_node();
3648    }
3649
3650    fn layer_rule_prelude_is_valid(&self) -> bool {
3651        #[derive(Clone, Copy, PartialEq, Eq)]
3652        enum LayerNameState {
3653            FirstSegment,
3654            SegmentAfterComma,
3655            SegmentAfterDot,
3656            AfterSegment,
3657        }
3658
3659        let mut state = LayerNameState::FirstSegment;
3660        let mut trivia_since_significant = false;
3661        let mut index = self.position;
3662
3663        while let Some(token) = self.tokens.get(index) {
3664            if token.kind.is_trivia() {
3665                trivia_since_significant = true;
3666                index += 1;
3667                continue;
3668            }
3669            if is_at_rule_prelude_boundary(token.kind) {
3670                return state == LayerNameState::AfterSegment;
3671            }
3672            if is_interpolation_start(token.kind) {
3673                return true;
3674            }
3675            match token.kind {
3676                SyntaxKind::Ident
3677                    if matches!(
3678                        state,
3679                        LayerNameState::FirstSegment | LayerNameState::SegmentAfterComma
3680                    ) || (state == LayerNameState::SegmentAfterDot
3681                        && !trivia_since_significant) =>
3682                {
3683                    state = LayerNameState::AfterSegment;
3684                }
3685                SyntaxKind::Comma if state == LayerNameState::AfterSegment => {
3686                    state = LayerNameState::SegmentAfterComma;
3687                }
3688                SyntaxKind::Dot
3689                    if state == LayerNameState::AfterSegment && !trivia_since_significant =>
3690                {
3691                    state = LayerNameState::SegmentAfterDot;
3692                }
3693                _ => return false,
3694            }
3695            trivia_since_significant = false;
3696            index += 1;
3697        }
3698
3699        state == LayerNameState::AfterSegment
3700    }
3701
3702    fn parse_container_rule_prelude(&mut self) {
3703        self.eat_trivia();
3704        let valid = self.container_rule_prelude_is_valid();
3705        if !valid {
3706            self.error_at_current(ParseErrorCode::ExpectedValue, "invalid @container prelude");
3707        }
3708        self.builder.start_node(if valid {
3709            SyntaxKind::ContainerCondition
3710        } else {
3711            SyntaxKind::BogusContainerCondition
3712        });
3713        self.consume_at_rule_prelude_tokens_without_wrapping();
3714        self.builder.finish_node();
3715    }
3716
3717    fn container_rule_prelude_is_valid(&self) -> bool {
3718        let Some((first_index, first_kind)) = self.non_trivia_token_from(self.position) else {
3719            return false;
3720        };
3721        if is_at_rule_prelude_boundary(first_kind) {
3722            return false;
3723        }
3724        if !self.current_prelude_parentheses_are_balanced_until(&[
3725            SyntaxKind::LeftBrace,
3726            SyntaxKind::SassIndent,
3727            SyntaxKind::Semicolon,
3728            SyntaxKind::SassOptionalSemicolon,
3729        ]) {
3730            return false;
3731        }
3732        if self.container_condition_starts_at(first_index, first_kind) {
3733            return true;
3734        }
3735        if first_kind != SyntaxKind::Ident {
3736            return false;
3737        }
3738        self.non_trivia_token_from(first_index + 1).is_some_and(
3739            |(condition_index, condition_kind)| {
3740                self.container_condition_starts_at(condition_index, condition_kind)
3741            },
3742        )
3743    }
3744
3745    fn container_condition_starts_at(&self, index: usize, kind: SyntaxKind) -> bool {
3746        if matches!(kind, SyntaxKind::LeftParen | SyntaxKind::KeywordNot)
3747            || is_interpolation_start(kind)
3748        {
3749            return true;
3750        }
3751        kind == SyntaxKind::Ident
3752            && self
3753                .non_trivia_token_from(index + 1)
3754                .is_some_and(|(_, next_kind)| next_kind == SyntaxKind::LeftParen)
3755    }
3756
3757    fn parse_supports_rule_prelude(&mut self) {
3758        self.eat_trivia();
3759        let valid = self.supports_rule_prelude_is_valid();
3760        if !valid {
3761            self.error_at_current(ParseErrorCode::ExpectedValue, "invalid @supports prelude");
3762        }
3763        self.builder.start_node(if valid {
3764            SyntaxKind::SupportsCondition
3765        } else {
3766            SyntaxKind::BogusSupportsCondition
3767        });
3768        self.consume_at_rule_prelude_tokens_without_wrapping();
3769        self.builder.finish_node();
3770    }
3771
3772    fn supports_rule_prelude_is_valid(&self) -> bool {
3773        let Some((first_index, first_kind)) = self.non_trivia_token_from(self.position) else {
3774            return false;
3775        };
3776        if is_at_rule_prelude_boundary(first_kind) {
3777            return false;
3778        }
3779        if !self.current_prelude_parentheses_are_balanced_until(&[
3780            SyntaxKind::LeftBrace,
3781            SyntaxKind::SassIndent,
3782            SyntaxKind::Semicolon,
3783            SyntaxKind::SassOptionalSemicolon,
3784        ]) {
3785            return false;
3786        }
3787        self.supports_condition_starts_at(first_index, first_kind)
3788    }
3789
3790    fn supports_condition_starts_at(&self, index: usize, kind: SyntaxKind) -> bool {
3791        if kind == SyntaxKind::KeywordNot || self.token_text_matches(index, "not") {
3792            return self
3793                .non_trivia_token_from(index + 1)
3794                .is_some_and(|(next_index, next_kind)| {
3795                    self.supports_condition_starts_at(next_index, next_kind)
3796                });
3797        }
3798        if kind == SyntaxKind::LeftParen || is_interpolation_start(kind) {
3799            return true;
3800        }
3801        kind == SyntaxKind::Ident
3802            && self
3803                .non_trivia_token_from(index + 1)
3804                .is_some_and(|(_, next_kind)| next_kind == SyntaxKind::LeftParen)
3805    }
3806
3807    fn parse_scope_rule_prelude(&mut self) {
3808        self.eat_trivia();
3809        let valid = self.scope_rule_prelude_is_valid();
3810        if !valid {
3811            self.error_at_current(ParseErrorCode::ExpectedValue, "invalid @scope prelude");
3812        }
3813        self.builder.start_node(if valid {
3814            SyntaxKind::ScopeRange
3815        } else {
3816            SyntaxKind::BogusScopeRange
3817        });
3818        self.consume_at_rule_prelude_tokens_without_wrapping();
3819        self.builder.finish_node();
3820    }
3821
3822    fn scope_rule_prelude_is_valid(&self) -> bool {
3823        let Some((start_index, start_kind)) = self.non_trivia_token_from(self.position) else {
3824            return false;
3825        };
3826        if is_at_rule_prelude_boundary(start_kind) {
3827            return false;
3828        }
3829        if !self.current_prelude_parentheses_are_balanced_until(&[
3830            SyntaxKind::LeftBrace,
3831            SyntaxKind::SassIndent,
3832            SyntaxKind::Semicolon,
3833            SyntaxKind::SassOptionalSemicolon,
3834        ]) {
3835            return false;
3836        }
3837        if is_interpolation_start(start_kind) {
3838            return true;
3839        }
3840        if start_kind != SyntaxKind::LeftParen {
3841            return false;
3842        }
3843
3844        let Some(start_close_index) = self.parenthesized_prelude_close_index(start_index) else {
3845            return false;
3846        };
3847        let Some((after_start_index, after_start_kind)) =
3848            self.non_trivia_token_from(start_close_index + 1)
3849        else {
3850            return true;
3851        };
3852        if is_at_rule_prelude_boundary(after_start_kind) {
3853            return true;
3854        }
3855        if after_start_kind != SyntaxKind::Ident
3856            || !self
3857                .tokens
3858                .get(after_start_index)
3859                .is_some_and(|token| matches_ignore_ascii_case(token.text, &["to"]))
3860        {
3861            return false;
3862        }
3863
3864        let Some((end_index, end_kind)) = self.non_trivia_token_from(after_start_index + 1) else {
3865            return false;
3866        };
3867        if is_interpolation_start(end_kind) {
3868            return true;
3869        }
3870        if end_kind != SyntaxKind::LeftParen {
3871            return false;
3872        }
3873        let Some(end_close_index) = self.parenthesized_prelude_close_index(end_index) else {
3874            return false;
3875        };
3876        self.non_trivia_token_from(end_close_index + 1)
3877            .is_none_or(|(_, kind)| is_at_rule_prelude_boundary(kind))
3878    }
3879
3880    fn parenthesized_prelude_close_index(&self, open_index: usize) -> Option<usize> {
3881        let mut depth = 0usize;
3882        for (index, token) in self.tokens.iter().enumerate().skip(open_index) {
3883            match token.kind {
3884                SyntaxKind::LeftParen => depth += 1,
3885                SyntaxKind::RightParen => {
3886                    depth = depth.saturating_sub(1);
3887                    if depth == 0 {
3888                        return Some(index);
3889                    }
3890                }
3891                kind if depth == 0 && is_at_rule_prelude_boundary(kind) => return None,
3892                _ => {}
3893            }
3894        }
3895        None
3896    }
3897
3898    fn parse_page_rule_prelude(&mut self) {
3899        self.eat_trivia();
3900        if self.current_kind().is_none_or(is_at_rule_prelude_boundary) {
3901            return;
3902        }
3903        let valid = self.page_rule_prelude_is_valid();
3904        if !valid {
3905            self.error_at_current(ParseErrorCode::ExpectedValue, "invalid @page prelude");
3906        }
3907        self.builder.start_node(if valid {
3908            SyntaxKind::AtRulePrelude
3909        } else {
3910            SyntaxKind::BogusAtRulePrelude
3911        });
3912        self.consume_at_rule_prelude_tokens_without_wrapping();
3913        self.builder.finish_node();
3914    }
3915
3916    fn page_rule_prelude_is_valid(&self) -> bool {
3917        let mut expecting_selector = true;
3918        let mut expecting_pseudo_name = false;
3919        let mut saw_selector = false;
3920
3921        for token in self.tokens.iter().skip(self.position) {
3922            if token.kind.is_trivia() {
3923                continue;
3924            }
3925            if is_at_rule_prelude_boundary(token.kind) {
3926                return saw_selector && !expecting_selector && !expecting_pseudo_name;
3927            }
3928            if is_interpolation_start(token.kind) {
3929                return true;
3930            }
3931            if expecting_pseudo_name {
3932                if token.kind != SyntaxKind::Ident {
3933                    return false;
3934                }
3935                saw_selector = true;
3936                expecting_selector = false;
3937                expecting_pseudo_name = false;
3938                continue;
3939            }
3940            match token.kind {
3941                SyntaxKind::Ident if expecting_selector => {
3942                    saw_selector = true;
3943                    expecting_selector = false;
3944                }
3945                SyntaxKind::Colon => {
3946                    expecting_pseudo_name = true;
3947                }
3948                SyntaxKind::Comma if saw_selector && !expecting_selector => {
3949                    expecting_selector = true;
3950                }
3951                _ => return false,
3952            }
3953        }
3954
3955        saw_selector && !expecting_selector && !expecting_pseudo_name
3956    }
3957
3958    fn parse_import_prelude(&mut self) {
3959        self.eat_trivia();
3960        if self.dialect == StyleDialect::Less && self.current_kind() == Some(SyntaxKind::LeftParen)
3961        {
3962            self.builder.start_node(SyntaxKind::AtRulePrelude);
3963            self.parse_balanced_parenthesized_prelude(None);
3964            self.builder.finish_node();
3965            self.eat_trivia();
3966        }
3967        if !self.parse_import_source() {
3968            self.parse_bogus_import_prelude();
3969            return;
3970        }
3971        while !self.at_end() {
3972            match self.current_kind() {
3973                Some(kind) if is_at_rule_prelude_boundary(kind) => break,
3974                Some(kind) if kind.is_trivia() => self.token_current(),
3975                Some(SyntaxKind::Ident)
3976                    if self
3977                        .current_text()
3978                        .is_some_and(|text| css_keyword(text).equals("layer")) =>
3979                {
3980                    self.parse_import_layer_tail_node()
3981                }
3982                Some(SyntaxKind::Ident)
3983                    if self
3984                        .current_text()
3985                        .is_some_and(|text| css_keyword(text).equals("supports")) =>
3986                {
3987                    self.parse_import_supports_tail_node()
3988                }
3989                Some(_) => {
3990                    self.parse_media_query_list();
3991                    break;
3992                }
3993                None => break,
3994            }
3995        }
3996    }
3997
3998    fn parse_import_source(&mut self) -> bool {
3999        match self.current_kind() {
4000            Some(SyntaxKind::Url) => {
4001                self.builder.start_node(SyntaxKind::UrlValue);
4002                self.token_current();
4003                self.builder.finish_node();
4004                true
4005            }
4006            Some(SyntaxKind::Ident)
4007                if self
4008                    .current_text()
4009                    .is_some_and(|text| matches_ignore_ascii_case(text, &["url"]))
4010                    && self.next_kind() == Some(SyntaxKind::LeftParen) =>
4011            {
4012                self.builder.start_node(SyntaxKind::UrlValue);
4013                self.parse_function_call(&[SyntaxKind::LeftBrace, SyntaxKind::Semicolon]);
4014                self.builder.finish_node();
4015                true
4016            }
4017            Some(SyntaxKind::String) => {
4018                self.token_current();
4019                true
4020            }
4021            Some(kind) if is_interpolation_start(kind) => {
4022                self.parse_interpolation(kind, &[SyntaxKind::LeftBrace, SyntaxKind::Semicolon]);
4023                true
4024            }
4025            Some(_) | None => false,
4026        }
4027    }
4028
4029    fn parse_bogus_import_prelude(&mut self) {
4030        self.builder.start_node(SyntaxKind::BogusAtRulePrelude);
4031        self.error_at_current(ParseErrorCode::ExpectedValue, "invalid @import source");
4032        self.consume_at_rule_prelude_tokens_without_wrapping();
4033        self.builder.finish_node();
4034    }
4035
4036    fn parse_named_at_rule_prelude(
4037        &mut self,
4038        valid_head: fn(SyntaxKind) -> bool,
4039        message: &'static str,
4040    ) {
4041        if self.current_kind().is_none_or(is_at_rule_prelude_boundary) {
4042            return;
4043        }
4044        let valid_name = self
4045            .non_trivia_token_from(self.position)
4046            .is_some_and(|(_, kind)| valid_head(kind));
4047        if !valid_name {
4048            self.error_at_current(ParseErrorCode::ExpectedValue, message);
4049        }
4050        self.consume_at_rule_prelude_tokens();
4051    }
4052
4053    fn parse_import_layer_tail_node(&mut self) {
4054        let valid = self.import_layer_tail_is_valid();
4055        if !valid {
4056            self.error_at_current(ParseErrorCode::ExpectedValue, "invalid @import layer tail");
4057        }
4058        self.builder.start_node(if valid {
4059            SyntaxKind::LayerName
4060        } else {
4061            SyntaxKind::BogusLayerName
4062        });
4063        self.token_current();
4064        if self.current_kind() == Some(SyntaxKind::LeftParen) {
4065            self.parse_balanced_parenthesized_prelude(None);
4066        }
4067        self.builder.finish_node();
4068    }
4069
4070    fn import_layer_tail_is_valid(&self) -> bool {
4071        let Some((open_index, next_kind)) = self.non_trivia_token_from(self.position + 1) else {
4072            return true;
4073        };
4074        if next_kind != SyntaxKind::LeftParen {
4075            return true;
4076        }
4077        let Some(close_index) = self.parenthesized_prelude_close_index(open_index) else {
4078            return false;
4079        };
4080        self.layer_name_is_valid_between(open_index + 1, close_index)
4081    }
4082
4083    fn layer_name_is_valid_between(&self, start: usize, end: usize) -> bool {
4084        let mut saw_name = false;
4085        let mut expecting_segment = true;
4086
4087        for token in self.tokens[start..end]
4088            .iter()
4089            .filter(|token| !token.kind.is_trivia())
4090        {
4091            if is_interpolation_start(token.kind) {
4092                return true;
4093            }
4094            match token.kind {
4095                SyntaxKind::Ident if expecting_segment => {
4096                    saw_name = true;
4097                    expecting_segment = false;
4098                }
4099                SyntaxKind::Dot if saw_name && !expecting_segment => {
4100                    expecting_segment = true;
4101                }
4102                _ => return false,
4103            }
4104        }
4105
4106        saw_name && !expecting_segment
4107    }
4108
4109    fn parse_import_supports_tail_node(&mut self) {
4110        let valid = self.import_supports_tail_is_valid();
4111        if !valid {
4112            self.error_at_current(
4113                ParseErrorCode::ExpectedValue,
4114                "invalid @import supports tail",
4115            );
4116        }
4117        self.builder.start_node(if valid {
4118            SyntaxKind::SupportsCondition
4119        } else {
4120            SyntaxKind::BogusSupportsCondition
4121        });
4122        self.token_current();
4123        if self.current_kind() == Some(SyntaxKind::LeftParen) {
4124            self.parse_balanced_parenthesized_prelude(None);
4125        }
4126        self.builder.finish_node();
4127    }
4128
4129    fn import_supports_tail_is_valid(&self) -> bool {
4130        let Some((open_index, SyntaxKind::LeftParen)) =
4131            self.non_trivia_token_from(self.position + 1)
4132        else {
4133            return false;
4134        };
4135        let Some(close_index) = self.parenthesized_prelude_close_index(open_index) else {
4136            return false;
4137        };
4138        self.non_trivia_token_from(open_index + 1)
4139            .is_some_and(|(inner_index, inner_kind)| {
4140                inner_index < close_index && inner_kind != SyntaxKind::RightParen
4141            })
4142    }
4143
4144    fn consume_at_rule_prelude_tokens(&mut self) {
4145        if self.current_kind().is_none_or(is_at_rule_prelude_boundary) {
4146            return;
4147        }
4148        self.builder
4149            .start_node(self.current_generic_at_rule_prelude_node_kind());
4150        self.consume_at_rule_prelude_tokens_without_wrapping();
4151        self.builder.finish_node();
4152    }
4153
4154    fn consume_at_rule_prelude_tokens_without_wrapping(&mut self) {
4155        while !self.at_end() {
4156            match self.current_kind() {
4157                Some(kind) if is_at_rule_prelude_boundary(kind) => break,
4158                Some(SyntaxKind::LeftParen) => self.parse_balanced_parenthesized_prelude(None),
4159                Some(kind) if is_interpolation_start(kind) => {
4160                    self.parse_interpolation(kind, &[SyntaxKind::LeftBrace, SyntaxKind::Semicolon])
4161                }
4162                Some(_) => self.token_current(),
4163                None => break,
4164            }
4165        }
4166    }
4167
4168    fn parse_balanced_parenthesized_prelude(&mut self, node_kind: Option<SyntaxKind>) {
4169        self.parse_balanced_parenthesized_prelude_until(
4170            node_kind,
4171            &[SyntaxKind::LeftBrace, SyntaxKind::Semicolon],
4172        );
4173    }
4174
4175    fn parse_balanced_parenthesized_prelude_until(
4176        &mut self,
4177        node_kind: Option<SyntaxKind>,
4178        recovery: &[SyntaxKind],
4179    ) {
4180        if let Some(kind) = node_kind {
4181            self.builder.start_node(kind);
4182        }
4183        let mut depth = 0usize;
4184        let mut closed = false;
4185        while !self.at_end() {
4186            match self.current_kind() {
4187                Some(SyntaxKind::LeftParen) => {
4188                    depth += 1;
4189                    self.token_current();
4190                }
4191                Some(SyntaxKind::RightParen) => {
4192                    self.token_current();
4193                    depth = depth.saturating_sub(1);
4194                    if depth == 0 {
4195                        closed = true;
4196                        break;
4197                    }
4198                }
4199                Some(kind) if depth == 0 && recovery.contains(&kind) => break,
4200                Some(kind) if is_interpolation_start(kind) => {
4201                    self.parse_interpolation(kind, &[SyntaxKind::LeftBrace, SyntaxKind::Semicolon])
4202                }
4203                Some(_) => self.token_current(),
4204                None => break,
4205            }
4206        }
4207        if node_kind.is_some() {
4208            self.builder.finish_node();
4209        }
4210        if !closed {
4211            self.error_at_current(
4212                ParseErrorCode::UnexpectedCharacter,
4213                "unterminated parenthesized prelude",
4214            );
4215        }
4216    }
4217
4218    fn parse_interpolation(&mut self, start_kind: SyntaxKind, recovery: &[SyntaxKind]) {
4219        let Some(end_kind) = interpolation_end_kind(start_kind) else {
4220            self.token_current();
4221            return;
4222        };
4223        let closed = self.find_before_recovery(end_kind, recovery);
4224        self.builder.start_node(if closed {
4225            SyntaxKind::Interpolation
4226        } else {
4227            SyntaxKind::BogusInterpolation
4228        });
4229        if self.current_kind() == Some(start_kind) {
4230            self.token_current();
4231        }
4232        while !self.at_end() {
4233            match self.current_kind() {
4234                Some(kind) if kind == end_kind => {
4235                    self.token_current();
4236                    break;
4237                }
4238                Some(kind) if !closed && recovery.contains(&kind) => break,
4239                Some(_) => self.token_current(),
4240                None => break,
4241            }
4242        }
4243        if !closed {
4244            self.error_at_current(
4245                ParseErrorCode::UnexpectedCharacter,
4246                "unterminated interpolation",
4247            );
4248        }
4249        self.builder.finish_node();
4250    }
4251
4252    fn parse_group_at_rule_block(&mut self) {
4253        self.token_current();
4254        self.builder.start_node(SyntaxKind::RuleList);
4255        self.parse_rule_list_items();
4256        self.builder.finish_node();
4257        if self.current_kind() == Some(SyntaxKind::RightBrace) {
4258            self.token_current();
4259        }
4260    }
4261
4262    fn parse_rule_list_items(&mut self) {
4263        while !self.at_end() {
4264            self.eat_trivia();
4265            match self.current_kind() {
4266                Some(SyntaxKind::RightBrace | SyntaxKind::SassDedent) | None => break,
4267                Some(SyntaxKind::Semicolon | SyntaxKind::SassOptionalSemicolon) => {
4268                    self.token_current()
4269                }
4270                Some(SyntaxKind::AtKeyword) if self.current_is_css_module_value_rule() => {
4271                    self.parse_css_module_value_rule()
4272                }
4273                Some(SyntaxKind::AtKeyword) if self.current_dialect_at_rule_spec().is_some() => {
4274                    self.parse_dialect_at_rule()
4275                }
4276                Some(SyntaxKind::AtKeyword) => self.parse_at_rule(),
4277                Some(_) => self.parse_rule(),
4278            }
4279        }
4280    }
4281
4282    fn parse_declaration_block(&mut self) {
4283        self.token_current();
4284        self.builder
4285            .start_node(if self.previous_left_brace_has_match() {
4286                SyntaxKind::DeclarationList
4287            } else {
4288                SyntaxKind::BogusDeclarationList
4289            });
4290        self.parse_declaration_list();
4291        self.builder.finish_node();
4292        if self.current_kind() == Some(SyntaxKind::RightBrace) {
4293            self.token_current();
4294        } else {
4295            self.missing_token_bogus_trivia(
4296                ParseErrorCode::UnexpectedCharacter,
4297                "unterminated declaration block",
4298            );
4299        }
4300    }
4301
4302    fn parse_sass_indented_at_rule_block(&mut self, block_kind: AtRuleBlockKind) {
4303        self.builder.start_node(SyntaxKind::SassIndentedBlock);
4304        if self.current_kind() == Some(SyntaxKind::SassIndent) {
4305            self.token_current();
4306        }
4307        match block_kind {
4308            AtRuleBlockKind::GroupRuleList => {
4309                self.builder.start_node(SyntaxKind::RuleList);
4310                self.parse_rule_list_items();
4311                self.builder.finish_node();
4312            }
4313            AtRuleBlockKind::DeclarationList | AtRuleBlockKind::Keyframes => {
4314                self.builder.start_node(SyntaxKind::DeclarationList);
4315                self.parse_declaration_list();
4316                self.builder.finish_node();
4317            }
4318            AtRuleBlockKind::Raw => self.consume_sass_indented_raw_body(),
4319        }
4320        if self.current_kind() == Some(SyntaxKind::SassDedent) {
4321            self.token_current();
4322        } else {
4323            self.error_at_current(
4324                ParseErrorCode::UnexpectedCharacter,
4325                "unterminated Sass indented at-rule block",
4326            );
4327        }
4328        self.builder.finish_node();
4329    }
4330
4331    fn consume_sass_indented_raw_body(&mut self) {
4332        let mut depth = 0usize;
4333        while !self.at_end() {
4334            match self.current_kind() {
4335                Some(SyntaxKind::SassIndent) => {
4336                    depth += 1;
4337                    self.token_current();
4338                }
4339                Some(SyntaxKind::SassDedent) if depth == 0 => break,
4340                Some(SyntaxKind::SassDedent) => {
4341                    depth = depth.saturating_sub(1);
4342                    self.token_current();
4343                }
4344                Some(_) => self.token_current(),
4345                None => break,
4346            }
4347        }
4348    }
4349
4350    fn parse_keyframes_block(&mut self) {
4351        self.token_current();
4352        while !self.at_end() {
4353            self.eat_trivia();
4354            match self.current_kind() {
4355                Some(SyntaxKind::RightBrace) | None => break,
4356                Some(_) => self.parse_keyframe_block(),
4357            }
4358        }
4359        if self.current_kind() == Some(SyntaxKind::RightBrace) {
4360            self.token_current();
4361        }
4362    }
4363
4364    fn parse_keyframe_block(&mut self) {
4365        let has_block = self.find_before_recovery(SyntaxKind::LeftBrace, &[SyntaxKind::RightBrace]);
4366        self.builder.start_node(if has_block {
4367            SyntaxKind::KeyframeBlock
4368        } else {
4369            SyntaxKind::BogusKeyframeBlock
4370        });
4371        if has_block && !self.keyframe_selector_list_is_valid() {
4372            self.error_at_current(ParseErrorCode::ExpectedValue, "invalid keyframe selector");
4373        }
4374        while !self.at_end() {
4375            match self.current_kind() {
4376                Some(SyntaxKind::LeftBrace) => {
4377                    self.parse_declaration_block();
4378                    break;
4379                }
4380                Some(SyntaxKind::RightBrace) | None => break,
4381                Some(_) => self.token_current(),
4382            }
4383        }
4384        if !has_block {
4385            self.error_at_current(
4386                ParseErrorCode::UnexpectedCharacter,
4387                "expected keyframe declaration block",
4388            );
4389        }
4390        self.builder.finish_node();
4391    }
4392
4393    fn keyframe_selector_list_is_valid(&self) -> bool {
4394        let mut index = self.position;
4395        let mut saw_selector = false;
4396        let mut expect_selector = true;
4397        loop {
4398            let Some((token_index, kind)) = self.non_trivia_token_from(index) else {
4399                return false;
4400            };
4401            if kind == SyntaxKind::LeftBrace {
4402                return saw_selector && !expect_selector;
4403            }
4404            if expect_selector {
4405                if is_interpolation_start(kind) {
4406                    return true;
4407                }
4408                if !keyframe_selector_token_is_valid(self.tokens[token_index]) {
4409                    return false;
4410                }
4411                saw_selector = true;
4412                expect_selector = false;
4413                index = token_index + 1;
4414                continue;
4415            }
4416            if kind != SyntaxKind::Comma {
4417                return false;
4418            }
4419            expect_selector = true;
4420            index = token_index + 1;
4421        }
4422    }
4423
4424    fn consume_balanced_block(&mut self) {
4425        let mut depth = 0usize;
4426        while !self.at_end() {
4427            match self.current_kind() {
4428                Some(SyntaxKind::LeftBrace) => {
4429                    depth += 1;
4430                    self.token_current();
4431                }
4432                Some(SyntaxKind::RightBrace) => {
4433                    self.token_current();
4434                    depth = depth.saturating_sub(1);
4435                    if depth == 0 {
4436                        break;
4437                    }
4438                }
4439                Some(_) => self.token_current(),
4440                None => break,
4441            }
4442        }
4443    }
4444
4445    fn eat_trivia(&mut self) {
4446        while matches!(self.current_kind(), Some(kind) if kind.is_trivia()) {
4447            self.token_current();
4448        }
4449    }
4450
4451    fn consume_until_recovery(&mut self, recovery: &[SyntaxKind]) {
4452        let should_wrap = self
4453            .current_kind()
4454            .is_some_and(|kind| !recovery.contains(&kind));
4455        if should_wrap {
4456            self.builder.start_node(SyntaxKind::BogusRecovery);
4457        }
4458        while !self.at_end() {
4459            match self.current_kind() {
4460                Some(kind) if recovery.contains(&kind) => break,
4461                Some(_) => self.token_current(),
4462                None => break,
4463            }
4464        }
4465        if should_wrap {
4466            self.builder.finish_node();
4467        }
4468    }
4469
4470    fn find_before_recovery(&self, target: SyntaxKind, recovery: &[SyntaxKind]) -> bool {
4471        let mut index = self.position;
4472        while let Some(token) = self.tokens.get(index) {
4473            if token.kind == target {
4474                return true;
4475            }
4476            if recovery.contains(&token.kind) {
4477                return false;
4478            }
4479            index += 1;
4480        }
4481        false
4482    }
4483
4484    fn find_rule_block_open_before_recovery(&self, recovery: &[SyntaxKind]) -> bool {
4485        let mut index = self.position;
4486        while let Some(token) = self.tokens.get(index) {
4487            if token.kind == SyntaxKind::LeftBrace
4488                || (self.dialect == StyleDialect::Sass && token.kind == SyntaxKind::SassIndent)
4489            {
4490                return true;
4491            }
4492            if recovery.contains(&token.kind) {
4493                return false;
4494            }
4495            index += 1;
4496        }
4497        false
4498    }
4499
4500    fn find_text_before_recovery(&self, target: &str, recovery: &[SyntaxKind]) -> bool {
4501        let mut index = self.position;
4502        while let Some(token) = self.tokens.get(index) {
4503            if token.text == target {
4504                return true;
4505            }
4506            if recovery.contains(&token.kind) {
4507                return false;
4508            }
4509            index += 1;
4510        }
4511        false
4512    }
4513
4514    fn find_keyword_before_recovery(&self, target: &str, recovery: &[SyntaxKind]) -> bool {
4515        let mut index = self.position;
4516        while let Some(token) = self.tokens.get(index) {
4517            if css_keyword(token.text).equals(target) {
4518                return true;
4519            }
4520            if recovery.contains(&token.kind) {
4521                return false;
4522            }
4523            index += 1;
4524        }
4525        false
4526    }
4527
4528    fn current_function_has_closing_paren_before(&self, recovery: &[SyntaxKind]) -> bool {
4529        let Some(open_index) = self.position.checked_add(1) else {
4530            return false;
4531        };
4532        if self
4533            .tokens
4534            .get(open_index)
4535            .is_none_or(|token| token.kind != SyntaxKind::LeftParen)
4536        {
4537            return false;
4538        }
4539
4540        let mut depth = 0usize;
4541        for token in self.tokens.iter().skip(open_index) {
4542            match token.kind {
4543                SyntaxKind::LeftParen => depth += 1,
4544                SyntaxKind::RightParen => {
4545                    depth = depth.saturating_sub(1);
4546                    if depth == 0 {
4547                        return true;
4548                    }
4549                }
4550                kind if depth == 1 && recovery.contains(&kind) => return false,
4551                _ => {}
4552            }
4553        }
4554        false
4555    }
4556
4557    fn current_split_important_annotation(&self) -> bool {
4558        self.current_text() == Some("!")
4559            && self
4560                .non_trivia_token_from(self.position + 1)
4561                .is_some_and(|(index, kind)| {
4562                    matches!(kind, SyntaxKind::Ident | SyntaxKind::KeywordImportant)
4563                        && self.tokens.get(index).is_some_and(|token| {
4564                            matches_ignore_ascii_case(token.text, &["important"])
4565                        })
4566                })
4567    }
4568
4569    fn current_scss_variable_flag_annotation(&self) -> bool {
4570        matches!(self.dialect, StyleDialect::Scss | StyleDialect::Sass)
4571            && self.current_text() == Some("!")
4572            && self
4573                .non_trivia_token_from(self.position + 1)
4574                .is_some_and(|(index, kind)| {
4575                    kind == SyntaxKind::Ident
4576                        && self.tokens.get(index).is_some_and(|token| {
4577                            matches_ignore_ascii_case(token.text, &["default", "global"])
4578                        })
4579                })
4580    }
4581
4582    fn current_bracketed_value_has_closing_bracket_before(&self, recovery: &[SyntaxKind]) -> bool {
4583        let mut depth = 0usize;
4584        for token in self.tokens.iter().skip(self.position) {
4585            match token.kind {
4586                SyntaxKind::LeftBracket => depth += 1,
4587                SyntaxKind::RightBracket => {
4588                    depth = depth.saturating_sub(1);
4589                    if depth == 0 {
4590                        return true;
4591                    }
4592                }
4593                kind if depth == 1 && recovery.contains(&kind) => return false,
4594                _ => {}
4595            }
4596        }
4597        false
4598    }
4599
4600    fn current_simple_block_has_matching_close(&self, recovery: &[SyntaxKind]) -> bool {
4601        let Some(open_kind) = self.current_kind() else {
4602            return false;
4603        };
4604        if matching_simple_block_close(open_kind).is_none() {
4605            return false;
4606        }
4607
4608        let mut expected_closes = Vec::new();
4609        for token in self.tokens.iter().skip(self.position) {
4610            if let Some(close_kind) = matching_simple_block_close(token.kind) {
4611                expected_closes.push(close_kind);
4612                continue;
4613            }
4614
4615            if expected_closes.last().copied() == Some(token.kind) {
4616                expected_closes.pop();
4617                if expected_closes.is_empty() {
4618                    return true;
4619                }
4620                continue;
4621            }
4622
4623            if expected_closes.len() == 1 && recovery.contains(&token.kind) {
4624                return false;
4625            }
4626        }
4627        false
4628    }
4629
4630    fn current_dialect_at_rule_node_kind(&self, spec: AtRuleSpec) -> SyntaxKind {
4631        if !self.find_rule_block_open_before_recovery(&[
4632            SyntaxKind::Semicolon,
4633            SyntaxKind::SassOptionalSemicolon,
4634            SyntaxKind::RightBrace,
4635            SyntaxKind::SassDedent,
4636        ]) {
4637            return match spec.node_kind {
4638                SyntaxKind::ScssMixinDeclaration => SyntaxKind::BogusScssMixin,
4639                SyntaxKind::ScssFunctionDeclaration => SyntaxKind::BogusScssFunction,
4640                SyntaxKind::ScssControlIf
4641                | SyntaxKind::ScssControlElse
4642                | SyntaxKind::ScssControlEach
4643                | SyntaxKind::ScssControlFor
4644                | SyntaxKind::ScssControlWhile => SyntaxKind::BogusScssControl,
4645                _ => spec.node_kind,
4646            };
4647        }
4648        spec.node_kind
4649    }
4650
4651    fn current_less_guard_has_condition_before(&self, recovery: &[SyntaxKind]) -> bool {
4652        let mut index = self.position + 1;
4653        while let Some(token) = self.tokens.get(index) {
4654            if recovery.contains(&token.kind) {
4655                return false;
4656            }
4657            if token.kind == SyntaxKind::LeftParen {
4658                return true;
4659            }
4660            index += 1;
4661        }
4662        false
4663    }
4664
4665    fn current_scss_module_config_has_balanced_parens(&self) -> bool {
4666        let Some((_, SyntaxKind::LeftParen)) = self.non_trivia_token_from(self.position + 1) else {
4667            return false;
4668        };
4669        self.current_prelude_parentheses_are_balanced_until(&[
4670            SyntaxKind::Semicolon,
4671            SyntaxKind::SassOptionalSemicolon,
4672            SyntaxKind::LeftBrace,
4673            SyntaxKind::SassIndent,
4674        ])
4675    }
4676
4677    fn current_scss_parenthesized_collection_kind(
4678        &self,
4679        recovery: &[SyntaxKind],
4680    ) -> Option<SyntaxKind> {
4681        if !matches!(self.dialect, StyleDialect::Scss | StyleDialect::Sass)
4682            || self.current_kind() != Some(SyntaxKind::LeftParen)
4683        {
4684            return None;
4685        }
4686        let mut depth = 0usize;
4687        let mut saw_top_level_colon = false;
4688        let mut saw_top_level_comma = false;
4689        for token in self.tokens.iter().skip(self.position) {
4690            match token.kind {
4691                kind if depth == 1 && recovery.contains(&kind) => break,
4692                SyntaxKind::LeftParen => depth += 1,
4693                SyntaxKind::RightParen => {
4694                    depth = depth.saturating_sub(1);
4695                    if depth == 0 {
4696                        break;
4697                    }
4698                }
4699                SyntaxKind::Colon if depth == 1 => saw_top_level_colon = true,
4700                SyntaxKind::Comma if depth == 1 => saw_top_level_comma = true,
4701                _ => {}
4702            }
4703        }
4704        if saw_top_level_colon {
4705            Some(SyntaxKind::ScssMap)
4706        } else if saw_top_level_comma {
4707            Some(SyntaxKind::ScssList)
4708        } else {
4709            None
4710        }
4711    }
4712
4713    fn current_parenthesized_collection_has_closing_paren_before(
4714        &self,
4715        recovery: &[SyntaxKind],
4716    ) -> bool {
4717        let mut depth = 0usize;
4718        for token in self.tokens.iter().skip(self.position) {
4719            match token.kind {
4720                kind if depth == 1 && recovery.contains(&kind) => return false,
4721                SyntaxKind::LeftParen => depth += 1,
4722                SyntaxKind::RightParen => {
4723                    depth = depth.saturating_sub(1);
4724                    if depth == 0 {
4725                        return true;
4726                    }
4727                }
4728                _ => {}
4729            }
4730        }
4731        false
4732    }
4733
4734    fn current_scss_map_entry_has_colon_before(&self, recovery: &[SyntaxKind]) -> bool {
4735        let mut paren_depth = 0usize;
4736        let mut bracket_depth = 0usize;
4737        for token in self.tokens.iter().skip(self.position) {
4738            match token.kind {
4739                kind if paren_depth == 0 && bracket_depth == 0 && recovery.contains(&kind) => {
4740                    return false;
4741                }
4742                SyntaxKind::LeftParen => paren_depth += 1,
4743                SyntaxKind::RightParen => paren_depth = paren_depth.saturating_sub(1),
4744                SyntaxKind::LeftBracket => bracket_depth += 1,
4745                SyntaxKind::RightBracket => bracket_depth = bracket_depth.saturating_sub(1),
4746                SyntaxKind::Colon if paren_depth == 0 && bracket_depth == 0 => return true,
4747                _ => {}
4748            }
4749        }
4750        false
4751    }
4752
4753    fn current_scss_map_entry_has_value_before(&self, recovery: &[SyntaxKind]) -> bool {
4754        let mut paren_depth = 0usize;
4755        let mut bracket_depth = 0usize;
4756        let mut saw_colon = false;
4757        for token in self.tokens.iter().skip(self.position) {
4758            if token.kind.is_trivia() {
4759                continue;
4760            }
4761            match token.kind {
4762                kind if paren_depth == 0 && bracket_depth == 0 && recovery.contains(&kind) => {
4763                    return false;
4764                }
4765                SyntaxKind::LeftParen => paren_depth += 1,
4766                SyntaxKind::RightParen => paren_depth = paren_depth.saturating_sub(1),
4767                SyntaxKind::LeftBracket => bracket_depth += 1,
4768                SyntaxKind::RightBracket => bracket_depth = bracket_depth.saturating_sub(1),
4769                SyntaxKind::Colon if paren_depth == 0 && bracket_depth == 0 => saw_colon = true,
4770                _ if saw_colon && paren_depth == 0 && bracket_depth == 0 => return true,
4771                _ => {}
4772            }
4773        }
4774        false
4775    }
4776
4777    fn current_starts_scss_space_list_before(&self, recovery: &[SyntaxKind]) -> bool {
4778        if !matches!(self.dialect, StyleDialect::Scss | StyleDialect::Sass) {
4779            return false;
4780        }
4781        let mut paren_depth = 0usize;
4782        let mut bracket_depth = 0usize;
4783        let mut item_count = 0usize;
4784        let mut expecting_item = true;
4785        for token in self.tokens.iter().skip(self.position) {
4786            if token.kind.is_trivia() {
4787                if paren_depth == 0 && bracket_depth == 0 {
4788                    expecting_item = true;
4789                }
4790                continue;
4791            }
4792            match token.kind {
4793                kind if paren_depth == 0 && bracket_depth == 0 && recovery.contains(&kind) => {
4794                    return item_count >= 2;
4795                }
4796                SyntaxKind::Comma | SyntaxKind::Colon if paren_depth == 0 && bracket_depth == 0 => {
4797                    return false;
4798                }
4799                SyntaxKind::Plus
4800                | SyntaxKind::Minus
4801                | SyntaxKind::Star
4802                | SyntaxKind::Slash
4803                | SyntaxKind::Percent
4804                | SyntaxKind::LessThan
4805                | SyntaxKind::GreaterThan
4806                | SyntaxKind::Equals
4807                | SyntaxKind::DoubleAmpersand
4808                | SyntaxKind::ColumnCombinator
4809                | SyntaxKind::Delim
4810                    if paren_depth == 0 && bracket_depth == 0 =>
4811                {
4812                    return false;
4813                }
4814                SyntaxKind::Ident
4815                    if paren_depth == 0
4816                        && bracket_depth == 0
4817                        && matches_ignore_ascii_case(token.text, &["and", "or"]) =>
4818                {
4819                    return false;
4820                }
4821                SyntaxKind::LeftParen => {
4822                    if paren_depth == 0 && bracket_depth == 0 && expecting_item {
4823                        item_count += 1;
4824                    }
4825                    paren_depth += 1;
4826                    expecting_item = false;
4827                }
4828                SyntaxKind::RightParen => {
4829                    paren_depth = paren_depth.saturating_sub(1);
4830                    expecting_item = false;
4831                }
4832                SyntaxKind::LeftBracket => {
4833                    if paren_depth == 0 && bracket_depth == 0 && expecting_item {
4834                        item_count += 1;
4835                    }
4836                    bracket_depth += 1;
4837                    expecting_item = false;
4838                }
4839                SyntaxKind::RightBracket => {
4840                    bracket_depth = bracket_depth.saturating_sub(1);
4841                    expecting_item = false;
4842                }
4843                _ if paren_depth == 0 && bracket_depth == 0 && expecting_item => {
4844                    item_count += 1;
4845                    expecting_item = false;
4846                }
4847                _ => expecting_item = false,
4848            }
4849        }
4850        item_count >= 2
4851    }
4852
4853    fn current_value_has_top_level_comma_before(&self, recovery: &[SyntaxKind]) -> bool {
4854        let mut paren_depth = 0usize;
4855        let mut bracket_depth = 0usize;
4856        for token in self.tokens.iter().skip(self.position) {
4857            match token.kind {
4858                kind if paren_depth == 0 && bracket_depth == 0 && recovery.contains(&kind) => {
4859                    return false;
4860                }
4861                SyntaxKind::LeftParen => paren_depth += 1,
4862                SyntaxKind::RightParen => paren_depth = paren_depth.saturating_sub(1),
4863                SyntaxKind::LeftBracket => bracket_depth += 1,
4864                SyntaxKind::RightBracket => bracket_depth = bracket_depth.saturating_sub(1),
4865                SyntaxKind::Comma if paren_depth == 0 && bracket_depth == 0 => return true,
4866                _ => {}
4867            }
4868        }
4869        false
4870    }
4871
4872    fn current_value_list_is_bogus(&self, recovery: &[SyntaxKind]) -> bool {
4873        let mut paren_depth = 0usize;
4874        let mut bracket_depth = 0usize;
4875        let mut expecting_item = true;
4876        for token in self.tokens.iter().skip(self.position) {
4877            if token.kind.is_trivia() {
4878                continue;
4879            }
4880            match token.kind {
4881                kind if paren_depth == 0 && bracket_depth == 0 && recovery.contains(&kind) => {
4882                    return expecting_item;
4883                }
4884                SyntaxKind::LeftParen => {
4885                    paren_depth += 1;
4886                    expecting_item = false;
4887                }
4888                SyntaxKind::RightParen => {
4889                    paren_depth = paren_depth.saturating_sub(1);
4890                    expecting_item = false;
4891                }
4892                SyntaxKind::LeftBracket => {
4893                    bracket_depth += 1;
4894                    expecting_item = false;
4895                }
4896                SyntaxKind::RightBracket => {
4897                    bracket_depth = bracket_depth.saturating_sub(1);
4898                    expecting_item = false;
4899                }
4900                SyntaxKind::Comma if paren_depth == 0 && bracket_depth == 0 => {
4901                    if expecting_item {
4902                        return true;
4903                    }
4904                    expecting_item = true;
4905                }
4906                _ => expecting_item = false,
4907            }
4908        }
4909        expecting_item
4910    }
4911
4912    fn current_starts_missing_semicolon_declaration(&self, recovery: &[SyntaxKind]) -> bool {
4913        match self.current_kind() {
4914            Some(SyntaxKind::Ident | SyntaxKind::CustomPropertyName) => {}
4915            _ => return false,
4916        }
4917
4918        let mut index = self.position + 1;
4919        while let Some(token) = self.tokens.get(index) {
4920            if token.kind.is_trivia() {
4921                index += 1;
4922                continue;
4923            }
4924            if recovery.contains(&token.kind) {
4925                return false;
4926            }
4927            return token.kind == SyntaxKind::Colon;
4928        }
4929        false
4930    }
4931
4932    fn current_selector_item_is_bogus(&self, recovery: &[SyntaxKind]) -> bool {
4933        self.selector_item_is_bogus_from(self.position, recovery)
4934    }
4935
4936    fn selector_item_is_bogus_from(&self, start: usize, recovery: &[SyntaxKind]) -> bool {
4937        let mut paren_depth = 0usize;
4938        let mut bracket_depth = 0usize;
4939        let mut saw_selector_token = false;
4940
4941        for token in self.tokens.iter().skip(start) {
4942            if token.kind.is_trivia() {
4943                continue;
4944            }
4945            if paren_depth == 0
4946                && bracket_depth == 0
4947                && (token.kind == SyntaxKind::Comma
4948                    || is_selector_boundary_until(token.kind, recovery))
4949            {
4950                break;
4951            }
4952
4953            match token.kind {
4954                SyntaxKind::LeftParen => paren_depth += 1,
4955                SyntaxKind::RightParen => paren_depth = paren_depth.saturating_sub(1),
4956                SyntaxKind::LeftBracket => bracket_depth += 1,
4957                SyntaxKind::RightBracket => bracket_depth = bracket_depth.saturating_sub(1),
4958                _ => {}
4959            }
4960
4961            if !selector_item_token_is_recoverable(token.kind) {
4962                return true;
4963            }
4964            saw_selector_token = true;
4965        }
4966
4967        !saw_selector_token
4968    }
4969
4970    fn selector_list_contains_bogus_item_until(&self, recovery: &[SyntaxKind]) -> bool {
4971        let mut index = self.position;
4972        while let Some(token) = self.tokens.get(index) {
4973            if token.kind.is_trivia() || token.kind == SyntaxKind::Comma {
4974                index += 1;
4975                continue;
4976            }
4977            if is_selector_boundary_until(token.kind, recovery) {
4978                return false;
4979            }
4980            if self.selector_item_is_bogus_from(index, recovery) {
4981                return true;
4982            }
4983
4984            let mut paren_depth = 0usize;
4985            let mut bracket_depth = 0usize;
4986            while let Some(token) = self.tokens.get(index) {
4987                if paren_depth == 0
4988                    && bracket_depth == 0
4989                    && (token.kind == SyntaxKind::Comma
4990                        || is_selector_boundary_until(token.kind, recovery))
4991                {
4992                    break;
4993                }
4994                match token.kind {
4995                    SyntaxKind::LeftParen => paren_depth += 1,
4996                    SyntaxKind::RightParen => paren_depth = paren_depth.saturating_sub(1),
4997                    SyntaxKind::LeftBracket => bracket_depth += 1,
4998                    SyntaxKind::RightBracket => bracket_depth = bracket_depth.saturating_sub(1),
4999                    _ => {}
5000                }
5001                index += 1;
5002            }
5003        }
5004        false
5005    }
5006
5007    fn current_generic_at_rule_prelude_node_kind(&self) -> SyntaxKind {
5008        if self.current_prelude_parentheses_are_balanced_until(&[
5009            SyntaxKind::LeftBrace,
5010            SyntaxKind::Semicolon,
5011        ]) {
5012            SyntaxKind::AtRulePrelude
5013        } else {
5014            SyntaxKind::BogusAtRulePrelude
5015        }
5016    }
5017
5018    fn current_prelude_parentheses_are_balanced_until(&self, recovery: &[SyntaxKind]) -> bool {
5019        let mut depth = 0usize;
5020        for token in self.tokens.iter().skip(self.position) {
5021            match token.kind {
5022                kind if depth == 0 && recovery.contains(&kind) => return true,
5023                SyntaxKind::LeftParen => depth += 1,
5024                SyntaxKind::RightParen => {
5025                    if depth == 0 {
5026                        return false;
5027                    }
5028                    depth -= 1;
5029                }
5030                _ => {}
5031            }
5032        }
5033        depth == 0
5034    }
5035
5036    fn previous_left_brace_has_match(&self) -> bool {
5037        let Some(open_index) = self.position.checked_sub(1) else {
5038            return false;
5039        };
5040        let Some(open) = self.tokens.get(open_index) else {
5041            return false;
5042        };
5043        if open.kind != SyntaxKind::LeftBrace {
5044            return false;
5045        }
5046
5047        let mut depth = 0usize;
5048        for token in self.tokens.iter().skip(open_index) {
5049            match token.kind {
5050                SyntaxKind::LeftBrace => depth += 1,
5051                SyntaxKind::RightBrace => {
5052                    depth = depth.saturating_sub(1);
5053                    if depth == 0 {
5054                        return true;
5055                    }
5056                }
5057                _ => {}
5058            }
5059        }
5060        false
5061    }
5062
5063    fn current_starts_nested_rule(&self) -> bool {
5064        matches!(
5065            self.current_kind(),
5066            Some(
5067                SyntaxKind::Dot
5068                    | SyntaxKind::Hash
5069                    | SyntaxKind::Ampersand
5070                    | SyntaxKind::Colon
5071                    | SyntaxKind::DoubleColon
5072                    | SyntaxKind::LeftBracket
5073                    | SyntaxKind::GreaterThan
5074                    | SyntaxKind::Plus
5075                    | SyntaxKind::Tilde
5076                    | SyntaxKind::ColumnCombinator
5077            )
5078        ) && self.find_rule_block_open_before_recovery(&[
5079            SyntaxKind::Semicolon,
5080            SyntaxKind::SassOptionalSemicolon,
5081            SyntaxKind::RightBrace,
5082            SyntaxKind::SassDedent,
5083        ])
5084    }
5085
5086    fn current_starts_scss_nested_property(&self) -> bool {
5087        if !matches!(self.dialect, StyleDialect::Scss | StyleDialect::Sass) {
5088            return false;
5089        }
5090        if !matches!(
5091            self.current_kind(),
5092            Some(SyntaxKind::Ident | SyntaxKind::CustomPropertyName)
5093        ) {
5094            return false;
5095        }
5096
5097        let mut saw_colon = false;
5098        for token in self.tokens.iter().skip(self.position) {
5099            match token.kind {
5100                SyntaxKind::Colon => saw_colon = true,
5101                SyntaxKind::LeftBrace if saw_colon => return true,
5102                SyntaxKind::SassIndent if saw_colon && self.dialect == StyleDialect::Sass => {
5103                    return true;
5104                }
5105                SyntaxKind::Semicolon
5106                | SyntaxKind::SassOptionalSemicolon
5107                | SyntaxKind::RightBrace
5108                | SyntaxKind::SassDedent => return false,
5109                _ => {}
5110            }
5111        }
5112        false
5113    }
5114
5115    fn current_starts_less_mixin_declaration(&self) -> bool {
5116        self.dialect == StyleDialect::Less
5117            && self.current_starts_less_callable_signature()
5118            && self.find_before_recovery(
5119                SyntaxKind::LeftBrace,
5120                &[SyntaxKind::Semicolon, SyntaxKind::RightBrace],
5121            )
5122    }
5123
5124    fn current_starts_less_mixin_call(&self) -> bool {
5125        self.dialect == StyleDialect::Less
5126            && self.current_starts_less_callable_signature()
5127            && !self.find_before_recovery(
5128                SyntaxKind::LeftBrace,
5129                &[SyntaxKind::Semicolon, SyntaxKind::RightBrace],
5130            )
5131    }
5132
5133    fn current_starts_less_callable_signature(&self) -> bool {
5134        match self.current_kind() {
5135            Some(SyntaxKind::Dot) => {
5136                let Some((index, SyntaxKind::Ident | SyntaxKind::CustomPropertyName)) =
5137                    self.non_trivia_token_from(self.position + 1)
5138                else {
5139                    return false;
5140                };
5141                self.non_trivia_token_from(index + 1)
5142                    .is_some_and(|(_, kind)| kind == SyntaxKind::LeftParen)
5143            }
5144            Some(SyntaxKind::Hash) => self
5145                .non_trivia_token_from(self.position + 1)
5146                .is_some_and(|(_, kind)| kind == SyntaxKind::LeftParen),
5147            _ => false,
5148        }
5149    }
5150
5151    fn current_starts_less_extend_rule(&self) -> bool {
5152        self.dialect == StyleDialect::Less
5153            && self.current_kind() == Some(SyntaxKind::Colon)
5154            && self
5155                .non_trivia_token_from(self.position + 1)
5156                .is_some_and(|(index, kind)| {
5157                    kind == SyntaxKind::Ident
5158                        && self
5159                            .tokens
5160                            .get(index)
5161                            .is_some_and(|token| token.text == "extend")
5162                })
5163    }
5164
5165    fn current_starts_less_namespace_access(&self) -> bool {
5166        self.dialect == StyleDialect::Less
5167            && matches!(
5168                self.current_kind(),
5169                Some(SyntaxKind::Dot | SyntaxKind::Hash)
5170            )
5171            && self.find_before_recovery(
5172                SyntaxKind::GreaterThan,
5173                &[
5174                    SyntaxKind::Semicolon,
5175                    SyntaxKind::LeftBrace,
5176                    SyntaxKind::RightBrace,
5177                ],
5178            )
5179            && self.find_before_recovery(
5180                SyntaxKind::LeftParen,
5181                &[
5182                    SyntaxKind::Semicolon,
5183                    SyntaxKind::LeftBrace,
5184                    SyntaxKind::RightBrace,
5185                ],
5186            )
5187    }
5188
5189    fn current_left_brace_has_match(&self) -> bool {
5190        let mut depth = 0usize;
5191        for token in self.tokens.iter().skip(self.position) {
5192            match token.kind {
5193                SyntaxKind::LeftBrace => depth += 1,
5194                SyntaxKind::RightBrace => {
5195                    depth = depth.saturating_sub(1);
5196                    if depth == 0 {
5197                        return true;
5198                    }
5199                }
5200                _ => {}
5201            }
5202        }
5203        false
5204    }
5205
5206    fn token_current(&mut self) {
5207        if let Some(token) = self.tokens.get(self.position).copied() {
5208            self.builder.token(token.kind, token.text);
5209            self.position += 1;
5210        }
5211    }
5212
5213    fn empty_bogus_node(&mut self, kind: SyntaxKind, code: ParseErrorCode, message: &'static str) {
5214        self.builder.start_node(kind);
5215        self.builder.finish_node();
5216        self.error_at_current(code, message);
5217    }
5218
5219    fn missing_token_bogus_trivia(&mut self, code: ParseErrorCode, message: &'static str) {
5220        self.builder.start_node(SyntaxKind::BogusTrivia);
5221        self.builder.finish_node();
5222        self.error_at_current(code, message);
5223    }
5224
5225    fn error_at_current(&mut self, code: ParseErrorCode, message: &'static str) {
5226        self.errors.push(ParseError {
5227            code,
5228            range: self.current_range(),
5229            message,
5230        });
5231    }
5232
5233    fn current_kind(&self) -> Option<SyntaxKind> {
5234        self.tokens.get(self.position).map(|token| token.kind)
5235    }
5236
5237    fn current_range(&self) -> TextRange {
5238        if let Some(token) = self.tokens.get(self.position) {
5239            return token.range;
5240        }
5241        let end = self
5242            .tokens
5243            .last()
5244            .map(|token| token.range.end())
5245            .unwrap_or_else(|| TextSize::from(0));
5246        TextRange::new(end, end)
5247    }
5248
5249    fn current_text(&self) -> Option<&'text str> {
5250        self.tokens.get(self.position).map(|token| token.text)
5251    }
5252
5253    fn token_text_matches(&self, index: usize, expected: &str) -> bool {
5254        self.tokens
5255            .get(index)
5256            .is_some_and(|token| matches_ignore_ascii_case(token.text, &[expected]))
5257    }
5258
5259    fn current_token_is_adjacent_to_next(&self) -> bool {
5260        let Some(current) = self.tokens.get(self.position) else {
5261            return false;
5262        };
5263        let Some(next) = self.tokens.get(self.position + 1) else {
5264            return false;
5265        };
5266        current.range.end() == next.range.start()
5267    }
5268
5269    fn current_dialect_at_rule_spec(&self) -> Option<AtRuleSpec> {
5270        let text = self.current_text()?;
5271        match self.dialect {
5272            StyleDialect::Scss | StyleDialect::Sass => scss_at_rule_spec(text),
5273            StyleDialect::Css | StyleDialect::Less => None,
5274        }
5275    }
5276
5277    fn current_is_css_module_value_rule(&self) -> bool {
5278        self.current_text()
5279            .is_some_and(|text| css_keyword(text).equals("@value"))
5280    }
5281
5282    fn next_kind(&self) -> Option<SyntaxKind> {
5283        self.tokens.get(self.position + 1).map(|token| token.kind)
5284    }
5285
5286    fn next_non_trivia_kind(&self) -> Option<SyntaxKind> {
5287        let mut index = self.position + 1;
5288        while let Some(token) = self.tokens.get(index) {
5289            if !token.kind.is_trivia() {
5290                return Some(token.kind);
5291            }
5292            index += 1;
5293        }
5294        None
5295    }
5296
5297    fn non_trivia_token_from(&self, mut index: usize) -> Option<(usize, SyntaxKind)> {
5298        while let Some(token) = self.tokens.get(index) {
5299            if !token.kind.is_trivia() {
5300                return Some((index, token.kind));
5301            }
5302            index += 1;
5303        }
5304        None
5305    }
5306
5307    fn non_trivia_token_after_interpolation(
5308        &self,
5309        mut index: usize,
5310        start_kind: SyntaxKind,
5311    ) -> Option<(usize, SyntaxKind)> {
5312        let end_kind = interpolation_end_kind(start_kind)?;
5313        index += 1;
5314        while let Some(token) = self.tokens.get(index) {
5315            if token.kind == end_kind {
5316                return self.non_trivia_token_from(index + 1);
5317            }
5318            if is_at_rule_prelude_boundary(token.kind) {
5319                return None;
5320            }
5321            index += 1;
5322        }
5323        None
5324    }
5325
5326    fn current_starts_namespace_qualified_selector(&self, kind: SyntaxKind) -> bool {
5327        match kind {
5328            SyntaxKind::Ident | SyntaxKind::Star => {
5329                self.next_kind() == Some(SyntaxKind::Pipe)
5330                    && self
5331                        .tokens
5332                        .get(self.position + 2)
5333                        .is_some_and(|token| namespace_selector_target_can_start(token.kind))
5334            }
5335            SyntaxKind::Pipe => self
5336                .tokens
5337                .get(self.position + 1)
5338                .is_some_and(|token| namespace_selector_target_can_start(token.kind)),
5339            _ => false,
5340        }
5341    }
5342
5343    fn namespace_qualified_selector_target_kind(&self) -> Option<SyntaxKind> {
5344        let target_index = if self.current_kind() == Some(SyntaxKind::Pipe) {
5345            self.position + 1
5346        } else {
5347            self.position + 2
5348        };
5349        self.tokens.get(target_index).map(|token| token.kind)
5350    }
5351
5352    fn at_end(&self) -> bool {
5353        self.position >= self.tokens.len()
5354    }
5355}