Skip to content

Commit

Permalink
Support completions for macros
Browse files Browse the repository at this point in the history
commit-id:5e59b551
  • Loading branch information
Draggu committed Jan 21, 2025
1 parent f3b6596 commit 54497ae
Show file tree
Hide file tree
Showing 3 changed files with 152 additions and 14 deletions.
103 changes: 100 additions & 3 deletions src/ide/completion/completions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,101 @@ use cairo_lang_semantic::lookup_item::{HasResolverData, LookupItemEx};
use cairo_lang_semantic::resolve::{ResolvedConcreteItem, ResolvedGenericItem, Resolver};
use cairo_lang_semantic::types::peel_snapshots;
use cairo_lang_semantic::{ConcreteTypeId, Pattern, TypeLongId};
use cairo_lang_syntax::node::ast::PathSegment;
use cairo_lang_syntax::node::{TypedStablePtr, TypedSyntaxNode, ast};
use cairo_lang_syntax::node::ast::{
ArgClause, Attribute, Expr, OptionArgListParenthesized, PathSegment,
};
use cairo_lang_syntax::node::kind::SyntaxKind;
use cairo_lang_syntax::node::{SyntaxNode, TypedStablePtr, TypedSyntaxNode, ast};
use cairo_lang_utils::{LookupIntern, Upcast};
use lsp_types::{CompletionItem, CompletionItemKind, InsertTextFormat, Position, Range, TextEdit};
use tracing::debug;

use crate::lang::db::{AnalysisDatabase, LsSemanticGroup};
use crate::lang::db::{AnalysisDatabase, LsSemanticGroup, LsSyntaxGroup};
use crate::lang::lsp::ToLsp;
use crate::lang::methods::find_methods_for_type;

pub fn attribute_completions(
db: &AnalysisDatabase,
origin_node: SyntaxNode,
) -> Option<Vec<CompletionItem>> {
fn macro_completion(name: String) -> CompletionItem {
CompletionItem {
label: name,
kind: Some(CompletionItemKind::FUNCTION),
..Default::default()
}
}

let node =
db.first_ancestor_of_kind_respective_child(origin_node.clone(), SyntaxKind::Attribute)?;

let attribute = Attribute::from_syntax_node(db, node.parent().unwrap());

let plugins = db.macro_plugins();

let attr = attribute.attr(db);
let attr_node = attr.as_syntax_node();
let attr_name = attr_node.get_text(db);

// Check if cursor is on attribute name.
// #[my_a<cursor>ttr(arg1, args2: 1234)]
if attr_node == node {
return Some(
plugins
.iter()
.flat_map(|plugin| plugin.declared_attributes())
.filter(|name| {
// Don't suggest already typed one.
name.starts_with(&attr_name) && name != &attr_name
})
.map(macro_completion)
.collect(),
);
};

// If we are not on attribute name, check if cursor is on `#[derive(Arg1, Ar<cursor>)]`
// arguments list.

let arguments = attribute.arguments(db);

if attr_name != "derive" || arguments.as_syntax_node() != node {
return None;
}

let OptionArgListParenthesized::ArgListParenthesized(args_list) = arguments else {
return None;
};

for arg in args_list.arguments(db).elements(db) {
let ArgClause::Unnamed(arg) = arg.arg_clause(db) else {
continue;
};

let Expr::Path(path) = arg.value(db) else {
continue;
};

let path_node = path.as_syntax_node();

if std::iter::successors(Some(origin_node.clone()), SyntaxNode::parent)
.any(|node| node == path_node)
{
let derive_name = path_node.get_text(db);

return Some(
plugins
.iter()
.flat_map(|plugin| plugin.declared_derives())
.filter(|name| name.starts_with(&derive_name) && name != &derive_name)
.map(macro_completion)
.collect(),
);
}
}

Default::default()
}

