Skip to main content

omena_parser/facts/
at_rules.rs

1//! Parser facts for at-rule headers.
2//!
3//! The fact layer records the at-rule kind and source span used by summaries,
4//! diagnostics, and transform planning.
5
6use cstree::text::TextRange;
7use omena_syntax::{StyleDialect, SyntaxKind};
8
9use crate::{ParseResult, at_rule_spec, scss_at_rule_spec};
10
11#[derive(Debug, Clone, PartialEq, Eq)]
12pub struct ParsedAtRuleFact {
13    pub name: String,
14    pub node_kind: Option<SyntaxKind>,
15    pub range: TextRange,
16}
17
18pub(crate) fn collect_at_rule_facts_from_cst(
19    text: &str,
20    parsed: &ParseResult,
21) -> Vec<ParsedAtRuleFact> {
22    parsed
23        .syntax_token_views()
24        .iter()
25        .filter(|token| token.kind == SyntaxKind::AtKeyword)
26        .map(|token| at_rule_fact_from_cst_token(text, token.range, parsed.dialect()))
27        .collect()
28}
29
30fn at_rule_fact_from_cst_token(
31    text: &str,
32    range: TextRange,
33    dialect: StyleDialect,
34) -> ParsedAtRuleFact {
35    let start = u32::from(range.start()) as usize;
36    let end = u32::from(range.end()) as usize;
37    let source_text = text.get(start..end).unwrap_or_default();
38    let css_spec = at_rule_spec(source_text);
39    let node_kind = css_spec
40        .or_else(|| match dialect {
41            StyleDialect::Scss | StyleDialect::Sass => scss_at_rule_spec(source_text),
42            StyleDialect::Css | StyleDialect::Less => None,
43        })
44        .map(|spec| spec.node_kind);
45    let name = if css_spec.is_some() {
46        source_text.to_ascii_lowercase()
47    } else {
48        source_text.to_string()
49    };
50    ParsedAtRuleFact {
51        name,
52        node_kind,
53        range,
54    }
55}