aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide_api/src/completion/presentation.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ide_api/src/completion/presentation.rs')
-rw-r--r--crates/ra_ide_api/src/completion/presentation.rs389
1 files changed, 288 insertions, 101 deletions
diff --git a/crates/ra_ide_api/src/completion/presentation.rs b/crates/ra_ide_api/src/completion/presentation.rs
index 48028a2f9..aed4ce6d4 100644
--- a/crates/ra_ide_api/src/completion/presentation.rs
+++ b/crates/ra_ide_api/src/completion/presentation.rs
@@ -1,12 +1,12 @@
1//! This modules takes care of rendering various definitions as completion items. 1//! This modules takes care of rendering various definitions as completion items.
2 2
3use hir::{Docs, HasSource, HirDisplay, ScopeDef, Ty, TypeWalk}; 3use hir::{db::HirDatabase, Docs, HasSource, HirDisplay, ScopeDef, Ty, TypeWalk};
4use join_to_string::join; 4use join_to_string::join;
5use ra_syntax::ast::NameOwner; 5use ra_syntax::ast::NameOwner;
6use test_utils::tested_by; 6use test_utils::tested_by;
7 7
8use crate::completion::{ 8use crate::completion::{
9 CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions, 9 db, CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions,
10}; 10};
11 11
12use crate::display::{const_label, function_label, macro_label, type_label}; 12use crate::display::{const_label, function_label, macro_label, type_label};
@@ -44,54 +44,56 @@ impl Completions {
44 ) { 44 ) {
45 use hir::ModuleDef::*; 45 use hir::ModuleDef::*;
46 46
47 let mut completion_kind = CompletionKind::Reference; 47 let completion_kind = match resolution {
48 let (kind, docs) = match resolution { 48 ScopeDef::ModuleDef(BuiltinType(..)) => CompletionKind::BuiltinType,
49 ScopeDef::ModuleDef(Module(it)) => (CompletionItemKind::Module, it.docs(ctx.db)), 49 _ => CompletionKind::Reference,
50 };
51
52 let kind = match resolution {
53 ScopeDef::ModuleDef(Module(..)) => CompletionItemKind::Module,
50 ScopeDef::ModuleDef(Function(func)) => { 54 ScopeDef::ModuleDef(Function(func)) => {
51 return self.add_function_with_name(ctx, Some(local_name), *func); 55 return self.add_function_with_name(ctx, Some(local_name), *func);
52 } 56 }
53 ScopeDef::ModuleDef(Adt(hir::Adt::Struct(it))) => { 57 ScopeDef::ModuleDef(Adt(hir::Adt::Struct(_))) => CompletionItemKind::Struct,
54 (CompletionItemKind::Struct, it.docs(ctx.db)) 58 // FIXME: add CompletionItemKind::Union
55 } 59 ScopeDef::ModuleDef(Adt(hir::Adt::Union(_))) => CompletionItemKind::Struct,
56 ScopeDef::ModuleDef(Adt(hir::Adt::Union(it))) => { 60 ScopeDef::ModuleDef(Adt(hir::Adt::Enum(_))) => CompletionItemKind::Enum,
57 (CompletionItemKind::Struct, it.docs(ctx.db)) 61
58 } 62 ScopeDef::ModuleDef(EnumVariant(..)) => CompletionItemKind::EnumVariant,
59 ScopeDef::ModuleDef(Adt(hir::Adt::Enum(it))) => { 63 ScopeDef::ModuleDef(Const(..)) => CompletionItemKind::Const,
60 (CompletionItemKind::Enum, it.docs(ctx.db)) 64 ScopeDef::ModuleDef(Static(..)) => CompletionItemKind::Static,
61 } 65 ScopeDef::ModuleDef(Trait(..)) => CompletionItemKind::Trait,
62 ScopeDef::ModuleDef(EnumVariant(it)) => { 66 ScopeDef::ModuleDef(TypeAlias(..)) => CompletionItemKind::TypeAlias,
63 (CompletionItemKind::EnumVariant, it.docs(ctx.db)) 67 ScopeDef::ModuleDef(BuiltinType(..)) => CompletionItemKind::BuiltinType,
64 } 68 ScopeDef::GenericParam(..) => CompletionItemKind::TypeParam,
65 ScopeDef::ModuleDef(Const(it)) => (CompletionItemKind::Const, it.docs(ctx.db)), 69 ScopeDef::LocalBinding(..) => CompletionItemKind::Binding,
66 ScopeDef::ModuleDef(Static(it)) => (CompletionItemKind::Static, it.docs(ctx.db)), 70 // (does this need its own kind?)
67 ScopeDef::ModuleDef(Trait(it)) => (CompletionItemKind::Trait, it.docs(ctx.db)), 71 ScopeDef::AdtSelfType(..) | ScopeDef::ImplSelfType(..) => CompletionItemKind::TypeParam,
68 ScopeDef::ModuleDef(TypeAlias(it)) => (CompletionItemKind::TypeAlias, it.docs(ctx.db)),
69 ScopeDef::ModuleDef(BuiltinType(..)) => {
70 completion_kind = CompletionKind::BuiltinType;
71 (CompletionItemKind::BuiltinType, None)
72 }
73 ScopeDef::GenericParam(..) => (CompletionItemKind::TypeParam, None),
74 ScopeDef::LocalBinding(..) => (CompletionItemKind::Binding, None),
75 ScopeDef::SelfType(..) => (
76 CompletionItemKind::TypeParam, // (does this need its own kind?)
77 None,
78 ),
79 ScopeDef::MacroDef(mac) => { 72 ScopeDef::MacroDef(mac) => {
80 self.add_macro(ctx, Some(local_name), *mac); 73 return self.add_macro(ctx, Some(local_name), *mac);
81 return;
82 } 74 }
83 ScopeDef::Unknown => { 75 ScopeDef::Unknown => {
84 self.add(CompletionItem::new( 76 return self.add(CompletionItem::new(
85 CompletionKind::Reference, 77 CompletionKind::Reference,
86 ctx.source_range(), 78 ctx.source_range(),
87 local_name, 79 local_name,
88 )); 80 ));
89 return;
90 } 81 }
91 }; 82 };
92 83
84 let docs = match resolution {
85 ScopeDef::ModuleDef(Module(it)) => it.docs(ctx.db),
86 ScopeDef::ModuleDef(Adt(it)) => it.docs(ctx.db),
87 ScopeDef::ModuleDef(EnumVariant(it)) => it.docs(ctx.db),
88 ScopeDef::ModuleDef(Const(it)) => it.docs(ctx.db),
89 ScopeDef::ModuleDef(Static(it)) => it.docs(ctx.db),
90 ScopeDef::ModuleDef(Trait(it)) => it.docs(ctx.db),
91 ScopeDef::ModuleDef(TypeAlias(it)) => it.docs(ctx.db),
92 _ => None,
93 };
94
93 let mut completion_item = 95 let mut completion_item =
94 CompletionItem::new(completion_kind, ctx.source_range(), local_name); 96 CompletionItem::new(completion_kind, ctx.source_range(), local_name.clone());
95 if let ScopeDef::LocalBinding(pat_id) = resolution { 97 if let ScopeDef::LocalBinding(pat_id) = resolution {
96 let ty = ctx 98 let ty = ctx
97 .analyzer 99 .analyzer
@@ -100,6 +102,28 @@ impl Completions {
100 .map(|t| t.display(ctx.db).to_string()); 102 .map(|t| t.display(ctx.db).to_string());
101 completion_item = completion_item.set_detail(ty); 103 completion_item = completion_item.set_detail(ty);
102 }; 104 };
105
106 // If not an import, add parenthesis automatically.
107 if ctx.is_path_type
108 && !ctx.has_type_args
109 && ctx.db.feature_flags.get("completion.insertion.add-call-parenthesis")
110 {
111 let generic_def: Option<hir::GenericDef> = match resolution {
112 ScopeDef::ModuleDef(Adt(it)) => Some((*it).into()),
113 ScopeDef::ModuleDef(TypeAlias(it)) => Some((*it).into()),
114 _ => None,
115 };
116 if let Some(def) = generic_def {
117 if has_non_default_type_params(def, ctx.db) {
118 tested_by!(inserts_angle_brackets_for_generics);
119 completion_item = completion_item
120 .lookup_by(local_name.clone())
121 .label(format!("{}<…>", local_name))
122 .insert_snippet(format!("{}<$0>", local_name));
123 }
124 }
125 }
126
103 completion_item.kind(kind).set_documentation(docs).add_to(self) 127 completion_item.kind(kind).set_documentation(docs).add_to(self)
104 } 128 }
105 129
@@ -107,6 +131,33 @@ impl Completions {
107 self.add_function_with_name(ctx, None, func) 131 self.add_function_with_name(ctx, None, func)
108 } 132 }
109 133
134 fn guess_macro_braces(&self, macro_name: &str, docs: &str) -> &'static str {
135 let mut votes = [0, 0, 0];
136 for (idx, s) in docs.match_indices(&macro_name) {
137 let (before, after) = (&docs[..idx], &docs[idx + s.len()..]);
138 // Ensure to match the full word
139 if after.starts_with("!")
140 && before
141 .chars()
142 .rev()
143 .next()
144 .map_or(true, |c| c != '_' && !c.is_ascii_alphanumeric())
145 {
146 // It may have spaces before the braces like `foo! {}`
147 match after[1..].chars().find(|&c| !c.is_whitespace()) {
148 Some('{') => votes[0] += 1,
149 Some('[') => votes[1] += 1,
150 Some('(') => votes[2] += 1,
151 _ => {}
152 }
153 }
154 }
155
156 // Insert a space before `{}`.
157 // We prefer the last one when some votes equal.
158 *votes.iter().zip(&[" {$0}", "[$0]", "($0)"]).max_by_key(|&(&vote, _)| vote).unwrap().1
159 }
160
110 pub(crate) fn add_macro( 161 pub(crate) fn add_macro(
111 &mut self, 162 &mut self,
112 ctx: &CompletionContext, 163 ctx: &CompletionContext,
@@ -117,10 +168,9 @@ impl Completions {
117 if let Some(name) = name { 168 if let Some(name) = name {
118 let detail = macro_label(&ast_node); 169 let detail = macro_label(&ast_node);
119 170
120 let macro_braces_to_insert = match name.as_str() { 171 let docs = macro_.docs(ctx.db);
121 "vec" => "[$0]", 172 let macro_braces_to_insert =
122 _ => "($0)", 173 self.guess_macro_braces(&name, docs.as_ref().map_or("", |s| s.as_str()));
123 };
124 let macro_declaration = name + "!"; 174 let macro_declaration = name + "!";
125 175
126 let builder = CompletionItem::new( 176 let builder = CompletionItem::new(
@@ -129,7 +179,7 @@ impl Completions {
129 &macro_declaration, 179 &macro_declaration,
130 ) 180 )
131 .kind(CompletionItemKind::Macro) 181 .kind(CompletionItemKind::Macro)
132 .set_documentation(macro_.docs(ctx.db)) 182 .set_documentation(docs)
133 .detail(detail) 183 .detail(detail)
134 .insert_snippet(macro_declaration + macro_braces_to_insert); 184 .insert_snippet(macro_declaration + macro_braces_to_insert);
135 185
@@ -148,28 +198,31 @@ impl Completions {
148 let ast_node = func.source(ctx.db).ast; 198 let ast_node = func.source(ctx.db).ast;
149 let detail = function_label(&ast_node); 199 let detail = function_label(&ast_node);
150 200
151 let mut builder = CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name) 201 let mut builder =
152 .kind(if data.has_self_param() { 202 CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.clone())
153 CompletionItemKind::Method 203 .kind(if data.has_self_param() {
154 } else { 204 CompletionItemKind::Method
155 CompletionItemKind::Function 205 } else {
156 }) 206 CompletionItemKind::Function
157 .set_documentation(func.docs(ctx.db)) 207 })
158 .detail(detail); 208 .set_documentation(func.docs(ctx.db))
159 // If not an import, add parenthesis automatically. 209 .detail(detail);
210
211 // Add `<>` for generic types
160 if ctx.use_item_syntax.is_none() 212 if ctx.use_item_syntax.is_none()
161 && !ctx.is_call 213 && !ctx.is_call
162 && ctx.db.feature_flags.get("completion.insertion.add-call-parenthesis") 214 && ctx.db.feature_flags.get("completion.insertion.add-call-parenthesis")
163 { 215 {
164 tested_by!(inserts_parens_for_function_calls); 216 tested_by!(inserts_parens_for_function_calls);
165 let snippet = 217 let (snippet, label) =
166 if data.params().is_empty() || data.has_self_param() && data.params().len() == 1 { 218 if data.params().is_empty() || data.has_self_param() && data.params().len() == 1 {
167 format!("{}()$0", data.name()) 219 (format!("{}()$0", data.name()), format!("{}()", name))
168 } else { 220 } else {
169 format!("{}($0)", data.name()) 221 (format!("{}($0)", data.name()), format!("{}(…)", name))
170 }; 222 };
171 builder = builder.insert_snippet(snippet); 223 builder = builder.lookup_by(name.clone()).label(label).insert_snippet(snippet);
172 } 224 }
225
173 self.add(builder) 226 self.add(builder)
174 } 227 }
175 228
@@ -213,7 +266,6 @@ impl Completions {
213 .separator(", ") 266 .separator(", ")
214 .surround_with("(", ")") 267 .surround_with("(", ")")
215 .to_string(); 268 .to_string();
216
217 CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.to_string()) 269 CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.to_string())
218 .kind(CompletionItemKind::EnumVariant) 270 .kind(CompletionItemKind::EnumVariant)
219 .set_documentation(variant.docs(ctx.db)) 271 .set_documentation(variant.docs(ctx.db))
@@ -222,6 +274,11 @@ impl Completions {
222 } 274 }
223} 275}
224 276
277fn has_non_default_type_params(def: hir::GenericDef, db: &db::RootDatabase) -> bool {
278 let subst = db.generic_defaults(def);
279 subst.iter().any(|ty| ty == &Ty::Unknown)
280}
281
225#[cfg(test)] 282#[cfg(test)]
226mod tests { 283mod tests {
227 use crate::completion::{do_completion, CompletionItem, CompletionKind}; 284 use crate::completion::{do_completion, CompletionItem, CompletionKind};
@@ -242,24 +299,28 @@ mod tests {
242 fn main() { no_<|> } 299 fn main() { no_<|> }
243 " 300 "
244 ), 301 ),
245 @r###"[ 302 @r###"
246 CompletionItem { 303 [
247 label: "main", 304 CompletionItem {
248 source_range: [61; 64), 305 label: "main()",
249 delete: [61; 64), 306 source_range: [61; 64),
250 insert: "main()$0", 307 delete: [61; 64),
251 kind: Function, 308 insert: "main()$0",
252 detail: "fn main()", 309 kind: Function,
253 }, 310 lookup: "main",
254 CompletionItem { 311 detail: "fn main()",
255 label: "no_args", 312 },
256 source_range: [61; 64), 313 CompletionItem {
257 delete: [61; 64), 314 label: "no_args()",
258 insert: "no_args()$0", 315 source_range: [61; 64),
259 kind: Function, 316 delete: [61; 64),
260 detail: "fn no_args()", 317 insert: "no_args()$0",
261 }, 318 kind: Function,
262]"### 319 lookup: "no_args",
320 detail: "fn no_args()",
321 },
322 ]
323 "###
263 ); 324 );
264 assert_debug_snapshot!( 325 assert_debug_snapshot!(
265 do_reference_completion( 326 do_reference_completion(
@@ -268,24 +329,28 @@ mod tests {
268 fn main() { with_<|> } 329 fn main() { with_<|> }
269 " 330 "
270 ), 331 ),
271 @r###"[ 332 @r###"
272 CompletionItem { 333 [
273 label: "main", 334 CompletionItem {
274 source_range: [80; 85), 335 label: "main()",
275 delete: [80; 85), 336 source_range: [80; 85),
276 insert: "main()$0", 337 delete: [80; 85),
277 kind: Function, 338 insert: "main()$0",
278 detail: "fn main()", 339 kind: Function,
279 }, 340 lookup: "main",
280 CompletionItem { 341 detail: "fn main()",
281 label: "with_args", 342 },
282 source_range: [80; 85), 343 CompletionItem {
283 delete: [80; 85), 344 label: "with_args(…)",
284 insert: "with_args($0)", 345 source_range: [80; 85),
285 kind: Function, 346 delete: [80; 85),
286 detail: "fn with_args(x: i32, y: String)", 347 insert: "with_args($0)",
287 }, 348 kind: Function,
288]"### 349 lookup: "with_args",
350 detail: "fn with_args(x: i32, y: String)",
351 },
352 ]
353 "###
289 ); 354 );
290 assert_debug_snapshot!( 355 assert_debug_snapshot!(
291 do_reference_completion( 356 do_reference_completion(
@@ -299,16 +364,19 @@ mod tests {
299 } 364 }
300 " 365 "
301 ), 366 ),
302 @r###"[ 367 @r###"
303 CompletionItem { 368 [
304 label: "foo", 369 CompletionItem {
305 source_range: [163; 164), 370 label: "foo()",
306 delete: [163; 164), 371 source_range: [163; 164),
307 insert: "foo()$0", 372 delete: [163; 164),
308 kind: Method, 373 insert: "foo()$0",
309 detail: "fn foo(&self)", 374 kind: Method,
310 }, 375 lookup: "foo",
311]"### 376 detail: "fn foo(&self)",
377 },
378 ]
379 "###
312 ); 380 );
313 } 381 }
314 382
@@ -389,4 +457,123 @@ mod tests {
389]"# 457]"#
390 ); 458 );
391 } 459 }
460
461 #[test]
462 fn inserts_angle_brackets_for_generics() {
463 covers!(inserts_angle_brackets_for_generics);
464 assert_debug_snapshot!(
465 do_reference_completion(
466 r"
467 struct Vec<T> {}
468 fn foo(xs: Ve<|>)
469 "
470 ),
471 @r###"
472 [
473 CompletionItem {
474 label: "Vec<…>",
475 source_range: [61; 63),
476 delete: [61; 63),
477 insert: "Vec<$0>",
478 kind: Struct,
479 lookup: "Vec",
480 },
481 CompletionItem {
482 label: "foo(…)",
483 source_range: [61; 63),
484 delete: [61; 63),
485 insert: "foo($0)",
486 kind: Function,
487 lookup: "foo",
488 detail: "fn foo(xs: Ve)",
489 },
490 ]
491 "###
492 );
493 assert_debug_snapshot!(
494 do_reference_completion(
495 r"
496 type Vec<T> = (T,);
497 fn foo(xs: Ve<|>)
498 "
499 ),
500 @r###"
501 [
502 CompletionItem {
503 label: "Vec<…>",
504 source_range: [64; 66),
505 delete: [64; 66),
506 insert: "Vec<$0>",
507 kind: TypeAlias,
508 lookup: "Vec",
509 },
510 CompletionItem {
511 label: "foo(…)",
512 source_range: [64; 66),
513 delete: [64; 66),
514 insert: "foo($0)",
515 kind: Function,
516 lookup: "foo",
517 detail: "fn foo(xs: Ve)",
518 },
519 ]
520 "###
521 );
522 assert_debug_snapshot!(
523 do_reference_completion(
524 r"
525 struct Vec<T = i128> {}
526 fn foo(xs: Ve<|>)
527 "
528 ),
529 @r###"
530 [
531 CompletionItem {
532 label: "Vec",
533 source_range: [68; 70),
534 delete: [68; 70),
535 insert: "Vec",
536 kind: Struct,
537 },
538 CompletionItem {
539 label: "foo(…)",
540 source_range: [68; 70),
541 delete: [68; 70),
542 insert: "foo($0)",
543 kind: Function,
544 lookup: "foo",
545 detail: "fn foo(xs: Ve)",
546 },
547 ]
548 "###
549 );
550 assert_debug_snapshot!(
551 do_reference_completion(
552 r"
553 struct Vec<T> {}
554 fn foo(xs: Ve<|><i128>)
555 "
556 ),
557 @r###"
558 [
559 CompletionItem {
560 label: "Vec",
561 source_range: [61; 63),
562 delete: [61; 63),
563 insert: "Vec",
564 kind: Struct,
565 },
566 CompletionItem {
567 label: "foo(…)",
568 source_range: [61; 63),
569 delete: [61; 63),
570 insert: "foo($0)",
571 kind: Function,
572 lookup: "foo",
573 detail: "fn foo(xs: Ve<i128>)",
574 },
575 ]
576 "###
577 );
578 }
392} 579}