Skip to content

Commit

Permalink
Introduced struct members completions
Browse files Browse the repository at this point in the history
  • Loading branch information
integraledelebesgue committed Oct 25, 2024
1 parent f98c2b0 commit 0081864
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ use cairo_lang_semantic::db::SemanticGroup;
use cairo_lang_semantic::diagnostic::{NotFoundItemType, SemanticDiagnostics};
use cairo_lang_semantic::expr::inference::InferenceId;
use cairo_lang_semantic::items::function_with_body::SemanticExprLookup;
use cairo_lang_semantic::items::structure::concrete_struct_members;
use cairo_lang_semantic::items::us::SemanticUseEx;
use cairo_lang_semantic::items::visibility::peek_visible_in;
use cairo_lang_semantic::lookup_item::{HasResolverData, LookupItemEx};
use cairo_lang_semantic::resolve::{ResolvedConcreteItem, ResolvedGenericItem, Resolver};
use cairo_lang_semantic::types::peel_snapshots;
Expand Down Expand Up @@ -318,3 +320,67 @@ fn module_has_trait(
}
Some(false)
}

/// Discovers members of a struct the [`ExprStructCtorCall`] refers to and returns completions with
/// those which have not yet been specified in the constructor call.
pub fn struct_constructor_completions(
db: &AnalysisDatabase,
lookup_items: Vec<LookupItemId>,
constructor: ast::ExprStructCtorCall,
) -> Option<Vec<CompletionItem>> {
let module_id = db.find_module_containing_node(&constructor.as_syntax_node())?;
let lookup_item_id = lookup_items.into_iter().next()?;
let function_id = lookup_item_id.function_with_body()?;

let already_present_members = constructor
.arguments(db)
.arguments(db)
.elements(db)
.into_iter()
.filter_map(|member| match member {
ast::StructArg::StructArgSingle(struct_arg_single) => {
Some(struct_arg_single.identifier(db).token(db).as_syntax_node().get_text(db))
}
// although tail covers all remaining unspecified members, we still want to show them in
// completion.
ast::StructArg::StructArgTail(_) => None,
})
.collect::<Vec<_>>();

let constructor_expr_id =
db.lookup_expr_by_ptr(function_id, constructor.stable_ptr().into()).ok()?;

let semantic_expr = db.expr_semantic(function_id, constructor_expr_id);

let cairo_lang_semantic::Expr::StructCtor(constructor_semantic_expr) = semantic_expr else {
return None;
};

let struct_parent_module_id =
constructor_semantic_expr.concrete_struct_id.struct_id(db).parent_module(db);

let struct_members =
concrete_struct_members(db, constructor_semantic_expr.concrete_struct_id).ok()?;

let completions = struct_members
.iter()
.filter_map(|(name, data)| {
let name = name.to_string();

let visible = peek_visible_in(db, data.visibility, struct_parent_module_id, module_id);

if !visible || already_present_members.contains(&name) {
None
} else {
Some(CompletionItem {
label: name,
detail: Some(data.ty.format(db)),
kind: Some(CompletionItemKind::VALUE),
..Default::default()
})
}
})
.collect::<Vec<_>>();

if completions.is_empty() { None } else { Some(completions) }
}
20 changes: 20 additions & 0 deletions crates/cairo-lang-language-server/src/ide/completion/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use cairo_lang_syntax::node::db::SyntaxGroup;
use cairo_lang_syntax::node::kind::SyntaxKind;
use cairo_lang_syntax::node::{SyntaxNode, TypedSyntaxNode, ast};
use cairo_lang_utils::Upcast;
use completions::struct_constructor_completions;
use lsp_types::{CompletionParams, CompletionResponse, CompletionTriggerKind};
use tracing::debug;

Expand Down Expand Up @@ -44,6 +45,10 @@ pub fn complete(params: CompletionParams, db: &AnalysisDatabase) -> Option<Compl
colon_colon_completions(db, module_file_id, lookup_items, segments)
.map(CompletionResponse::Array)
}
CompletionKind::StructConstructor(constructor) => {
struct_constructor_completions(db, lookup_items, constructor)
.map(CompletionResponse::Array)
}
_ if trigger_kind == CompletionTriggerKind::INVOKED => {
Some(CompletionResponse::Array(generic_completions(db, module_file_id, lookup_items)))
}
Expand All @@ -54,6 +59,7 @@ pub fn complete(params: CompletionParams, db: &AnalysisDatabase) -> Option<Compl
enum CompletionKind {
Dot(ast::ExprBinary),
ColonColon(Vec<PathSegment>),
StructConstructor(ast::ExprStructCtorCall),
}

fn completion_kind(db: &AnalysisDatabase, node: SyntaxNode) -> CompletionKind {
Expand Down Expand Up @@ -136,6 +142,20 @@ fn completion_kind(db: &AnalysisDatabase, node: SyntaxNode) -> CompletionKind {
return CompletionKind::ColonColon(segments);
}
}
SyntaxKind::TerminalLBrace
| SyntaxKind::TerminalRBrace
| SyntaxKind::StructArgExpr
| SyntaxKind::StructArgList
| SyntaxKind::StructArgTail
| SyntaxKind::TerminalComma => {
if let Some(constructor_node) =
db.first_ancestor_of_kind(node, SyntaxKind::ExprStructCtorCall)
{
return CompletionKind::StructConstructor(
ast::ExprStructCtorCall::from_syntax_node(db, constructor_node),
);
}
}
_ => (),
}
debug!("Generic");
Expand Down

0 comments on commit 0081864

Please sign in to comment.