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