diff options
Diffstat (limited to 'crates/ra_ide/src/completion')
-rw-r--r-- | crates/ra_ide/src/completion/complete_dot.rs | 2 | ||||
-rw-r--r-- | crates/ra_ide/src/completion/complete_path.rs | 24 | ||||
-rw-r--r-- | crates/ra_ide/src/completion/complete_pattern.rs | 58 | ||||
-rw-r--r-- | crates/ra_ide/src/completion/complete_scope.rs | 10 | ||||
-rw-r--r-- | crates/ra_ide/src/completion/completion_context.rs | 31 | ||||
-rw-r--r-- | crates/ra_ide/src/completion/presentation.rs | 235 |
6 files changed, 233 insertions, 127 deletions
diff --git a/crates/ra_ide/src/completion/complete_dot.rs b/crates/ra_ide/src/completion/complete_dot.rs index 82ec16913..f433faef3 100644 --- a/crates/ra_ide/src/completion/complete_dot.rs +++ b/crates/ra_ide/src/completion/complete_dot.rs | |||
@@ -61,7 +61,7 @@ fn complete_methods(acc: &mut Completions, ctx: &CompletionContext, receiver: &T | |||
61 | && ctx.scope().module().map_or(true, |m| func.is_visible_from(ctx.db, m)) | 61 | && ctx.scope().module().map_or(true, |m| func.is_visible_from(ctx.db, m)) |
62 | && seen_methods.insert(func.name(ctx.db)) | 62 | && seen_methods.insert(func.name(ctx.db)) |
63 | { | 63 | { |
64 | acc.add_function(ctx, func); | 64 | acc.add_function(ctx, func, None); |
65 | } | 65 | } |
66 | None::<()> | 66 | None::<()> |
67 | }); | 67 | }); |
diff --git a/crates/ra_ide/src/completion/complete_path.rs b/crates/ra_ide/src/completion/complete_path.rs index 3db17f15f..3ed2ae2b6 100644 --- a/crates/ra_ide/src/completion/complete_path.rs +++ b/crates/ra_ide/src/completion/complete_path.rs | |||
@@ -38,7 +38,7 @@ pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) { | |||
38 | hir::ModuleDef::Adt(_) | hir::ModuleDef::TypeAlias(_) => { | 38 | hir::ModuleDef::Adt(_) | hir::ModuleDef::TypeAlias(_) => { |
39 | if let hir::ModuleDef::Adt(Adt::Enum(e)) = def { | 39 | if let hir::ModuleDef::Adt(Adt::Enum(e)) = def { |
40 | for variant in e.variants(ctx.db) { | 40 | for variant in e.variants(ctx.db) { |
41 | acc.add_enum_variant(ctx, variant); | 41 | acc.add_enum_variant(ctx, variant, None); |
42 | } | 42 | } |
43 | } | 43 | } |
44 | let ty = match def { | 44 | let ty = match def { |
@@ -58,7 +58,7 @@ pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) { | |||
58 | match item { | 58 | match item { |
59 | hir::AssocItem::Function(func) => { | 59 | hir::AssocItem::Function(func) => { |
60 | if !func.has_self_param(ctx.db) { | 60 | if !func.has_self_param(ctx.db) { |
61 | acc.add_function(ctx, func); | 61 | acc.add_function(ctx, func, None); |
62 | } | 62 | } |
63 | } | 63 | } |
64 | hir::AssocItem::Const(ct) => acc.add_const(ctx, ct), | 64 | hir::AssocItem::Const(ct) => acc.add_const(ctx, ct), |
@@ -87,7 +87,7 @@ pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) { | |||
87 | match item { | 87 | match item { |
88 | hir::AssocItem::Function(func) => { | 88 | hir::AssocItem::Function(func) => { |
89 | if !func.has_self_param(ctx.db) { | 89 | if !func.has_self_param(ctx.db) { |
90 | acc.add_function(ctx, func); | 90 | acc.add_function(ctx, func, None); |
91 | } | 91 | } |
92 | } | 92 | } |
93 | hir::AssocItem::Const(ct) => acc.add_const(ctx, ct), | 93 | hir::AssocItem::Const(ct) => acc.add_const(ctx, ct), |
@@ -355,15 +355,17 @@ mod tests { | |||
355 | @r###" | 355 | @r###" |
356 | [ | 356 | [ |
357 | CompletionItem { | 357 | CompletionItem { |
358 | label: "Bar", | 358 | label: "Bar(…)", |
359 | source_range: [116; 116), | 359 | source_range: [116; 116), |
360 | delete: [116; 116), | 360 | delete: [116; 116), |
361 | insert: "Bar", | 361 | insert: "Bar($0)", |
362 | kind: EnumVariant, | 362 | kind: EnumVariant, |
363 | lookup: "Bar", | ||
363 | detail: "(i32)", | 364 | detail: "(i32)", |
364 | documentation: Documentation( | 365 | documentation: Documentation( |
365 | "Bar Variant with i32", | 366 | "Bar Variant with i32", |
366 | ), | 367 | ), |
368 | trigger_call_info: true, | ||
367 | }, | 369 | }, |
368 | CompletionItem { | 370 | CompletionItem { |
369 | label: "Foo", | 371 | label: "Foo", |
@@ -403,15 +405,17 @@ mod tests { | |||
403 | @r###" | 405 | @r###" |
404 | [ | 406 | [ |
405 | CompletionItem { | 407 | CompletionItem { |
406 | label: "Bar", | 408 | label: "Bar(…)", |
407 | source_range: [180; 180), | 409 | source_range: [180; 180), |
408 | delete: [180; 180), | 410 | delete: [180; 180), |
409 | insert: "Bar", | 411 | insert: "Bar($0)", |
410 | kind: EnumVariant, | 412 | kind: EnumVariant, |
413 | lookup: "Bar", | ||
411 | detail: "(i32, u32)", | 414 | detail: "(i32, u32)", |
412 | documentation: Documentation( | 415 | documentation: Documentation( |
413 | "Bar Variant with i32 and u32", | 416 | "Bar Variant with i32 and u32", |
414 | ), | 417 | ), |
418 | trigger_call_info: true, | ||
415 | }, | 419 | }, |
416 | CompletionItem { | 420 | CompletionItem { |
417 | label: "Foo", | 421 | label: "Foo", |
@@ -425,15 +429,17 @@ mod tests { | |||
425 | ), | 429 | ), |
426 | }, | 430 | }, |
427 | CompletionItem { | 431 | CompletionItem { |
428 | label: "S", | 432 | label: "S(…)", |
429 | source_range: [180; 180), | 433 | source_range: [180; 180), |
430 | delete: [180; 180), | 434 | delete: [180; 180), |
431 | insert: "S", | 435 | insert: "S($0)", |
432 | kind: EnumVariant, | 436 | kind: EnumVariant, |
437 | lookup: "S", | ||
433 | detail: "(S)", | 438 | detail: "(S)", |
434 | documentation: Documentation( | 439 | documentation: Documentation( |
435 | "", | 440 | "", |
436 | ), | 441 | ), |
442 | trigger_call_info: true, | ||
437 | }, | 443 | }, |
438 | ] | 444 | ] |
439 | "### | 445 | "### |
diff --git a/crates/ra_ide/src/completion/complete_pattern.rs b/crates/ra_ide/src/completion/complete_pattern.rs index bc8fade6f..1b7d3122f 100644 --- a/crates/ra_ide/src/completion/complete_pattern.rs +++ b/crates/ra_ide/src/completion/complete_pattern.rs | |||
@@ -4,23 +4,25 @@ use crate::completion::{CompletionContext, Completions}; | |||
4 | 4 | ||
5 | /// Completes constats and paths in patterns. | 5 | /// Completes constats and paths in patterns. |
6 | pub(super) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) { | 6 | pub(super) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) { |
7 | if !ctx.is_pat_binding { | 7 | if !ctx.is_pat_binding_or_const { |
8 | return; | 8 | return; |
9 | } | 9 | } |
10 | // FIXME: ideally, we should look at the type we are matching against and | 10 | // FIXME: ideally, we should look at the type we are matching against and |
11 | // suggest variants + auto-imports | 11 | // suggest variants + auto-imports |
12 | ctx.scope().process_all_names(&mut |name, res| { | 12 | ctx.scope().process_all_names(&mut |name, res| { |
13 | let def = match &res { | 13 | match &res { |
14 | hir::ScopeDef::ModuleDef(def) => def, | 14 | hir::ScopeDef::ModuleDef(def) => match def { |
15 | hir::ModuleDef::Adt(hir::Adt::Enum(..)) | ||
16 | | hir::ModuleDef::Adt(hir::Adt::Struct(..)) | ||
17 | | hir::ModuleDef::EnumVariant(..) | ||
18 | | hir::ModuleDef::Const(..) | ||
19 | | hir::ModuleDef::Module(..) => (), | ||
20 | _ => return, | ||
21 | }, | ||
22 | hir::ScopeDef::MacroDef(_) => (), | ||
15 | _ => return, | 23 | _ => return, |
16 | }; | 24 | }; |
17 | match def { | 25 | |
18 | hir::ModuleDef::Adt(hir::Adt::Enum(..)) | ||
19 | | hir::ModuleDef::EnumVariant(..) | ||
20 | | hir::ModuleDef::Const(..) | ||
21 | | hir::ModuleDef::Module(..) => (), | ||
22 | _ => return, | ||
23 | } | ||
24 | acc.add_resolution(ctx, name.to_string(), &res) | 26 | acc.add_resolution(ctx, name.to_string(), &res) |
25 | }); | 27 | }); |
26 | } | 28 | } |
@@ -70,32 +72,12 @@ mod tests { | |||
70 | kind: Enum, | 72 | kind: Enum, |
71 | }, | 73 | }, |
72 | CompletionItem { | 74 | CompletionItem { |
73 | label: "E", | ||
74 | source_range: [246; 246), | ||
75 | delete: [246; 246), | ||
76 | insert: "E", | ||
77 | kind: Enum, | ||
78 | }, | ||
79 | CompletionItem { | ||
80 | label: "X", | 75 | label: "X", |
81 | source_range: [246; 246), | 76 | source_range: [246; 246), |
82 | delete: [246; 246), | 77 | delete: [246; 246), |
83 | insert: "X", | 78 | insert: "X", |
84 | kind: EnumVariant, | 79 | kind: EnumVariant, |
85 | }, | 80 | detail: "()", |
86 | CompletionItem { | ||
87 | label: "X", | ||
88 | source_range: [246; 246), | ||
89 | delete: [246; 246), | ||
90 | insert: "X", | ||
91 | kind: EnumVariant, | ||
92 | }, | ||
93 | CompletionItem { | ||
94 | label: "Z", | ||
95 | source_range: [246; 246), | ||
96 | delete: [246; 246), | ||
97 | insert: "Z", | ||
98 | kind: Const, | ||
99 | }, | 81 | }, |
100 | CompletionItem { | 82 | CompletionItem { |
101 | label: "Z", | 83 | label: "Z", |
@@ -111,13 +93,6 @@ mod tests { | |||
111 | insert: "m", | 93 | insert: "m", |
112 | kind: Module, | 94 | kind: Module, |
113 | }, | 95 | }, |
114 | CompletionItem { | ||
115 | label: "m", | ||
116 | source_range: [246; 246), | ||
117 | delete: [246; 246), | ||
118 | insert: "m", | ||
119 | kind: Module, | ||
120 | }, | ||
121 | ] | 96 | ] |
122 | "###); | 97 | "###); |
123 | } | 98 | } |
@@ -146,13 +121,6 @@ mod tests { | |||
146 | kind: Enum, | 121 | kind: Enum, |
147 | }, | 122 | }, |
148 | CompletionItem { | 123 | CompletionItem { |
149 | label: "E", | ||
150 | source_range: [151; 151), | ||
151 | delete: [151; 151), | ||
152 | insert: "E", | ||
153 | kind: Enum, | ||
154 | }, | ||
155 | CompletionItem { | ||
156 | label: "m!", | 124 | label: "m!", |
157 | source_range: [151; 151), | 125 | source_range: [151; 151), |
158 | delete: [151; 151), | 126 | delete: [151; 151), |
diff --git a/crates/ra_ide/src/completion/complete_scope.rs b/crates/ra_ide/src/completion/complete_scope.rs index 2ca552733..665597e4c 100644 --- a/crates/ra_ide/src/completion/complete_scope.rs +++ b/crates/ra_ide/src/completion/complete_scope.rs | |||
@@ -1,19 +1,13 @@ | |||
1 | //! Completion of names from the current scope, e.g. locals and imported items. | 1 | //! Completion of names from the current scope, e.g. locals and imported items. |
2 | 2 | ||
3 | use crate::completion::{CompletionContext, Completions}; | 3 | use crate::completion::{CompletionContext, Completions}; |
4 | use hir::{ModuleDef, ScopeDef}; | ||
5 | 4 | ||
6 | pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) { | 5 | pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) { |
7 | if !ctx.is_trivial_path && !ctx.is_pat_binding_and_path { | 6 | if !(ctx.is_trivial_path && !ctx.is_pat_binding_or_const) { |
8 | return; | 7 | return; |
9 | } | 8 | } |
10 | 9 | ||
11 | ctx.scope().process_all_names(&mut |name, res| match (ctx.is_pat_binding_and_path, &res) { | 10 | ctx.scope().process_all_names(&mut |name, res| acc.add_resolution(ctx, name.to_string(), &res)); |
12 | (true, ScopeDef::ModuleDef(ModuleDef::Function(..))) => (), | ||
13 | (true, ScopeDef::ModuleDef(ModuleDef::Static(..))) => (), | ||
14 | (true, ScopeDef::Local(..)) => (), | ||
15 | _ => acc.add_resolution(ctx, name.to_string(), &res), | ||
16 | }); | ||
17 | } | 11 | } |
18 | 12 | ||
19 | #[cfg(test)] | 13 | #[cfg(test)] |
diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs index fdc0da2c5..b8213d62f 100644 --- a/crates/ra_ide/src/completion/completion_context.rs +++ b/crates/ra_ide/src/completion/completion_context.rs | |||
@@ -35,10 +35,7 @@ pub(crate) struct CompletionContext<'a> { | |||
35 | pub(super) is_param: bool, | 35 | pub(super) is_param: bool, |
36 | /// If a name-binding or reference to a const in a pattern. | 36 | /// If a name-binding or reference to a const in a pattern. |
37 | /// Irrefutable patterns (like let) are excluded. | 37 | /// Irrefutable patterns (like let) are excluded. |
38 | pub(super) is_pat_binding: bool, | 38 | pub(super) is_pat_binding_or_const: bool, |
39 | // A bind battern which may also be part of a path. | ||
40 | // if let Some(En<|>) = Some(Enum::A) | ||
41 | pub(super) is_pat_binding_and_path: bool, | ||
42 | /// A single-indent path, like `foo`. `::foo` should not be considered a trivial path. | 39 | /// A single-indent path, like `foo`. `::foo` should not be considered a trivial path. |
43 | pub(super) is_trivial_path: bool, | 40 | pub(super) is_trivial_path: bool, |
44 | /// If not a trivial path, the prefix (qualifier). | 41 | /// If not a trivial path, the prefix (qualifier). |
@@ -97,8 +94,7 @@ impl<'a> CompletionContext<'a> { | |||
97 | record_lit_pat: None, | 94 | record_lit_pat: None, |
98 | impl_def: None, | 95 | impl_def: None, |
99 | is_param: false, | 96 | is_param: false, |
100 | is_pat_binding: false, | 97 | is_pat_binding_or_const: false, |
101 | is_pat_binding_and_path: false, | ||
102 | is_trivial_path: false, | 98 | is_trivial_path: false, |
103 | path_prefix: None, | 99 | path_prefix: None, |
104 | after_if: false, | 100 | after_if: false, |
@@ -190,18 +186,19 @@ impl<'a> CompletionContext<'a> { | |||
190 | // suggest declaration names, see `CompletionKind::Magic`. | 186 | // suggest declaration names, see `CompletionKind::Magic`. |
191 | if let Some(name) = find_node_at_offset::<ast::Name>(&file_with_fake_ident, offset) { | 187 | if let Some(name) = find_node_at_offset::<ast::Name>(&file_with_fake_ident, offset) { |
192 | if let Some(bind_pat) = name.syntax().ancestors().find_map(ast::BindPat::cast) { | 188 | if let Some(bind_pat) = name.syntax().ancestors().find_map(ast::BindPat::cast) { |
193 | let parent = bind_pat.syntax().parent(); | 189 | self.is_pat_binding_or_const = true; |
194 | if parent.clone().and_then(ast::MatchArm::cast).is_some() | 190 | if bind_pat.has_at() || bind_pat.is_ref() || bind_pat.is_mutable() { |
195 | || parent.clone().and_then(ast::Condition::cast).is_some() | 191 | self.is_pat_binding_or_const = false; |
196 | { | ||
197 | self.is_pat_binding = true; | ||
198 | } | 192 | } |
199 | 193 | if bind_pat.syntax().parent().and_then(ast::RecordFieldPatList::cast).is_some() { | |
200 | if parent.and_then(ast::RecordFieldPatList::cast).is_none() | 194 | self.is_pat_binding_or_const = false; |
201 | && bind_pat.pat().is_none() | 195 | } |
202 | && !bind_pat.is_ref() | 196 | if let Some(let_stmt) = bind_pat.syntax().ancestors().find_map(ast::LetStmt::cast) { |
203 | { | 197 | if let Some(pat) = let_stmt.pat() { |
204 | self.is_pat_binding_and_path = true; | 198 | if bind_pat.syntax().text_range().is_subrange(&pat.syntax().text_range()) { |
199 | self.is_pat_binding_or_const = false; | ||
200 | } | ||
201 | } | ||
205 | } | 202 | } |
206 | } | 203 | } |
207 | if is_node::<ast::Param>(name.syntax()) { | 204 | if is_node::<ast::Param>(name.syntax()) { |
diff --git a/crates/ra_ide/src/completion/presentation.rs b/crates/ra_ide/src/completion/presentation.rs index 1c7c0924d..cdfd7bc32 100644 --- a/crates/ra_ide/src/completion/presentation.rs +++ b/crates/ra_ide/src/completion/presentation.rs | |||
@@ -7,7 +7,8 @@ use test_utils::tested_by; | |||
7 | 7 | ||
8 | use crate::{ | 8 | use crate::{ |
9 | completion::{ | 9 | completion::{ |
10 | CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions, | 10 | completion_item::Builder, CompletionContext, CompletionItem, CompletionItemKind, |
11 | CompletionKind, Completions, | ||
11 | }, | 12 | }, |
12 | display::{const_label, macro_label, type_label, FunctionSignature}, | 13 | display::{const_label, macro_label, type_label, FunctionSignature}, |
13 | RootDatabase, | 14 | RootDatabase, |
@@ -56,14 +57,16 @@ impl Completions { | |||
56 | let kind = match resolution { | 57 | let kind = match resolution { |
57 | ScopeDef::ModuleDef(Module(..)) => CompletionItemKind::Module, | 58 | ScopeDef::ModuleDef(Module(..)) => CompletionItemKind::Module, |
58 | ScopeDef::ModuleDef(Function(func)) => { | 59 | ScopeDef::ModuleDef(Function(func)) => { |
59 | return self.add_function_with_name(ctx, Some(local_name), *func); | 60 | return self.add_function(ctx, *func, Some(local_name)); |
60 | } | 61 | } |
61 | ScopeDef::ModuleDef(Adt(hir::Adt::Struct(_))) => CompletionItemKind::Struct, | 62 | ScopeDef::ModuleDef(Adt(hir::Adt::Struct(_))) => CompletionItemKind::Struct, |
62 | // FIXME: add CompletionItemKind::Union | 63 | // FIXME: add CompletionItemKind::Union |
63 | ScopeDef::ModuleDef(Adt(hir::Adt::Union(_))) => CompletionItemKind::Struct, | 64 | ScopeDef::ModuleDef(Adt(hir::Adt::Union(_))) => CompletionItemKind::Struct, |
64 | ScopeDef::ModuleDef(Adt(hir::Adt::Enum(_))) => CompletionItemKind::Enum, | 65 | ScopeDef::ModuleDef(Adt(hir::Adt::Enum(_))) => CompletionItemKind::Enum, |
65 | 66 | ||
66 | ScopeDef::ModuleDef(EnumVariant(..)) => CompletionItemKind::EnumVariant, | 67 | ScopeDef::ModuleDef(EnumVariant(var)) => { |
68 | return self.add_enum_variant(ctx, *var, Some(local_name)); | ||
69 | } | ||
67 | ScopeDef::ModuleDef(Const(..)) => CompletionItemKind::Const, | 70 | ScopeDef::ModuleDef(Const(..)) => CompletionItemKind::Const, |
68 | ScopeDef::ModuleDef(Static(..)) => CompletionItemKind::Static, | 71 | ScopeDef::ModuleDef(Static(..)) => CompletionItemKind::Static, |
69 | ScopeDef::ModuleDef(Trait(..)) => CompletionItemKind::Trait, | 72 | ScopeDef::ModuleDef(Trait(..)) => CompletionItemKind::Trait, |
@@ -124,10 +127,6 @@ impl Completions { | |||
124 | completion_item.kind(kind).set_documentation(docs).add_to(self) | 127 | completion_item.kind(kind).set_documentation(docs).add_to(self) |
125 | } | 128 | } |
126 | 129 | ||
127 | pub(crate) fn add_function(&mut self, ctx: &CompletionContext, func: hir::Function) { | ||
128 | self.add_function_with_name(ctx, None, func) | ||
129 | } | ||
130 | |||
131 | fn guess_macro_braces(&self, macro_name: &str, docs: &str) -> &'static str { | 130 | fn guess_macro_braces(&self, macro_name: &str, docs: &str) -> &'static str { |
132 | let mut votes = [0, 0, 0]; | 131 | let mut votes = [0, 0, 0]; |
133 | for (idx, s) in docs.match_indices(¯o_name) { | 132 | for (idx, s) in docs.match_indices(¯o_name) { |
@@ -186,16 +185,15 @@ impl Completions { | |||
186 | self.add(builder); | 185 | self.add(builder); |
187 | } | 186 | } |
188 | 187 | ||
189 | fn add_function_with_name( | 188 | pub(crate) fn add_function( |
190 | &mut self, | 189 | &mut self, |
191 | ctx: &CompletionContext, | 190 | ctx: &CompletionContext, |
192 | name: Option<String>, | ||
193 | func: hir::Function, | 191 | func: hir::Function, |
192 | local_name: Option<String>, | ||
194 | ) { | 193 | ) { |
195 | let has_self_param = func.has_self_param(ctx.db); | 194 | let has_self_param = func.has_self_param(ctx.db); |
196 | let params = func.params(ctx.db); | ||
197 | 195 | ||
198 | let name = name.unwrap_or_else(|| func.name(ctx.db).to_string()); | 196 | let name = local_name.unwrap_or_else(|| func.name(ctx.db).to_string()); |
199 | let ast_node = func.source(ctx.db).value; | 197 | let ast_node = func.source(ctx.db).value; |
200 | let function_signature = FunctionSignature::from(&ast_node); | 198 | let function_signature = FunctionSignature::from(&ast_node); |
201 | 199 | ||
@@ -210,32 +208,14 @@ impl Completions { | |||
210 | .set_deprecated(is_deprecated(func, ctx.db)) | 208 | .set_deprecated(is_deprecated(func, ctx.db)) |
211 | .detail(function_signature.to_string()); | 209 | .detail(function_signature.to_string()); |
212 | 210 | ||
213 | // If not an import, add parenthesis automatically. | 211 | let params = function_signature |
214 | if ctx.use_item_syntax.is_none() && !ctx.is_call && ctx.config.add_call_parenthesis { | 212 | .parameter_names |
215 | tested_by!(inserts_parens_for_function_calls); | 213 | .iter() |
216 | 214 | .skip(if function_signature.has_self_param { 1 } else { 0 }) | |
217 | let (snippet, label) = if params.is_empty() || has_self_param && params.len() == 1 { | 215 | .cloned() |
218 | (format!("{}()$0", name), format!("{}()", name)) | 216 | .collect(); |
219 | } else { | ||
220 | builder = builder.trigger_call_info(); | ||
221 | let snippet = if ctx.config.add_call_argument_snippets { | ||
222 | let to_skip = if has_self_param { 1 } else { 0 }; | ||
223 | let function_params_snippet = function_signature | ||
224 | .parameter_names | ||
225 | .iter() | ||
226 | .skip(to_skip) | ||
227 | .enumerate() | ||
228 | .map(|(index, param_name)| format!("${{{}:{}}}", index + 1, param_name)) | ||
229 | .sep_by(", "); | ||
230 | format!("{}({})$0", name, function_params_snippet) | ||
231 | } else { | ||
232 | format!("{}($0)", name) | ||
233 | }; | ||
234 | 217 | ||
235 | (snippet, format!("{}(…)", name)) | 218 | builder = builder.add_call_parens(ctx, name, Params::Named(params)); |
236 | }; | ||
237 | builder = builder.lookup_by(name).label(label).insert_snippet(snippet); | ||
238 | } | ||
239 | 219 | ||
240 | self.add(builder) | 220 | self.add(builder) |
241 | } | 221 | } |
@@ -272,14 +252,20 @@ impl Completions { | |||
272 | .add_to(self); | 252 | .add_to(self); |
273 | } | 253 | } |
274 | 254 | ||
275 | pub(crate) fn add_enum_variant(&mut self, ctx: &CompletionContext, variant: hir::EnumVariant) { | 255 | pub(crate) fn add_enum_variant( |
256 | &mut self, | ||
257 | ctx: &CompletionContext, | ||
258 | variant: hir::EnumVariant, | ||
259 | local_name: Option<String>, | ||
260 | ) { | ||
276 | let is_deprecated = is_deprecated(variant, ctx.db); | 261 | let is_deprecated = is_deprecated(variant, ctx.db); |
277 | let name = variant.name(ctx.db); | 262 | let name = local_name.unwrap_or_else(|| variant.name(ctx.db).to_string()); |
278 | let detail_types = variant | 263 | let detail_types = variant |
279 | .fields(ctx.db) | 264 | .fields(ctx.db) |
280 | .into_iter() | 265 | .into_iter() |
281 | .map(|field| (field.name(ctx.db), field.signature_ty(ctx.db))); | 266 | .map(|field| (field.name(ctx.db), field.signature_ty(ctx.db))); |
282 | let detail = match variant.kind(ctx.db) { | 267 | let variant_kind = variant.kind(ctx.db); |
268 | let detail = match variant_kind { | ||
283 | StructKind::Tuple | StructKind::Unit => detail_types | 269 | StructKind::Tuple | StructKind::Unit => detail_types |
284 | .map(|(_, t)| t.display(ctx.db).to_string()) | 270 | .map(|(_, t)| t.display(ctx.db).to_string()) |
285 | .sep_by(", ") | 271 | .sep_by(", ") |
@@ -291,12 +277,70 @@ impl Completions { | |||
291 | .surround_with("{ ", " }") | 277 | .surround_with("{ ", " }") |
292 | .to_string(), | 278 | .to_string(), |
293 | }; | 279 | }; |
294 | CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.to_string()) | 280 | let mut res = |
295 | .kind(CompletionItemKind::EnumVariant) | 281 | CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.clone()) |
296 | .set_documentation(variant.docs(ctx.db)) | 282 | .kind(CompletionItemKind::EnumVariant) |
297 | .set_deprecated(is_deprecated) | 283 | .set_documentation(variant.docs(ctx.db)) |
298 | .detail(detail) | 284 | .set_deprecated(is_deprecated) |
299 | .add_to(self); | 285 | .detail(detail); |
286 | |||
287 | if variant_kind == StructKind::Tuple { | ||
288 | let params = Params::Anonymous(variant.fields(ctx.db).len()); | ||
289 | res = res.add_call_parens(ctx, name, params) | ||
290 | } | ||
291 | |||
292 | res.add_to(self); | ||
293 | } | ||
294 | } | ||
295 | |||
296 | enum Params { | ||
297 | Named(Vec<String>), | ||
298 | Anonymous(usize), | ||
299 | } | ||
300 | |||
301 | impl Params { | ||
302 | fn len(&self) -> usize { | ||
303 | match self { | ||
304 | Params::Named(xs) => xs.len(), | ||
305 | Params::Anonymous(len) => *len, | ||
306 | } | ||
307 | } | ||
308 | |||
309 | fn is_empty(&self) -> bool { | ||
310 | self.len() == 0 | ||
311 | } | ||
312 | } | ||
313 | |||
314 | impl Builder { | ||
315 | fn add_call_parens(mut self, ctx: &CompletionContext, name: String, params: Params) -> Builder { | ||
316 | if !ctx.config.add_call_parenthesis { | ||
317 | return self; | ||
318 | } | ||
319 | if ctx.use_item_syntax.is_some() || ctx.is_call { | ||
320 | return self; | ||
321 | } | ||
322 | // If not an import, add parenthesis automatically. | ||
323 | tested_by!(inserts_parens_for_function_calls); | ||
324 | |||
325 | let (snippet, label) = if params.is_empty() { | ||
326 | (format!("{}()$0", name), format!("{}()", name)) | ||
327 | } else { | ||
328 | self = self.trigger_call_info(); | ||
329 | let snippet = match (ctx.config.add_call_argument_snippets, params) { | ||
330 | (true, Params::Named(params)) => { | ||
331 | let function_params_snippet = params | ||
332 | .iter() | ||
333 | .enumerate() | ||
334 | .map(|(index, param_name)| format!("${{{}:{}}}", index + 1, param_name)) | ||
335 | .sep_by(", "); | ||
336 | format!("{}({})$0", name, function_params_snippet) | ||
337 | } | ||
338 | _ => format!("{}($0)", name), | ||
339 | }; | ||
340 | |||
341 | (snippet, format!("{}(…)", name)) | ||
342 | }; | ||
343 | self.lookup_by(name).label(label).insert_snippet(snippet) | ||
300 | } | 344 | } |
301 | } | 345 | } |
302 | 346 | ||
@@ -366,12 +410,14 @@ mod tests { | |||
366 | @r###" | 410 | @r###" |
367 | [ | 411 | [ |
368 | CompletionItem { | 412 | CompletionItem { |
369 | label: "Foo", | 413 | label: "Foo(…)", |
370 | source_range: [115; 117), | 414 | source_range: [115; 117), |
371 | delete: [115; 117), | 415 | delete: [115; 117), |
372 | insert: "Foo", | 416 | insert: "Foo($0)", |
373 | kind: EnumVariant, | 417 | kind: EnumVariant, |
418 | lookup: "Foo", | ||
374 | detail: "(i32, i32)", | 419 | detail: "(i32, i32)", |
420 | trigger_call_info: true, | ||
375 | }, | 421 | }, |
376 | ]"### | 422 | ]"### |
377 | ); | 423 | ); |
@@ -546,6 +592,101 @@ mod tests { | |||
546 | } | 592 | } |
547 | 593 | ||
548 | #[test] | 594 | #[test] |
595 | fn inserts_parens_for_tuple_enums() { | ||
596 | assert_debug_snapshot!( | ||
597 | do_reference_completion( | ||
598 | r" | ||
599 | enum Option<T> { Some(T), None } | ||
600 | use Option::*; | ||
601 | fn main() -> Option<i32> { | ||
602 | Som<|> | ||
603 | } | ||
604 | " | ||
605 | ), | ||
606 | @r###" | ||
607 | [ | ||
608 | CompletionItem { | ||
609 | label: "None", | ||
610 | source_range: [144; 147), | ||
611 | delete: [144; 147), | ||
612 | insert: "None", | ||
613 | kind: EnumVariant, | ||
614 | detail: "()", | ||
615 | }, | ||
616 | CompletionItem { | ||
617 | label: "Option", | ||
618 | source_range: [144; 147), | ||
619 | delete: [144; 147), | ||
620 | insert: "Option", | ||
621 | kind: Enum, | ||
622 | }, | ||
623 | CompletionItem { | ||
624 | label: "Some(…)", | ||
625 | source_range: [144; 147), | ||
626 | delete: [144; 147), | ||
627 | insert: "Some($0)", | ||
628 | kind: EnumVariant, | ||
629 | lookup: "Some", | ||
630 | detail: "(T)", | ||
631 | trigger_call_info: true, | ||
632 | }, | ||
633 | CompletionItem { | ||
634 | label: "main()", | ||
635 | source_range: [144; 147), | ||
636 | delete: [144; 147), | ||
637 | insert: "main()$0", | ||
638 | kind: Function, | ||
639 | lookup: "main", | ||
640 | detail: "fn main() -> Option<i32>", | ||
641 | }, | ||
642 | ] | ||
643 | "### | ||
644 | ); | ||
645 | assert_debug_snapshot!( | ||
646 | do_reference_completion( | ||
647 | r" | ||
648 | enum Option<T> { Some(T), None } | ||
649 | use Option::*; | ||
650 | fn main(value: Option<i32>) { | ||
651 | match value { | ||
652 | Som<|> | ||
653 | } | ||
654 | } | ||
655 | " | ||
656 | ), | ||
657 | @r###" | ||
658 | [ | ||
659 | CompletionItem { | ||
660 | label: "None", | ||
661 | source_range: [185; 188), | ||
662 | delete: [185; 188), | ||
663 | insert: "None", | ||
664 | kind: EnumVariant, | ||
665 | detail: "()", | ||
666 | }, | ||
667 | CompletionItem { | ||
668 | label: "Option", | ||
669 | source_range: [185; 188), | ||
670 | delete: [185; 188), | ||
671 | insert: "Option", | ||
672 | kind: Enum, | ||
673 | }, | ||
674 | CompletionItem { | ||
675 | label: "Some(…)", | ||
676 | source_range: [185; 188), | ||
677 | delete: [185; 188), | ||
678 | insert: "Some($0)", | ||
679 | kind: EnumVariant, | ||
680 | lookup: "Some", | ||
681 | detail: "(T)", | ||
682 | trigger_call_info: true, | ||
683 | }, | ||
684 | ] | ||
685 | "### | ||
686 | ); | ||
687 | } | ||
688 | |||
689 | #[test] | ||
549 | fn arg_snippets_for_method_call() { | 690 | fn arg_snippets_for_method_call() { |
550 | assert_debug_snapshot!( | 691 | assert_debug_snapshot!( |
551 | do_reference_completion( | 692 | do_reference_completion( |