diff options
author | Laurențiu Nicola <[email protected]> | 2019-02-12 08:33:23 +0000 |
---|---|---|
committer | Laurențiu Nicola <[email protected]> | 2019-02-12 10:51:01 +0000 |
commit | 7e8527f74831b37c5757ea6b25e40bcbb61bb6d4 (patch) | |
tree | e7bddef556f430d31903a54fced0df760673a0e8 | |
parent | 37148000dcd43e5ccba4737a3e379f1ae6861893 (diff) |
Implement completion for associated items
7 files changed, 265 insertions, 1 deletions
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 { | |||
174 | } | 174 | } |
175 | None | 175 | None |
176 | } | 176 | } |
177 | |||
178 | // This would be nicer if it just returned an iterator, but that runs into | ||
179 | // lifetime problems, because we need to borrow temp `CrateImplBlocks`. | ||
180 | pub fn iterate_impl_items<T>( | ||
181 | self, | ||
182 | db: &impl HirDatabase, | ||
183 | mut callback: impl FnMut(ImplItem) -> Option<T>, | ||
184 | ) -> Option<T> { | ||
185 | let krate = def_crate(db, &self)?; | ||
186 | let impls = db.impls_in_crate(krate); | ||
187 | |||
188 | for (_, impl_block) in impls.lookup_impl_blocks(db, &self) { | ||
189 | for item in impl_block.items() { | ||
190 | if let Some(result) = callback(*item) { | ||
191 | return Some(result); | ||
192 | } | ||
193 | } | ||
194 | } | ||
195 | None | ||
196 | } | ||
177 | } | 197 | } |
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<String> { | |||
80 | 80 | ||
81 | Some(label.trim().to_owned()) | 81 | Some(label.trim().to_owned()) |
82 | } | 82 | } |
83 | |||
84 | pub fn const_label(node: &ast::ConstDef) -> String { | ||
85 | let label: String = node | ||
86 | .syntax() | ||
87 | .children() | ||
88 | .filter(|child| ast::Comment::cast(child).is_none()) | ||
89 | .map(|node| node.text().to_string()) | ||
90 | .collect(); | ||
91 | |||
92 | label.trim().to_owned() | ||
93 | } | ||
94 | |||
95 | pub fn type_label(node: &ast::TypeDef) -> String { | ||
96 | let label: String = node | ||
97 | .syntax() | ||
98 | .children() | ||
99 | .filter(|child| ast::Comment::cast(child).is_none()) | ||
100 | .map(|node| node.text().to_string()) | ||
101 | .collect(); | ||
102 | |||
103 | label.trim().to_owned() | ||
104 | } | ||
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 @@ | |||
1 | use join_to_string::join; | 1 | use join_to_string::join; |
2 | use hir::{Docs, Resolution}; | 2 | use hir::{Docs, Resolution}; |
3 | use ra_syntax::AstNode; | 3 | use ra_syntax::{AstNode, ast::NameOwner}; |
4 | use test_utils::tested_by; | 4 | use test_utils::tested_by; |
5 | 5 | ||
6 | use crate::completion::{CompletionItem, CompletionItemKind, Completions, CompletionKind, CompletionContext}; | 6 | use crate::completion::{CompletionItem, CompletionItemKind, Completions, CompletionKind, CompletionContext}; |
@@ -58,6 +58,51 @@ pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) { | |||
58 | } | 58 | } |
59 | }); | 59 | }); |
60 | } | 60 | } |
61 | hir::ModuleDef::Struct(s) => { | ||
62 | let ty = s.ty(ctx.db); | ||
63 | ty.iterate_impl_items(ctx.db, |item| match item { | ||
64 | hir::ImplItem::Method(func) => { | ||
65 | let sig = func.signature(ctx.db); | ||
66 | if !sig.has_self_param() { | ||
67 | CompletionItem::new( | ||
68 | CompletionKind::Reference, | ||
69 | ctx.source_range(), | ||
70 | sig.name().to_string(), | ||
71 | ) | ||
72 | .from_function(ctx, func) | ||
73 | .kind(CompletionItemKind::Method) | ||
74 | .add_to(acc); | ||
75 | } | ||
76 | None::<()> | ||
77 | } | ||
78 | hir::ImplItem::Const(ct) => { | ||
79 | let source = ct.source(ctx.db); | ||
80 | if let Some(name) = source.1.name() { | ||
81 | CompletionItem::new( | ||
82 | CompletionKind::Reference, | ||
83 | ctx.source_range(), | ||
84 | name.text().to_string(), | ||
85 | ) | ||
86 | .from_const(ctx, ct) | ||
87 | .add_to(acc); | ||
88 | } | ||
89 | None::<()> | ||
90 | } | ||
91 | hir::ImplItem::Type(ty) => { | ||
92 | let source = ty.source(ctx.db); | ||
93 | if let Some(name) = source.1.name() { | ||
94 | CompletionItem::new( | ||
95 | CompletionKind::Reference, | ||
96 | ctx.source_range(), | ||
97 | name.text().to_string(), | ||
98 | ) | ||
99 | .from_type(ctx, ty) | ||
100 | .add_to(acc); | ||
101 | } | ||
102 | None::<()> | ||
103 | } | ||
104 | }); | ||
105 | } | ||
61 | _ => return, | 106 | _ => return, |
62 | }; | 107 | }; |
63 | } | 108 | } |
@@ -198,6 +243,63 @@ mod tests { | |||
198 | } | 243 | } |
199 | 244 | ||
200 | #[test] | 245 | #[test] |
246 | fn completes_struct_associated_method() { | ||
247 | check_reference_completion( | ||
248 | "struct_associated_method", | ||
249 | " | ||
250 | //- /lib.rs | ||
251 | /// A Struct | ||
252 | struct S; | ||
253 | |||
254 | impl S { | ||
255 | /// An associated method | ||
256 | fn m() { } | ||
257 | } | ||
258 | |||
259 | fn foo() { let _ = S::<|> } | ||
260 | ", | ||
261 | ); | ||
262 | } | ||
263 | |||
264 | #[test] | ||
265 | fn completes_struct_associated_const() { | ||
266 | check_reference_completion( | ||
267 | "struct_associated_const", | ||
268 | " | ||
269 | //- /lib.rs | ||
270 | /// A Struct | ||
271 | struct S; | ||
272 | |||
273 | impl S { | ||
274 | /// An associated const | ||
275 | const C: i32 = 42; | ||
276 | } | ||
277 | |||
278 | fn foo() { let _ = S::<|> } | ||
279 | ", | ||
280 | ); | ||
281 | } | ||
282 | |||
283 | #[test] | ||
284 | fn completes_struct_associated_type() { | ||
285 | check_reference_completion( | ||
286 | "struct_associated_type", | ||
287 | " | ||
288 | //- /lib.rs | ||
289 | /// A Struct | ||
290 | struct S; | ||
291 | |||
292 | impl S { | ||
293 | /// An associated type | ||
294 | type T = i32; | ||
295 | } | ||
296 | |||
297 | fn foo() { let _ = S::<|> } | ||
298 | ", | ||
299 | ); | ||
300 | } | ||
301 | |||
302 | #[test] | ||
201 | fn completes_use_paths_across_crates() { | 303 | fn completes_use_paths_across_crates() { |
202 | check_reference_completion( | 304 | check_reference_completion( |
203 | "completes_use_paths_across_crates", | 305 | "completes_use_paths_across_crates", |
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; | |||
8 | use crate::completion::{ | 8 | use crate::completion::{ |
9 | completion_context::CompletionContext, | 9 | completion_context::CompletionContext, |
10 | function_label, | 10 | function_label, |
11 | const_label, | ||
12 | type_label | ||
11 | }; | 13 | }; |
12 | 14 | ||
13 | /// `CompletionItem` describes a single completion variant in the editor pop-up. | 15 | /// `CompletionItem` describes a single completion variant in the editor pop-up. |
@@ -267,6 +269,28 @@ impl Builder { | |||
267 | self.kind = Some(CompletionItemKind::Function); | 269 | self.kind = Some(CompletionItemKind::Function); |
268 | self | 270 | self |
269 | } | 271 | } |
272 | |||
273 | pub(super) fn from_const(mut self, ctx: &CompletionContext, ct: hir::Const) -> Builder { | ||
274 | if let Some(docs) = ct.docs(ctx.db) { | ||
275 | self.documentation = Some(docs); | ||
276 | } | ||
277 | |||
278 | self.detail = Some(const_item_label(ctx, ct)); | ||
279 | self.kind = Some(CompletionItemKind::Const); | ||
280 | |||
281 | self | ||
282 | } | ||
283 | |||
284 | pub(super) fn from_type(mut self, ctx: &CompletionContext, ty: hir::Type) -> Builder { | ||
285 | if let Some(docs) = ty.docs(ctx.db) { | ||
286 | self.documentation = Some(docs); | ||
287 | } | ||
288 | |||
289 | self.detail = Some(type_item_label(ctx, ty)); | ||
290 | self.kind = Some(CompletionItemKind::TypeAlias); | ||
291 | |||
292 | self | ||
293 | } | ||
270 | } | 294 | } |
271 | 295 | ||
272 | impl<'a> Into<CompletionItem> for Builder { | 296 | impl<'a> Into<CompletionItem> for Builder { |
@@ -305,6 +329,16 @@ fn function_item_label(ctx: &CompletionContext, function: hir::Function) -> Opti | |||
305 | function_label(&node) | 329 | function_label(&node) |
306 | } | 330 | } |
307 | 331 | ||
332 | fn const_item_label(ctx: &CompletionContext, ct: hir::Const) -> String { | ||
333 | let node = ct.source(ctx.db).1; | ||
334 | const_label(&node) | ||
335 | } | ||
336 | |||
337 | fn type_item_label(ctx: &CompletionContext, ty: hir::Type) -> String { | ||
338 | let node = ty.source(ctx.db).1; | ||
339 | type_label(&node) | ||
340 | } | ||
341 | |||
308 | #[cfg(test)] | 342 | #[cfg(test)] |
309 | pub(crate) fn do_completion(code: &str, kind: CompletionKind) -> Vec<CompletionItem> { | 343 | pub(crate) fn do_completion(code: &str, kind: CompletionKind) -> Vec<CompletionItem> { |
310 | use crate::mock_analysis::{single_file_with_position, analysis_and_position}; | 344 | 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 @@ | |||
1 | --- | ||
2 | created: "2019-02-12T09:57:51.107816726Z" | ||
3 | creator: [email protected] | ||
4 | source: crates/ra_ide_api/src/completion/completion_item.rs | ||
5 | expression: kind_completions | ||
6 | --- | ||
7 | [ | ||
8 | CompletionItem { | ||
9 | completion_kind: Reference, | ||
10 | label: "C", | ||
11 | kind: Some( | ||
12 | Const | ||
13 | ), | ||
14 | detail: Some( | ||
15 | "const C: i32 = 42;" | ||
16 | ), | ||
17 | documentation: Some( | ||
18 | Documentation( | ||
19 | "An associated const" | ||
20 | ) | ||
21 | ), | ||
22 | lookup: None, | ||
23 | insert_text: None, | ||
24 | insert_text_format: PlainText, | ||
25 | source_range: [107; 107), | ||
26 | text_edit: None | ||
27 | } | ||
28 | ] | ||
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 @@ | |||
1 | --- | ||
2 | created: "2019-02-12T09:57:51.106389138Z" | ||
3 | creator: [email protected] | ||
4 | source: crates/ra_ide_api/src/completion/completion_item.rs | ||
5 | expression: kind_completions | ||
6 | --- | ||
7 | [ | ||
8 | CompletionItem { | ||
9 | completion_kind: Reference, | ||
10 | label: "m", | ||
11 | kind: Some( | ||
12 | Method | ||
13 | ), | ||
14 | detail: Some( | ||
15 | "fn m()" | ||
16 | ), | ||
17 | documentation: Some( | ||
18 | Documentation( | ||
19 | "An associated method" | ||
20 | ) | ||
21 | ), | ||
22 | lookup: None, | ||
23 | insert_text: Some( | ||
24 | "m()$0" | ||
25 | ), | ||
26 | insert_text_format: Snippet, | ||
27 | source_range: [100; 100), | ||
28 | text_edit: None | ||
29 | } | ||
30 | ] | ||
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 @@ | |||
1 | --- | ||
2 | created: "2019-02-12T09:33:54.719956203Z" | ||
3 | creator: [email protected] | ||
4 | source: crates/ra_ide_api/src/completion/completion_item.rs | ||
5 | expression: kind_completions | ||
6 | --- | ||
7 | [ | ||
8 | CompletionItem { | ||
9 | completion_kind: Reference, | ||
10 | label: "T", | ||
11 | kind: Some( | ||
12 | TypeAlias | ||
13 | ), | ||
14 | detail: Some( | ||
15 | "type T = i32;" | ||
16 | ), | ||
17 | documentation: Some( | ||
18 | Documentation( | ||
19 | "An associated type" | ||
20 | ) | ||
21 | ), | ||
22 | lookup: None, | ||
23 | insert_text: None, | ||
24 | insert_text_format: PlainText, | ||
25 | source_range: [101; 101), | ||
26 | text_edit: None | ||
27 | } | ||
28 | ] | ||