aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ra_ide/src/completion/complete_dot.rs2
-rw-r--r--crates/ra_ide/src/completion/complete_path.rs24
-rw-r--r--crates/ra_ide/src/completion/complete_pattern.rs58
-rw-r--r--crates/ra_ide/src/completion/complete_scope.rs10
-rw-r--r--crates/ra_ide/src/completion/completion_context.rs31
-rw-r--r--crates/ra_ide/src/completion/presentation.rs235
-rw-r--r--crates/ra_syntax/src/ast/extensions.rs3
7 files changed, 236 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.
6pub(super) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) { 6pub(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
3use crate::completion::{CompletionContext, Completions}; 3use crate::completion::{CompletionContext, Completions};
4use hir::{ModuleDef, ScopeDef};
5 4
6pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) { 5pub(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
8use crate::{ 8use 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(&macro_name) { 132 for (idx, s) in docs.match_indices(&macro_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
296enum Params {
297 Named(Vec<String>),
298 Anonymous(usize),
299}
300
301impl 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
314impl 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(
diff --git a/crates/ra_syntax/src/ast/extensions.rs b/crates/ra_syntax/src/ast/extensions.rs
index 392731dac..bf7d137be 100644
--- a/crates/ra_syntax/src/ast/extensions.rs
+++ b/crates/ra_syntax/src/ast/extensions.rs
@@ -325,6 +325,9 @@ impl ast::BindPat {
325 pub fn is_ref(&self) -> bool { 325 pub fn is_ref(&self) -> bool {
326 self.syntax().children_with_tokens().any(|n| n.kind() == T![ref]) 326 self.syntax().children_with_tokens().any(|n| n.kind() == T![ref])
327 } 327 }
328 pub fn has_at(&self) -> bool {
329 self.syntax().children_with_tokens().any(|it| it.kind() == T![@])
330 }
328} 331}
329 332
330pub struct SlicePatComponents { 333pub struct SlicePatComponents {