use std::collections::HashMap;
use cairo_lang_semantic::items::function_with_body::{
FunctionWithBodySemantic, SemanticExprLookup,
};
use cairo_lang_semantic::lookup_item::LookupItemEx;
use cairo_lang_syntax::node::{TypedSyntaxNode, ast};
use cairo_language_common::CommonGroup;
use lsp_types::{CodeAction, CodeActionKind, TextEdit, Url, WorkspaceEdit};
use crate::lang::analysis_context::AnalysisContext;
use crate::lang::db::{AnalysisDatabase, LsSemanticGroup};
use crate::lang::lsp::ToLsp;
use crate::lang::members::find_members_for_type;
use crate::lang::text_matching::text_matches;
pub fn suggest_similar_member<'db>(
db: &'db AnalysisDatabase,
ctx: &AnalysisContext<'db>,
uri: Url,
) -> Option<Vec<CodeAction>> {
db.get_node_resultants(ctx.node)?.iter().find_map(|resultant_node| {
let expr_binary = resultant_node.ancestor_of_type::<ast::ExprBinary>(db)?;
let lhs_stable_ptr = expr_binary.lhs(db).stable_ptr(db);
let lookup_item = db.find_lookup_item(*resultant_node)?;
let resultants_function_with_body = lookup_item.function_with_body()?;
let expr_id = db.lookup_expr_by_ptr(resultants_function_with_body, lhs_stable_ptr).ok()?;
let ty = db.expr_semantic(resultants_function_with_body, expr_id).ty();
if ty.is_missing(db) {
return None;
}
let member_candidates = find_members_for_type(db, resultants_function_with_body, ty)?;
let typed_member_name = ctx.node.get_text_without_trivia(db);
let suggestions: Vec<String> = member_candidates
.into_iter()
.map(|name| name.to_string(db))
.filter(|name| text_matches(name, typed_member_name.to_string(db)))
.collect();
if suggestions.is_empty() {
return None;
}
let bad_member_span = ctx
.node
.span_without_trivia(db)
.position_in_file(db, ctx.node.stable_ptr(db).file_id(db))?;
let code_actions = suggestions
.into_iter()
.map(|member_name| {
let edits = vec![TextEdit {
range: bad_member_span.to_lsp(),
new_text: member_name.clone(),
}];
CodeAction {
title: format!("Use {member_name} instead"),
kind: Some(CodeActionKind::QUICKFIX),
edit: Some(WorkspaceEdit {
changes: Some(HashMap::from([(uri.clone(), edits)])),
document_changes: None,
change_annotations: None,
}),
..Default::default()
}
})
.collect();
Some(code_actions)
})
}