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