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::{at_rule_spec, scss_at_rule_spec};
10
11use super::StyleFactSink;
12
13#[derive(Debug, Clone, PartialEq, Eq)]
14pub struct ParsedAtRuleFact {
15    pub name: String,
16    pub node_kind: Option<SyntaxKind>,
17    pub range: TextRange,
18}
19
20pub(crate) fn collect_at_rule_facts_from_sink(sink: &StyleFactSink<'_>) -> Vec<ParsedAtRuleFact> {
21    sink.tokens()
22        .iter()
23        .filter(|token| token.kind == SyntaxKind::AtKeyword)
24        .map(|token| at_rule_fact_from_cst_token(sink.text(), token.range, sink.dialect()))
25        .collect()
26}
27
28fn at_rule_fact_from_cst_token(
29    text: &str,
30    range: TextRange,
31    dialect: StyleDialect,
32) -> ParsedAtRuleFact {
33    let start = u32::from(range.start()) as usize;
34    let end = u32::from(range.end()) as usize;
35    let source_text = text.get(start..end).unwrap_or_default();
36    let css_spec = at_rule_spec(source_text);
37    let node_kind = css_spec
38        .or_else(|| match dialect {
39            StyleDialect::Scss | StyleDialect::Sass => scss_at_rule_spec(source_text),
40            StyleDialect::Css | StyleDialect::Less => None,
41        })
42        .map(|spec| spec.node_kind);
43    let name = if css_spec.is_some() {
44        source_text.to_ascii_lowercase()
45    } else {
46        source_text.to_string()
47    };
48    ParsedAtRuleFact {
49        name,
50        node_kind,
51        range,
52    }
53}