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