omena-scss-eval 0.3.0

Oracle-first SCSS and Less value evaluator rail for Omena CSS
Documentation
use omena_parser::LexedToken;
use omena_syntax::SyntaxKind;

use super::{
    StaticScssFunctionLocalScope, StaticScssFunctionLocalVariable,
    canonical_static_scss_variable_name, static_stylesheet_matching_token_index,
    static_stylesheet_skip_trivia_tokens, static_stylesheet_token_end,
    static_stylesheet_token_start, static_stylesheet_value_end_token_until,
    static_stylesheet_variable_name_is_safe,
};

pub(super) fn collect_static_scss_function_local_variables(
    source: &str,
    tokens: &[LexedToken],
    start: usize,
    end: usize,
) -> Option<Vec<StaticScssFunctionLocalVariable>> {
    let mut variables = Vec::new();
    let mut scope_stack = Vec::<StaticScssFunctionLocalScope>::new();
    let function_scope_start = tokens
        .get(start)
        .map(static_stylesheet_token_start)
        .or_else(|| tokens.get(end).map(static_stylesheet_token_start))?;
    let function_scope_end = tokens
        .get(end)
        .map(static_stylesheet_token_start)
        .unwrap_or(function_scope_start);
    let mut index = start;
    while index < end {
        while scope_stack
            .last()
            .is_some_and(|scope| index > scope.end_index)
        {
            scope_stack.pop();
        }
        match tokens[index].kind {
            SyntaxKind::LeftBrace | SyntaxKind::SassIndent => {
                let close_kind = if tokens[index].kind == SyntaxKind::SassIndent {
                    SyntaxKind::SassDedent
                } else {
                    SyntaxKind::RightBrace
                };
                let scope_end_index = static_stylesheet_matching_token_index(
                    tokens,
                    index,
                    tokens[index].kind,
                    close_kind,
                )?;
                scope_stack.push(StaticScssFunctionLocalScope {
                    end_index: scope_end_index,
                    span_start: static_stylesheet_token_end(&tokens[index]),
                    span_end: static_stylesheet_token_start(&tokens[scope_end_index]),
                });
                index += 1;
            }
            SyntaxKind::RightBrace | SyntaxKind::SassDedent => {
                if scope_stack
                    .last()
                    .is_some_and(|scope| scope.end_index == index)
                {
                    scope_stack.pop();
                }
                index += 1;
            }
            SyntaxKind::ScssVariable => {
                let colon_index = static_stylesheet_skip_trivia_tokens(tokens, index + 1);
                if tokens
                    .get(colon_index)
                    .is_none_or(|token| token.kind != SyntaxKind::Colon)
                {
                    index += 1;
                    continue;
                }
                let value_end_index =
                    static_stylesheet_value_end_token_until(tokens, colon_index + 1, end)?;
                let name = canonical_static_scss_variable_name(tokens[index].text.as_str());
                if !static_stylesheet_variable_name_is_safe(name.as_str()) {
                    return None;
                }
                let value_start = static_stylesheet_token_end(&tokens[colon_index]);
                let value_end = static_stylesheet_token_start(&tokens[value_end_index]);
                let value = source.get(value_start..value_end)?.trim();
                let (scope_start, scope_end) = scope_stack
                    .last()
                    .map(|scope| (scope.span_start, scope.span_end))
                    .unwrap_or((function_scope_start, function_scope_end));
                variables.push(StaticScssFunctionLocalVariable {
                    name,
                    value: value.to_string(),
                    span_start: static_stylesheet_token_start(&tokens[index]),
                    scope_start,
                    scope_end,
                });
                index = value_end_index + 1;
            }
            _ => {
                index += 1;
            }
        }
    }
    Some(variables)
}