pub fn generic_completions(
db: &AnalysisDatabase,
module_file_id: ModuleFileId,
Expand Down Expand Up @@ -58,6 +143,7 @@ pub fn generic_completions(
let Some(lookup_item_id) = lookup_items.into_iter().next() else {
return completions;
};

let Some(function_id) = lookup_item_id.function_with_body() else {
return completions;
};
Expand All @@ -75,6 +161,17 @@ pub fn generic_completions(
let Ok(body) = db.function_body(function_id) else {
return completions;
};

let inline_plugins = db.inline_macro_plugins();

let inline_macros = inline_plugins.iter().map(|plugin| CompletionItem {
label: format!("{}!", plugin.0),
kind: Some(CompletionItemKind::FUNCTION),
..CompletionItem::default()
});

completions.extend(inline_macros);

for (_id, pat) in &body.arenas.patterns {
if let Pattern::Variable(var) = pat {
completions.push(CompletionItem {
Expand Down
9 changes: 6 additions & 3 deletions src/ide/completion/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,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 completions::{attribute_completions, struct_constructor_completions};
use lsp_types::{CompletionParams, CompletionResponse, CompletionTriggerKind, Position};
use tracing::debug;

Expand Down Expand Up @@ -38,7 +38,7 @@ pub fn complete(params: CompletionParams, db: &AnalysisDatabase) -> Option<Compl
let trigger_kind =
params.context.map(|it| it.trigger_kind).unwrap_or(CompletionTriggerKind::INVOKED);

match completion_kind(db, node, position, file_id) {
match completion_kind(db, node.clone(), position, file_id) {
CompletionKind::Dot(expr) => {
dot_completions(db, file_id, lookup_items, expr).map(CompletionResponse::Array)
}
Expand All @@ -51,7 +51,10 @@ pub fn complete(params: CompletionParams, db: &AnalysisDatabase) -> Option<Compl
.map(CompletionResponse::Array)
}
_ if trigger_kind == CompletionTriggerKind::INVOKED => {
Some(CompletionResponse::Array(generic_completions(db, module_file_id, lookup_items)))
let result = attribute_completions(db, node)
.unwrap_or_else(|| generic_completions(db, module_file_id, lookup_items));

Some(CompletionResponse::Array(result))
}
_ => None,
}
Expand Down
54 changes: 46 additions & 8 deletions tests/test_data/completions/structs.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ test_completions_text_edits(detail: true)

//! > cairo_code
mod some_module {
#[derive(D<caret>)]
#[d<caret>]
pub struct Struct {
x: u32,
pub y: felt252,
Expand Down Expand Up @@ -54,6 +56,18 @@ mod unhappy_cases {
}

//! > Completions #0
#[derive(D<caret>)]

//! > Completions #1
#[d<caret>]
--------------------------
Completion: derive
--------------------------
Completion: default
--------------------------
Completion: doc

//! > Completions #2
let a = Struct { <caret> };
--------------------------
Completion: x
Expand All @@ -65,7 +79,7 @@ Detail: core::felt252
Completion: z
Detail: core::integer::i16

//! > Completions #1
//! > Completions #3
let b = Struct { x: 0x0, <caret> };
--------------------------
Completion: y
Expand All @@ -74,7 +88,7 @@ Detail: core::felt252
Completion: z
Detail: core::integer::i16

//! > Completions #2
//! > Completions #4
<caret>
--------------------------
Completion: y
Expand All @@ -83,7 +97,7 @@ Detail: core::felt252
Completion: z
Detail: core::integer::i16

//! > Completions #3
//! > Completions #5
<caret>..s
--------------------------
Completion: core
Expand All @@ -94,6 +108,30 @@ Completion: Struct
--------------------------
Completion: build_struct
--------------------------
Completion: array!
--------------------------
Completion: assert!
--------------------------
Completion: consteval_int!
--------------------------
Completion: format!
--------------------------
Completion: panic!
--------------------------
Completion: print!
--------------------------
Completion: println!
--------------------------
Completion: write!
--------------------------
Completion: writeln!
--------------------------
Completion: selector!
--------------------------
Completion: get_dep_component!
--------------------------
Completion: get_dep_component_mut!
--------------------------
Completion: s
--------------------------
Completion: a
Expand All @@ -106,7 +144,7 @@ Completion: d
--------------------------
Completion: e

//! > Completions #4
//! > Completions #6
let e = Struct { <caret>..s };
--------------------------
Completion: x
Expand All @@ -118,7 +156,7 @@ Detail: core::felt252
Completion: z
Detail: core::integer::i16

//! > Completions #5
//! > Completions #7
let a = Struct { <caret> };
--------------------------
Completion: y
Expand All @@ -127,17 +165,17 @@ Detail: core::felt252
Completion: z
Detail: core::integer::i16

//! > Completions #6
//! > Completions #8
let b = Struct { y: 0x0, <caret> };
--------------------------
Completion: z
Detail: core::integer::i16

//! > Completions #7
//! > Completions #9
let c = Struct { y: 0x0, x: 0x0, <caret> }
--------------------------
Completion: z
Detail: core::integer::i16

//! > Completions #8
//! > Completions #10
let a = NonexsitentStruct { <caret> };

0 comments on commit 54497ae

Please sign in to comment.