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