1use std::collections::{BTreeMap, BTreeSet};
9
10use crate::{
11 ParsedAnimationFactKind, ParsedCssModuleComposesEdgeKind, ParsedCssModuleComposesFactKind,
12 ParsedCssModuleValueFactKind, ParsedSassModuleEdgeFactKind, ParsedSassSymbolFactKind,
13 ParsedSelectorFactKind, ParsedStyleFacts, ParsedVariableFactKind, ParserByteSpanV0,
14 ParserPositionV0, ParserRangeV0, StyleDialect, css_keyword, parse, product_facts_from_cst,
15 summarize_omena_parser_parity_lite,
16};
17use cstree::text::TextRange;
18use serde::Serialize;
19
20mod style_blocks;
21mod syntax_index;
22
23#[cfg(test)]
24mod tests;
25
26use syntax_index::ProductSyntaxIndexV0;
27
28#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
29#[serde(rename_all = "camelCase")]
30pub struct ParserIndexSummaryV0 {
31 schema_version: &'static str,
32 language: &'static str,
33 selectors: ParserIndexSelectorFactsV0,
34 values: ParserIndexValueFactsV0,
35 custom_properties: ParserIndexCustomPropertyFactsV0,
36 sass: ParserIndexSassFactsV0,
37 keyframes: ParserIndexKeyframesFactsV0,
38 composes: ParserIndexComposesFactsV0,
39 wrappers: ParserIndexWrapperFactsV0,
40}
41
42#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
43#[serde(rename_all = "camelCase")]
44pub struct ParserCanonicalCandidateBundleV0 {
45 schema_version: &'static str,
46 language: &'static str,
47 parity_lite: crate::OmenaParserParityLiteSummaryV0,
48 css_modules_intermediate: ParserIndexSummaryV0,
49}
50
51#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
52#[serde(rename_all = "camelCase")]
53struct ParserEvaluatorCandidateV0 {
54 kind: &'static str,
55 selector_name: String,
56 nested_safety_kind: &'static str,
57 #[serde(skip_serializing_if = "Option::is_none")]
58 bem_suffix_parent_name: Option<String>,
59 under_media: bool,
60 under_supports: bool,
61 under_layer: bool,
62 has_value_refs: bool,
63 has_local_value_refs: bool,
64 has_imported_value_refs: bool,
65 has_custom_property_refs: bool,
66 has_animation_ref: bool,
67 has_animation_name_ref: bool,
68 has_composes: bool,
69 has_local_composes: bool,
70 has_imported_composes: bool,
71 has_global_composes: bool,
72}
73
74#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
75#[serde(rename_all = "camelCase")]
76pub struct ParserEvaluatorCandidatesV0 {
77 schema_version: &'static str,
78 language: &'static str,
79 results: Vec<ParserEvaluatorCandidateV0>,
80}
81
82#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
83#[serde(rename_all = "camelCase")]
84pub struct ParserCanonicalProducerSignalV0 {
85 schema_version: &'static str,
86 language: &'static str,
87 canonical_candidate: ParserCanonicalCandidateBundleV0,
88 evaluator_candidates: ParserEvaluatorCandidatesV0,
89 public_product_gate: ParserPublicProductGateSignalV0,
90}
91
92#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
93#[serde(rename_all = "camelCase")]
94struct ParserPublicProductGateSignalV0 {
95 canonical_candidate_command: &'static str,
96 consumer_boundary_command: &'static str,
97 public_product_gate_command: &'static str,
98 included_in_parser_lane: bool,
99 included_in_rust_lane_bundle: bool,
100 included_in_rust_release_bundle: bool,
101}
102
103#[derive(Debug, Clone, PartialEq, Eq, Serialize, Default)]
104#[serde(rename_all = "camelCase")]
105struct ParserIndexSelectorFactsV0 {
106 names: Vec<String>,
107 definition_facts: Vec<ParserIndexSelectorDefinitionFactV0>,
108 bem_suffix_parent_names: Vec<String>,
109 bem_suffix_safe_names: Vec<String>,
110 nested_unsafe_names: Vec<String>,
111 selectors_with_value_refs_names: Vec<String>,
112 selectors_with_animation_ref_names: Vec<String>,
113 selectors_with_animation_name_ref_names: Vec<String>,
114 bem_suffix_count: usize,
115 nested_safety_counts: NestedSafetyCountsV0,
116}
117
118#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Default)]
119#[serde(rename_all = "camelCase")]
120struct ParserIndexSelectorDefinitionFactV0 {
121 name: String,
122 source_order: usize,
123 byte_span: ParserByteSpanV0,
124 range: ParserRangeV0,
125 rule_byte_span: ParserByteSpanV0,
126 rule_range: ParserRangeV0,
127 full_selector: String,
128 declarations: String,
129 nested_safety_kind: &'static str,
130 #[serde(skip_serializing_if = "Option::is_none")]
131 bem_suffix_parent_name: Option<String>,
132 under_media: bool,
133 under_supports: bool,
134 under_layer: bool,
135}
136
137#[derive(Debug, Clone, PartialEq, Eq, Serialize, Default)]
138#[serde(rename_all = "camelCase")]
139struct ParserIndexValueFactsV0 {
140 decl_names: Vec<String>,
141 decl_facts: Vec<ParserIndexValueDeclFactV0>,
142 decl_names_with_local_refs: Vec<String>,
143 decl_names_with_imported_refs: Vec<String>,
144 import_names: Vec<String>,
145 import_facts: Vec<ParserIndexValueImportFactV0>,
146 import_sources: Vec<String>,
147 import_alias_count: usize,
148 ref_names: Vec<String>,
149 ref_facts: Vec<ParserIndexValueRefFactV0>,
150 local_ref_names: Vec<String>,
151 imported_ref_names: Vec<String>,
152 imported_ref_sources: Vec<String>,
153 declaration_ref_names: Vec<String>,
154 declaration_imported_ref_sources: Vec<String>,
155 value_decl_ref_names: Vec<String>,
156 value_decl_imported_ref_sources: Vec<String>,
157 selectors_with_refs_names: Vec<String>,
158 selectors_with_local_refs_names: Vec<String>,
159 selectors_with_imported_refs_names: Vec<String>,
160 selectors_with_refs_under_media_names: Vec<String>,
161 selectors_with_refs_under_supports_names: Vec<String>,
162 selectors_with_refs_under_layer_names: Vec<String>,
163 selectors_with_local_refs_under_media_names: Vec<String>,
164 selectors_with_local_refs_under_supports_names: Vec<String>,
165 selectors_with_local_refs_under_layer_names: Vec<String>,
166 selectors_with_imported_refs_under_media_names: Vec<String>,
167 selectors_with_imported_refs_under_supports_names: Vec<String>,
168 selectors_with_imported_refs_under_layer_names: Vec<String>,
169}
170
171#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Default)]
172#[serde(rename_all = "camelCase")]
173struct ParserIndexValueDeclFactV0 {
174 name: String,
175 value: String,
176 source_order: usize,
177 byte_span: ParserByteSpanV0,
178 range: ParserRangeV0,
179 rule_byte_span: ParserByteSpanV0,
180 rule_range: ParserRangeV0,
181}
182
183#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Default)]
184#[serde(rename_all = "camelCase")]
185struct ParserIndexValueImportFactV0 {
186 name: String,
187 imported_name: String,
188 from: String,
189 source_order: usize,
190 byte_span: ParserByteSpanV0,
191 range: ParserRangeV0,
192 #[serde(skip_serializing_if = "Option::is_none")]
193 imported_name_byte_span: Option<ParserByteSpanV0>,
194 #[serde(skip_serializing_if = "Option::is_none")]
195 imported_name_range: Option<ParserRangeV0>,
196 rule_byte_span: ParserByteSpanV0,
197 rule_range: ParserRangeV0,
198}
199
200#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Default)]
201#[serde(rename_all = "camelCase")]
202struct ParserIndexValueRefFactV0 {
203 name: String,
204 source: &'static str,
205 source_order: usize,
206 byte_span: ParserByteSpanV0,
207 range: ParserRangeV0,
208}
209
210#[derive(Debug, Clone, PartialEq, Eq, Serialize, Default)]
211#[serde(rename_all = "camelCase")]
212struct ParserIndexCustomPropertyFactsV0 {
213 decl_names: Vec<String>,
214 decl_facts: Vec<ParserIndexCustomPropertyDeclFactV0>,
215 decl_context_selectors: Vec<String>,
216 decl_names_under_media: Vec<String>,
217 decl_names_under_supports: Vec<String>,
218 decl_names_under_layer: Vec<String>,
219 ref_names: Vec<String>,
220 ref_facts: Vec<ParserIndexCustomPropertyRefFactV0>,
221 selectors_with_refs_names: Vec<String>,
222 selectors_with_refs_under_media_names: Vec<String>,
223 selectors_with_refs_under_supports_names: Vec<String>,
224 selectors_with_refs_under_layer_names: Vec<String>,
225}
226
227#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Default)]
228#[serde(rename_all = "camelCase")]
229struct ParserIndexCustomPropertyDeclFactV0 {
230 name: String,
231 value: String,
232 source_order: usize,
233 byte_span: ParserByteSpanV0,
234 range: ParserRangeV0,
235 rule_byte_span: ParserByteSpanV0,
236 rule_range: ParserRangeV0,
237 selector_contexts: Vec<String>,
238 wrapper_at_rules: Vec<ParserIndexAtRuleContextV0>,
239 under_media: bool,
240 under_supports: bool,
241 under_layer: bool,
242}
243
244#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Default)]
245#[serde(rename_all = "camelCase")]
246struct ParserIndexCustomPropertyRefFactV0 {
247 name: String,
248 source_order: usize,
249 byte_span: ParserByteSpanV0,
250 range: ParserRangeV0,
251 selector_contexts: Vec<String>,
252 wrapper_at_rules: Vec<ParserIndexAtRuleContextV0>,
253 under_media: bool,
254 under_supports: bool,
255 under_layer: bool,
256}
257
258#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Default)]
259#[serde(rename_all = "camelCase")]
260struct ParserIndexAtRuleContextV0 {
261 name: String,
262 params: String,
263 byte_span: ParserByteSpanV0,
264 range: ParserRangeV0,
265}
266
267#[derive(Debug, Clone, PartialEq, Eq, Serialize, Default)]
268#[serde(rename_all = "camelCase")]
269struct ParserIndexSassFactsV0 {
270 variable_decl_names: Vec<String>,
271 symbol_decl_facts: Vec<ParserIndexSassSymbolDeclFactV0>,
272 variable_parameter_names: Vec<String>,
273 variable_ref_names: Vec<String>,
274 selectors_with_variable_refs_names: Vec<String>,
275 selectors_with_resolved_variable_refs_names: Vec<String>,
276 selectors_with_unresolved_variable_refs_names: Vec<String>,
277 mixin_decl_names: Vec<String>,
278 mixin_include_names: Vec<String>,
279 selectors_with_mixin_includes_names: Vec<String>,
280 selectors_with_resolved_mixin_includes_names: Vec<String>,
281 selectors_with_unresolved_mixin_includes_names: Vec<String>,
282 function_decl_names: Vec<String>,
283 function_call_names: Vec<String>,
284 selectors_with_function_calls_names: Vec<String>,
285 selector_symbol_facts: Vec<ParserIndexSassSelectorSymbolFactV0>,
286 module_use_sources: Vec<String>,
287 module_use_edges: Vec<ParserIndexSassModuleUseFactV0>,
288 module_forward_sources: Vec<String>,
289 module_forward_edges: Vec<ParserIndexSassModuleForwardFactV0>,
290 module_import_sources: Vec<String>,
291 same_file_resolution: ParserIndexSassSameFileResolutionFactsV0,
292}
293
294#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Default)]
295#[serde(rename_all = "camelCase")]
296struct ParserIndexSassSymbolDeclFactV0 {
297 symbol_kind: &'static str,
298 name: String,
299 role: &'static str,
300 byte_span: ParserByteSpanV0,
301 range: ParserRangeV0,
302}
303
304#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize)]
305#[serde(rename_all = "camelCase")]
306struct ParserIndexSassModuleUseFactV0 {
307 source: String,
308 namespace_kind: &'static str,
309 namespace: Option<String>,
310 byte_span: ParserByteSpanV0,
311 range: ParserRangeV0,
312}
313
314#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize)]
315#[serde(rename_all = "camelCase")]
316struct ParserIndexSassModuleForwardFactV0 {
317 source: String,
318 prefix: String,
319 visibility_kind: &'static str,
320 visibility_members: Vec<ParserIndexSassModuleForwardMemberV0>,
321 byte_span: ParserByteSpanV0,
322 range: ParserRangeV0,
323 rule_byte_span: ParserByteSpanV0,
324 rule_range: ParserRangeV0,
325}
326
327#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize)]
328#[serde(rename_all = "camelCase")]
329struct ParserIndexSassModuleForwardMemberV0 {
330 name: String,
331 symbol_kind: Option<&'static str>,
332}
333
334#[derive(Debug, Clone, PartialEq, Eq, Serialize, Default)]
335#[serde(rename_all = "camelCase")]
336struct ParserIndexSassSameFileResolutionFactsV0 {
337 resolved_variable_ref_names: Vec<String>,
338 unresolved_variable_ref_names: Vec<String>,
339 resolved_mixin_include_names: Vec<String>,
340 unresolved_mixin_include_names: Vec<String>,
341 resolved_function_call_names: Vec<String>,
342}
343
344#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize)]
345#[serde(rename_all = "camelCase")]
346struct ParserIndexSassSelectorSymbolFactV0 {
347 selector_name: String,
348 symbol_kind: &'static str,
349 name: String,
350 namespace: Option<String>,
351 role: &'static str,
352 resolution: &'static str,
353 byte_span: ParserByteSpanV0,
354 range: ParserRangeV0,
355}
356
357#[derive(Debug, Clone, PartialEq, Eq, Serialize, Default)]
358#[serde(rename_all = "camelCase")]
359struct ParserIndexKeyframesFactsV0 {
360 names: Vec<String>,
361 decl_facts: Vec<ParserIndexKeyframesDeclFactV0>,
362 names_under_media: Vec<String>,
363 names_under_supports: Vec<String>,
364 names_under_layer: Vec<String>,
365 animation_ref_names: Vec<String>,
366 animation_name_ref_names: Vec<String>,
367 ref_facts: Vec<ParserIndexAnimationNameRefFactV0>,
368 selectors_with_animation_ref_names: Vec<String>,
369 selectors_with_animation_name_ref_names: Vec<String>,
370 selectors_with_animation_refs_under_media_names: Vec<String>,
371 selectors_with_animation_refs_under_supports_names: Vec<String>,
372 selectors_with_animation_refs_under_layer_names: Vec<String>,
373 selectors_with_animation_name_refs_under_media_names: Vec<String>,
374 selectors_with_animation_name_refs_under_supports_names: Vec<String>,
375 selectors_with_animation_name_refs_under_layer_names: Vec<String>,
376}
377
378#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Default)]
379#[serde(rename_all = "camelCase")]
380struct ParserIndexKeyframesDeclFactV0 {
381 name: String,
382 source_order: usize,
383 byte_span: ParserByteSpanV0,
384 range: ParserRangeV0,
385 rule_byte_span: ParserByteSpanV0,
386 rule_range: ParserRangeV0,
387}
388
389#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Default)]
390#[serde(rename_all = "camelCase")]
391struct ParserIndexAnimationNameRefFactV0 {
392 name: String,
393 property: &'static str,
394 source_order: usize,
395 byte_span: ParserByteSpanV0,
396 range: ParserRangeV0,
397}
398
399#[derive(Debug, Clone, PartialEq, Eq, Serialize, Default)]
400#[serde(rename_all = "camelCase")]
401struct ParserIndexComposesFactsV0 {
402 edges: Vec<ParserIndexComposesEdgeFactV0>,
403 selectors_with_composes_names: Vec<String>,
404 selectors_with_composes_under_media_names: Vec<String>,
405 selectors_with_composes_under_supports_names: Vec<String>,
406 selectors_with_composes_under_layer_names: Vec<String>,
407 local_selector_names: Vec<String>,
408 imported_selector_names: Vec<String>,
409 global_selector_names: Vec<String>,
410 local_selector_names_under_media: Vec<String>,
411 local_selector_names_under_supports: Vec<String>,
412 local_selector_names_under_layer: Vec<String>,
413 imported_selector_names_under_media: Vec<String>,
414 imported_selector_names_under_supports: Vec<String>,
415 imported_selector_names_under_layer: Vec<String>,
416 global_selector_names_under_media: Vec<String>,
417 global_selector_names_under_supports: Vec<String>,
418 global_selector_names_under_layer: Vec<String>,
419 import_sources: Vec<String>,
420 import_sources_under_media: Vec<String>,
421 import_sources_under_supports: Vec<String>,
422 import_sources_under_layer: Vec<String>,
423 class_name_count: usize,
424 local_class_name_count: usize,
425 imported_class_name_count: usize,
426 global_class_name_count: usize,
427}
428
429#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize)]
430#[serde(rename_all = "camelCase")]
431struct ParserIndexComposesEdgeFactV0 {
432 kind: &'static str,
433 owner_selector_names: Vec<String>,
434 target_names: Vec<String>,
435 import_source: Option<String>,
436 class_tokens: Vec<ParserIndexComposesClassTokenV0>,
437 byte_span: ParserByteSpanV0,
438 range: ParserRangeV0,
439}
440
441#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize)]
442#[serde(rename_all = "camelCase")]
443struct ParserIndexComposesClassTokenV0 {
444 class_name: String,
445 byte_span: ParserByteSpanV0,
446 range: ParserRangeV0,
447}
448
449#[derive(Debug, Clone, PartialEq, Eq, Serialize, Default)]
450#[serde(rename_all = "camelCase")]
451struct ParserIndexWrapperFactsV0 {
452 selectors_under_media_names: Vec<String>,
453 selectors_under_supports_names: Vec<String>,
454 selectors_under_layer_names: Vec<String>,
455}
456
457#[derive(Debug, Clone, PartialEq, Eq, Serialize, Default)]
458#[serde(rename_all = "camelCase")]
459struct NestedSafetyCountsV0 {
460 flat: usize,
461 bem_suffix_safe: usize,
462 nested_unsafe: usize,
463}
464
465#[derive(Debug, Clone, PartialEq, Eq)]
466struct SelectorBranch {
467 name: String,
468 name_span: ParserByteSpanV0,
469 bare_suffix_base: bool,
470 amp_suffix_depth: usize,
471}
472
473#[derive(Debug, Clone, PartialEq, Eq)]
474struct SassVariableDeclScope {
475 name: String,
476 selector_names: Vec<String>,
477}
478
479#[derive(Debug, Clone, PartialEq, Eq)]
480struct StyleBlock {
481 names: Vec<String>,
482 context_text: Option<String>,
483 start: usize,
484 end: usize,
485 rule_start: usize,
486 rule_end: usize,
487 body_start: usize,
488 body_end: usize,
489 header_text: Option<String>,
490 under_media: bool,
491 under_supports: bool,
492 under_layer: bool,
493 wrapper_at_rules: Vec<ParserIndexAtRuleContextV0>,
494}
495
496#[derive(Debug, Clone, PartialEq, Eq, Default)]
497struct WrapperContext {
498 under_media: bool,
499 under_supports: bool,
500 under_layer: bool,
501 wrapper_at_rules: Vec<ParserIndexAtRuleContextV0>,
502}
503
504pub fn summarize_css_modules_intermediate(
505 source: &str,
506 dialect: StyleDialect,
507) -> ParserIndexSummaryV0 {
508 let line_index = SourceLineIndex::new(source);
509 let parsed = parse(source, dialect);
510 let facts = product_facts_from_cst(source, &parsed);
511 let blocks = style_blocks::collect_style_blocks_from_cst(source, &line_index, &parsed);
512 let syntax_index = ProductSyntaxIndexV0::new(source, &parsed);
513 let selectors = summarize_selectors(source, &line_index, &facts, &blocks);
514 let values = summarize_values(source, &line_index, &facts, &blocks, &syntax_index);
515 let custom_properties =
516 summarize_custom_properties(source, &line_index, &facts, &blocks, &syntax_index);
517 let sass = summarize_sass(source, &line_index, &facts, &blocks, &syntax_index);
518 let keyframes = summarize_keyframes(source, &line_index, &facts, &blocks, &syntax_index);
519 let composes = summarize_composes(source, &line_index, &facts, &blocks);
520 let wrappers = summarize_wrappers(&blocks);
521
522 ParserIndexSummaryV0 {
523 schema_version: "0",
524 language: dialect_label(dialect),
525 selectors: ParserIndexSelectorFactsV0 {
526 selectors_with_value_refs_names: values.selectors_with_refs_names.clone(),
527 selectors_with_animation_ref_names: keyframes
528 .selectors_with_animation_ref_names
529 .clone(),
530 selectors_with_animation_name_ref_names: keyframes
531 .selectors_with_animation_name_ref_names
532 .clone(),
533 ..selectors
534 },
535 values,
536 custom_properties,
537 sass,
538 keyframes,
539 composes,
540 wrappers,
541 }
542}
543
544pub fn summarize_parser_canonical_candidate(
545 source: &str,
546 dialect: StyleDialect,
547) -> ParserCanonicalCandidateBundleV0 {
548 let parity_lite = summarize_omena_parser_parity_lite(source, dialect);
549 let css_modules_intermediate = summarize_css_modules_intermediate(source, dialect);
550
551 ParserCanonicalCandidateBundleV0 {
552 schema_version: "0",
553 language: parity_lite.language,
554 parity_lite,
555 css_modules_intermediate,
556 }
557}
558
559pub fn summarize_parser_evaluator_candidates(
560 source: &str,
561 dialect: StyleDialect,
562) -> ParserEvaluatorCandidatesV0 {
563 let intermediate = summarize_css_modules_intermediate(source, dialect);
564 let bem_suffix_safe_names: BTreeSet<&str> = intermediate
565 .selectors
566 .bem_suffix_safe_names
567 .iter()
568 .map(String::as_str)
569 .collect();
570 let nested_unsafe_names: BTreeSet<&str> = intermediate
571 .selectors
572 .nested_unsafe_names
573 .iter()
574 .map(String::as_str)
575 .collect();
576 let selectors_under_media_names: BTreeSet<&str> = intermediate
577 .wrappers
578 .selectors_under_media_names
579 .iter()
580 .map(String::as_str)
581 .collect();
582 let selectors_under_supports_names: BTreeSet<&str> = intermediate
583 .wrappers
584 .selectors_under_supports_names
585 .iter()
586 .map(String::as_str)
587 .collect();
588 let selectors_under_layer_names: BTreeSet<&str> = intermediate
589 .wrappers
590 .selectors_under_layer_names
591 .iter()
592 .map(String::as_str)
593 .collect();
594 let selectors_with_refs_names: BTreeSet<&str> = intermediate
595 .values
596 .selectors_with_refs_names
597 .iter()
598 .map(String::as_str)
599 .collect();
600 let selectors_with_local_refs_names: BTreeSet<&str> = intermediate
601 .values
602 .selectors_with_local_refs_names
603 .iter()
604 .map(String::as_str)
605 .collect();
606 let selectors_with_imported_refs_names: BTreeSet<&str> = intermediate
607 .values
608 .selectors_with_imported_refs_names
609 .iter()
610 .map(String::as_str)
611 .collect();
612 let selectors_with_custom_property_refs_names: BTreeSet<&str> = intermediate
613 .custom_properties
614 .selectors_with_refs_names
615 .iter()
616 .map(String::as_str)
617 .collect();
618 let selectors_with_animation_ref_names: BTreeSet<&str> = intermediate
619 .keyframes
620 .selectors_with_animation_ref_names
621 .iter()
622 .map(String::as_str)
623 .collect();
624 let selectors_with_animation_name_ref_names: BTreeSet<&str> = intermediate
625 .keyframes
626 .selectors_with_animation_name_ref_names
627 .iter()
628 .map(String::as_str)
629 .collect();
630 let selectors_with_composes_names: BTreeSet<&str> = intermediate
631 .composes
632 .selectors_with_composes_names
633 .iter()
634 .map(String::as_str)
635 .collect();
636 let local_selector_names: BTreeSet<&str> = intermediate
637 .composes
638 .local_selector_names
639 .iter()
640 .map(String::as_str)
641 .collect();
642 let imported_selector_names: BTreeSet<&str> = intermediate
643 .composes
644 .imported_selector_names
645 .iter()
646 .map(String::as_str)
647 .collect();
648 let global_selector_names: BTreeSet<&str> = intermediate
649 .composes
650 .global_selector_names
651 .iter()
652 .map(String::as_str)
653 .collect();
654
655 let results = intermediate
656 .selectors
657 .names
658 .iter()
659 .map(|selector_name| {
660 let selector = selector_name.as_str();
661 let nested_safety_kind = if nested_unsafe_names.contains(selector) {
662 "nestedUnsafe"
663 } else if bem_suffix_safe_names.contains(selector) {
664 "bemSuffixSafe"
665 } else {
666 "flat"
667 };
668 ParserEvaluatorCandidateV0 {
669 kind: "selector-index-facts",
670 selector_name: selector_name.clone(),
671 nested_safety_kind,
672 bem_suffix_parent_name: if nested_safety_kind == "bemSuffixSafe" {
673 bem_suffix_parent_name(selector)
674 } else {
675 None
676 },
677 under_media: selectors_under_media_names.contains(selector),
678 under_supports: selectors_under_supports_names.contains(selector),
679 under_layer: selectors_under_layer_names.contains(selector),
680 has_value_refs: selectors_with_refs_names.contains(selector),
681 has_local_value_refs: selectors_with_local_refs_names.contains(selector),
682 has_imported_value_refs: selectors_with_imported_refs_names.contains(selector),
683 has_custom_property_refs: selectors_with_custom_property_refs_names
684 .contains(selector),
685 has_animation_ref: selectors_with_animation_ref_names.contains(selector),
686 has_animation_name_ref: selectors_with_animation_name_ref_names.contains(selector),
687 has_composes: selectors_with_composes_names.contains(selector),
688 has_local_composes: local_selector_names.contains(selector),
689 has_imported_composes: imported_selector_names.contains(selector),
690 has_global_composes: global_selector_names.contains(selector),
691 }
692 })
693 .collect();
694
695 ParserEvaluatorCandidatesV0 {
696 schema_version: "0",
697 language: intermediate.language,
698 results,
699 }
700}
701
702pub fn summarize_parser_canonical_producer_signal(
703 source: &str,
704 dialect: StyleDialect,
705) -> ParserCanonicalProducerSignalV0 {
706 let canonical_candidate = summarize_parser_canonical_candidate(source, dialect);
707 let evaluator_candidates = summarize_parser_evaluator_candidates(source, dialect);
708
709 ParserCanonicalProducerSignalV0 {
710 schema_version: "0",
711 language: canonical_candidate.language,
712 canonical_candidate,
713 evaluator_candidates,
714 public_product_gate: ParserPublicProductGateSignalV0 {
715 canonical_candidate_command: "pnpm check:rust-parser-canonical-candidate",
716 consumer_boundary_command: "pnpm check:rust-parser-consumer-boundary",
717 public_product_gate_command: "pnpm check:rust-parser-public-product",
718 included_in_parser_lane: true,
719 included_in_rust_lane_bundle: true,
720 included_in_rust_release_bundle: true,
721 },
722 }
723}
724
725fn summarize_selectors(
726 source: &str,
727 line_index: &SourceLineIndex,
728 facts: &ParsedStyleFacts,
729 blocks: &[StyleBlock],
730) -> ParserIndexSelectorFactsV0 {
731 let mut names = Vec::new();
732 let mut definition_facts = Vec::new();
733 let mut bem_suffix_parent_names = Vec::new();
734 let mut bem_suffix_safe_names = Vec::new();
735 let mut nested_unsafe_names = Vec::new();
736 let mut nested_safety_counts = NestedSafetyCountsV0::default();
737
738 for selector in &facts.selectors {
739 if selector.kind != ParsedSelectorFactKind::Class {
740 continue;
741 }
742 let name = selector.name.clone();
743 names.push(name.clone());
744 let byte_span = byte_span_for_range(selector.range);
745 let nested_safety_kind = nested_safety_for_selector(blocks, &name).unwrap_or("flat");
746 let rule_block = selector_rule_block(blocks, &name, byte_span.start);
747 let rule_byte_span = rule_block
748 .map(|block| ParserByteSpanV0 {
749 start: block.rule_start,
750 end: block.rule_end,
751 })
752 .unwrap_or(byte_span);
753 let full_selector = rule_block
754 .and_then(|block| block.header_text.clone())
755 .unwrap_or_else(|| format!(".{name}"));
756 let declarations = rule_block
757 .and_then(|block| source.get(block.body_start..block.body_end))
758 .unwrap_or_default()
759 .trim()
760 .to_string();
761 let bem_suffix_parent_name = if nested_safety_kind == "bemSuffixSafe" {
762 bem_suffix_parent_name(&name)
763 } else {
764 None
765 };
766 match nested_safety_kind {
767 "bemSuffixSafe" => {
768 nested_safety_counts.bem_suffix_safe += 1;
769 bem_suffix_safe_names.push(name.clone());
770 if let Some(parent) = &bem_suffix_parent_name {
771 bem_suffix_parent_names.push(parent.clone());
772 }
773 }
774 "nestedUnsafe" => {
775 nested_safety_counts.nested_unsafe += 1;
776 nested_unsafe_names.push(name.clone());
777 }
778 _ => nested_safety_counts.flat += 1,
779 }
780 let wrapper = wrapper_for_offset(blocks, byte_span.start);
781 definition_facts.push(ParserIndexSelectorDefinitionFactV0 {
782 name,
783 source_order: definition_facts.len(),
784 byte_span,
785 range: parser_range_for_byte_span(source, line_index, byte_span),
786 rule_byte_span,
787 rule_range: parser_range_for_byte_span(source, line_index, rule_byte_span),
788 full_selector,
789 declarations,
790 nested_safety_kind,
791 bem_suffix_parent_name,
792 under_media: wrapper.under_media,
793 under_supports: wrapper.under_supports,
794 under_layer: wrapper.under_layer,
795 });
796 }
797
798 names.sort();
799 definition_facts.sort();
800 bem_suffix_parent_names.sort();
801 bem_suffix_safe_names.sort();
802 nested_unsafe_names.sort();
803
804 ParserIndexSelectorFactsV0 {
805 names,
806 definition_facts,
807 bem_suffix_count: bem_suffix_safe_names.len(),
808 bem_suffix_parent_names,
809 bem_suffix_safe_names,
810 nested_unsafe_names,
811 nested_safety_counts,
812 ..ParserIndexSelectorFactsV0::default()
813 }
814}
815
816fn summarize_values(
817 source: &str,
818 line_index: &SourceLineIndex,
819 facts: &ParsedStyleFacts,
820 blocks: &[StyleBlock],
821 syntax_index: &ProductSyntaxIndexV0,
822) -> ParserIndexValueFactsV0 {
823 let imported_sources_by_name = facts
824 .css_module_value_import_edges
825 .iter()
826 .map(|edge| (edge.local_name.clone(), edge.import_source.clone()))
827 .collect::<BTreeMap<_, _>>();
828 let imported_names = imported_sources_by_name
829 .keys()
830 .cloned()
831 .collect::<BTreeSet<_>>();
832 let local_decl_names = facts
833 .css_module_values
834 .iter()
835 .filter(|value| value.kind == ParsedCssModuleValueFactKind::Definition)
836 .map(|value| value.name.clone())
837 .filter(|name| !imported_names.contains(name))
838 .collect::<BTreeSet<_>>();
839 let mut decl_facts = Vec::new();
840 for value in &facts.css_module_values {
841 if value.kind != ParsedCssModuleValueFactKind::Definition
842 || !local_decl_names.contains(&value.name)
843 {
844 continue;
845 }
846 let byte_span = byte_span_for_range(value.range);
847 let rule_byte_span = syntax_index
848 .css_module_value_span_for_offset(byte_span.start)
849 .unwrap_or(byte_span);
850 decl_facts.push(ParserIndexValueDeclFactV0 {
851 name: value.name.clone(),
852 value: syntax_index
853 .css_module_value_text(source, byte_span.start)
854 .unwrap_or_default(),
855 source_order: decl_facts.len(),
856 byte_span,
857 range: parser_range_for_byte_span(source, line_index, byte_span),
858 rule_byte_span,
859 rule_range: parser_range_for_byte_span(source, line_index, rule_byte_span),
860 });
861 }
862 decl_facts.sort();
863 decl_facts.dedup();
864 let mut import_facts = Vec::new();
865 for edge in &facts.css_module_value_import_edges {
866 let byte_span = byte_span_for_range(edge.local_range);
867 let remote_byte_span = byte_span_for_range(edge.remote_range);
868 let imported_name_byte_span =
869 (edge.remote_name != edge.local_name).then_some(remote_byte_span);
870 let rule_byte_span = syntax_index
871 .css_module_value_span_for_offset(byte_span.start)
872 .unwrap_or(byte_span);
873 import_facts.push(ParserIndexValueImportFactV0 {
874 name: edge.local_name.clone(),
875 imported_name: edge.remote_name.clone(),
876 from: edge.import_source.clone(),
877 source_order: import_facts.len(),
878 byte_span,
879 range: parser_range_for_byte_span(source, line_index, byte_span),
880 imported_name_byte_span,
881 imported_name_range: imported_name_byte_span
882 .map(|span| parser_range_for_byte_span(source, line_index, span)),
883 rule_byte_span,
884 rule_range: parser_range_for_byte_span(source, line_index, rule_byte_span),
885 });
886 }
887 import_facts.sort();
888 import_facts.dedup();
889 let mut ref_facts = Vec::new();
890 let value_decl_ref_names = facts
891 .css_module_value_definition_edges
892 .iter()
893 .flat_map(|edge| edge.reference_names.iter().cloned())
894 .collect::<Vec<_>>();
895 let mut declaration_ref_names = Vec::new();
896 let mut selectors_with_refs = BTreeSet::new();
897 let mut selectors_with_local_refs = BTreeSet::new();
898 let mut selectors_with_imported_refs = BTreeSet::new();
899 let mut selectors_with_refs_under_media = BTreeSet::new();
900 let mut selectors_with_refs_under_supports = BTreeSet::new();
901 let mut selectors_with_refs_under_layer = BTreeSet::new();
902 let mut selectors_with_local_refs_under_media = BTreeSet::new();
903 let mut selectors_with_local_refs_under_supports = BTreeSet::new();
904 let mut selectors_with_local_refs_under_layer = BTreeSet::new();
905 let mut selectors_with_imported_refs_under_media = BTreeSet::new();
906 let mut selectors_with_imported_refs_under_supports = BTreeSet::new();
907 let mut selectors_with_imported_refs_under_layer = BTreeSet::new();
908
909 for value in &facts.css_module_values {
910 if value.kind != ParsedCssModuleValueFactKind::Reference {
911 continue;
912 }
913 if !local_decl_names.contains(&value.name) && !imported_names.contains(&value.name) {
914 continue;
915 }
916 let offset = range_start(value.range);
917 let selector_names = selector_names_for_offset(blocks, offset);
918 if !selector_names.is_empty() {
919 declaration_ref_names.push(value.name.clone());
920 let byte_span = byte_span_for_range(value.range);
921 ref_facts.push(ParserIndexValueRefFactV0 {
922 name: value.name.clone(),
923 source: "declaration",
924 source_order: ref_facts.len(),
925 byte_span,
926 range: parser_range_for_byte_span(source, line_index, byte_span),
927 });
928 let wrapper = wrapper_for_offset(blocks, offset);
929 for selector in selector_names {
930 selectors_with_refs.insert(selector.clone());
931 insert_by_wrapper(
932 &mut selectors_with_refs_under_media,
933 &mut selectors_with_refs_under_supports,
934 &mut selectors_with_refs_under_layer,
935 &selector,
936 &wrapper,
937 );
938 if local_decl_names.contains(&value.name) {
939 selectors_with_local_refs.insert(selector.clone());
940 insert_by_wrapper(
941 &mut selectors_with_local_refs_under_media,
942 &mut selectors_with_local_refs_under_supports,
943 &mut selectors_with_local_refs_under_layer,
944 &selector,
945 &wrapper,
946 );
947 }
948 if imported_names.contains(&value.name) {
949 selectors_with_imported_refs.insert(selector.clone());
950 insert_by_wrapper(
951 &mut selectors_with_imported_refs_under_media,
952 &mut selectors_with_imported_refs_under_supports,
953 &mut selectors_with_imported_refs_under_layer,
954 &selector,
955 &wrapper,
956 );
957 }
958 }
959 } else {
960 let byte_span = byte_span_for_range(value.range);
961 ref_facts.push(ParserIndexValueRefFactV0 {
962 name: value.name.clone(),
963 source: "valueDecl",
964 source_order: ref_facts.len(),
965 byte_span,
966 range: parser_range_for_byte_span(source, line_index, byte_span),
967 });
968 }
969 }
970 ref_facts.sort();
971 ref_facts.dedup();
972
973 let mut value_decl_imported_ref_sources = Vec::new();
974 for name in &value_decl_ref_names {
975 if let Some(source) = imported_sources_by_name.get(name) {
976 value_decl_imported_ref_sources.push(source.clone());
977 }
978 }
979 let mut declaration_imported_ref_sources = Vec::new();
980 for name in &declaration_ref_names {
981 if let Some(source) = imported_sources_by_name.get(name) {
982 declaration_imported_ref_sources.push(source.clone());
983 }
984 }
985 let semantic_ref_names = declaration_ref_names
986 .iter()
987 .chain(value_decl_ref_names.iter())
988 .cloned()
989 .collect::<Vec<_>>();
990
991 ParserIndexValueFactsV0 {
992 decl_names: sorted(local_decl_names.clone()),
993 decl_facts,
994 decl_names_with_local_refs: facts
995 .css_module_value_definition_edges
996 .iter()
997 .filter(|edge| {
998 edge.reference_names
999 .iter()
1000 .any(|name| local_decl_names.contains(name))
1001 })
1002 .map(|edge| edge.definition_name.clone())
1003 .collect::<BTreeSet<_>>()
1004 .into_iter()
1005 .collect(),
1006 decl_names_with_imported_refs: facts
1007 .css_module_value_definition_edges
1008 .iter()
1009 .filter(|edge| {
1010 edge.reference_names
1011 .iter()
1012 .any(|name| imported_names.contains(name))
1013 })
1014 .map(|edge| edge.definition_name.clone())
1015 .collect::<BTreeSet<_>>()
1016 .into_iter()
1017 .collect(),
1018 import_names: facts
1019 .css_module_value_import_edges
1020 .iter()
1021 .map(|edge| edge.local_name.clone())
1022 .collect::<BTreeSet<_>>()
1023 .into_iter()
1024 .collect(),
1025 import_facts,
1026 import_sources: facts
1027 .css_module_value_import_edges
1028 .iter()
1029 .map(|edge| edge.import_source.clone())
1030 .collect::<Vec<_>>()
1031 .tap_sort(),
1032 import_alias_count: facts
1033 .css_module_value_import_edges
1034 .iter()
1035 .filter(|edge| edge.remote_name != edge.local_name)
1036 .count(),
1037 ref_names: semantic_ref_names.clone().tap_sort(),
1038 ref_facts,
1039 local_ref_names: semantic_ref_names
1040 .iter()
1041 .filter(|name| local_decl_names.contains(*name))
1042 .cloned()
1043 .collect::<Vec<_>>()
1044 .tap_sort(),
1045 imported_ref_names: semantic_ref_names
1046 .iter()
1047 .filter(|name| imported_names.contains(*name))
1048 .cloned()
1049 .collect::<Vec<_>>()
1050 .tap_sort(),
1051 imported_ref_sources: semantic_ref_names
1052 .iter()
1053 .filter_map(|name| imported_sources_by_name.get(name).cloned())
1054 .collect::<Vec<_>>()
1055 .tap_sort(),
1056 declaration_ref_names: declaration_ref_names.tap_sort(),
1057 declaration_imported_ref_sources: declaration_imported_ref_sources.tap_sort(),
1058 value_decl_ref_names: value_decl_ref_names.tap_sort(),
1059 value_decl_imported_ref_sources: value_decl_imported_ref_sources.tap_sort(),
1060 selectors_with_refs_names: sorted(selectors_with_refs),
1061 selectors_with_local_refs_names: sorted(selectors_with_local_refs),
1062 selectors_with_imported_refs_names: sorted(selectors_with_imported_refs),
1063 selectors_with_refs_under_media_names: sorted(selectors_with_refs_under_media),
1064 selectors_with_refs_under_supports_names: sorted(selectors_with_refs_under_supports),
1065 selectors_with_refs_under_layer_names: sorted(selectors_with_refs_under_layer),
1066 selectors_with_local_refs_under_media_names: sorted(selectors_with_local_refs_under_media),
1067 selectors_with_local_refs_under_supports_names: sorted(
1068 selectors_with_local_refs_under_supports,
1069 ),
1070 selectors_with_local_refs_under_layer_names: sorted(selectors_with_local_refs_under_layer),
1071 selectors_with_imported_refs_under_media_names: sorted(
1072 selectors_with_imported_refs_under_media,
1073 ),
1074 selectors_with_imported_refs_under_supports_names: sorted(
1075 selectors_with_imported_refs_under_supports,
1076 ),
1077 selectors_with_imported_refs_under_layer_names: sorted(
1078 selectors_with_imported_refs_under_layer,
1079 ),
1080 }
1081}
1082
1083fn summarize_custom_properties(
1084 source: &str,
1085 line_index: &SourceLineIndex,
1086 facts: &ParsedStyleFacts,
1087 blocks: &[StyleBlock],
1088 syntax_index: &ProductSyntaxIndexV0,
1089) -> ParserIndexCustomPropertyFactsV0 {
1090 let mut decl_facts = Vec::new();
1091 let mut ref_facts = Vec::new();
1092 for variable in &facts.variables {
1093 match variable.kind {
1094 ParsedVariableFactKind::CustomPropertyDeclaration => {
1095 let byte_span = byte_span_for_range(variable.range);
1096 let wrapper = wrapper_for_offset(blocks, byte_span.start);
1097 let rule_byte_span = style_block_for_offset(blocks, byte_span.start)
1098 .map(|block| ParserByteSpanV0 {
1099 start: block.rule_start,
1100 end: block.rule_end,
1101 })
1102 .or_else(|| syntax_index.declaration_span_for_offset(byte_span.start))
1103 .unwrap_or(byte_span);
1104 decl_facts.push(ParserIndexCustomPropertyDeclFactV0 {
1105 name: variable.name.clone(),
1106 value: syntax_index
1107 .declaration_value_text(source, byte_span.start)
1108 .unwrap_or_default(),
1109 source_order: decl_facts.len(),
1110 byte_span,
1111 range: parser_range_for_byte_span(source, line_index, byte_span),
1112 rule_byte_span,
1113 rule_range: parser_range_for_byte_span(source, line_index, rule_byte_span),
1114 selector_contexts: selector_contexts_for_offset(blocks, byte_span.start),
1115 wrapper_at_rules: wrapper.wrapper_at_rules.clone(),
1116 under_media: wrapper.under_media,
1117 under_supports: wrapper.under_supports,
1118 under_layer: wrapper.under_layer,
1119 });
1120 }
1121 ParsedVariableFactKind::CustomPropertyReference => {
1122 let byte_span = byte_span_for_range(variable.range);
1123 let wrapper = wrapper_for_offset(blocks, byte_span.start);
1124 ref_facts.push(ParserIndexCustomPropertyRefFactV0 {
1125 name: variable.name.clone(),
1126 source_order: ref_facts.len(),
1127 byte_span,
1128 range: parser_range_for_byte_span(source, line_index, byte_span),
1129 selector_contexts: selector_contexts_for_offset(blocks, byte_span.start),
1130 wrapper_at_rules: wrapper.wrapper_at_rules.clone(),
1131 under_media: wrapper.under_media,
1132 under_supports: wrapper.under_supports,
1133 under_layer: wrapper.under_layer,
1134 });
1135 }
1136 _ => {}
1137 }
1138 }
1139 decl_facts.sort();
1140 decl_facts.dedup();
1141 ref_facts.sort();
1142 ref_facts.dedup();
1143 ParserIndexCustomPropertyFactsV0 {
1144 decl_names: sorted(decl_facts.iter().map(|fact| fact.name.clone()).collect()),
1145 decl_context_selectors: sorted(
1146 decl_facts
1147 .iter()
1148 .flat_map(|fact| fact.selector_contexts.iter().cloned())
1149 .collect(),
1150 ),
1151 decl_names_under_media: sorted(
1152 decl_facts
1153 .iter()
1154 .filter(|fact| fact.under_media)
1155 .map(|fact| fact.name.clone())
1156 .collect(),
1157 ),
1158 decl_names_under_supports: sorted(
1159 decl_facts
1160 .iter()
1161 .filter(|fact| fact.under_supports)
1162 .map(|fact| fact.name.clone())
1163 .collect(),
1164 ),
1165 decl_names_under_layer: sorted(
1166 decl_facts
1167 .iter()
1168 .filter(|fact| fact.under_layer)
1169 .map(|fact| fact.name.clone())
1170 .collect(),
1171 ),
1172 ref_names: sorted(ref_facts.iter().map(|fact| fact.name.clone()).collect()),
1173 selectors_with_refs_names: sorted(
1174 ref_facts
1175 .iter()
1176 .flat_map(|fact| selector_names_from_contexts(&fact.selector_contexts))
1177 .collect(),
1178 ),
1179 selectors_with_refs_under_media_names: sorted(
1180 ref_facts
1181 .iter()
1182 .filter(|fact| fact.under_media)
1183 .flat_map(|fact| selector_names_from_contexts(&fact.selector_contexts))
1184 .collect(),
1185 ),
1186 selectors_with_refs_under_supports_names: sorted(
1187 ref_facts
1188 .iter()
1189 .filter(|fact| fact.under_supports)
1190 .flat_map(|fact| selector_names_from_contexts(&fact.selector_contexts))
1191 .collect(),
1192 ),
1193 selectors_with_refs_under_layer_names: sorted(
1194 ref_facts
1195 .iter()
1196 .filter(|fact| fact.under_layer)
1197 .flat_map(|fact| selector_names_from_contexts(&fact.selector_contexts))
1198 .collect(),
1199 ),
1200 decl_facts,
1201 ref_facts,
1202 }
1203}
1204
1205fn summarize_sass(
1206 source: &str,
1207 line_index: &SourceLineIndex,
1208 facts: &ParsedStyleFacts,
1209 blocks: &[StyleBlock],
1210 syntax_index: &ProductSyntaxIndexV0,
1211) -> ParserIndexSassFactsV0 {
1212 let mut variable_decl_names = BTreeSet::new();
1213 let mut variable_parameter_names = BTreeSet::new();
1214 let mut variable_ref_names = BTreeSet::new();
1215 let mut mixin_decl_names = BTreeSet::new();
1216 let mut mixin_include_names = BTreeSet::new();
1217 let mut function_decl_names = BTreeSet::new();
1218 let mut function_call_names = BTreeSet::new();
1219 let mut symbol_decl_facts = Vec::new();
1220 let mut selector_symbol_facts = Vec::new();
1221 let mut global_variable_decl_names = BTreeSet::new();
1222 let mut variable_decl_scopes = Vec::new();
1223
1224 for symbol in &facts.sass_symbols {
1225 let byte_span = byte_span_for_range(symbol.range);
1226 let range = parser_range_for_byte_span(source, line_index, byte_span);
1227 match symbol.kind {
1228 ParsedSassSymbolFactKind::VariableDeclaration => {
1229 if symbol.role == "parameter"
1230 || syntax_index.sass_parameter_list_contains(byte_span.start)
1231 {
1232 variable_parameter_names.insert(symbol.name.clone());
1233 } else {
1234 variable_decl_names.insert(symbol.name.clone());
1235 let selector_names = selector_names_for_offset(blocks, byte_span.start);
1236 if selector_names.is_empty() {
1237 global_variable_decl_names.insert(symbol.name.clone());
1238 }
1239 variable_decl_scopes.push(SassVariableDeclScope {
1240 name: symbol.name.clone(),
1241 selector_names,
1242 });
1243 }
1244 symbol_decl_facts.push(ParserIndexSassSymbolDeclFactV0 {
1245 symbol_kind: symbol.symbol_kind,
1246 name: symbol.name.clone(),
1247 role: symbol.role,
1248 byte_span,
1249 range,
1250 });
1251 }
1252 ParsedSassSymbolFactKind::MixinDeclaration => {
1253 mixin_decl_names.insert(symbol.name.clone());
1254 symbol_decl_facts.push(ParserIndexSassSymbolDeclFactV0 {
1255 symbol_kind: symbol.symbol_kind,
1256 name: symbol.name.clone(),
1257 role: symbol.role,
1258 byte_span,
1259 range,
1260 });
1261 }
1262 ParsedSassSymbolFactKind::FunctionDeclaration => {
1263 function_decl_names.insert(symbol.name.clone());
1264 symbol_decl_facts.push(ParserIndexSassSymbolDeclFactV0 {
1265 symbol_kind: symbol.symbol_kind,
1266 name: symbol.name.clone(),
1267 role: symbol.role,
1268 byte_span,
1269 range,
1270 });
1271 }
1272 ParsedSassSymbolFactKind::VariableReference => {
1273 variable_ref_names.insert(symbol.name.clone());
1274 }
1275 ParsedSassSymbolFactKind::MixinInclude => {
1276 if symbol.namespace.is_none() {
1277 mixin_include_names.insert(symbol.name.clone());
1278 }
1279 }
1280 ParsedSassSymbolFactKind::FunctionCall => {
1281 if symbol.namespace.is_none() {
1282 function_call_names.insert(symbol.name.clone());
1283 }
1284 }
1285 }
1286 }
1287
1288 let mut resolved_variable_ref_names = BTreeSet::new();
1289 let mut unresolved_variable_ref_names = BTreeSet::new();
1290 for symbol in &facts.sass_symbols {
1291 if symbol.kind != ParsedSassSymbolFactKind::VariableReference || symbol.namespace.is_some()
1292 {
1293 continue;
1294 }
1295 if is_sass_variable_reference_resolved(
1296 &symbol.name,
1297 range_start(symbol.range),
1298 blocks,
1299 &global_variable_decl_names,
1300 &variable_parameter_names,
1301 &variable_decl_scopes,
1302 ) {
1303 resolved_variable_ref_names.insert(symbol.name.clone());
1304 } else {
1305 unresolved_variable_ref_names.insert(symbol.name.clone());
1306 }
1307 }
1308
1309 let same_file_resolution = ParserIndexSassSameFileResolutionFactsV0 {
1310 resolved_variable_ref_names: sorted(resolved_variable_ref_names),
1311 unresolved_variable_ref_names: sorted(unresolved_variable_ref_names),
1312 resolved_mixin_include_names: sorted(
1313 mixin_include_names
1314 .iter()
1315 .filter(|name| mixin_decl_names.contains(*name))
1316 .cloned()
1317 .collect(),
1318 ),
1319 unresolved_mixin_include_names: sorted(
1320 mixin_include_names
1321 .iter()
1322 .filter(|name| !mixin_decl_names.contains(*name))
1323 .cloned()
1324 .collect(),
1325 ),
1326 resolved_function_call_names: sorted(
1327 function_call_names
1328 .iter()
1329 .filter(|name| function_decl_names.contains(*name))
1330 .cloned()
1331 .collect(),
1332 ),
1333 };
1334
1335 for symbol in &facts.sass_symbols {
1336 if matches!(
1337 symbol.kind,
1338 ParsedSassSymbolFactKind::VariableDeclaration
1339 | ParsedSassSymbolFactKind::MixinDeclaration
1340 | ParsedSassSymbolFactKind::FunctionDeclaration
1341 ) {
1342 continue;
1343 }
1344 let offset = range_start(symbol.range);
1345 let byte_span = byte_span_for_range(symbol.range);
1346 for selector_name in selector_names_for_offset(blocks, offset) {
1347 let resolution = match symbol.kind {
1348 ParsedSassSymbolFactKind::VariableReference if symbol.namespace.is_some() => {
1349 "external"
1350 }
1351 ParsedSassSymbolFactKind::VariableReference
1352 if is_sass_variable_reference_resolved(
1353 &symbol.name,
1354 offset,
1355 blocks,
1356 &global_variable_decl_names,
1357 &variable_parameter_names,
1358 &variable_decl_scopes,
1359 ) =>
1360 {
1361 "resolved"
1362 }
1363 ParsedSassSymbolFactKind::MixinInclude if symbol.namespace.is_some() => "external",
1364 ParsedSassSymbolFactKind::MixinInclude
1365 if same_file_resolution
1366 .resolved_mixin_include_names
1367 .contains(&symbol.name) =>
1368 {
1369 "resolved"
1370 }
1371 ParsedSassSymbolFactKind::FunctionCall if symbol.namespace.is_some() => "external",
1372 ParsedSassSymbolFactKind::FunctionCall
1373 if same_file_resolution
1374 .resolved_function_call_names
1375 .contains(&symbol.name) =>
1376 {
1377 "resolved"
1378 }
1379 _ => "unresolved",
1380 };
1381 selector_symbol_facts.push(ParserIndexSassSelectorSymbolFactV0 {
1382 selector_name,
1383 symbol_kind: symbol.symbol_kind,
1384 name: symbol.name.clone(),
1385 namespace: symbol.namespace.clone(),
1386 role: symbol.role,
1387 resolution,
1388 byte_span,
1389 range: parser_range_for_byte_span(source, line_index, byte_span),
1390 });
1391 }
1392 }
1393 selector_symbol_facts.sort();
1394 selector_symbol_facts.dedup();
1395
1396 let mut module_use_sources = BTreeSet::new();
1397 let mut module_forward_sources = BTreeSet::new();
1398 let mut module_import_sources = BTreeSet::new();
1399 let mut module_use_edges = Vec::new();
1400 let mut module_forward_edges = Vec::new();
1401 for edge in &facts.sass_module_edges {
1402 match edge.kind {
1403 ParsedSassModuleEdgeFactKind::Use => {
1404 let byte_span = byte_span_for_range(edge.range);
1405 module_use_sources.insert(edge.source.clone());
1406 module_use_edges.push(ParserIndexSassModuleUseFactV0 {
1407 source: edge.source.clone(),
1408 namespace_kind: edge.namespace_kind.unwrap_or("default"),
1409 namespace: edge.namespace.clone(),
1410 byte_span,
1411 range: parser_range_for_byte_span(source, line_index, byte_span),
1412 });
1413 }
1414 ParsedSassModuleEdgeFactKind::Forward => {
1415 let byte_span = byte_span_for_range(edge.range);
1416 let rule_byte_span = syntax_index
1417 .scss_forward_span_for_offset(byte_span.start)
1418 .unwrap_or(byte_span);
1419 module_forward_sources.insert(edge.source.clone());
1420 module_forward_edges.push(ParserIndexSassModuleForwardFactV0 {
1421 source: edge.source.clone(),
1422 prefix: sass_module_forward_prefix_from_statement(source, rule_byte_span),
1423 visibility_kind: edge.visibility_filter_kind.unwrap_or("all"),
1424 visibility_members: edge
1425 .visibility_filter_names
1426 .iter()
1427 .map(|name| ParserIndexSassModuleForwardMemberV0 {
1428 name: name.clone(),
1429 symbol_kind: sass_module_forward_member_symbol_kind(
1430 source,
1431 rule_byte_span,
1432 name,
1433 ),
1434 })
1435 .collect(),
1436 byte_span,
1437 range: parser_range_for_byte_span(source, line_index, byte_span),
1438 rule_byte_span,
1439 rule_range: parser_range_for_byte_span(source, line_index, rule_byte_span),
1440 });
1441 }
1442 ParsedSassModuleEdgeFactKind::Import => {
1443 let byte_span = byte_span_for_range(edge.range);
1444 module_use_sources.insert(edge.source.clone());
1445 module_import_sources.insert(edge.source.clone());
1446 module_use_edges.push(ParserIndexSassModuleUseFactV0 {
1447 source: edge.source.clone(),
1448 namespace_kind: "wildcard",
1449 namespace: None,
1450 byte_span,
1451 range: parser_range_for_byte_span(source, line_index, byte_span),
1452 });
1453 }
1454 }
1455 }
1456 module_use_edges.sort();
1457 module_use_edges.dedup();
1458 module_forward_edges.sort();
1459 module_forward_edges.dedup();
1460
1461 ParserIndexSassFactsV0 {
1462 variable_decl_names: sorted(variable_decl_names),
1463 symbol_decl_facts,
1464 variable_parameter_names: sorted(variable_parameter_names.clone()),
1465 variable_ref_names: sorted(variable_ref_names),
1466 selectors_with_variable_refs_names: selector_names_for_variable_symbols(
1467 blocks,
1468 facts,
1469 &global_variable_decl_names,
1470 &variable_parameter_names,
1471 &variable_decl_scopes,
1472 None,
1473 ),
1474 selectors_with_resolved_variable_refs_names: selector_names_for_variable_symbols(
1475 blocks,
1476 facts,
1477 &global_variable_decl_names,
1478 &variable_parameter_names,
1479 &variable_decl_scopes,
1480 Some(true),
1481 ),
1482 selectors_with_unresolved_variable_refs_names: selector_names_for_variable_symbols(
1483 blocks,
1484 facts,
1485 &global_variable_decl_names,
1486 &variable_parameter_names,
1487 &variable_decl_scopes,
1488 Some(false),
1489 ),
1490 mixin_decl_names: sorted(mixin_decl_names),
1491 mixin_include_names: sorted(mixin_include_names),
1492 selectors_with_mixin_includes_names: selector_names_for_symbols(
1493 blocks,
1494 facts,
1495 ParsedSassSymbolFactKind::MixinInclude,
1496 None,
1497 ),
1498 selectors_with_resolved_mixin_includes_names: selector_names_for_symbols(
1499 blocks,
1500 facts,
1501 ParsedSassSymbolFactKind::MixinInclude,
1502 Some(&same_file_resolution.resolved_mixin_include_names),
1503 ),
1504 selectors_with_unresolved_mixin_includes_names: selector_names_for_symbols(
1505 blocks,
1506 facts,
1507 ParsedSassSymbolFactKind::MixinInclude,
1508 Some(&same_file_resolution.unresolved_mixin_include_names),
1509 ),
1510 function_decl_names: sorted(function_decl_names),
1511 function_call_names: sorted(function_call_names),
1512 selectors_with_function_calls_names: selector_names_for_symbols(
1513 blocks,
1514 facts,
1515 ParsedSassSymbolFactKind::FunctionCall,
1516 None,
1517 ),
1518 selector_symbol_facts,
1519 module_use_sources: sorted(module_use_sources),
1520 module_use_edges,
1521 module_forward_sources: sorted(module_forward_sources),
1522 module_forward_edges,
1523 module_import_sources: sorted(module_import_sources),
1524 same_file_resolution,
1525 }
1526}
1527
1528fn summarize_keyframes(
1529 source: &str,
1530 line_index: &SourceLineIndex,
1531 facts: &ParsedStyleFacts,
1532 blocks: &[StyleBlock],
1533 syntax_index: &ProductSyntaxIndexV0,
1534) -> ParserIndexKeyframesFactsV0 {
1535 let mut names = Vec::new();
1536 let mut decl_facts = Vec::new();
1537 let mut names_under_media = BTreeSet::new();
1538 let mut names_under_supports = BTreeSet::new();
1539 let mut names_under_layer = BTreeSet::new();
1540 let mut animation_ref_names = Vec::new();
1541 let mut animation_name_ref_names = Vec::new();
1542 let mut ref_facts = Vec::new();
1543 let mut selectors_with_animation_ref_names = BTreeSet::new();
1544 let mut selectors_with_animation_name_ref_names = BTreeSet::new();
1545 let mut selectors_with_animation_refs_under_media_names = BTreeSet::new();
1546 let mut selectors_with_animation_refs_under_supports_names = BTreeSet::new();
1547 let mut selectors_with_animation_refs_under_layer_names = BTreeSet::new();
1548 let mut selectors_with_animation_name_refs_under_media_names = BTreeSet::new();
1549 let mut selectors_with_animation_name_refs_under_supports_names = BTreeSet::new();
1550 let mut selectors_with_animation_name_refs_under_layer_names = BTreeSet::new();
1551 let declared_keyframes = facts
1552 .animations
1553 .iter()
1554 .filter(|animation| animation.kind == ParsedAnimationFactKind::KeyframesDeclaration)
1555 .map(|animation| animation.name.clone())
1556 .collect::<BTreeSet<_>>();
1557
1558 for animation in &facts.animations {
1559 let offset = range_start(animation.range);
1560 let wrapper = wrapper_for_offset(blocks, offset);
1561 match animation.kind {
1562 ParsedAnimationFactKind::KeyframesDeclaration => {
1563 let byte_span = byte_span_for_range(animation.range);
1564 let rule_byte_span = syntax_index
1565 .keyframes_span_for_offset(byte_span.start)
1566 .unwrap_or(byte_span);
1567 decl_facts.push(ParserIndexKeyframesDeclFactV0 {
1568 name: animation.name.clone(),
1569 source_order: decl_facts.len(),
1570 byte_span,
1571 range: parser_range_for_byte_span(source, line_index, byte_span),
1572 rule_byte_span,
1573 rule_range: parser_range_for_byte_span(source, line_index, rule_byte_span),
1574 });
1575 names.push(animation.name.clone());
1576 insert_by_wrapper(
1577 &mut names_under_media,
1578 &mut names_under_supports,
1579 &mut names_under_layer,
1580 &animation.name,
1581 &wrapper,
1582 );
1583 }
1584 ParsedAnimationFactKind::AnimationNameReference => {
1585 let byte_span = byte_span_for_range(animation.range);
1586 let property = if syntax_index
1587 .declaration_property_name_for_offset(offset)
1588 .is_some_and(|name| name == "animation-name")
1589 {
1590 "animation-name"
1591 } else {
1592 "animation"
1593 };
1594 ref_facts.push(ParserIndexAnimationNameRefFactV0 {
1595 name: animation.name.clone(),
1596 property,
1597 source_order: ref_facts.len(),
1598 byte_span,
1599 range: parser_range_for_byte_span(source, line_index, byte_span),
1600 });
1601 if !declared_keyframes.contains(&animation.name) {
1602 continue;
1603 }
1604 let selectors = selector_names_for_offset(blocks, offset);
1605 if property == "animation-name" {
1606 animation_name_ref_names.push(animation.name.clone());
1607 for selector in selectors {
1608 selectors_with_animation_name_ref_names.insert(selector.clone());
1609 insert_by_wrapper(
1610 &mut selectors_with_animation_name_refs_under_media_names,
1611 &mut selectors_with_animation_name_refs_under_supports_names,
1612 &mut selectors_with_animation_name_refs_under_layer_names,
1613 &selector,
1614 &wrapper,
1615 );
1616 }
1617 } else {
1618 animation_ref_names.push(animation.name.clone());
1619 for selector in selectors {
1620 selectors_with_animation_ref_names.insert(selector.clone());
1621 insert_by_wrapper(
1622 &mut selectors_with_animation_refs_under_media_names,
1623 &mut selectors_with_animation_refs_under_supports_names,
1624 &mut selectors_with_animation_refs_under_layer_names,
1625 &selector,
1626 &wrapper,
1627 );
1628 }
1629 }
1630 }
1631 }
1632 }
1633 decl_facts.sort();
1634 decl_facts.dedup();
1635 ref_facts.sort();
1636 ref_facts.dedup();
1637
1638 ParserIndexKeyframesFactsV0 {
1639 names: names.tap_sort_unique(),
1640 decl_facts,
1641 names_under_media: sorted(names_under_media),
1642 names_under_supports: sorted(names_under_supports),
1643 names_under_layer: sorted(names_under_layer),
1644 animation_ref_names: animation_ref_names.tap_sort_unique(),
1645 animation_name_ref_names: animation_name_ref_names.tap_sort_unique(),
1646 ref_facts,
1647 selectors_with_animation_ref_names: sorted(selectors_with_animation_ref_names),
1648 selectors_with_animation_name_ref_names: sorted(selectors_with_animation_name_ref_names),
1649 selectors_with_animation_refs_under_media_names: sorted(
1650 selectors_with_animation_refs_under_media_names,
1651 ),
1652 selectors_with_animation_refs_under_supports_names: sorted(
1653 selectors_with_animation_refs_under_supports_names,
1654 ),
1655 selectors_with_animation_refs_under_layer_names: sorted(
1656 selectors_with_animation_refs_under_layer_names,
1657 ),
1658 selectors_with_animation_name_refs_under_media_names: sorted(
1659 selectors_with_animation_name_refs_under_media_names,
1660 ),
1661 selectors_with_animation_name_refs_under_supports_names: sorted(
1662 selectors_with_animation_name_refs_under_supports_names,
1663 ),
1664 selectors_with_animation_name_refs_under_layer_names: sorted(
1665 selectors_with_animation_name_refs_under_layer_names,
1666 ),
1667 }
1668}
1669
1670fn summarize_composes(
1671 source: &str,
1672 line_index: &SourceLineIndex,
1673 facts: &ParsedStyleFacts,
1674 blocks: &[StyleBlock],
1675) -> ParserIndexComposesFactsV0 {
1676 let mut summary = ParserIndexComposesFactsV0::default();
1677 for edge in &facts.css_module_composes_edges {
1678 let byte_span = byte_span_for_range(edge.range);
1679 summary.edges.push(ParserIndexComposesEdgeFactV0 {
1680 kind: match edge.kind {
1681 ParsedCssModuleComposesEdgeKind::Local => "local",
1682 ParsedCssModuleComposesEdgeKind::External => "external",
1683 ParsedCssModuleComposesEdgeKind::Global => "global",
1684 },
1685 owner_selector_names: edge.owner_selector_names.clone(),
1686 target_names: edge.target_names.clone(),
1687 import_source: edge.import_source.clone(),
1688 class_tokens: composes_class_tokens_for_edge(source, line_index, facts, edge),
1689 byte_span,
1690 range: parser_range_for_byte_span(source, line_index, byte_span),
1691 });
1692 let wrapper = wrapper_for_offset(blocks, range_start(edge.range));
1693 let count = edge.owner_selector_names.len() * edge.target_names.len();
1694 summary.class_name_count += count;
1695 for owner in &edge.owner_selector_names {
1696 summary.selectors_with_composes_names.push(owner.clone());
1697 insert_vec_by_wrapper(
1698 &mut summary.selectors_with_composes_under_media_names,
1699 &mut summary.selectors_with_composes_under_supports_names,
1700 &mut summary.selectors_with_composes_under_layer_names,
1701 owner,
1702 &wrapper,
1703 );
1704 }
1705 match edge.kind {
1706 ParsedCssModuleComposesEdgeKind::Local => {
1707 summary.local_class_name_count += count;
1708 for owner in &edge.owner_selector_names {
1709 summary.local_selector_names.push(owner.clone());
1710 insert_vec_by_wrapper(
1711 &mut summary.local_selector_names_under_media,
1712 &mut summary.local_selector_names_under_supports,
1713 &mut summary.local_selector_names_under_layer,
1714 owner,
1715 &wrapper,
1716 );
1717 }
1718 }
1719 ParsedCssModuleComposesEdgeKind::External => {
1720 summary.imported_class_name_count += count;
1721 for owner in &edge.owner_selector_names {
1722 summary.imported_selector_names.push(owner.clone());
1723 insert_vec_by_wrapper(
1724 &mut summary.imported_selector_names_under_media,
1725 &mut summary.imported_selector_names_under_supports,
1726 &mut summary.imported_selector_names_under_layer,
1727 owner,
1728 &wrapper,
1729 );
1730 if let Some(source) = &edge.import_source {
1731 summary.import_sources.push(source.clone());
1732 if wrapper.under_media {
1733 summary.import_sources_under_media.push(source.clone());
1734 }
1735 if wrapper.under_supports {
1736 summary.import_sources_under_supports.push(source.clone());
1737 }
1738 if wrapper.under_layer {
1739 summary.import_sources_under_layer.push(source.clone());
1740 }
1741 }
1742 }
1743 }
1744 ParsedCssModuleComposesEdgeKind::Global => {
1745 summary.global_class_name_count += count;
1746 for owner in &edge.owner_selector_names {
1747 summary.global_selector_names.push(owner.clone());
1748 insert_vec_by_wrapper(
1749 &mut summary.global_selector_names_under_media,
1750 &mut summary.global_selector_names_under_supports,
1751 &mut summary.global_selector_names_under_layer,
1752 owner,
1753 &wrapper,
1754 );
1755 }
1756 }
1757 }
1758 }
1759 sort_all_composes(&mut summary);
1760 summary.edges.sort();
1761 summary.edges.dedup();
1762 summary
1763}
1764
1765fn composes_class_tokens_for_edge(
1766 source: &str,
1767 line_index: &SourceLineIndex,
1768 facts: &ParsedStyleFacts,
1769 edge: &crate::ParsedCssModuleComposesEdgeFact,
1770) -> Vec<ParserIndexComposesClassTokenV0> {
1771 let target_names = edge
1772 .target_names
1773 .iter()
1774 .map(String::as_str)
1775 .collect::<BTreeSet<_>>();
1776 let edge_start = range_start(edge.range);
1777 let edge_end = u32::from(edge.range.end()) as usize;
1778 let mut class_tokens = facts
1779 .css_module_composes
1780 .iter()
1781 .filter(|fact| fact.kind == ParsedCssModuleComposesFactKind::Target)
1782 .filter(|fact| target_names.contains(fact.name.as_str()))
1783 .filter(|fact| {
1784 let token_start = range_start(fact.range);
1785 let token_end = u32::from(fact.range.end()) as usize;
1786 token_start >= edge_start && token_end <= edge_end
1787 })
1788 .map(|fact| {
1789 let byte_span = byte_span_for_range(fact.range);
1790 ParserIndexComposesClassTokenV0 {
1791 class_name: fact.name.clone(),
1792 byte_span,
1793 range: parser_range_for_byte_span(source, line_index, byte_span),
1794 }
1795 })
1796 .collect::<Vec<_>>();
1797 class_tokens.sort();
1798 class_tokens.dedup();
1799 class_tokens
1800}
1801
1802fn summarize_wrappers(blocks: &[StyleBlock]) -> ParserIndexWrapperFactsV0 {
1803 ParserIndexWrapperFactsV0 {
1804 selectors_under_media_names: sorted(
1805 blocks
1806 .iter()
1807 .filter(|block| block.under_media)
1808 .flat_map(|block| {
1809 block
1810 .names
1811 .iter()
1812 .filter(|name| !name.starts_with("__selector_meta:"))
1813 .cloned()
1814 })
1815 .collect(),
1816 ),
1817 selectors_under_supports_names: sorted(
1818 blocks
1819 .iter()
1820 .filter(|block| block.under_supports)
1821 .flat_map(|block| {
1822 block
1823 .names
1824 .iter()
1825 .filter(|name| !name.starts_with("__selector_meta:"))
1826 .cloned()
1827 })
1828 .collect(),
1829 ),
1830 selectors_under_layer_names: sorted(
1831 blocks
1832 .iter()
1833 .filter(|block| block.under_layer)
1834 .flat_map(|block| {
1835 block
1836 .names
1837 .iter()
1838 .filter(|name| !name.starts_with("__selector_meta:"))
1839 .cloned()
1840 })
1841 .collect(),
1842 ),
1843 }
1844}
1845
1846fn resolve_selector_header_text(
1847 source: &str,
1848 header: &str,
1849 parent_branches: &[SelectorBranch],
1850) -> Vec<SelectorBranch> {
1851 split_selector_groups_text(header)
1852 .into_iter()
1853 .flat_map(|group| resolve_selector_group_text(source, header, group, parent_branches))
1854 .collect()
1855}
1856
1857fn resolve_selector_group_text(
1858 source: &str,
1859 full_header: &str,
1860 group: &str,
1861 parent_branches: &[SelectorBranch],
1862) -> Vec<SelectorBranch> {
1863 let group = group.trim();
1864 if group.starts_with(":global") && !group.starts_with(":local") {
1865 return Vec::new();
1866 }
1867 let tail = selector_tail(group);
1868 if let Some(suffix) = tail.strip_prefix('&').map(str::trim)
1869 && is_ampersand_suffix_text(suffix)
1870 {
1871 let span = source_span_for_header_piece(source, full_header, suffix);
1872 return parent_branches
1873 .iter()
1874 .map(|parent| SelectorBranch {
1875 name: format!("{}{}", parent.name, suffix),
1876 name_span: span,
1877 bare_suffix_base: parent.bare_suffix_base,
1878 amp_suffix_depth: parent.amp_suffix_depth + 1,
1879 })
1880 .collect();
1881 }
1882 let names = class_names_in_selector(tail, source, full_header);
1883 let bare_suffix_base = parent_branches.is_empty() && names.len() == 1;
1884 names
1885 .into_iter()
1886 .map(|(name, name_span)| SelectorBranch {
1887 name,
1888 name_span,
1889 bare_suffix_base,
1890 amp_suffix_depth: 0,
1891 })
1892 .collect()
1893}
1894
1895fn is_ampersand_suffix_text(suffix: &str) -> bool {
1896 suffix
1897 .chars()
1898 .next()
1899 .is_some_and(|ch| ch == '-' || ch == '_' || ch.is_ascii_alphanumeric())
1900}
1901
1902fn classify_nested_safety(
1903 header: &str,
1904 branches: &[SelectorBranch],
1905 parent_branches: &[SelectorBranch],
1906 parent_is_grouped: bool,
1907) -> &'static str {
1908 if branches.is_empty() {
1909 return "flat";
1910 }
1911 let is_nested = !parent_branches.is_empty() || header.contains('&');
1912 if !is_nested {
1913 return "flat";
1914 }
1915 let header = header.trim();
1916 let bem_suffix_safe = branches.len() == 1
1917 && parent_branches.len() == 1
1918 && parent_branches[0].bare_suffix_base
1919 && !parent_is_grouped
1920 && header.starts_with('&')
1921 && (header[1..].trim_start().starts_with("__")
1922 || header[1..].trim_start().starts_with("--"));
1923 let chained_bem_modifier_safe = header.starts_with('&')
1924 && header[1..].trim_start().starts_with("--")
1925 && !parent_branches.is_empty()
1926 && parent_branches
1927 .iter()
1928 .all(|parent| parent.amp_suffix_depth > 0);
1929 if bem_suffix_safe || chained_bem_modifier_safe {
1930 "bemSuffixSafe"
1931 } else {
1932 "nestedUnsafe"
1933 }
1934}
1935
1936fn nested_safety_for_selector(blocks: &[StyleBlock], name: &str) -> Option<&'static str> {
1937 blocks.iter().find_map(|block| {
1938 block.names.iter().find_map(|entry| {
1939 entry
1940 .strip_prefix("__selector_meta:")
1941 .and_then(|rest| rest.rsplit_once(':'))
1942 .and_then(|(entry_name, kind)| {
1943 (entry_name == name).then_some(match kind {
1944 "bemSuffixSafe" => "bemSuffixSafe",
1945 "nestedUnsafe" => "nestedUnsafe",
1946 _ => "flat",
1947 })
1948 })
1949 })
1950 })
1951}
1952
1953fn selector_rule_block<'a>(
1954 blocks: &'a [StyleBlock],
1955 name: &str,
1956 selector_offset: usize,
1957) -> Option<&'a StyleBlock> {
1958 blocks
1959 .iter()
1960 .filter(|block| block.start <= selector_offset && selector_offset < block.end)
1961 .filter(|block| {
1962 block.names.iter().any(|entry| {
1963 entry
1964 .strip_prefix("__selector_meta:")
1965 .and_then(|rest| rest.rsplit_once(':'))
1966 .is_some_and(|(entry_name, _)| entry_name == name)
1967 })
1968 })
1969 .max_by_key(|block| block.rule_start)
1970}
1971
1972fn style_block_for_offset(blocks: &[StyleBlock], offset: usize) -> Option<&StyleBlock> {
1973 blocks
1974 .iter()
1975 .filter(|block| !block.names.is_empty())
1976 .filter(|block| block.start <= offset && offset < block.end)
1977 .max_by_key(|block| block.rule_start)
1978}
1979
1980fn split_selector_groups_text(header: &str) -> Vec<&str> {
1981 let mut groups = Vec::new();
1982 let mut start = 0usize;
1983 let mut paren_depth = 0usize;
1984 let mut bracket_depth = 0usize;
1985 for (index, byte) in header.bytes().enumerate() {
1986 match byte {
1987 b'(' => paren_depth += 1,
1988 b')' => paren_depth = paren_depth.saturating_sub(1),
1989 b'[' => bracket_depth += 1,
1990 b']' => bracket_depth = bracket_depth.saturating_sub(1),
1991 b',' if paren_depth == 0 && bracket_depth == 0 => {
1992 groups.push(&header[start..index]);
1993 start = index + 1;
1994 }
1995 _ => {}
1996 }
1997 }
1998 groups.push(&header[start..]);
1999 groups
2000}
2001
2002fn selector_tail(group: &str) -> &str {
2003 let mut tail_start = 0usize;
2004 let mut paren_depth = 0usize;
2005 let mut bracket_depth = 0usize;
2006 let bytes = group.as_bytes();
2007 let mut index = 0usize;
2008 while index < bytes.len() {
2009 match bytes[index] {
2010 b'(' => paren_depth += 1,
2011 b')' => paren_depth = paren_depth.saturating_sub(1),
2012 b'[' => bracket_depth += 1,
2013 b']' => bracket_depth = bracket_depth.saturating_sub(1),
2014 b'>' | b'+' | b'~' if paren_depth == 0 && bracket_depth == 0 => tail_start = index + 1,
2015 byte if byte.is_ascii_whitespace() && paren_depth == 0 && bracket_depth == 0 => {
2016 let previous = group[..index].trim_end().as_bytes().last().copied();
2017 let next = group[index + 1..].trim_start().as_bytes().first().copied();
2018 if previous.is_some()
2019 && next.is_some_and(|value| value == b'.' || value == b':' || value == b'&')
2020 {
2021 tail_start = index + 1;
2022 }
2023 }
2024 _ => {}
2025 }
2026 index += 1;
2027 }
2028 group[tail_start..].trim()
2029}
2030
2031fn class_names_in_selector(
2032 selector: &str,
2033 source: &str,
2034 full_header: &str,
2035) -> Vec<(String, ParserByteSpanV0)> {
2036 let mut names = Vec::new();
2037 let mut index = 0usize;
2038 let mut paren_depth = 0usize;
2039 let mut bracket_depth = 0usize;
2040 let bytes = selector.as_bytes();
2041 while index < bytes.len() {
2042 match bytes[index] {
2043 b'(' => paren_depth += 1,
2044 b')' => paren_depth = paren_depth.saturating_sub(1),
2045 b'[' => bracket_depth += 1,
2046 b']' => bracket_depth = bracket_depth.saturating_sub(1),
2047 b'.' if paren_depth == 0 && bracket_depth == 0 => {
2048 let start = index + 1;
2049 let mut end = start;
2050 while end < bytes.len()
2051 && (bytes[end].is_ascii_alphanumeric() || matches!(bytes[end], b'_' | b'-'))
2052 {
2053 end += 1;
2054 }
2055 if end > start {
2056 let name = selector[start..end].to_string();
2057 names.push((
2058 name.clone(),
2059 source_span_for_header_piece(source, full_header, &name),
2060 ));
2061 }
2062 index = end;
2063 continue;
2064 }
2065 _ => {}
2066 }
2067 index += 1;
2068 }
2069 names
2070}
2071
2072fn selector_names_for_offset(blocks: &[StyleBlock], offset: usize) -> Vec<String> {
2073 let Some(max_start) = blocks
2074 .iter()
2075 .filter(|block| block.start <= offset && offset < block.end && !block.names.is_empty())
2076 .map(|block| block.start)
2077 .max()
2078 else {
2079 return Vec::new();
2080 };
2081 blocks
2082 .iter()
2083 .filter(|block| block.start == max_start && block.start <= offset && offset < block.end)
2084 .flat_map(|block| {
2085 block
2086 .names
2087 .iter()
2088 .filter(|name| !name.starts_with("__selector_meta:"))
2089 .cloned()
2090 })
2091 .collect::<BTreeSet<_>>()
2092 .into_iter()
2093 .collect()
2094}
2095
2096fn selector_contexts_for_offset(blocks: &[StyleBlock], offset: usize) -> Vec<String> {
2097 let Some(max_start) = blocks
2098 .iter()
2099 .filter(|block| block.start <= offset && offset < block.end)
2100 .map(|block| block.start)
2101 .max()
2102 else {
2103 return Vec::new();
2104 };
2105 let mut contexts = BTreeSet::new();
2106 for block in blocks
2107 .iter()
2108 .filter(|block| block.start == max_start && block.start <= offset && offset < block.end)
2109 {
2110 if block.names.is_empty() {
2111 if let Some(context) = &block.context_text {
2112 contexts.insert(context.clone());
2113 }
2114 } else {
2115 for name in &block.names {
2116 if !name.starts_with("__selector_meta:") {
2117 contexts.insert(format!(".{name}"));
2118 }
2119 }
2120 }
2121 }
2122 contexts.into_iter().collect()
2123}
2124
2125fn wrapper_for_offset(blocks: &[StyleBlock], offset: usize) -> WrapperContext {
2126 blocks
2127 .iter()
2128 .filter(|block| block.start <= offset && offset < block.end)
2129 .max_by_key(|block| block.start)
2130 .map(|block| WrapperContext {
2131 under_media: block.under_media,
2132 under_supports: block.under_supports,
2133 under_layer: block.under_layer,
2134 wrapper_at_rules: block.wrapper_at_rules.clone(),
2135 })
2136 .unwrap_or_default()
2137}
2138
2139fn insert_by_wrapper(
2140 media: &mut BTreeSet<String>,
2141 supports: &mut BTreeSet<String>,
2142 layer: &mut BTreeSet<String>,
2143 value: &str,
2144 wrapper: &WrapperContext,
2145) {
2146 if wrapper.under_media {
2147 media.insert(value.to_string());
2148 }
2149 if wrapper.under_supports {
2150 supports.insert(value.to_string());
2151 }
2152 if wrapper.under_layer {
2153 layer.insert(value.to_string());
2154 }
2155}
2156
2157fn insert_vec_by_wrapper(
2158 media: &mut Vec<String>,
2159 supports: &mut Vec<String>,
2160 layer: &mut Vec<String>,
2161 value: &str,
2162 wrapper: &WrapperContext,
2163) {
2164 if wrapper.under_media {
2165 media.push(value.to_string());
2166 }
2167 if wrapper.under_supports {
2168 supports.push(value.to_string());
2169 }
2170 if wrapper.under_layer {
2171 layer.push(value.to_string());
2172 }
2173}
2174
2175fn selector_names_from_contexts(contexts: &[String]) -> Vec<String> {
2176 contexts
2177 .iter()
2178 .filter_map(|context| context.strip_prefix('.').map(ToString::to_string))
2179 .collect()
2180}
2181
2182fn selector_names_for_variable_symbols(
2183 blocks: &[StyleBlock],
2184 facts: &ParsedStyleFacts,
2185 global_variable_decl_names: &BTreeSet<String>,
2186 variable_parameter_names: &BTreeSet<String>,
2187 variable_decl_scopes: &[SassVariableDeclScope],
2188 resolved_filter: Option<bool>,
2189) -> Vec<String> {
2190 facts
2191 .sass_symbols
2192 .iter()
2193 .filter(|symbol| symbol.kind == ParsedSassSymbolFactKind::VariableReference)
2194 .filter(|symbol| {
2195 let Some(expected_resolved) = resolved_filter else {
2196 return true;
2197 };
2198 if symbol.namespace.is_some() {
2199 return false;
2200 }
2201 is_sass_variable_reference_resolved(
2202 &symbol.name,
2203 range_start(symbol.range),
2204 blocks,
2205 global_variable_decl_names,
2206 variable_parameter_names,
2207 variable_decl_scopes,
2208 ) == expected_resolved
2209 })
2210 .flat_map(|symbol| selector_names_for_offset(blocks, range_start(symbol.range)))
2211 .collect::<BTreeSet<_>>()
2212 .into_iter()
2213 .collect()
2214}
2215
2216fn is_sass_variable_reference_resolved(
2217 name: &str,
2218 offset: usize,
2219 blocks: &[StyleBlock],
2220 global_variable_decl_names: &BTreeSet<String>,
2221 variable_parameter_names: &BTreeSet<String>,
2222 variable_decl_scopes: &[SassVariableDeclScope],
2223) -> bool {
2224 if global_variable_decl_names.contains(name) || variable_parameter_names.contains(name) {
2225 return true;
2226 }
2227 let reference_selectors = selector_names_for_offset(blocks, offset);
2228 !reference_selectors.is_empty()
2229 && variable_decl_scopes.iter().any(|scope| {
2230 scope.name == name
2231 && !scope.selector_names.is_empty()
2232 && scope
2233 .selector_names
2234 .iter()
2235 .any(|selector| reference_selectors.contains(selector))
2236 })
2237}
2238
2239fn selector_names_for_symbols(
2240 blocks: &[StyleBlock],
2241 facts: &ParsedStyleFacts,
2242 kind: ParsedSassSymbolFactKind,
2243 names_filter: Option<&[String]>,
2244) -> Vec<String> {
2245 facts
2246 .sass_symbols
2247 .iter()
2248 .filter(|symbol| symbol.kind == kind)
2249 .filter(|symbol| {
2250 names_filter
2251 .map(|names| names.contains(&symbol.name))
2252 .unwrap_or(true)
2253 })
2254 .flat_map(|symbol| selector_names_for_offset(blocks, range_start(symbol.range)))
2255 .collect::<BTreeSet<_>>()
2256 .into_iter()
2257 .collect()
2258}
2259
2260fn sass_module_forward_prefix_from_statement(source: &str, span: ParserByteSpanV0) -> String {
2261 let Some(statement) = source.get(span.start..span.end) else {
2262 return String::new();
2263 };
2264 let Some(as_index) = css_keyword(statement).find(" as ") else {
2265 return String::new();
2266 };
2267 let after_as = &statement[as_index + 4..];
2268 let Some(star_index) = after_as.find('*') else {
2269 return String::new();
2270 };
2271 after_as[..star_index].trim().to_string()
2272}
2273
2274fn sass_module_forward_member_symbol_kind(
2275 source: &str,
2276 span: ParserByteSpanV0,
2277 name: &str,
2278) -> Option<&'static str> {
2279 let statement = source.get(span.start..span.end)?;
2280 statement
2281 .contains(&format!("${name}"))
2282 .then_some("variable")
2283}
2284
2285fn sort_all_composes(summary: &mut ParserIndexComposesFactsV0) {
2286 sort_unique(&mut summary.selectors_with_composes_names);
2287 sort_unique(&mut summary.selectors_with_composes_under_media_names);
2288 sort_unique(&mut summary.selectors_with_composes_under_supports_names);
2289 sort_unique(&mut summary.selectors_with_composes_under_layer_names);
2290 sort_unique(&mut summary.local_selector_names);
2291 sort_unique(&mut summary.imported_selector_names);
2292 sort_unique(&mut summary.global_selector_names);
2293 sort_unique(&mut summary.local_selector_names_under_media);
2294 sort_unique(&mut summary.local_selector_names_under_supports);
2295 sort_unique(&mut summary.local_selector_names_under_layer);
2296 sort_unique(&mut summary.imported_selector_names_under_media);
2297 sort_unique(&mut summary.imported_selector_names_under_supports);
2298 sort_unique(&mut summary.imported_selector_names_under_layer);
2299 sort_unique(&mut summary.global_selector_names_under_media);
2300 sort_unique(&mut summary.global_selector_names_under_supports);
2301 sort_unique(&mut summary.global_selector_names_under_layer);
2302 summary.import_sources.sort();
2303 summary.import_sources_under_media.sort();
2304 summary.import_sources_under_supports.sort();
2305 summary.import_sources_under_layer.sort();
2306}
2307
2308fn sort_unique(values: &mut Vec<String>) {
2309 values.sort();
2310 values.dedup();
2311}
2312
2313fn source_span_for_header_piece(source: &str, full_header: &str, piece: &str) -> ParserByteSpanV0 {
2314 if let Some(header_offset) = source.find(full_header)
2315 && let Some(piece_offset) = full_header.find(piece)
2316 {
2317 let start = header_offset + piece_offset;
2318 return ParserByteSpanV0 {
2319 start,
2320 end: start + piece.len(),
2321 };
2322 }
2323 ParserByteSpanV0 {
2324 start: 0,
2325 end: piece.len(),
2326 }
2327}
2328
2329fn byte_span_for_range(range: TextRange) -> ParserByteSpanV0 {
2330 ParserByteSpanV0 {
2331 start: range_start(range),
2332 end: u32::from(range.end()) as usize,
2333 }
2334}
2335
2336fn range_start(range: TextRange) -> usize {
2337 u32::from(range.start()) as usize
2338}
2339
2340struct SourceLineIndex {
2341 line_starts: Vec<usize>,
2342}
2343
2344impl SourceLineIndex {
2345 fn new(source: &str) -> Self {
2346 let mut line_starts = vec![0];
2347 for (index, byte) in source.as_bytes().iter().enumerate() {
2348 if *byte == b'\n' {
2349 line_starts.push(index + 1);
2350 }
2351 }
2352 Self { line_starts }
2353 }
2354
2355 fn position_for_byte_offset(&self, source: &str, byte_offset: usize) -> ParserPositionV0 {
2356 let offset = byte_offset.min(source.len());
2357 let line = self.line_starts.partition_point(|start| *start <= offset);
2358 let line_index = line.saturating_sub(1);
2359 let line_start = self.line_starts.get(line_index).copied().unwrap_or(0);
2360 ParserPositionV0 {
2361 line: line_index,
2362 character: source
2363 .get(line_start..offset)
2364 .map(|text| text.encode_utf16().count())
2365 .unwrap_or_else(|| offset.saturating_sub(line_start)),
2366 }
2367 }
2368}
2369
2370fn parser_range_for_byte_span(
2371 source: &str,
2372 line_index: &SourceLineIndex,
2373 span: ParserByteSpanV0,
2374) -> ParserRangeV0 {
2375 ParserRangeV0 {
2376 start: line_index.position_for_byte_offset(source, span.start),
2377 end: line_index.position_for_byte_offset(source, span.end),
2378 }
2379}
2380
2381fn bem_suffix_parent_name(name: &str) -> Option<String> {
2382 let marker = [name.rfind("__"), name.rfind("--")]
2383 .into_iter()
2384 .flatten()
2385 .max()?;
2386 (marker > 0).then(|| name[..marker].to_string())
2387}
2388
2389fn sorted(values: BTreeSet<String>) -> Vec<String> {
2390 values.into_iter().collect()
2391}
2392
2393trait SortVec {
2394 fn tap_sort(self) -> Self;
2395 fn tap_sort_unique(self) -> Self;
2396}
2397
2398impl SortVec for Vec<String> {
2399 fn tap_sort(mut self) -> Self {
2400 self.sort();
2401 self
2402 }
2403
2404 fn tap_sort_unique(mut self) -> Self {
2405 self.sort();
2406 self.dedup();
2407 self
2408 }
2409}
2410
2411pub fn dialect_for_path(file_path: &str) -> StyleDialect {
2412 if file_path.ends_with(".sass") || file_path.ends_with(".module.sass") {
2413 StyleDialect::Sass
2414 } else if file_path.ends_with(".scss") || file_path.ends_with(".module.scss") {
2415 StyleDialect::Scss
2416 } else if file_path.ends_with(".less") || file_path.ends_with(".module.less") {
2417 StyleDialect::Less
2418 } else {
2419 StyleDialect::Css
2420 }
2421}
2422
2423fn dialect_label(dialect: StyleDialect) -> &'static str {
2424 match dialect {
2425 StyleDialect::Css => "css",
2426 StyleDialect::Scss => "scss",
2427 StyleDialect::Sass => "sass",
2428 StyleDialect::Less => "less",
2429 }
2430}