use std::collections::HashMap;
use cairo_lang_semantic::lsp_helpers::LspHelpers;
use cairo_lang_syntax::node::ast::ExprPath;
use cairo_lang_syntax::node::helpers::GetIdentifier;
use cairo_language_common::CommonGroup;
use lsp_types::{CodeAction, CodeActionKind, Url, WorkspaceEdit};
use crate::lang::analysis_context::AnalysisContext;
use crate::lang::db::AnalysisDatabase;
use crate::lang::importer::new_import_edit;
pub fn missing_import<'db>(
db: &'db AnalysisDatabase,
ctx: &AnalysisContext<'db>,
uri: Url,
) -> Option<Vec<CodeAction>> {
let resultants = db.get_node_resultants(ctx.node)?;
resultants.iter().chain(std::iter::once(&ctx.node)).find_map(|node| {
let typed_path_generic = node.ancestor_of_type::<ExprPath>(db)?;
let typed_path_segments: Vec<_> = typed_path_generic
.segments(db)
.elements(db)
.map(|e| e.identifier(db).to_string(db))
.rev()
.collect();
let items = db.visible_importables_from_module(ctx.module_id)?;
let items: Vec<_> = items
.iter()
.filter_map(|(_item, proposed_path)| {
let mut proposed_path_segments: Vec<_> = proposed_path.split("::").collect();
if proposed_path_segments.len() == 1 {
return None;
}
let mut last_path_segment = None;
for typed_path_segment in &typed_path_segments {
last_path_segment = proposed_path_segments.pop();
if typed_path_segment != last_path_segment? {
return None;
}
}
proposed_path_segments.extend(last_path_segment);
Some(proposed_path_segments.join("::"))
})
.collect();
let is_preferred = is_preferred(&items);
Some(
items
.into_iter()
.map(|path| CodeAction {
title: format!("Import `{path}`"),
kind: Some(CodeActionKind::QUICKFIX),
is_preferred,
edit: Some(WorkspaceEdit {
changes: new_import_edit(db, ctx, path)
.map(|edit| HashMap::from_iter([(uri.clone(), vec![edit])])),
..Default::default()
}),
..Default::default()
})
.collect(),
)
})
}
pub fn is_preferred<T>(items: &[T]) -> Option<bool> {
let is_unambiguous = match items.len() {
0 => return None,
1 => true,
_ => false,
};
is_unambiguous.then_some(true)
}