From 7e8527f74831b37c5757ea6b25e40bcbb61bb6d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Tue, 12 Feb 2019 10:33:23 +0200 Subject: Implement completion for associated items --- crates/ra_hir/src/ty/method_resolution.rs | 20 ++++ crates/ra_ide_api/src/completion.rs | 22 +++++ crates/ra_ide_api/src/completion/complete_path.rs | 104 ++++++++++++++++++++- .../ra_ide_api/src/completion/completion_item.rs | 34 +++++++ .../completion_item__struct_associated_const.snap | 28 ++++++ .../completion_item__struct_associated_method.snap | 30 ++++++ .../completion_item__struct_associated_type.snap | 28 ++++++ 7 files changed, 265 insertions(+), 1 deletion(-) create mode 100644 crates/ra_ide_api/src/completion/snapshots/completion_item__struct_associated_const.snap create mode 100644 crates/ra_ide_api/src/completion/snapshots/completion_item__struct_associated_method.snap create mode 100644 crates/ra_ide_api/src/completion/snapshots/completion_item__struct_associated_type.snap diff --git a/crates/ra_hir/src/ty/method_resolution.rs b/crates/ra_hir/src/ty/method_resolution.rs index b8e911b88..e81bd3e06 100644 --- a/crates/ra_hir/src/ty/method_resolution.rs +++ b/crates/ra_hir/src/ty/method_resolution.rs @@ -174,4 +174,24 @@ impl Ty { } None } + + // This would be nicer if it just returned an iterator, but that runs into + // lifetime problems, because we need to borrow temp `CrateImplBlocks`. + pub fn iterate_impl_items( + self, + db: &impl HirDatabase, + mut callback: impl FnMut(ImplItem) -> Option, + ) -> Option { + let krate = def_crate(db, &self)?; + let impls = db.impls_in_crate(krate); + + for (_, impl_block) in impls.lookup_impl_blocks(db, &self) { + for item in impl_block.items() { + if let Some(result) = callback(*item) { + return Some(result); + } + } + } + None + } } diff --git a/crates/ra_ide_api/src/completion.rs b/crates/ra_ide_api/src/completion.rs index 722d94f3a..83c243944 100644 --- a/crates/ra_ide_api/src/completion.rs +++ b/crates/ra_ide_api/src/completion.rs @@ -80,3 +80,25 @@ pub fn function_label(node: &ast::FnDef) -> Option { Some(label.trim().to_owned()) } + +pub fn const_label(node: &ast::ConstDef) -> String { + let label: String = node + .syntax() + .children() + .filter(|child| ast::Comment::cast(child).is_none()) + .map(|node| node.text().to_string()) + .collect(); + + label.trim().to_owned() +} + +pub fn type_label(node: &ast::TypeDef) -> String { + let label: String = node + .syntax() + .children() + .filter(|child| ast::Comment::cast(child).is_none()) + .map(|node| node.text().to_string()) + .collect(); + + label.trim().to_owned() +} diff --git a/crates/ra_ide_api/src/completion/complete_path.rs b/crates/ra_ide_api/src/completion/complete_path.rs index 91ca7525e..d337fe970 100644 --- a/crates/ra_ide_api/src/completion/complete_path.rs +++ b/crates/ra_ide_api/src/completion/complete_path.rs @@ -1,6 +1,6 @@ use join_to_string::join; use hir::{Docs, Resolution}; -use ra_syntax::AstNode; +use ra_syntax::{AstNode, ast::NameOwner}; use test_utils::tested_by; use crate::completion::{CompletionItem, CompletionItemKind, Completions, CompletionKind, CompletionContext}; @@ -58,6 +58,51 @@ pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) { } }); } + hir::ModuleDef::Struct(s) => { + let ty = s.ty(ctx.db); + ty.iterate_impl_items(ctx.db, |item| match item { + hir::ImplItem::Method(func) => { + let sig = func.signature(ctx.db); + if !sig.has_self_param() { + CompletionItem::new( + CompletionKind::Reference, + ctx.source_range(), + sig.name().to_string(), + ) + .from_function(ctx, func) + .kind(CompletionItemKind::Method) + .add_to(acc); + } + None::<()> + } + hir::ImplItem::Const(ct) => { + let source = ct.source(ctx.db); + if let Some(name) = source.1.name() { + CompletionItem::new( + CompletionKind::Reference, + ctx.source_range(), + name.text().to_string(), + ) + .from_const(ctx, ct) + .add_to(acc); + } + None::<()> + } + hir::ImplItem::Type(ty) => { + let source = ty.source(ctx.db); + if let Some(name) = source.1.name() { + CompletionItem::new( + CompletionKind::Reference, + ctx.source_range(), + name.text().to_string(), + ) + .from_type(ctx, ty) + .add_to(acc); + } + None::<()> + } + }); + } _ => return, }; } @@ -197,6 +242,63 @@ mod tests { ); } + #[test] + fn completes_struct_associated_method() { + check_reference_completion( + "struct_associated_method", + " + //- /lib.rs + /// A Struct + struct S; + + impl S { + /// An associated method + fn m() { } + } + + fn foo() { let _ = S::<|> } + ", + ); + } + + #[test] + fn completes_struct_associated_const() { + check_reference_completion( + "struct_associated_const", + " + //- /lib.rs + /// A Struct + struct S; + + impl S { + /// An associated const + const C: i32 = 42; + } + + fn foo() { let _ = S::<|> } + ", + ); + } + + #[test] + fn completes_struct_associated_type() { + check_reference_completion( + "struct_associated_type", + " + //- /lib.rs + /// A Struct + struct S; + + impl S { + /// An associated type + type T = i32; + } + + fn foo() { let _ = S::<|> } + ", + ); + } + #[test] fn completes_use_paths_across_crates() { check_reference_completion( diff --git a/crates/ra_ide_api/src/completion/completion_item.rs b/crates/ra_ide_api/src/completion/completion_item.rs index 22ff08a23..6003e1d89 100644 --- a/crates/ra_ide_api/src/completion/completion_item.rs +++ b/crates/ra_ide_api/src/completion/completion_item.rs @@ -8,6 +8,8 @@ use test_utils::tested_by; use crate::completion::{ completion_context::CompletionContext, function_label, + const_label, + type_label }; /// `CompletionItem` describes a single completion variant in the editor pop-up. @@ -267,6 +269,28 @@ impl Builder { self.kind = Some(CompletionItemKind::Function); self } + + pub(super) fn from_const(mut self, ctx: &CompletionContext, ct: hir::Const) -> Builder { + if let Some(docs) = ct.docs(ctx.db) { + self.documentation = Some(docs); + } + + self.detail = Some(const_item_label(ctx, ct)); + self.kind = Some(CompletionItemKind::Const); + + self + } + + pub(super) fn from_type(mut self, ctx: &CompletionContext, ty: hir::Type) -> Builder { + if let Some(docs) = ty.docs(ctx.db) { + self.documentation = Some(docs); + } + + self.detail = Some(type_item_label(ctx, ty)); + self.kind = Some(CompletionItemKind::TypeAlias); + + self + } } impl<'a> Into for Builder { @@ -305,6 +329,16 @@ fn function_item_label(ctx: &CompletionContext, function: hir::Function) -> Opti function_label(&node) } +fn const_item_label(ctx: &CompletionContext, ct: hir::Const) -> String { + let node = ct.source(ctx.db).1; + const_label(&node) +} + +fn type_item_label(ctx: &CompletionContext, ty: hir::Type) -> String { + let node = ty.source(ctx.db).1; + type_label(&node) +} + #[cfg(test)] pub(crate) fn do_completion(code: &str, kind: CompletionKind) -> Vec { use crate::mock_analysis::{single_file_with_position, analysis_and_position}; diff --git a/crates/ra_ide_api/src/completion/snapshots/completion_item__struct_associated_const.snap b/crates/ra_ide_api/src/completion/snapshots/completion_item__struct_associated_const.snap new file mode 100644 index 000000000..ff1eef5f5 --- /dev/null +++ b/crates/ra_ide_api/src/completion/snapshots/completion_item__struct_associated_const.snap @@ -0,0 +1,28 @@ +--- +created: "2019-02-12T09:57:51.107816726Z" +creator: insta@0.6.2 +source: crates/ra_ide_api/src/completion/completion_item.rs +expression: kind_completions +--- +[ + CompletionItem { + completion_kind: Reference, + label: "C", + kind: Some( + Const + ), + detail: Some( + "const C: i32 = 42;" + ), + documentation: Some( + Documentation( + "An associated const" + ) + ), + lookup: None, + insert_text: None, + insert_text_format: PlainText, + source_range: [107; 107), + text_edit: None + } +] diff --git a/crates/ra_ide_api/src/completion/snapshots/completion_item__struct_associated_method.snap b/crates/ra_ide_api/src/completion/snapshots/completion_item__struct_associated_method.snap new file mode 100644 index 000000000..c53c61d0e --- /dev/null +++ b/crates/ra_ide_api/src/completion/snapshots/completion_item__struct_associated_method.snap @@ -0,0 +1,30 @@ +--- +created: "2019-02-12T09:57:51.106389138Z" +creator: insta@0.6.2 +source: crates/ra_ide_api/src/completion/completion_item.rs +expression: kind_completions +--- +[ + CompletionItem { + completion_kind: Reference, + label: "m", + kind: Some( + Method + ), + detail: Some( + "fn m()" + ), + documentation: Some( + Documentation( + "An associated method" + ) + ), + lookup: None, + insert_text: Some( + "m()$0" + ), + insert_text_format: Snippet, + source_range: [100; 100), + text_edit: None + } +] diff --git a/crates/ra_ide_api/src/completion/snapshots/completion_item__struct_associated_type.snap b/crates/ra_ide_api/src/completion/snapshots/completion_item__struct_associated_type.snap new file mode 100644 index 000000000..e993fb1b0 --- /dev/null +++ b/crates/ra_ide_api/src/completion/snapshots/completion_item__struct_associated_type.snap @@ -0,0 +1,28 @@ +--- +created: "2019-02-12T09:33:54.719956203Z" +creator: insta@0.6.2 +source: crates/ra_ide_api/src/completion/completion_item.rs +expression: kind_completions +--- +[ + CompletionItem { + completion_kind: Reference, + label: "T", + kind: Some( + TypeAlias + ), + detail: Some( + "type T = i32;" + ), + documentation: Some( + Documentation( + "An associated type" + ) + ), + lookup: None, + insert_text: None, + insert_text_format: PlainText, + source_range: [101; 101), + text_edit: None + } +] -- cgit v1.2.3