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        let mut saw_name = false;
3652        let mut expecting_segment = true;
3653        let mut index = self.position;
3654
3655        while let Some(token) = self.tokens.get(index) {
3656            if token.kind.is_trivia() {
3657                index += 1;
3658                continue;
3659            }
3660            if is_at_rule_prelude_boundary(token.kind) {
3661                return saw_name && !expecting_segment;
3662            }
3663            if is_interpolation_start(token.kind) {
3664                return true;
3665            }
3666            match token.kind {
3667                SyntaxKind::Ident if expecting_segment => {
3668                    saw_name = true;
3669                    expecting_segment = false;
3670                }
3671                SyntaxKind::Comma if saw_name && !expecting_segment => {
3672                    expecting_segment = true;
3673                }
3674                SyntaxKind::Dot if saw_name && !expecting_segment => {
3675                    expecting_segment = true;
3676                }
3677                _ => return false,
3678            }
3679            index += 1;
3680        }
3681
3682        saw_name && !expecting_segment
3683    }
3684
3685    fn parse_container_rule_prelude(&mut self) {
3686        self.eat_trivia();
3687        let valid = self.container_rule_prelude_is_valid();
3688        if !valid {
3689            self.error_at_current(ParseErrorCode::ExpectedValue, "invalid @container prelude");
3690        }
3691        self.builder.start_node(if valid {
3692            SyntaxKind::ContainerCondition
3693        } else {
3694            SyntaxKind::BogusContainerCondition
3695        });
3696        self.consume_at_rule_prelude_tokens_without_wrapping();
3697        self.builder.finish_node();
3698    }
3699
3700    fn container_rule_prelude_is_valid(&self) -> bool {
3701        let Some((first_index, first_kind)) = self.non_trivia_token_from(self.position) else {
3702            return false;
3703        };
3704        if is_at_rule_prelude_boundary(first_kind) {
3705            return false;
3706        }
3707        if !self.current_prelude_parentheses_are_balanced_until(&[
3708            SyntaxKind::LeftBrace,
3709            SyntaxKind::SassIndent,
3710            SyntaxKind::Semicolon,
3711            SyntaxKind::SassOptionalSemicolon,
3712        ]) {
3713            return false;
3714        }
3715        if self.container_condition_starts_at(first_index, first_kind) {
3716            return true;
3717        }
3718        if first_kind != SyntaxKind::Ident {
3719            return false;
3720        }
3721        self.non_trivia_token_from(first_index + 1).is_some_and(
3722            |(condition_index, condition_kind)| {
3723                self.container_condition_starts_at(condition_index, condition_kind)
3724            },
3725        )
3726    }
3727
3728    fn container_condition_starts_at(&self, index: usize, kind: SyntaxKind) -> bool {
3729        if matches!(kind, SyntaxKind::LeftParen | SyntaxKind::KeywordNot)
3730            || is_interpolation_start(kind)
3731        {
3732            return true;
3733        }
3734        kind == SyntaxKind::Ident
3735            && self
3736                .non_trivia_token_from(index + 1)
3737                .is_some_and(|(_, next_kind)| next_kind == SyntaxKind::LeftParen)
3738    }
3739
3740    fn parse_supports_rule_prelude(&mut self) {
3741        self.eat_trivia();
3742        let valid = self.supports_rule_prelude_is_valid();
3743        if !valid {
3744            self.error_at_current(ParseErrorCode::ExpectedValue, "invalid @supports prelude");
3745        }
3746        self.builder.start_node(if valid {
3747            SyntaxKind::SupportsCondition
3748        } else {
3749            SyntaxKind::BogusSupportsCondition
3750        });
3751        self.consume_at_rule_prelude_tokens_without_wrapping();
3752        self.builder.finish_node();
3753    }
3754
3755    fn supports_rule_prelude_is_valid(&self) -> bool {
3756        let Some((first_index, first_kind)) = self.non_trivia_token_from(self.position) else {
3757            return false;
3758        };
3759        if is_at_rule_prelude_boundary(first_kind) {
3760            return false;
3761        }
3762        if !self.current_prelude_parentheses_are_balanced_until(&[
3763            SyntaxKind::LeftBrace,
3764            SyntaxKind::SassIndent,
3765            SyntaxKind::Semicolon,
3766            SyntaxKind::SassOptionalSemicolon,
3767        ]) {
3768            return false;
3769        }
3770        self.supports_condition_starts_at(first_index, first_kind)
3771    }
3772
3773    fn supports_condition_starts_at(&self, index: usize, kind: SyntaxKind) -> bool {
3774        if kind == SyntaxKind::KeywordNot || self.token_text_matches(index, "not") {
3775            return self
3776                .non_trivia_token_from(index + 1)
3777                .is_some_and(|(next_index, next_kind)| {
3778                    self.supports_condition_starts_at(next_index, next_kind)
3779                });
3780        }
3781        if kind == SyntaxKind::LeftParen || is_interpolation_start(kind) {
3782            return true;
3783        }
3784        kind == SyntaxKind::Ident
3785            && self
3786                .non_trivia_token_from(index + 1)
3787                .is_some_and(|(_, next_kind)| next_kind == SyntaxKind::LeftParen)
3788    }
3789
3790    fn parse_scope_rule_prelude(&mut self) {
3791        self.eat_trivia();
3792        let valid = self.scope_rule_prelude_is_valid();
3793        if !valid {
3794            self.error_at_current(ParseErrorCode::ExpectedValue, "invalid @scope prelude");
3795        }
3796        self.builder.start_node(if valid {
3797            SyntaxKind::ScopeRange
3798        } else {
3799            SyntaxKind::BogusScopeRange
3800        });
3801        self.consume_at_rule_prelude_tokens_without_wrapping();
3802        self.builder.finish_node();
3803    }
3804
3805    fn scope_rule_prelude_is_valid(&self) -> bool {
3806        let Some((start_index, start_kind)) = self.non_trivia_token_from(self.position) else {
3807            return false;
3808        };
3809        if is_at_rule_prelude_boundary(start_kind) {
3810            return false;
3811        }
3812        if !self.current_prelude_parentheses_are_balanced_until(&[
3813            SyntaxKind::LeftBrace,
3814            SyntaxKind::SassIndent,
3815            SyntaxKind::Semicolon,
3816            SyntaxKind::SassOptionalSemicolon,
3817        ]) {
3818            return false;
3819        }
3820        if is_interpolation_start(start_kind) {
3821            return true;
3822        }
3823        if start_kind != SyntaxKind::LeftParen {
3824            return false;
3825        }
3826
3827        let Some(start_close_index) = self.parenthesized_prelude_close_index(start_index) else {
3828            return false;
3829        };
3830        let Some((after_start_index, after_start_kind)) =
3831            self.non_trivia_token_from(start_close_index + 1)
3832        else {
3833            return true;
3834        };
3835        if is_at_rule_prelude_boundary(after_start_kind) {
3836            return true;
3837        }
3838        if after_start_kind != SyntaxKind::Ident
3839            || !self
3840                .tokens
3841                .get(after_start_index)
3842                .is_some_and(|token| matches_ignore_ascii_case(token.text, &["to"]))
3843        {
3844            return false;
3845        }
3846
3847        let Some((end_index, end_kind)) = self.non_trivia_token_from(after_start_index + 1) else {
3848            return false;
3849        };
3850        if is_interpolation_start(end_kind) {
3851            return true;
3852        }
3853        if end_kind != SyntaxKind::LeftParen {
3854            return false;
3855        }
3856        let Some(end_close_index) = self.parenthesized_prelude_close_index(end_index) else {
3857            return false;
3858        };
3859        self.non_trivia_token_from(end_close_index + 1)
3860            .is_none_or(|(_, kind)| is_at_rule_prelude_boundary(kind))
3861    }
3862
3863    fn parenthesized_prelude_close_index(&self, open_index: usize) -> Option<usize> {
3864        let mut depth = 0usize;
3865        for (index, token) in self.tokens.iter().enumerate().skip(open_index) {
3866            match token.kind {
3867                SyntaxKind::LeftParen => depth += 1,
3868                SyntaxKind::RightParen => {
3869                    depth = depth.saturating_sub(1);
3870                    if depth == 0 {
3871                        return Some(index);
3872                    }
3873                }
3874                kind if depth == 0 && is_at_rule_prelude_boundary(kind) => return None,
3875                _ => {}
3876            }
3877        }
3878        None
3879    }
3880
3881    fn parse_page_rule_prelude(&mut self) {
3882        self.eat_trivia();
3883        if self.current_kind().is_none_or(is_at_rule_prelude_boundary) {
3884            return;
3885        }
3886        let valid = self.page_rule_prelude_is_valid();
3887        if !valid {
3888            self.error_at_current(ParseErrorCode::ExpectedValue, "invalid @page prelude");
3889        }
3890        self.builder.start_node(if valid {
3891            SyntaxKind::AtRulePrelude
3892        } else {
3893            SyntaxKind::BogusAtRulePrelude
3894        });
3895        self.consume_at_rule_prelude_tokens_without_wrapping();
3896        self.builder.finish_node();
3897    }
3898
3899    fn page_rule_prelude_is_valid(&self) -> bool {
3900        let mut expecting_selector = true;
3901        let mut expecting_pseudo_name = false;
3902        let mut saw_selector = false;
3903
3904        for token in self.tokens.iter().skip(self.position) {
3905            if token.kind.is_trivia() {
3906                continue;
3907            }
3908            if is_at_rule_prelude_boundary(token.kind) {
3909                return saw_selector && !expecting_selector && !expecting_pseudo_name;
3910            }
3911            if is_interpolation_start(token.kind) {
3912                return true;
3913            }
3914            if expecting_pseudo_name {
3915                if token.kind != SyntaxKind::Ident {
3916                    return false;
3917                }
3918                saw_selector = true;
3919                expecting_selector = false;
3920                expecting_pseudo_name = false;
3921                continue;
3922            }
3923            match token.kind {
3924                SyntaxKind::Ident if expecting_selector => {
3925                    saw_selector = true;
3926                    expecting_selector = false;
3927                }
3928                SyntaxKind::Colon => {
3929                    expecting_pseudo_name = true;
3930                }
3931                SyntaxKind::Comma if saw_selector && !expecting_selector => {
3932                    expecting_selector = true;
3933                }
3934                _ => return false,
3935            }
3936        }
3937
3938        saw_selector && !expecting_selector && !expecting_pseudo_name
3939    }
3940
3941    fn parse_import_prelude(&mut self) {
3942        self.eat_trivia();
3943        if self.dialect == StyleDialect::Less && self.current_kind() == Some(SyntaxKind::LeftParen)
3944        {
3945            self.builder.start_node(SyntaxKind::AtRulePrelude);
3946            self.parse_balanced_parenthesized_prelude(None);
3947            self.builder.finish_node();
3948            self.eat_trivia();
3949        }
3950        if !self.parse_import_source() {
3951            self.parse_bogus_import_prelude();
3952            return;
3953        }
3954        while !self.at_end() {
3955            match self.current_kind() {
3956                Some(kind) if is_at_rule_prelude_boundary(kind) => break,
3957                Some(kind) if kind.is_trivia() => self.token_current(),
3958                Some(SyntaxKind::Ident)
3959                    if self
3960                        .current_text()
3961                        .is_some_and(|text| css_keyword(text).equals("layer")) =>
3962                {
3963                    self.parse_import_layer_tail_node()
3964                }
3965                Some(SyntaxKind::Ident)
3966                    if self
3967                        .current_text()
3968                        .is_some_and(|text| css_keyword(text).equals("supports")) =>
3969                {
3970                    self.parse_import_supports_tail_node()
3971                }
3972                Some(_) => {
3973                    self.parse_media_query_list();
3974                    break;
3975                }
3976                None => break,
3977            }
3978        }
3979    }
3980
3981    fn parse_import_source(&mut self) -> bool {
3982        match self.current_kind() {
3983            Some(SyntaxKind::Url) => {
3984                self.builder.start_node(SyntaxKind::UrlValue);
3985                self.token_current();
3986                self.builder.finish_node();
3987                true
3988            }
3989            Some(SyntaxKind::Ident)
3990                if self
3991                    .current_text()
3992                    .is_some_and(|text| matches_ignore_ascii_case(text, &["url"]))
3993                    && self.next_kind() == Some(SyntaxKind::LeftParen) =>
3994            {
3995                self.builder.start_node(SyntaxKind::UrlValue);
3996                self.parse_function_call(&[SyntaxKind::LeftBrace, SyntaxKind::Semicolon]);
3997                self.builder.finish_node();
3998                true
3999            }
4000            Some(SyntaxKind::String) => {
4001                self.token_current();
4002                true
4003            }
4004            Some(kind) if is_interpolation_start(kind) => {
4005                self.parse_interpolation(kind, &[SyntaxKind::LeftBrace, SyntaxKind::Semicolon]);
4006                true
4007            }
4008            Some(_) | None => false,
4009        }
4010    }
4011
4012    fn parse_bogus_import_prelude(&mut self) {
4013        self.builder.start_node(SyntaxKind::BogusAtRulePrelude);
4014        self.error_at_current(ParseErrorCode::ExpectedValue, "invalid @import source");
4015        self.consume_at_rule_prelude_tokens_without_wrapping();
4016        self.builder.finish_node();
4017    }
4018
4019    fn parse_named_at_rule_prelude(
4020        &mut self,
4021        valid_head: fn(SyntaxKind) -> bool,
4022        message: &'static str,
4023    ) {
4024        if self.current_kind().is_none_or(is_at_rule_prelude_boundary) {
4025            return;
4026        }
4027        let valid_name = self
4028            .non_trivia_token_from(self.position)
4029            .is_some_and(|(_, kind)| valid_head(kind));
4030        if !valid_name {
4031            self.error_at_current(ParseErrorCode::ExpectedValue, message);
4032        }
4033        self.consume_at_rule_prelude_tokens();
4034    }
4035
4036    fn parse_import_layer_tail_node(&mut self) {
4037        let valid = self.import_layer_tail_is_valid();
4038        if !valid {
4039            self.error_at_current(ParseErrorCode::ExpectedValue, "invalid @import layer tail");
4040        }
4041        self.builder.start_node(if valid {
4042            SyntaxKind::LayerName
4043        } else {
4044            SyntaxKind::BogusLayerName
4045        });
4046        self.token_current();
4047        if self.current_kind() == Some(SyntaxKind::LeftParen) {
4048            self.parse_balanced_parenthesized_prelude(None);
4049        }
4050        self.builder.finish_node();
4051    }
4052
4053    fn import_layer_tail_is_valid(&self) -> bool {
4054        let Some((open_index, next_kind)) = self.non_trivia_token_from(self.position + 1) else {
4055            return true;
4056        };
4057        if next_kind != SyntaxKind::LeftParen {
4058            return true;
4059        }
4060        let Some(close_index) = self.parenthesized_prelude_close_index(open_index) else {
4061            return false;
4062        };
4063        self.layer_name_is_valid_between(open_index + 1, close_index)
4064    }
4065
4066    fn layer_name_is_valid_between(&self, start: usize, end: usize) -> bool {
4067        let mut saw_name = false;
4068        let mut expecting_segment = true;
4069
4070        for token in self.tokens[start..end]
4071            .iter()
4072            .filter(|token| !token.kind.is_trivia())
4073        {
4074            if is_interpolation_start(token.kind) {
4075                return true;
4076            }
4077            match token.kind {
4078                SyntaxKind::Ident if expecting_segment => {
4079                    saw_name = true;
4080                    expecting_segment = false;
4081                }
4082                SyntaxKind::Dot if saw_name && !expecting_segment => {
4083                    expecting_segment = true;
4084                }
4085                _ => return false,
4086            }
4087        }
4088
4089        saw_name && !expecting_segment
4090    }
4091
4092    fn parse_import_supports_tail_node(&mut self) {
4093        let valid = self.import_supports_tail_is_valid();
4094        if !valid {
4095            self.error_at_current(
4096                ParseErrorCode::ExpectedValue,
4097                "invalid @import supports tail",
4098            );
4099        }
4100        self.builder.start_node(if valid {
4101            SyntaxKind::SupportsCondition
4102        } else {
4103            SyntaxKind::BogusSupportsCondition
4104        });
4105        self.token_current();
4106        if self.current_kind() == Some(SyntaxKind::LeftParen) {
4107            self.parse_balanced_parenthesized_prelude(None);
4108        }
4109        self.builder.finish_node();
4110    }
4111
4112    fn import_supports_tail_is_valid(&self) -> bool {
4113        let Some((open_index, SyntaxKind::LeftParen)) =
4114            self.non_trivia_token_from(self.position + 1)
4115        else {
4116            return false;
4117        };
4118        let Some(close_index) = self.parenthesized_prelude_close_index(open_index) else {
4119            return false;
4120        };
4121        self.non_trivia_token_from(open_index + 1)
4122            .is_some_and(|(inner_index, inner_kind)| {
4123                inner_index < close_index && inner_kind != SyntaxKind::RightParen
4124            })
4125    }
4126
4127    fn consume_at_rule_prelude_tokens(&mut self) {
4128        if self.current_kind().is_none_or(is_at_rule_prelude_boundary) {
4129            return;
4130        }
4131        self.builder
4132            .start_node(self.current_generic_at_rule_prelude_node_kind());
4133        self.consume_at_rule_prelude_tokens_without_wrapping();
4134        self.builder.finish_node();
4135    }
4136
4137    fn consume_at_rule_prelude_tokens_without_wrapping(&mut self) {
4138        while !self.at_end() {
4139            match self.current_kind() {
4140                Some(kind) if is_at_rule_prelude_boundary(kind) => break,
4141                Some(SyntaxKind::LeftParen) => self.parse_balanced_parenthesized_prelude(None),
4142                Some(kind) if is_interpolation_start(kind) => {
4143                    self.parse_interpolation(kind, &[SyntaxKind::LeftBrace, SyntaxKind::Semicolon])
4144                }
4145                Some(_) => self.token_current(),
4146                None => break,
4147            }
4148        }
4149    }
4150
4151    fn parse_balanced_parenthesized_prelude(&mut self, node_kind: Option<SyntaxKind>) {
4152        self.parse_balanced_parenthesized_prelude_until(
4153            node_kind,
4154            &[SyntaxKind::LeftBrace, SyntaxKind::Semicolon],
4155        );
4156    }
4157
4158    fn parse_balanced_parenthesized_prelude_until(
4159        &mut self,
4160        node_kind: Option<SyntaxKind>,
4161        recovery: &[SyntaxKind],
4162    ) {
4163        if let Some(kind) = node_kind {
4164            self.builder.start_node(kind);
4165        }
4166        let mut depth = 0usize;
4167        let mut closed = false;
4168        while !self.at_end() {
4169            match self.current_kind() {
4170                Some(SyntaxKind::LeftParen) => {
4171                    depth += 1;
4172                    self.token_current();
4173                }
4174                Some(SyntaxKind::RightParen) => {
4175                    self.token_current();
4176                    depth = depth.saturating_sub(1);
4177                    if depth == 0 {
4178                        closed = true;
4179                        break;
4180                    }
4181                }
4182                Some(kind) if depth == 0 && recovery.contains(&kind) => break,
4183                Some(kind) if is_interpolation_start(kind) => {
4184                    self.parse_interpolation(kind, &[SyntaxKind::LeftBrace, SyntaxKind::Semicolon])
4185                }
4186                Some(_) => self.token_current(),
4187                None => break,
4188            }
4189        }
4190        if node_kind.is_some() {
4191            self.builder.finish_node();
4192        }
4193        if !closed {
4194            self.error_at_current(
4195                ParseErrorCode::UnexpectedCharacter,
4196                "unterminated parenthesized prelude",
4197            );
4198        }
4199    }
4200
4201    fn parse_interpolation(&mut self, start_kind: SyntaxKind, recovery: &[SyntaxKind]) {
4202        let Some(end_kind) = interpolation_end_kind(start_kind) else {
4203            self.token_current();
4204            return;
4205        };
4206        let closed = self.find_before_recovery(end_kind, recovery);
4207        self.builder.start_node(if closed {
4208            SyntaxKind::Interpolation
4209        } else {
4210            SyntaxKind::BogusInterpolation
4211        });
4212        if self.current_kind() == Some(start_kind) {
4213            self.token_current();
4214        }
4215        while !self.at_end() {
4216            match self.current_kind() {
4217                Some(kind) if kind == end_kind => {
4218                    self.token_current();
4219                    break;
4220                }
4221                Some(kind) if !closed && recovery.contains(&kind) => break,
4222                Some(_) => self.token_current(),
4223                None => break,
4224            }
4225        }
4226        if !closed {
4227            self.error_at_current(
4228                ParseErrorCode::UnexpectedCharacter,
4229                "unterminated interpolation",
4230            );
4231        }
4232        self.builder.finish_node();
4233    }
4234
4235    fn parse_group_at_rule_block(&mut self) {
4236        self.token_current();
4237        self.builder.start_node(SyntaxKind::RuleList);
4238        self.parse_rule_list_items();
4239        self.builder.finish_node();
4240        if self.current_kind() == Some(SyntaxKind::RightBrace) {
4241            self.token_current();
4242        }
4243    }
4244
4245    fn parse_rule_list_items(&mut self) {
4246        while !self.at_end() {
4247            self.eat_trivia();
4248            match self.current_kind() {
4249                Some(SyntaxKind::RightBrace | SyntaxKind::SassDedent) | None => break,
4250                Some(SyntaxKind::Semicolon | SyntaxKind::SassOptionalSemicolon) => {
4251                    self.token_current()
4252                }
4253                Some(SyntaxKind::AtKeyword) if self.current_is_css_module_value_rule() => {
4254                    self.parse_css_module_value_rule()
4255                }
4256                Some(SyntaxKind::AtKeyword) if self.current_dialect_at_rule_spec().is_some() => {
4257                    self.parse_dialect_at_rule()
4258                }
4259                Some(SyntaxKind::AtKeyword) => self.parse_at_rule(),
4260                Some(_) => self.parse_rule(),
4261            }
4262        }
4263    }
4264
4265    fn parse_declaration_block(&mut self) {
4266        self.token_current();
4267        self.builder
4268            .start_node(if self.previous_left_brace_has_match() {
4269                SyntaxKind::DeclarationList
4270            } else {
4271                SyntaxKind::BogusDeclarationList
4272            });
4273        self.parse_declaration_list();
4274        self.builder.finish_node();
4275        if self.current_kind() == Some(SyntaxKind::RightBrace) {
4276            self.token_current();
4277        } else {
4278            self.missing_token_bogus_trivia(
4279                ParseErrorCode::UnexpectedCharacter,
4280                "unterminated declaration block",
4281            );
4282        }
4283    }
4284
4285    fn parse_sass_indented_at_rule_block(&mut self, block_kind: AtRuleBlockKind) {
4286        self.builder.start_node(SyntaxKind::SassIndentedBlock);
4287        if self.current_kind() == Some(SyntaxKind::SassIndent) {
4288            self.token_current();
4289        }
4290        match block_kind {
4291            AtRuleBlockKind::GroupRuleList => {
4292                self.builder.start_node(SyntaxKind::RuleList);
4293                self.parse_rule_list_items();
4294                self.builder.finish_node();
4295            }
4296            AtRuleBlockKind::DeclarationList | AtRuleBlockKind::Keyframes => {
4297                self.builder.start_node(SyntaxKind::DeclarationList);
4298                self.parse_declaration_list();
4299                self.builder.finish_node();
4300            }
4301            AtRuleBlockKind::Raw => self.consume_sass_indented_raw_body(),
4302        }
4303        if self.current_kind() == Some(SyntaxKind::SassDedent) {
4304            self.token_current();
4305        } else {
4306            self.error_at_current(
4307                ParseErrorCode::UnexpectedCharacter,
4308                "unterminated Sass indented at-rule block",
4309            );
4310        }
4311        self.builder.finish_node();
4312    }
4313
4314    fn consume_sass_indented_raw_body(&mut self) {
4315        let mut depth = 0usize;
4316        while !self.at_end() {
4317            match self.current_kind() {
4318                Some(SyntaxKind::SassIndent) => {
4319                    depth += 1;
4320                    self.token_current();
4321                }
4322                Some(SyntaxKind::SassDedent) if depth == 0 => break,
4323                Some(SyntaxKind::SassDedent) => {
4324                    depth = depth.saturating_sub(1);
4325                    self.token_current();
4326                }
4327                Some(_) => self.token_current(),
4328                None => break,
4329            }
4330        }
4331    }
4332
4333    fn parse_keyframes_block(&mut self) {
4334        self.token_current();
4335        while !self.at_end() {
4336            self.eat_trivia();
4337            match self.current_kind() {
4338                Some(SyntaxKind::RightBrace) | None => break,
4339                Some(_) => self.parse_keyframe_block(),
4340            }
4341        }
4342        if self.current_kind() == Some(SyntaxKind::RightBrace) {
4343            self.token_current();
4344        }
4345    }
4346
4347    fn parse_keyframe_block(&mut self) {
4348        let has_block = self.find_before_recovery(SyntaxKind::LeftBrace, &[SyntaxKind::RightBrace]);
4349        self.builder.start_node(if has_block {
4350            SyntaxKind::KeyframeBlock
4351        } else {
4352            SyntaxKind::BogusKeyframeBlock
4353        });
4354        if has_block && !self.keyframe_selector_list_is_valid() {
4355            self.error_at_current(ParseErrorCode::ExpectedValue, "invalid keyframe selector");
4356        }
4357        while !self.at_end() {
4358            match self.current_kind() {
4359                Some(SyntaxKind::LeftBrace) => {
4360                    self.parse_declaration_block();
4361                    break;
4362                }
4363                Some(SyntaxKind::RightBrace) | None => break,
4364                Some(_) => self.token_current(),
4365            }
4366        }
4367        if !has_block {
4368            self.error_at_current(
4369                ParseErrorCode::UnexpectedCharacter,
4370                "expected keyframe declaration block",
4371            );
4372        }
4373        self.builder.finish_node();
4374    }
4375
4376    fn keyframe_selector_list_is_valid(&self) -> bool {
4377        let mut index = self.position;
4378        let mut saw_selector = false;
4379        let mut expect_selector = true;
4380        loop {
4381            let Some((token_index, kind)) = self.non_trivia_token_from(index) else {
4382                return false;
4383            };
4384            if kind == SyntaxKind::LeftBrace {
4385                return saw_selector && !expect_selector;
4386            }
4387            if expect_selector {
4388                if is_interpolation_start(kind) {
4389                    return true;
4390                }
4391                if !keyframe_selector_token_is_valid(self.tokens[token_index]) {
4392                    return false;
4393                }
4394                saw_selector = true;
4395                expect_selector = false;
4396                index = token_index + 1;
4397                continue;
4398            }
4399            if kind != SyntaxKind::Comma {
4400                return false;
4401            }
4402            expect_selector = true;
4403            index = token_index + 1;
4404        }
4405    }
4406
4407    fn consume_balanced_block(&mut self) {
4408        let mut depth = 0usize;
4409        while !self.at_end() {
4410            match self.current_kind() {
4411                Some(SyntaxKind::LeftBrace) => {
4412                    depth += 1;
4413                    self.token_current();
4414                }
4415                Some(SyntaxKind::RightBrace) => {
4416                    self.token_current();
4417                    depth = depth.saturating_sub(1);
4418                    if depth == 0 {
4419                        break;
4420                    }
4421                }
4422                Some(_) => self.token_current(),
4423                None => break,
4424            }
4425        }
4426    }
4427
4428    fn eat_trivia(&mut self) {
4429        while matches!(self.current_kind(), Some(kind) if kind.is_trivia()) {
4430            self.token_current();
4431        }
4432    }
4433
4434    fn consume_until_recovery(&mut self, recovery: &[SyntaxKind]) {
4435        let should_wrap = self
4436            .current_kind()
4437            .is_some_and(|kind| !recovery.contains(&kind));
4438        if should_wrap {
4439            self.builder.start_node(SyntaxKind::BogusRecovery);
4440        }
4441        while !self.at_end() {
4442            match self.current_kind() {
4443                Some(kind) if recovery.contains(&kind) => break,
4444                Some(_) => self.token_current(),
4445                None => break,
4446            }
4447        }
4448        if should_wrap {
4449            self.builder.finish_node();
4450        }
4451    }
4452
4453    fn find_before_recovery(&self, target: SyntaxKind, recovery: &[SyntaxKind]) -> bool {
4454        let mut index = self.position;
4455        while let Some(token) = self.tokens.get(index) {
4456            if token.kind == target {
4457                return true;
4458            }
4459            if recovery.contains(&token.kind) {
4460                return false;
4461            }
4462            index += 1;
4463        }
4464        false
4465    }
4466
4467    fn find_rule_block_open_before_recovery(&self, recovery: &[SyntaxKind]) -> bool {
4468        let mut index = self.position;
4469        while let Some(token) = self.tokens.get(index) {
4470            if token.kind == SyntaxKind::LeftBrace
4471                || (self.dialect == StyleDialect::Sass && token.kind == SyntaxKind::SassIndent)
4472            {
4473                return true;
4474            }
4475            if recovery.contains(&token.kind) {
4476                return false;
4477            }
4478            index += 1;
4479        }
4480        false
4481    }
4482
4483    fn find_text_before_recovery(&self, target: &str, recovery: &[SyntaxKind]) -> bool {
4484        let mut index = self.position;
4485        while let Some(token) = self.tokens.get(index) {
4486            if token.text == target {
4487                return true;
4488            }
4489            if recovery.contains(&token.kind) {
4490                return false;
4491            }
4492            index += 1;
4493        }
4494        false
4495    }
4496
4497    fn find_keyword_before_recovery(&self, target: &str, recovery: &[SyntaxKind]) -> bool {
4498        let mut index = self.position;
4499        while let Some(token) = self.tokens.get(index) {
4500            if css_keyword(token.text).equals(target) {
4501                return true;
4502            }
4503            if recovery.contains(&token.kind) {
4504                return false;
4505            }
4506            index += 1;
4507        }
4508        false
4509    }
4510
4511    fn current_function_has_closing_paren_before(&self, recovery: &[SyntaxKind]) -> bool {
4512        let Some(open_index) = self.position.checked_add(1) else {
4513            return false;
4514        };
4515        if self
4516            .tokens
4517            .get(open_index)
4518            .is_none_or(|token| token.kind != SyntaxKind::LeftParen)
4519        {
4520            return false;
4521        }
4522
4523        let mut depth = 0usize;
4524        for token in self.tokens.iter().skip(open_index) {
4525            match token.kind {
4526                SyntaxKind::LeftParen => depth += 1,
4527                SyntaxKind::RightParen => {
4528                    depth = depth.saturating_sub(1);
4529                    if depth == 0 {
4530                        return true;
4531                    }
4532                }
4533                kind if depth == 1 && recovery.contains(&kind) => return false,
4534                _ => {}
4535            }
4536        }
4537        false
4538    }
4539
4540    fn current_split_important_annotation(&self) -> bool {
4541        self.current_text() == Some("!")
4542            && self
4543                .non_trivia_token_from(self.position + 1)
4544                .is_some_and(|(index, kind)| {
4545                    matches!(kind, SyntaxKind::Ident | SyntaxKind::KeywordImportant)
4546                        && self.tokens.get(index).is_some_and(|token| {
4547                            matches_ignore_ascii_case(token.text, &["important"])
4548                        })
4549                })
4550    }
4551
4552    fn current_scss_variable_flag_annotation(&self) -> bool {
4553        matches!(self.dialect, StyleDialect::Scss | StyleDialect::Sass)
4554            && self.current_text() == Some("!")
4555            && self
4556                .non_trivia_token_from(self.position + 1)
4557                .is_some_and(|(index, kind)| {
4558                    kind == SyntaxKind::Ident
4559                        && self.tokens.get(index).is_some_and(|token| {
4560                            matches_ignore_ascii_case(token.text, &["default", "global"])
4561                        })
4562                })
4563    }
4564
4565    fn current_bracketed_value_has_closing_bracket_before(&self, recovery: &[SyntaxKind]) -> bool {
4566        let mut depth = 0usize;
4567        for token in self.tokens.iter().skip(self.position) {
4568            match token.kind {
4569                SyntaxKind::LeftBracket => depth += 1,
4570                SyntaxKind::RightBracket => {
4571                    depth = depth.saturating_sub(1);
4572                    if depth == 0 {
4573                        return true;
4574                    }
4575                }
4576                kind if depth == 1 && recovery.contains(&kind) => return false,
4577                _ => {}
4578            }
4579        }
4580        false
4581    }
4582
4583    fn current_simple_block_has_matching_close(&self, recovery: &[SyntaxKind]) -> bool {
4584        let Some(open_kind) = self.current_kind() else {
4585            return false;
4586        };
4587        if matching_simple_block_close(open_kind).is_none() {
4588            return false;
4589        }
4590
4591        let mut expected_closes = Vec::new();
4592        for token in self.tokens.iter().skip(self.position) {
4593            if let Some(close_kind) = matching_simple_block_close(token.kind) {
4594                expected_closes.push(close_kind);
4595                continue;
4596            }
4597
4598            if expected_closes.last().copied() == Some(token.kind) {
4599                expected_closes.pop();
4600                if expected_closes.is_empty() {
4601                    return true;
4602                }
4603                continue;
4604            }
4605
4606            if expected_closes.len() == 1 && recovery.contains(&token.kind) {
4607                return false;
4608            }
4609        }
4610        false
4611    }
4612
4613    fn current_dialect_at_rule_node_kind(&self, spec: AtRuleSpec) -> SyntaxKind {
4614        if !self.find_rule_block_open_before_recovery(&[
4615            SyntaxKind::Semicolon,
4616            SyntaxKind::SassOptionalSemicolon,
4617            SyntaxKind::RightBrace,
4618            SyntaxKind::SassDedent,
4619        ]) {
4620            return match spec.node_kind {
4621                SyntaxKind::ScssMixinDeclaration => SyntaxKind::BogusScssMixin,
4622                SyntaxKind::ScssFunctionDeclaration => SyntaxKind::BogusScssFunction,
4623                SyntaxKind::ScssControlIf
4624                | SyntaxKind::ScssControlElse
4625                | SyntaxKind::ScssControlEach
4626                | SyntaxKind::ScssControlFor
4627                | SyntaxKind::ScssControlWhile => SyntaxKind::BogusScssControl,
4628                _ => spec.node_kind,
4629            };
4630        }
4631        spec.node_kind
4632    }
4633
4634    fn current_less_guard_has_condition_before(&self, recovery: &[SyntaxKind]) -> bool {
4635        let mut index = self.position + 1;
4636        while let Some(token) = self.tokens.get(index) {
4637            if recovery.contains(&token.kind) {
4638                return false;
4639            }
4640            if token.kind == SyntaxKind::LeftParen {
4641                return true;
4642            }
4643            index += 1;
4644        }
4645        false
4646    }
4647
4648    fn current_scss_module_config_has_balanced_parens(&self) -> bool {
4649        let Some((_, SyntaxKind::LeftParen)) = self.non_trivia_token_from(self.position + 1) else {
4650            return false;
4651        };
4652        self.current_prelude_parentheses_are_balanced_until(&[
4653            SyntaxKind::Semicolon,
4654            SyntaxKind::SassOptionalSemicolon,
4655            SyntaxKind::LeftBrace,
4656            SyntaxKind::SassIndent,
4657        ])
4658    }
4659
4660    fn current_scss_parenthesized_collection_kind(
4661        &self,
4662        recovery: &[SyntaxKind],
4663    ) -> Option<SyntaxKind> {
4664        if !matches!(self.dialect, StyleDialect::Scss | StyleDialect::Sass)
4665            || self.current_kind() != Some(SyntaxKind::LeftParen)
4666        {
4667            return None;
4668        }
4669        let mut depth = 0usize;
4670        let mut saw_top_level_colon = false;
4671        let mut saw_top_level_comma = false;
4672        for token in self.tokens.iter().skip(self.position) {
4673            match token.kind {
4674                kind if depth == 1 && recovery.contains(&kind) => break,
4675                SyntaxKind::LeftParen => depth += 1,
4676                SyntaxKind::RightParen => {
4677                    depth = depth.saturating_sub(1);
4678                    if depth == 0 {
4679                        break;
4680                    }
4681                }
4682                SyntaxKind::Colon if depth == 1 => saw_top_level_colon = true,
4683                SyntaxKind::Comma if depth == 1 => saw_top_level_comma = true,
4684                _ => {}
4685            }
4686        }
4687        if saw_top_level_colon {
4688            Some(SyntaxKind::ScssMap)
4689        } else if saw_top_level_comma {
4690            Some(SyntaxKind::ScssList)
4691        } else {
4692            None
4693        }
4694    }
4695
4696    fn current_parenthesized_collection_has_closing_paren_before(
4697        &self,
4698        recovery: &[SyntaxKind],
4699    ) -> bool {
4700        let mut depth = 0usize;
4701        for token in self.tokens.iter().skip(self.position) {
4702            match token.kind {
4703                kind if depth == 1 && recovery.contains(&kind) => return false,
4704                SyntaxKind::LeftParen => depth += 1,
4705                SyntaxKind::RightParen => {
4706                    depth = depth.saturating_sub(1);
4707                    if depth == 0 {
4708                        return true;
4709                    }
4710                }
4711                _ => {}
4712            }
4713        }
4714        false
4715    }
4716
4717    fn current_scss_map_entry_has_colon_before(&self, recovery: &[SyntaxKind]) -> bool {
4718        let mut paren_depth = 0usize;
4719        let mut bracket_depth = 0usize;
4720        for token in self.tokens.iter().skip(self.position) {
4721            match token.kind {
4722                kind if paren_depth == 0 && bracket_depth == 0 && recovery.contains(&kind) => {
4723                    return false;
4724                }
4725                SyntaxKind::LeftParen => paren_depth += 1,
4726                SyntaxKind::RightParen => paren_depth = paren_depth.saturating_sub(1),
4727                SyntaxKind::LeftBracket => bracket_depth += 1,
4728                SyntaxKind::RightBracket => bracket_depth = bracket_depth.saturating_sub(1),
4729                SyntaxKind::Colon if paren_depth == 0 && bracket_depth == 0 => return true,
4730                _ => {}
4731            }
4732        }
4733        false
4734    }
4735
4736    fn current_scss_map_entry_has_value_before(&self, recovery: &[SyntaxKind]) -> bool {
4737        let mut paren_depth = 0usize;
4738        let mut bracket_depth = 0usize;
4739        let mut saw_colon = false;
4740        for token in self.tokens.iter().skip(self.position) {
4741            if token.kind.is_trivia() {
4742                continue;
4743            }
4744            match token.kind {
4745                kind if paren_depth == 0 && bracket_depth == 0 && recovery.contains(&kind) => {
4746                    return false;
4747                }
4748                SyntaxKind::LeftParen => paren_depth += 1,
4749                SyntaxKind::RightParen => paren_depth = paren_depth.saturating_sub(1),
4750                SyntaxKind::LeftBracket => bracket_depth += 1,
4751                SyntaxKind::RightBracket => bracket_depth = bracket_depth.saturating_sub(1),
4752                SyntaxKind::Colon if paren_depth == 0 && bracket_depth == 0 => saw_colon = true,
4753                _ if saw_colon && paren_depth == 0 && bracket_depth == 0 => return true,
4754                _ => {}
4755            }
4756        }
4757        false
4758    }
4759
4760    fn current_starts_scss_space_list_before(&self, recovery: &[SyntaxKind]) -> bool {
4761        if !matches!(self.dialect, StyleDialect::Scss | StyleDialect::Sass) {
4762            return false;
4763        }
4764        let mut paren_depth = 0usize;
4765        let mut bracket_depth = 0usize;
4766        let mut item_count = 0usize;
4767        let mut expecting_item = true;
4768        for token in self.tokens.iter().skip(self.position) {
4769            if token.kind.is_trivia() {
4770                if paren_depth == 0 && bracket_depth == 0 {
4771                    expecting_item = true;
4772                }
4773                continue;
4774            }
4775            match token.kind {
4776                kind if paren_depth == 0 && bracket_depth == 0 && recovery.contains(&kind) => {
4777                    return item_count >= 2;
4778                }
4779                SyntaxKind::Comma | SyntaxKind::Colon if paren_depth == 0 && bracket_depth == 0 => {
4780                    return false;
4781                }
4782                SyntaxKind::Plus
4783                | SyntaxKind::Minus
4784                | SyntaxKind::Star
4785                | SyntaxKind::Slash
4786                | SyntaxKind::Percent
4787                | SyntaxKind::LessThan
4788                | SyntaxKind::GreaterThan
4789                | SyntaxKind::Equals
4790                | SyntaxKind::DoubleAmpersand
4791                | SyntaxKind::ColumnCombinator
4792                | SyntaxKind::Delim
4793                    if paren_depth == 0 && bracket_depth == 0 =>
4794                {
4795                    return false;
4796                }
4797                SyntaxKind::Ident
4798                    if paren_depth == 0
4799                        && bracket_depth == 0
4800                        && matches_ignore_ascii_case(token.text, &["and", "or"]) =>
4801                {
4802                    return false;
4803                }
4804                SyntaxKind::LeftParen => {
4805                    if paren_depth == 0 && bracket_depth == 0 && expecting_item {
4806                        item_count += 1;
4807                    }
4808                    paren_depth += 1;
4809                    expecting_item = false;
4810                }
4811                SyntaxKind::RightParen => {
4812                    paren_depth = paren_depth.saturating_sub(1);
4813                    expecting_item = false;
4814                }
4815                SyntaxKind::LeftBracket => {
4816                    if paren_depth == 0 && bracket_depth == 0 && expecting_item {
4817                        item_count += 1;
4818                    }
4819                    bracket_depth += 1;
4820                    expecting_item = false;
4821                }
4822                SyntaxKind::RightBracket => {
4823                    bracket_depth = bracket_depth.saturating_sub(1);
4824                    expecting_item = false;
4825                }
4826                _ if paren_depth == 0 && bracket_depth == 0 && expecting_item => {
4827                    item_count += 1;
4828                    expecting_item = false;
4829                }
4830                _ => expecting_item = false,
4831            }
4832        }
4833        item_count >= 2
4834    }
4835
4836    fn current_value_has_top_level_comma_before(&self, recovery: &[SyntaxKind]) -> bool {
4837        let mut paren_depth = 0usize;
4838        let mut bracket_depth = 0usize;
4839        for token in self.tokens.iter().skip(self.position) {
4840            match token.kind {
4841                kind if paren_depth == 0 && bracket_depth == 0 && recovery.contains(&kind) => {
4842                    return false;
4843                }
4844                SyntaxKind::LeftParen => paren_depth += 1,
4845                SyntaxKind::RightParen => paren_depth = paren_depth.saturating_sub(1),
4846                SyntaxKind::LeftBracket => bracket_depth += 1,
4847                SyntaxKind::RightBracket => bracket_depth = bracket_depth.saturating_sub(1),
4848                SyntaxKind::Comma if paren_depth == 0 && bracket_depth == 0 => return true,
4849                _ => {}
4850            }
4851        }
4852        false
4853    }
4854
4855    fn current_value_list_is_bogus(&self, recovery: &[SyntaxKind]) -> bool {
4856        let mut paren_depth = 0usize;
4857        let mut bracket_depth = 0usize;
4858        let mut expecting_item = true;
4859        for token in self.tokens.iter().skip(self.position) {
4860            if token.kind.is_trivia() {
4861                continue;
4862            }
4863            match token.kind {
4864                kind if paren_depth == 0 && bracket_depth == 0 && recovery.contains(&kind) => {
4865                    return expecting_item;
4866                }
4867                SyntaxKind::LeftParen => {
4868                    paren_depth += 1;
4869                    expecting_item = false;
4870                }
4871                SyntaxKind::RightParen => {
4872                    paren_depth = paren_depth.saturating_sub(1);
4873                    expecting_item = false;
4874                }
4875                SyntaxKind::LeftBracket => {
4876                    bracket_depth += 1;
4877                    expecting_item = false;
4878                }
4879                SyntaxKind::RightBracket => {
4880                    bracket_depth = bracket_depth.saturating_sub(1);
4881                    expecting_item = false;
4882                }
4883                SyntaxKind::Comma if paren_depth == 0 && bracket_depth == 0 => {
4884                    if expecting_item {
4885                        return true;
4886                    }
4887                    expecting_item = true;
4888                }
4889                _ => expecting_item = false,
4890            }
4891        }
4892        expecting_item
4893    }
4894
4895    fn current_starts_missing_semicolon_declaration(&self, recovery: &[SyntaxKind]) -> bool {
4896        match self.current_kind() {
4897            Some(SyntaxKind::Ident | SyntaxKind::CustomPropertyName) => {}
4898            _ => return false,
4899        }
4900
4901        let mut index = self.position + 1;
4902        while let Some(token) = self.tokens.get(index) {
4903            if token.kind.is_trivia() {
4904                index += 1;
4905                continue;
4906            }
4907            if recovery.contains(&token.kind) {
4908                return false;
4909            }
4910            return token.kind == SyntaxKind::Colon;
4911        }
4912        false
4913    }
4914
4915    fn current_selector_item_is_bogus(&self, recovery: &[SyntaxKind]) -> bool {
4916        self.selector_item_is_bogus_from(self.position, recovery)
4917    }
4918
4919    fn selector_item_is_bogus_from(&self, start: usize, recovery: &[SyntaxKind]) -> bool {
4920        let mut paren_depth = 0usize;
4921        let mut bracket_depth = 0usize;
4922        let mut saw_selector_token = false;
4923
4924        for token in self.tokens.iter().skip(start) {
4925            if token.kind.is_trivia() {
4926                continue;
4927            }
4928            if paren_depth == 0
4929                && bracket_depth == 0
4930                && (token.kind == SyntaxKind::Comma
4931                    || is_selector_boundary_until(token.kind, recovery))
4932            {
4933                break;
4934            }
4935
4936            match token.kind {
4937                SyntaxKind::LeftParen => paren_depth += 1,
4938                SyntaxKind::RightParen => paren_depth = paren_depth.saturating_sub(1),
4939                SyntaxKind::LeftBracket => bracket_depth += 1,
4940                SyntaxKind::RightBracket => bracket_depth = bracket_depth.saturating_sub(1),
4941                _ => {}
4942            }
4943
4944            if !selector_item_token_is_recoverable(token.kind) {
4945                return true;
4946            }
4947            saw_selector_token = true;
4948        }
4949
4950        !saw_selector_token
4951    }
4952
4953    fn selector_list_contains_bogus_item_until(&self, recovery: &[SyntaxKind]) -> bool {
4954        let mut index = self.position;
4955        while let Some(token) = self.tokens.get(index) {
4956            if token.kind.is_trivia() || token.kind == SyntaxKind::Comma {
4957                index += 1;
4958                continue;
4959            }
4960            if is_selector_boundary_until(token.kind, recovery) {
4961                return false;
4962            }
4963            if self.selector_item_is_bogus_from(index, recovery) {
4964                return true;
4965            }
4966
4967            let mut paren_depth = 0usize;
4968            let mut bracket_depth = 0usize;
4969            while let Some(token) = self.tokens.get(index) {
4970                if paren_depth == 0
4971                    && bracket_depth == 0
4972                    && (token.kind == SyntaxKind::Comma
4973                        || is_selector_boundary_until(token.kind, recovery))
4974                {
4975                    break;
4976                }
4977                match token.kind {
4978                    SyntaxKind::LeftParen => paren_depth += 1,
4979                    SyntaxKind::RightParen => paren_depth = paren_depth.saturating_sub(1),
4980                    SyntaxKind::LeftBracket => bracket_depth += 1,
4981                    SyntaxKind::RightBracket => bracket_depth = bracket_depth.saturating_sub(1),
4982                    _ => {}
4983                }
4984                index += 1;
4985            }
4986        }
4987        false
4988    }
4989
4990    fn current_generic_at_rule_prelude_node_kind(&self) -> SyntaxKind {
4991        if self.current_prelude_parentheses_are_balanced_until(&[
4992            SyntaxKind::LeftBrace,
4993            SyntaxKind::Semicolon,
4994        ]) {
4995            SyntaxKind::AtRulePrelude
4996        } else {
4997            SyntaxKind::BogusAtRulePrelude
4998        }
4999    }
5000
5001    fn current_prelude_parentheses_are_balanced_until(&self, recovery: &[SyntaxKind]) -> bool {
5002        let mut depth = 0usize;
5003        for token in self.tokens.iter().skip(self.position) {
5004            match token.kind {
5005                kind if depth == 0 && recovery.contains(&kind) => return true,
5006                SyntaxKind::LeftParen => depth += 1,
5007                SyntaxKind::RightParen => {
5008                    if depth == 0 {
5009                        return false;
5010                    }
5011                    depth -= 1;
5012                }
5013                _ => {}
5014            }
5015        }
5016        depth == 0
5017    }
5018
5019    fn previous_left_brace_has_match(&self) -> bool {
5020        let Some(open_index) = self.position.checked_sub(1) else {
5021            return false;
5022        };
5023        let Some(open) = self.tokens.get(open_index) else {
5024            return false;
5025        };
5026        if open.kind != SyntaxKind::LeftBrace {
5027            return false;
5028        }
5029
5030        let mut depth = 0usize;
5031        for token in self.tokens.iter().skip(open_index) {
5032            match token.kind {
5033                SyntaxKind::LeftBrace => depth += 1,
5034                SyntaxKind::RightBrace => {
5035                    depth = depth.saturating_sub(1);
5036                    if depth == 0 {
5037                        return true;
5038                    }
5039                }
5040                _ => {}
5041            }
5042        }
5043        false
5044    }
5045
5046    fn current_starts_nested_rule(&self) -> bool {
5047        matches!(
5048            self.current_kind(),
5049            Some(
5050                SyntaxKind::Dot
5051                    | SyntaxKind::Hash
5052                    | SyntaxKind::Ampersand
5053                    | SyntaxKind::Colon
5054                    | SyntaxKind::DoubleColon
5055                    | SyntaxKind::LeftBracket
5056            )
5057        ) && self.find_rule_block_open_before_recovery(&[
5058            SyntaxKind::Semicolon,
5059            SyntaxKind::SassOptionalSemicolon,
5060            SyntaxKind::RightBrace,
5061            SyntaxKind::SassDedent,
5062        ])
5063    }
5064
5065    fn current_starts_scss_nested_property(&self) -> bool {
5066        if !matches!(self.dialect, StyleDialect::Scss | StyleDialect::Sass) {
5067            return false;
5068        }
5069        if !matches!(
5070            self.current_kind(),
5071            Some(SyntaxKind::Ident | SyntaxKind::CustomPropertyName)
5072        ) {
5073            return false;
5074        }
5075
5076        let mut saw_colon = false;
5077        for token in self.tokens.iter().skip(self.position) {
5078            match token.kind {
5079                SyntaxKind::Colon => saw_colon = true,
5080                SyntaxKind::LeftBrace if saw_colon => return true,
5081                SyntaxKind::SassIndent if saw_colon && self.dialect == StyleDialect::Sass => {
5082                    return true;
5083                }
5084                SyntaxKind::Semicolon
5085                | SyntaxKind::SassOptionalSemicolon
5086                | SyntaxKind::RightBrace
5087                | SyntaxKind::SassDedent => return false,
5088                _ => {}
5089            }
5090        }
5091        false
5092    }
5093
5094    fn current_starts_less_mixin_declaration(&self) -> bool {
5095        self.dialect == StyleDialect::Less
5096            && self.current_starts_less_callable_signature()
5097            && self.find_before_recovery(
5098                SyntaxKind::LeftBrace,
5099                &[SyntaxKind::Semicolon, SyntaxKind::RightBrace],
5100            )
5101    }
5102
5103    fn current_starts_less_mixin_call(&self) -> bool {
5104        self.dialect == StyleDialect::Less
5105            && self.current_starts_less_callable_signature()
5106            && !self.find_before_recovery(
5107                SyntaxKind::LeftBrace,
5108                &[SyntaxKind::Semicolon, SyntaxKind::RightBrace],
5109            )
5110    }
5111
5112    fn current_starts_less_callable_signature(&self) -> bool {
5113        match self.current_kind() {
5114            Some(SyntaxKind::Dot) => {
5115                let Some((index, SyntaxKind::Ident | SyntaxKind::CustomPropertyName)) =
5116                    self.non_trivia_token_from(self.position + 1)
5117                else {
5118                    return false;
5119                };
5120                self.non_trivia_token_from(index + 1)
5121                    .is_some_and(|(_, kind)| kind == SyntaxKind::LeftParen)
5122            }
5123            Some(SyntaxKind::Hash) => self
5124                .non_trivia_token_from(self.position + 1)
5125                .is_some_and(|(_, kind)| kind == SyntaxKind::LeftParen),
5126            _ => false,
5127        }
5128    }
5129
5130    fn current_starts_less_extend_rule(&self) -> bool {
5131        self.dialect == StyleDialect::Less
5132            && self.current_kind() == Some(SyntaxKind::Colon)
5133            && self
5134                .non_trivia_token_from(self.position + 1)
5135                .is_some_and(|(index, kind)| {
5136                    kind == SyntaxKind::Ident
5137                        && self
5138                            .tokens
5139                            .get(index)
5140                            .is_some_and(|token| token.text == "extend")
5141                })
5142    }
5143
5144    fn current_starts_less_namespace_access(&self) -> bool {
5145        self.dialect == StyleDialect::Less
5146            && matches!(
5147                self.current_kind(),
5148                Some(SyntaxKind::Dot | SyntaxKind::Hash)
5149            )
5150            && self.find_before_recovery(
5151                SyntaxKind::GreaterThan,
5152                &[
5153                    SyntaxKind::Semicolon,
5154                    SyntaxKind::LeftBrace,
5155                    SyntaxKind::RightBrace,
5156                ],
5157            )
5158            && self.find_before_recovery(
5159                SyntaxKind::LeftParen,
5160                &[
5161                    SyntaxKind::Semicolon,
5162                    SyntaxKind::LeftBrace,
5163                    SyntaxKind::RightBrace,
5164                ],
5165            )
5166    }
5167
5168    fn current_left_brace_has_match(&self) -> bool {
5169        let mut depth = 0usize;
5170        for token in self.tokens.iter().skip(self.position) {
5171            match token.kind {
5172                SyntaxKind::LeftBrace => depth += 1,
5173                SyntaxKind::RightBrace => {
5174                    depth = depth.saturating_sub(1);
5175                    if depth == 0 {
5176                        return true;
5177                    }
5178                }
5179                _ => {}
5180            }
5181        }
5182        false
5183    }
5184
5185    fn token_current(&mut self) {
5186        if let Some(token) = self.tokens.get(self.position).copied() {
5187            self.builder.token(token.kind, token.text);
5188            self.position += 1;
5189        }
5190    }
5191
5192    fn empty_bogus_node(&mut self, kind: SyntaxKind, code: ParseErrorCode, message: &'static str) {
5193        self.builder.start_node(kind);
5194        self.builder.finish_node();
5195        self.error_at_current(code, message);
5196    }
5197
5198    fn missing_token_bogus_trivia(&mut self, code: ParseErrorCode, message: &'static str) {
5199        self.builder.start_node(SyntaxKind::BogusTrivia);
5200        self.builder.finish_node();
5201        self.error_at_current(code, message);
5202    }
5203
5204    fn error_at_current(&mut self, code: ParseErrorCode, message: &'static str) {
5205        self.errors.push(ParseError {
5206            code,
5207            range: self.current_range(),
5208            message,
5209        });
5210    }
5211
5212    fn current_kind(&self) -> Option<SyntaxKind> {
5213        self.tokens.get(self.position).map(|token| token.kind)
5214    }
5215
5216    fn current_range(&self) -> TextRange {
5217        if let Some(token) = self.tokens.get(self.position) {
5218            return token.range;
5219        }
5220        let end = self
5221            .tokens
5222            .last()
5223            .map(|token| token.range.end())
5224            .unwrap_or_else(|| TextSize::from(0));
5225        TextRange::new(end, end)
5226    }
5227
5228    fn current_text(&self) -> Option<&'text str> {
5229        self.tokens.get(self.position).map(|token| token.text)
5230    }
5231
5232    fn token_text_matches(&self, index: usize, expected: &str) -> bool {
5233        self.tokens
5234            .get(index)
5235            .is_some_and(|token| matches_ignore_ascii_case(token.text, &[expected]))
5236    }
5237
5238    fn current_token_is_adjacent_to_next(&self) -> bool {
5239        let Some(current) = self.tokens.get(self.position) else {
5240            return false;
5241        };
5242        let Some(next) = self.tokens.get(self.position + 1) else {
5243            return false;
5244        };
5245        current.range.end() == next.range.start()
5246    }
5247
5248    fn current_dialect_at_rule_spec(&self) -> Option<AtRuleSpec> {
5249        let text = self.current_text()?;
5250        match self.dialect {
5251            StyleDialect::Scss | StyleDialect::Sass => scss_at_rule_spec(text),
5252            StyleDialect::Css | StyleDialect::Less => None,
5253        }
5254    }
5255
5256    fn current_is_css_module_value_rule(&self) -> bool {
5257        self.current_text()
5258            .is_some_and(|text| css_keyword(text).equals("@value"))
5259    }
5260
5261    fn next_kind(&self) -> Option<SyntaxKind> {
5262        self.tokens.get(self.position + 1).map(|token| token.kind)
5263    }
5264
5265    fn next_non_trivia_kind(&self) -> Option<SyntaxKind> {
5266        let mut index = self.position + 1;
5267        while let Some(token) = self.tokens.get(index) {
5268            if !token.kind.is_trivia() {
5269                return Some(token.kind);
5270            }
5271            index += 1;
5272        }
5273        None
5274    }
5275
5276    fn non_trivia_token_from(&self, mut index: usize) -> Option<(usize, SyntaxKind)> {
5277        while let Some(token) = self.tokens.get(index) {
5278            if !token.kind.is_trivia() {
5279                return Some((index, token.kind));
5280            }
5281            index += 1;
5282        }
5283        None
5284    }
5285
5286    fn non_trivia_token_after_interpolation(
5287        &self,
5288        mut index: usize,
5289        start_kind: SyntaxKind,
5290    ) -> Option<(usize, SyntaxKind)> {
5291        let end_kind = interpolation_end_kind(start_kind)?;
5292        index += 1;
5293        while let Some(token) = self.tokens.get(index) {
5294            if token.kind == end_kind {
5295                return self.non_trivia_token_from(index + 1);
5296            }
5297            if is_at_rule_prelude_boundary(token.kind) {
5298                return None;
5299            }
5300            index += 1;
5301        }
5302        None
5303    }
5304
5305    fn current_starts_namespace_qualified_selector(&self, kind: SyntaxKind) -> bool {
5306        match kind {
5307            SyntaxKind::Ident | SyntaxKind::Star => {
5308                self.next_kind() == Some(SyntaxKind::Pipe)
5309                    && self
5310                        .tokens
5311                        .get(self.position + 2)
5312                        .is_some_and(|token| namespace_selector_target_can_start(token.kind))
5313            }
5314            SyntaxKind::Pipe => self
5315                .tokens
5316                .get(self.position + 1)
5317                .is_some_and(|token| namespace_selector_target_can_start(token.kind)),
5318            _ => false,
5319        }
5320    }
5321
5322    fn namespace_qualified_selector_target_kind(&self) -> Option<SyntaxKind> {
5323        let target_index = if self.current_kind() == Some(SyntaxKind::Pipe) {
5324            self.position + 1
5325        } else {
5326            self.position + 2
5327        };
5328        self.tokens.get(target_index).map(|token| token.kind)
5329    }
5330
5331    fn at_end(&self) -> bool {
5332        self.position >= self.tokens.len()
5333    }
5334}