cairo-language-server 2.18.0

The Cairo Language Server
Documentation
use cairo_lang_defs::ids::ModuleId;
use cairo_lang_filesystem::db::FilesGroup;
use cairo_lang_filesystem::ids::{FileId, FileKind, FileLongId, VirtualFile};
use cairo_lang_filesystem::ids::{SmolStrId, SpanInFile};
use cairo_lang_parser::db::ParserGroup;
use cairo_lang_syntax::node::kind::SyntaxKind;
use cairo_lang_syntax::node::{TypedSyntaxNode, ast::ExprInlineMacro};
use cairo_lang_utils::Intern;
use compiler::expand_single_inline_macro_no_context;
use indoc::indoc;

use crate::ide::macros::expand::format::format_output;
use crate::lang::db::AnalysisDatabase;

mod compiler;

const HEADER: &str = indoc!(
    "
        // ==================================================================
        // This expansion was done out of context.
        // It may be inaccurate — consider expanding the surrounding function
        // for more reliable results.
        // ==================================================================
    "
);

pub fn expand_inline_macro_no_context<'db>(
    db: &'db AnalysisDatabase,
    syntax: ExprInlineMacro<'db>,
    module_id: ModuleId<'db>,
) -> Option<String> {
    let content = expand_single_inline_macro_no_context(db, &syntax, module_id)?;

    let file = FileLongId::Virtual(VirtualFile {
        parent: None,
        name: SmolStrId::from(db, ""),
        content: SmolStrId::from(db, content.as_str()),
        code_mappings: Default::default(),
        kind: FileKind::Expr,
        original_item_removed: true,
    })
    .intern(db);

    let expansion = expand_inline_macro_recursively_no_context(db, file, module_id)?;

    Some(format!("{HEADER}\n\n{}", format_output(&expansion, SyntaxKind::ExprInlineMacro)))
}

pub fn expand_inline_macro_recursively_no_context<'db>(
    db: &'db AnalysisDatabase,
    file: FileId<'db>,
    module_id: ModuleId<'db>,
) -> Option<String> {
    let inline_macros = db
        .file_syntax(file)
        .ok()?
        .descendants(db)
        .filter_map(|node| ExprInlineMacro::cast(db, node))
        .collect::<Vec<_>>()
        .into_iter()
        .rev();

    let mut content = db.file_content(file)?.to_string();

    for inline_macro in inline_macros {
        let macro_content = expand_single_inline_macro_no_context(db, &inline_macro, module_id)?;
        let span = inline_macro.as_syntax_node().span_without_trivia(db);

        let file = FileLongId::Virtual(VirtualFile {
            parent: Some(SpanInFile { file_id: file, span }),
            name: SmolStrId::from(db, ""),
            content: SmolStrId::from(db, macro_content.as_str()),
            code_mappings: Default::default(),
            kind: FileKind::Expr,
            original_item_removed: true,
        })
        .intern(db);

        let expansion = expand_inline_macro_recursively_no_context(db, file, module_id)?;

        content.replace_range(span.to_str_range(), &expansion);
    }

    Some(content)
}