use std::collections::HashSet;
use cairo_lang_defs::ids::{GenericTypeId, LanguageElementId};
use cairo_lang_semantic::diagnostic::{NotFoundItemType, SemanticDiagnostics};
use cairo_lang_semantic::items::structure::StructSemantic;
use cairo_lang_semantic::resolve::ResolutionContext;
use cairo_lang_semantic::resolve::ResolvedGenericItem;
use cairo_lang_syntax::node::{
Token, TypedSyntaxNode,
ast::{PatternEnum, PatternIdentifier, PatternStruct, PatternStructParam},
};
use itertools::Itertools;
use lsp_types::{CompletionItem, CompletionItemKind, CompletionItemLabelDetails};
use crate::ide::completion::helpers::formatting::format_type_in_node_context;
use crate::ide::completion::{CompletionItemOrderable, CompletionRelevance};
use crate::lang::db::AnalysisDatabase;
use crate::lang::text_matching::text_matches;
use crate::lang::visibility::peek_visible_in_with_edition;
use crate::{
ide::completion::path::path_prefix_completions, lang::analysis_context::AnalysisContext,
};
pub fn struct_pattern_completions<'db>(
db: &'db AnalysisDatabase,
ctx: &AnalysisContext<'db>,
) -> Vec<CompletionItemOrderable> {
let (all_members, existing_members, typed, struct_parent_module) = if let Some(pattern) =
ctx.node.ancestor_of_type::<PatternStruct>(db)
&& let typed = ctx.node.ancestor_of_type::<PatternIdentifier>(db).filter(|ident| {
ident.as_syntax_node().parent(db).and_then(|p| p.parent(db))
== Some(pattern.as_syntax_node())
})
&& let Ok(ResolvedGenericItem::GenericType(GenericTypeId::Struct(struct_item))) =
ctx.resolver(db).resolve_generic_path(
&mut SemanticDiagnostics::new(ctx.module_id),
&pattern.path(db),
NotFoundItemType::Type,
ResolutionContext::Default,
)
&& let Ok(all_members) = db.struct_members(struct_item)
{
(all_members, pattern.params(db).elements(db), typed, struct_item.parent_module(db))
} else {
return Default::default();
};
if !all_members.values().all(|member| {
peek_visible_in_with_edition(db, member.visibility, struct_parent_module, ctx.module_id)
}) {
return Default::default();
}
let existing_members: HashSet<_> = existing_members
.into_iter()
.filter_map(|member| match member {
PatternStructParam::Single(ident) => Some(ident.name(db).token(db).text(db)),
PatternStructParam::WithExpr(params) => Some(params.name(db).token(db).text(db)),
PatternStructParam::Tail(_) => None,
})
.collect();
let typed_string = typed
.clone()
.map(|ident| ident.name(db).token(db).text(db).to_string(db))
.unwrap_or_default();
all_members
.iter()
.filter(|(member_name, _)| !existing_members.contains(member_name))
.filter(|(member_name, _)| text_matches(member_name.to_string(db), &typed_string))
.map(|(member_name, member)| CompletionItemOrderable {
item: CompletionItem {
label: member_name.to_string(db),
label_details: Some(CompletionItemLabelDetails {
description: Some(format_type_in_node_context(
db,
typed
.clone()
.map(|typed_pattern| typed_pattern.as_syntax_node())
.unwrap_or(ctx.node),
&member.ty,
)),
detail: None,
}),
kind: Some(CompletionItemKind::VARIABLE),
..CompletionItem::default()
},
relevance: CompletionRelevance::Highest,
})
.collect()
}
pub fn enum_pattern_completions<'db>(
db: &'db AnalysisDatabase,
ctx: &AnalysisContext<'db>,
) -> Vec<CompletionItemOrderable> {
if let Some(pattern) = ctx.node.ancestor_of_type::<PatternEnum>(db)
&& let path = pattern.path(db)
&& let mut segments = path.segments(db).elements(db).collect_vec()
&& let _ = {
if path.segments(db).has_tail(db) {
segments.pop();
}
}
&& let Some(result) = path_prefix_completions(db, ctx, segments)
{
result
} else {
Default::default()
}
}