diff options
Diffstat (limited to 'crates/ra_ide/src/completion/presentation.rs')
-rw-r--r-- | crates/ra_ide/src/completion/presentation.rs | 235 |
1 files changed, 188 insertions, 47 deletions
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( |