1use 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
362pub 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}