801: Implement completion for associated items r=matklad a=lnicola

Fixes #747.

r? @matklad

Co-authored-by: Laurențiu Nicola <lnicola@dend.ro>
This commit is contained in:
bors[bot] 2019-02-12 11:07:21 +00:00
commit 19718ea109
7 changed files with 265 additions and 1 deletions

View File

@ -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<T>(
self,
db: &impl HirDatabase,
mut callback: impl FnMut(ImplItem) -> Option<T>,
) -> Option<T> {
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
}
}

View File

@ -80,3 +80,25 @@ pub fn function_label(node: &ast::FnDef) -> Option<String> {
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()
}

View File

@ -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(

View File

@ -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<CompletionItem> 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<CompletionItem> {
use crate::mock_analysis::{single_file_with_position, analysis_and_position};

View File

@ -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
}
]

View File

@ -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
}
]

View File

@ -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
}
]