Skip to main content

omena_query/style/
parser_facade.rs

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