1use std::collections::{BTreeMap, BTreeSet};
2
3use omena_parser::{
4 LexResult, ParseResult, ParsedAnimationFactKind, ParsedCssModuleComposesEdgeKind,
5 ParsedCssModuleComposesFactKind, ParsedCssModuleValueFactKind, ParsedIcssFactKind,
6 ParsedSassModuleEdgeFactKind, ParsedSassSymbolFactKind, ParsedSelectorFactKind,
7 ParsedStyleFacts, ParsedVariableFactKind, collect_icss_export_values_from_cst,
8 collect_style_facts, facts_from_cst, lex, parse, parse_with_reuse_cache,
9};
10
11use crate::*;
12
13pub(super) fn collect_omena_query_omena_parser_style_facts_raw(
14 style_source: &str,
15 dialect: OmenaParserStyleDialect,
16) -> ParsedStyleFacts {
17 #[cfg(test)]
18 style_facts_collect_probe::record();
19 collect_style_facts(style_source, dialect)
20}
21
22pub fn summarize_omena_query_sass_module_source_edges(
23 style_source: &str,
24 dialect: OmenaParserStyleDialect,
25) -> Vec<OmenaQuerySassModuleSourceEdgeV0> {
26 collect_omena_query_omena_parser_style_facts_raw(style_source, dialect)
27 .sass_module_edges
28 .into_iter()
29 .map(|edge| OmenaQuerySassModuleSourceEdgeV0 {
30 kind: omena_query_sass_module_edge_fact_kind_label(edge.kind),
31 source: edge.source,
32 byte_span: ParserByteSpanV0 {
33 start: u32::from(edge.range.start()) as usize,
34 end: u32::from(edge.range.end()) as usize,
35 },
36 namespace_kind: edge.namespace_kind,
37 namespace: edge.namespace,
38 forward_prefix: edge.forward_prefix,
39 visibility_filter_kind: edge.visibility_filter_kind,
40 visibility_filter_names: edge.visibility_filter_names,
41 media_qualified: edge.media_qualified,
42 })
43 .collect()
44}
45
46pub(super) fn collect_omena_query_style_facts_with_icss_values_raw(
47 style_source: &str,
48 dialect: OmenaParserStyleDialect,
49) -> (ParsedStyleFacts, BTreeMap<String, String>) {
50 #[cfg(test)]
51 style_facts_collect_probe::record();
52 let parsed = parse(style_source, dialect);
53 let values = collect_icss_export_values_from_cst(style_source, &parsed)
54 .into_iter()
55 .collect();
56 (facts_from_cst(style_source, &parsed), values)
57}
58
59pub(super) fn parse_omena_query_omena_parser_style_source(
60 style_source: &str,
61 dialect: OmenaParserStyleDialect,
62) -> ParseResult {
63 parse(style_source, dialect)
64}
65
66pub(super) fn lex_omena_query_omena_parser_style_source(
67 style_source: &str,
68 dialect: OmenaParserStyleDialect,
69) -> LexResult {
70 lex(style_source, dialect)
71}
72
73#[derive(Default)]
74pub struct OmenaQueryStyleFrameRefreshParseCacheV0 {
75 reuse_cache: omena_parser::ParseReuseCache,
76}
77
78pub struct OmenaQueryStyleFrameRefreshFactsV0 {
79 pub token_count: usize,
80 pub error_count: usize,
81 pub dependency_ids: Vec<String>,
82}
83
84pub fn summarize_omena_query_style_frame_refresh_facts_with_reuse(
85 style_source: &str,
86 dialect: OmenaParserStyleDialect,
87 cache: &mut OmenaQueryStyleFrameRefreshParseCacheV0,
88) -> OmenaQueryStyleFrameRefreshFactsV0 {
89 let parsed = parse_with_reuse_cache(style_source, dialect, &mut cache.reuse_cache);
90 let facts = facts_from_cst(style_source, &parsed);
91 OmenaQueryStyleFrameRefreshFactsV0 {
92 token_count: parsed.token_count(),
93 error_count: parsed.errors().len(),
94 dependency_ids: frame_refresh_dependency_ids(&facts),
95 }
96}
97
98fn frame_refresh_dependency_ids(facts: &ParsedStyleFacts) -> Vec<String> {
99 let mut dependency_ids = BTreeSet::new();
100 for edge in &facts.sass_module_edges {
101 dependency_ids.insert(edge.source.clone());
102 }
103 for edge in &facts.css_module_value_import_edges {
104 dependency_ids.insert(edge.import_source.clone());
105 }
106 for edge in &facts.css_module_composes_edges {
107 if let Some(import_source) = &edge.import_source {
108 dependency_ids.insert(import_source.clone());
109 }
110 }
111 for edge in &facts.icss_import_edges {
112 dependency_ids.insert(edge.import_source.clone());
113 }
114 dependency_ids.into_iter().collect()
115}
116
117pub fn summarize_omena_query_style_document(
118 style_path: &str,
119 style_source: &str,
120) -> Option<OmenaQueryStyleDocumentSummaryV0> {
121 let dialect = omena_parser_dialect_for_style_path(style_path);
122 let facts = collect_omena_query_omena_parser_style_facts_raw(style_source, dialect);
123 let mut selector_names = Vec::new();
124 let mut custom_property_decl_names = Vec::new();
125 let mut custom_property_ref_names = Vec::new();
126 let mut sass_module_use_sources = BTreeSet::new();
127 let mut sass_module_forward_sources = BTreeSet::new();
128
129 for selector in facts.selectors {
130 if selector.kind == ParsedSelectorFactKind::Class {
131 selector_names.push(selector.name);
132 }
133 }
134
135 for variable in facts.variables {
136 match variable.kind {
137 ParsedVariableFactKind::CustomPropertyDeclaration => {
138 custom_property_decl_names.push(variable.name);
139 }
140 ParsedVariableFactKind::CustomPropertyReference => {
141 custom_property_ref_names.push(variable.name);
142 }
143 _ => {}
144 }
145 }
146
147 for edge in facts.sass_module_edges {
148 match edge.kind {
149 ParsedSassModuleEdgeFactKind::Use => {
150 sass_module_use_sources.insert(edge.source);
151 }
152 ParsedSassModuleEdgeFactKind::Forward => {
153 sass_module_forward_sources.insert(edge.source);
154 }
155 ParsedSassModuleEdgeFactKind::Import => {
156 sass_module_use_sources.insert(edge.source);
157 }
158 }
159 }
160
161 Some(OmenaQueryStyleDocumentSummaryV0 {
162 schema_version: "0",
163 product: "omena-query.style-document-summary",
164 language: omena_parser_style_dialect_label(dialect),
165 selector_names,
166 custom_property_decl_names,
167 custom_property_ref_names,
168 sass_module_use_sources: sass_module_use_sources.into_iter().collect(),
169 sass_module_forward_sources: sass_module_forward_sources.into_iter().collect(),
170 diagnostic_count: facts.error_count,
171 })
172}
173
174pub fn summarize_omena_query_omena_parser_style_facts(
175 style_source: &str,
176 dialect: OmenaParserStyleDialect,
177) -> OmenaQueryOmenaParserStyleFactsV0 {
178 let facts = collect_omena_query_omena_parser_style_facts_raw(style_source, dialect);
179 summarize_omena_query_omena_parser_style_facts_from_facts(facts, dialect)
180}
181
182pub(super) fn summarize_omena_query_omena_parser_style_facts_from_facts(
183 facts: ParsedStyleFacts,
184 dialect: OmenaParserStyleDialect,
185) -> OmenaQueryOmenaParserStyleFactsV0 {
186 let sass_symbol_resolution =
187 summarize_omena_query_sass_symbol_resolution(facts.sass_symbols.as_slice());
188 let mut class_selector_names = Vec::new();
189 let mut id_selector_names = Vec::new();
190 let mut placeholder_selector_names = Vec::new();
191 let mut keyframe_names = Vec::new();
192 let mut animation_reference_names = Vec::new();
193 let mut css_module_value_definition_names = BTreeSet::new();
194 let mut css_module_value_reference_names = BTreeSet::new();
195 let mut css_module_value_import_sources = BTreeSet::new();
196 let mut css_module_composes_target_names = BTreeSet::new();
197 let mut css_module_composes_import_sources = BTreeSet::new();
198 let mut icss_export_names = BTreeSet::new();
199 let mut icss_import_local_names = BTreeSet::new();
200 let mut icss_import_remote_names = BTreeSet::new();
201 let mut icss_import_sources = BTreeSet::new();
202 let mut variable_names = BTreeSet::new();
203 let mut sass_symbol_declaration_names = BTreeSet::new();
204 let mut sass_symbol_reference_names = BTreeSet::new();
205 let mut sass_module_use_sources = BTreeSet::new();
206 let mut sass_module_forward_sources = BTreeSet::new();
207 let mut sass_module_import_sources = BTreeSet::new();
208 let mut custom_property_names = BTreeSet::new();
209 let mut custom_property_decl_names = BTreeSet::new();
210 let mut custom_property_ref_names = BTreeSet::new();
211
212 for selector in facts.selectors {
213 match selector.kind {
214 ParsedSelectorFactKind::Class => class_selector_names.push(selector.name),
215 ParsedSelectorFactKind::Id => id_selector_names.push(selector.name),
216 ParsedSelectorFactKind::Placeholder => placeholder_selector_names.push(selector.name),
217 }
218 }
219
220 for variable in facts.variables {
221 match variable.kind {
222 ParsedVariableFactKind::ScssDeclaration
223 | ParsedVariableFactKind::ScssReference
224 | ParsedVariableFactKind::LessDeclaration
225 | ParsedVariableFactKind::LessReference => {
226 variable_names.insert(variable.name);
227 }
228 ParsedVariableFactKind::CustomPropertyDeclaration
229 | ParsedVariableFactKind::CustomPropertyReference => {
230 custom_property_names.insert(variable.name.clone());
231 match variable.kind {
232 ParsedVariableFactKind::CustomPropertyDeclaration => {
233 custom_property_decl_names.insert(variable.name);
234 }
235 ParsedVariableFactKind::CustomPropertyReference => {
236 custom_property_ref_names.insert(variable.name);
237 }
238 _ => {}
239 }
240 }
241 }
242 }
243
244 for symbol in &facts.sass_symbols {
245 match symbol.role {
246 "declaration" => {
247 sass_symbol_declaration_names.insert(symbol.name.clone());
248 }
249 _ => {
250 sass_symbol_reference_names.insert(symbol.name.clone());
251 }
252 }
253 }
254
255 for edge in &facts.sass_module_edges {
256 match edge.kind {
257 ParsedSassModuleEdgeFactKind::Use => {
258 sass_module_use_sources.insert(edge.source.clone());
259 }
260 ParsedSassModuleEdgeFactKind::Forward => {
261 sass_module_forward_sources.insert(edge.source.clone());
262 }
263 ParsedSassModuleEdgeFactKind::Import => {
264 sass_module_import_sources.insert(edge.source.clone());
265 }
266 }
267 }
268
269 for animation in facts.animations {
270 match animation.kind {
271 ParsedAnimationFactKind::KeyframesDeclaration => keyframe_names.push(animation.name),
272 ParsedAnimationFactKind::AnimationNameReference => {
273 animation_reference_names.push(animation.name);
274 }
275 }
276 }
277
278 for value in facts.css_module_values {
279 match value.kind {
280 ParsedCssModuleValueFactKind::Definition => {
281 css_module_value_definition_names.insert(value.name);
282 }
283 ParsedCssModuleValueFactKind::Reference => {
284 css_module_value_reference_names.insert(value.name);
285 }
286 ParsedCssModuleValueFactKind::ImportSource => {
287 css_module_value_import_sources.insert(value.name);
288 }
289 }
290 }
291
292 for composes in facts.css_module_composes {
293 match composes.kind {
294 ParsedCssModuleComposesFactKind::Target => {
295 css_module_composes_target_names.insert(composes.name);
296 }
297 ParsedCssModuleComposesFactKind::ImportSource => {
298 css_module_composes_import_sources.insert(composes.name);
299 }
300 }
301 }
302
303 for icss in facts.icss {
304 match icss.kind {
305 ParsedIcssFactKind::ExportName => {
306 icss_export_names.insert(icss.name);
307 }
308 ParsedIcssFactKind::ImportLocalName => {
309 icss_import_local_names.insert(icss.name);
310 }
311 ParsedIcssFactKind::ImportRemoteName => {
312 icss_import_remote_names.insert(icss.name);
313 }
314 ParsedIcssFactKind::ImportSource => {
315 icss_import_sources.insert(icss.name);
316 }
317 }
318 }
319
320 OmenaQueryOmenaParserStyleFactsV0 {
321 schema_version: "0",
322 product: "omena-query.omena-parser-style-facts",
323 dialect: omena_parser_style_dialect_label(dialect),
324 class_selector_names,
325 id_selector_names,
326 placeholder_selector_names,
327 keyframe_names,
328 animation_reference_names,
329 css_module_value_definition_names: css_module_value_definition_names.into_iter().collect(),
330 css_module_value_reference_names: css_module_value_reference_names.into_iter().collect(),
331 css_module_value_import_sources: css_module_value_import_sources.into_iter().collect(),
332 css_module_value_import_edges: facts
333 .css_module_value_import_edges
334 .into_iter()
335 .map(|edge| OmenaQueryCssModuleValueImportEdgeFactV0 {
336 remote_name: edge.remote_name,
337 local_name: edge.local_name,
338 import_source: edge.import_source,
339 })
340 .collect(),
341 css_module_value_definition_edges: facts
342 .css_module_value_definition_edges
343 .into_iter()
344 .map(|edge| OmenaQueryCssModuleValueDefinitionEdgeFactV0 {
345 definition_name: edge.definition_name,
346 reference_names: edge.reference_names,
347 })
348 .collect(),
349 css_module_composes_target_names: css_module_composes_target_names.into_iter().collect(),
350 css_module_composes_import_sources: css_module_composes_import_sources
351 .into_iter()
352 .collect(),
353 css_module_composes_edges: facts
354 .css_module_composes_edges
355 .into_iter()
356 .map(|edge| OmenaQueryCssModuleComposesEdgeFactV0 {
357 kind: omena_query_css_module_composes_edge_kind_label(edge.kind),
358 owner_selector_names: edge.owner_selector_names,
359 target_names: edge.target_names,
360 import_source: edge.import_source,
361 })
362 .collect(),
363 icss_export_names: icss_export_names.into_iter().collect(),
364 icss_import_local_names: icss_import_local_names.into_iter().collect(),
365 icss_import_remote_names: icss_import_remote_names.into_iter().collect(),
366 icss_import_sources: icss_import_sources.into_iter().collect(),
367 icss_import_edges: facts
368 .icss_import_edges
369 .into_iter()
370 .map(|edge| OmenaQueryIcssImportEdgeFactV0 {
371 local_name: edge.local_name,
372 remote_name: edge.remote_name,
373 import_source: edge.import_source,
374 })
375 .collect(),
376 icss_export_edges: facts
377 .icss_export_edges
378 .into_iter()
379 .map(|edge| OmenaQueryIcssExportEdgeFactV0 {
380 export_name: edge.export_name,
381 reference_names: edge.reference_names,
382 })
383 .collect(),
384 variable_names: variable_names.into_iter().collect(),
385 sass_symbol_declaration_names: sass_symbol_declaration_names.into_iter().collect(),
386 sass_symbol_reference_names: sass_symbol_reference_names.into_iter().collect(),
387 sass_symbol_facts: facts
388 .sass_symbols
389 .into_iter()
390 .map(|symbol| OmenaQuerySassSymbolFactV0 {
391 kind: omena_query_sass_symbol_fact_kind_label(symbol.kind),
392 symbol_kind: symbol.symbol_kind,
393 name: symbol.name,
394 role: symbol.role,
395 namespace: symbol.namespace,
396 })
397 .collect(),
398 sass_symbol_resolution,
399 sass_module_use_sources: sass_module_use_sources.into_iter().collect(),
400 sass_module_forward_sources: sass_module_forward_sources.into_iter().collect(),
401 sass_module_import_sources: sass_module_import_sources.into_iter().collect(),
402 sass_module_edges: facts
403 .sass_module_edges
404 .into_iter()
405 .map(|edge| OmenaQuerySassModuleEdgeFactV0 {
406 kind: omena_query_sass_module_edge_fact_kind_label(edge.kind),
407 source: edge.source,
408 namespace_kind: edge.namespace_kind,
409 namespace: edge.namespace,
410 forward_prefix: edge.forward_prefix,
411 visibility_filter_kind: edge.visibility_filter_kind,
412 visibility_filter_names: edge.visibility_filter_names,
413 })
414 .collect(),
415 custom_property_names: custom_property_names.into_iter().collect(),
416 custom_property_decl_names: custom_property_decl_names.into_iter().collect(),
417 custom_property_ref_names: custom_property_ref_names.into_iter().collect(),
418 at_rule_names: facts
419 .at_rules
420 .into_iter()
421 .map(|at_rule| at_rule.name)
422 .collect(),
423 parser_error_count: facts.error_count,
424 }
425}
426
427pub fn summarize_omena_query_omena_parser_css_modules_intermediate(
428 style_source: &str,
429 dialect: OmenaParserStyleDialect,
430) -> omena_parser::ParserIndexSummaryV0 {
431 omena_parser::summarize_css_modules_intermediate(style_source, dialect)
432}
433
434pub fn summarize_omena_query_omena_parser_lex(
435 style_source: &str,
436 dialect: OmenaParserStyleDialect,
437) -> omena_parser::OmenaParserLexSummaryV0 {
438 omena_parser::summarize_omena_parser_lex(style_source, dialect)
439}
440
441pub(super) fn omena_parser_dialect_for_style_path(style_path: &str) -> OmenaParserStyleDialect {
442 if style_path.ends_with(".sass") {
443 OmenaParserStyleDialect::Sass
444 } else if style_path.ends_with(".scss") {
445 OmenaParserStyleDialect::Scss
446 } else if style_path.ends_with(".less") {
447 OmenaParserStyleDialect::Less
448 } else {
449 OmenaParserStyleDialect::Css
450 }
451}
452
453pub(super) fn omena_parser_style_dialect_label(dialect: OmenaParserStyleDialect) -> &'static str {
454 match dialect {
455 OmenaParserStyleDialect::Css => "css",
456 OmenaParserStyleDialect::Scss => "scss",
457 OmenaParserStyleDialect::Sass => "sass",
458 OmenaParserStyleDialect::Less => "less",
459 }
460}
461
462pub(super) fn omena_query_css_module_composes_edge_kind_label(
463 kind: ParsedCssModuleComposesEdgeKind,
464) -> &'static str {
465 match kind {
466 ParsedCssModuleComposesEdgeKind::Local => "local",
467 ParsedCssModuleComposesEdgeKind::Global => "global",
468 ParsedCssModuleComposesEdgeKind::External => "external",
469 }
470}
471
472pub(super) fn omena_query_sass_symbol_fact_kind_label(
473 kind: ParsedSassSymbolFactKind,
474) -> &'static str {
475 match kind {
476 ParsedSassSymbolFactKind::VariableDeclaration => "sassVariableDeclaration",
477 ParsedSassSymbolFactKind::VariableReference => "sassVariableReference",
478 ParsedSassSymbolFactKind::MixinDeclaration => "sassMixinDeclaration",
479 ParsedSassSymbolFactKind::MixinInclude => "sassMixinInclude",
480 ParsedSassSymbolFactKind::FunctionDeclaration => "sassFunctionDeclaration",
481 ParsedSassSymbolFactKind::FunctionCall => "sassFunctionCall",
482 }
483}
484
485pub(super) fn omena_query_sass_module_edge_fact_kind_label(
486 kind: ParsedSassModuleEdgeFactKind,
487) -> &'static str {
488 match kind {
489 ParsedSassModuleEdgeFactKind::Use => "sassUse",
490 ParsedSassModuleEdgeFactKind::Forward => "sassForward",
491 ParsedSassModuleEdgeFactKind::Import => "sassImport",
492 }
493}
494
495fn summarize_omena_query_sass_symbol_resolution(
496 symbols: &[omena_parser::ParsedSassSymbolFact],
497) -> OmenaQuerySassSymbolResolutionV0 {
498 let mut declaration_by_symbol: BTreeMap<
499 (&'static str, Option<String>, String),
500 (usize, &'static str),
501 > = BTreeMap::new();
502 let mut declaration_count = 0usize;
503 let mut reference_count = 0usize;
504 let mut edges = Vec::new();
505
506 for (source_order, symbol) in symbols.iter().enumerate() {
507 let kind = omena_query_sass_symbol_fact_kind_label(symbol.kind);
508 if omena_query_sass_symbol_fact_kind_is_declaration(symbol.kind) {
509 declaration_count += 1;
510 declaration_by_symbol.insert(
511 (
512 symbol.symbol_kind,
513 symbol.namespace.clone(),
514 symbol.name.clone(),
515 ),
516 (source_order, kind),
517 );
518 continue;
519 }
520 if !omena_query_sass_symbol_fact_kind_is_reference(symbol.kind) {
521 continue;
522 }
523
524 reference_count += 1;
525 let declaration = declaration_by_symbol.get(&(
526 symbol.symbol_kind,
527 symbol.namespace.clone(),
528 symbol.name.clone(),
529 ));
530 edges.push(OmenaQuerySassSymbolResolutionEdgeV0 {
531 symbol_kind: symbol.symbol_kind,
532 name: symbol.name.clone(),
533 namespace: symbol.namespace.clone(),
534 reference_kind: kind,
535 reference_role: symbol.role,
536 reference_source_order: source_order,
537 declaration_kind: declaration.map(|(_, declaration_kind)| *declaration_kind),
538 declaration_source_order: declaration.map(|(declaration_order, _)| *declaration_order),
539 status: if declaration.is_some() {
540 "resolved"
541 } else {
542 "unresolved"
543 },
544 });
545 }
546
547 let resolved_reference_count = edges
548 .iter()
549 .filter(|edge| edge.status == "resolved")
550 .count();
551 OmenaQuerySassSymbolResolutionV0 {
552 schema_version: "0",
553 product: "omena-query.sass-symbol-same-file-resolution",
554 resolution_scope: "same-file",
555 declaration_count,
556 reference_count,
557 resolved_reference_count,
558 unresolved_reference_count: reference_count.saturating_sub(resolved_reference_count),
559 edges,
560 capabilities: OmenaQuerySassSymbolResolutionCapabilitiesV0 {
561 same_file_lexical_resolution_ready: true,
562 declaration_before_reference_ready: true,
563 unresolved_reference_reporting_ready: true,
564 cross_file_module_resolution_ready: false,
565 },
566 }
567}
568
569pub(super) fn omena_query_sass_symbol_fact_kind_is_declaration(
570 kind: ParsedSassSymbolFactKind,
571) -> bool {
572 matches!(
573 kind,
574 ParsedSassSymbolFactKind::VariableDeclaration
575 | ParsedSassSymbolFactKind::MixinDeclaration
576 | ParsedSassSymbolFactKind::FunctionDeclaration
577 )
578}
579
580pub(super) fn omena_query_sass_symbol_fact_kind_is_reference(
581 kind: ParsedSassSymbolFactKind,
582) -> bool {
583 matches!(
584 kind,
585 ParsedSassSymbolFactKind::VariableReference
586 | ParsedSassSymbolFactKind::MixinInclude
587 | ParsedSassSymbolFactKind::FunctionCall
588 )
589}
590
591#[cfg(test)]
594pub(crate) mod style_facts_collect_probe {
595 use std::cell::Cell;
596
597 thread_local! {
598 static COLLECT_CALLS: Cell<usize> = const { Cell::new(0) };
599 }
600
601 pub(crate) fn reset() {
602 COLLECT_CALLS.with(|calls| calls.set(0));
603 }
604
605 pub(crate) fn count() -> usize {
606 COLLECT_CALLS.with(Cell::get)
607 }
608
609 pub(super) fn record() {
610 COLLECT_CALLS.with(|calls| calls.set(calls.get() + 1));
611 }
612}