aboutsummaryrefslogtreecommitdiff
path: root/crates/completion/src/presentation.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/completion/src/presentation.rs')
-rw-r--r--crates/completion/src/presentation.rs1410
1 files changed, 1410 insertions, 0 deletions
diff --git a/crates/completion/src/presentation.rs b/crates/completion/src/presentation.rs
new file mode 100644
index 000000000..0a6f5a1ea
--- /dev/null
+++ b/crates/completion/src/presentation.rs
@@ -0,0 +1,1410 @@
1//! This modules takes care of rendering various definitions as completion items.
2//! It also handles scoring (sorting) completions.
3
4use hir::{HasAttrs, HasSource, HirDisplay, ModPath, Mutability, ScopeDef, StructKind, Type};
5use itertools::Itertools;
6use syntax::{ast::NameOwner, display::*};
7use test_utils::mark;
8
9use crate::{
10 // display::{const_label, function_declaration, macro_label, type_label},
11 CompletionScore,
12 RootDatabase,
13 {
14 completion_item::Builder, CompletionContext, CompletionItem, CompletionItemKind,
15 CompletionKind, Completions,
16 },
17};
18
19impl Completions {
20 pub(crate) fn add_field(&mut self, ctx: &CompletionContext, field: hir::Field, ty: &Type) {
21 let is_deprecated = is_deprecated(field, ctx.db);
22 let name = field.name(ctx.db);
23 let mut completion_item =
24 CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.to_string())
25 .kind(CompletionItemKind::Field)
26 .detail(ty.display(ctx.db).to_string())
27 .set_documentation(field.docs(ctx.db))
28 .set_deprecated(is_deprecated);
29
30 if let Some(score) = compute_score(ctx, &ty, &name.to_string()) {
31 completion_item = completion_item.set_score(score);
32 }
33
34 completion_item.add_to(self);
35 }
36
37 pub(crate) fn add_tuple_field(&mut self, ctx: &CompletionContext, field: usize, ty: &Type) {
38 CompletionItem::new(CompletionKind::Reference, ctx.source_range(), field.to_string())
39 .kind(CompletionItemKind::Field)
40 .detail(ty.display(ctx.db).to_string())
41 .add_to(self);
42 }
43
44 pub(crate) fn add_resolution(
45 &mut self,
46 ctx: &CompletionContext,
47 local_name: String,
48 resolution: &ScopeDef,
49 ) {
50 use hir::ModuleDef::*;
51
52 let completion_kind = match resolution {
53 ScopeDef::ModuleDef(BuiltinType(..)) => CompletionKind::BuiltinType,
54 _ => CompletionKind::Reference,
55 };
56
57 let kind = match resolution {
58 ScopeDef::ModuleDef(Module(..)) => CompletionItemKind::Module,
59 ScopeDef::ModuleDef(Function(func)) => {
60 return self.add_function(ctx, *func, Some(local_name));
61 }
62 ScopeDef::ModuleDef(Adt(hir::Adt::Struct(_))) => CompletionItemKind::Struct,
63 // FIXME: add CompletionItemKind::Union
64 ScopeDef::ModuleDef(Adt(hir::Adt::Union(_))) => CompletionItemKind::Struct,
65 ScopeDef::ModuleDef(Adt(hir::Adt::Enum(_))) => CompletionItemKind::Enum,
66
67 ScopeDef::ModuleDef(EnumVariant(var)) => {
68 return self.add_enum_variant(ctx, *var, Some(local_name));
69 }
70 ScopeDef::ModuleDef(Const(..)) => CompletionItemKind::Const,
71 ScopeDef::ModuleDef(Static(..)) => CompletionItemKind::Static,
72 ScopeDef::ModuleDef(Trait(..)) => CompletionItemKind::Trait,
73 ScopeDef::ModuleDef(TypeAlias(..)) => CompletionItemKind::TypeAlias,
74 ScopeDef::ModuleDef(BuiltinType(..)) => CompletionItemKind::BuiltinType,
75 ScopeDef::GenericParam(..) => CompletionItemKind::TypeParam,
76 ScopeDef::Local(..) => CompletionItemKind::Binding,
77 // (does this need its own kind?)
78 ScopeDef::AdtSelfType(..) | ScopeDef::ImplSelfType(..) => CompletionItemKind::TypeParam,
79 ScopeDef::MacroDef(mac) => {
80 return self.add_macro(ctx, Some(local_name), *mac);
81 }
82 ScopeDef::Unknown => {
83 return self.add(
84 CompletionItem::new(CompletionKind::Reference, ctx.source_range(), local_name)
85 .kind(CompletionItemKind::UnresolvedReference),
86 );
87 }
88 };
89
90 let docs = match resolution {
91 ScopeDef::ModuleDef(Module(it)) => it.docs(ctx.db),
92 ScopeDef::ModuleDef(Adt(it)) => it.docs(ctx.db),
93 ScopeDef::ModuleDef(EnumVariant(it)) => it.docs(ctx.db),
94 ScopeDef::ModuleDef(Const(it)) => it.docs(ctx.db),
95 ScopeDef::ModuleDef(Static(it)) => it.docs(ctx.db),
96 ScopeDef::ModuleDef(Trait(it)) => it.docs(ctx.db),
97 ScopeDef::ModuleDef(TypeAlias(it)) => it.docs(ctx.db),
98 _ => None,
99 };
100
101 let mut completion_item =
102 CompletionItem::new(completion_kind, ctx.source_range(), local_name.clone());
103 if let ScopeDef::Local(local) = resolution {
104 let ty = local.ty(ctx.db);
105 if !ty.is_unknown() {
106 completion_item = completion_item.detail(ty.display(ctx.db).to_string());
107 }
108 };
109
110 let mut ref_match = None;
111 if let ScopeDef::Local(local) = resolution {
112 if let Some((active_name, active_type)) = ctx.active_name_and_type() {
113 let ty = local.ty(ctx.db);
114 if let Some(score) =
115 compute_score_from_active(&active_type, &active_name, &ty, &local_name)
116 {
117 completion_item = completion_item.set_score(score);
118 }
119 ref_match = refed_type_matches(&active_type, &active_name, &ty, &local_name);
120 }
121 }
122
123 // Add `<>` for generic types
124 if ctx.is_path_type && !ctx.has_type_args && ctx.config.add_call_parenthesis {
125 if let Some(cap) = ctx.config.snippet_cap {
126 let has_non_default_type_params = match resolution {
127 ScopeDef::ModuleDef(Adt(it)) => it.has_non_default_type_params(ctx.db),
128 ScopeDef::ModuleDef(TypeAlias(it)) => it.has_non_default_type_params(ctx.db),
129 _ => false,
130 };
131 if has_non_default_type_params {
132 mark::hit!(inserts_angle_brackets_for_generics);
133 completion_item = completion_item
134 .lookup_by(local_name.clone())
135 .label(format!("{}<…>", local_name))
136 .insert_snippet(cap, format!("{}<$0>", local_name));
137 }
138 }
139 }
140
141 completion_item.kind(kind).set_documentation(docs).set_ref_match(ref_match).add_to(self)
142 }
143
144 pub(crate) fn add_macro(
145 &mut self,
146 ctx: &CompletionContext,
147 name: Option<String>,
148 macro_: hir::MacroDef,
149 ) {
150 // FIXME: Currently proc-macro do not have ast-node,
151 // such that it does not have source
152 if macro_.is_proc_macro() {
153 return;
154 }
155
156 let name = match name {
157 Some(it) => it,
158 None => return,
159 };
160
161 let ast_node = macro_.source(ctx.db).value;
162 let detail = macro_label(&ast_node);
163
164 let docs = macro_.docs(ctx.db);
165
166 let mut builder = CompletionItem::new(
167 CompletionKind::Reference,
168 ctx.source_range(),
169 &format!("{}!", name),
170 )
171 .kind(CompletionItemKind::Macro)
172 .set_documentation(docs.clone())
173 .set_deprecated(is_deprecated(macro_, ctx.db))
174 .detail(detail);
175
176 let needs_bang = ctx.use_item_syntax.is_none() && !ctx.is_macro_call;
177 builder = match ctx.config.snippet_cap {
178 Some(cap) if needs_bang => {
179 let docs = docs.as_ref().map_or("", |s| s.as_str());
180 let (bra, ket) = guess_macro_braces(&name, docs);
181 builder
182 .insert_snippet(cap, format!("{}!{}$0{}", name, bra, ket))
183 .label(format!("{}!{}…{}", name, bra, ket))
184 .lookup_by(format!("{}!", name))
185 }
186 None if needs_bang => builder.insert_text(format!("{}!", name)),
187 _ => {
188 mark::hit!(dont_insert_macro_call_parens_unncessary);
189 builder.insert_text(name)
190 }
191 };
192
193 self.add(builder);
194 }
195
196 pub(crate) fn add_function(
197 &mut self,
198 ctx: &CompletionContext,
199 func: hir::Function,
200 local_name: Option<String>,
201 ) {
202 fn add_arg(arg: &str, ty: &Type, ctx: &CompletionContext) -> String {
203 if let Some(derefed_ty) = ty.remove_ref() {
204 for (name, local) in ctx.locals.iter() {
205 if name == arg && local.ty(ctx.db) == derefed_ty {
206 return (if ty.is_mutable_reference() { "&mut " } else { "&" }).to_string()
207 + &arg.to_string();
208 }
209 }
210 }
211 arg.to_string()
212 };
213 let name = local_name.unwrap_or_else(|| func.name(ctx.db).to_string());
214 let ast_node = func.source(ctx.db).value;
215
216 let mut builder =
217 CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.clone())
218 .kind(if func.self_param(ctx.db).is_some() {
219 CompletionItemKind::Method
220 } else {
221 CompletionItemKind::Function
222 })
223 .set_documentation(func.docs(ctx.db))
224 .set_deprecated(is_deprecated(func, ctx.db))
225 .detail(function_declaration(&ast_node));
226
227 let params_ty = func.params(ctx.db);
228 let params = ast_node
229 .param_list()
230 .into_iter()
231 .flat_map(|it| it.params())
232 .zip(params_ty)
233 .flat_map(|(it, param_ty)| {
234 if let Some(pat) = it.pat() {
235 let name = pat.to_string();
236 let arg = name.trim_start_matches("mut ").trim_start_matches('_');
237 return Some(add_arg(arg, param_ty.ty(), ctx));
238 }
239 None
240 })
241 .collect();
242
243 builder = builder.add_call_parens(ctx, name, Params::Named(params));
244
245 self.add(builder)
246 }
247
248 pub(crate) fn add_const(&mut self, ctx: &CompletionContext, constant: hir::Const) {
249 let ast_node = constant.source(ctx.db).value;
250 let name = match ast_node.name() {
251 Some(name) => name,
252 _ => return,
253 };
254 let detail = const_label(&ast_node);
255
256 CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.text().to_string())
257 .kind(CompletionItemKind::Const)
258 .set_documentation(constant.docs(ctx.db))
259 .set_deprecated(is_deprecated(constant, ctx.db))
260 .detail(detail)
261 .add_to(self);
262 }
263
264 pub(crate) fn add_type_alias(&mut self, ctx: &CompletionContext, type_alias: hir::TypeAlias) {
265 let type_def = type_alias.source(ctx.db).value;
266 let name = match type_def.name() {
267 Some(name) => name,
268 _ => return,
269 };
270 let detail = type_label(&type_def);
271
272 CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.text().to_string())
273 .kind(CompletionItemKind::TypeAlias)
274 .set_documentation(type_alias.docs(ctx.db))
275 .set_deprecated(is_deprecated(type_alias, ctx.db))
276 .detail(detail)
277 .add_to(self);
278 }
279
280 pub(crate) fn add_qualified_enum_variant(
281 &mut self,
282 ctx: &CompletionContext,
283 variant: hir::EnumVariant,
284 path: ModPath,
285 ) {
286 self.add_enum_variant_impl(ctx, variant, None, Some(path))
287 }
288
289 pub(crate) fn add_enum_variant(
290 &mut self,
291 ctx: &CompletionContext,
292 variant: hir::EnumVariant,
293 local_name: Option<String>,
294 ) {
295 self.add_enum_variant_impl(ctx, variant, local_name, None)
296 }
297
298 fn add_enum_variant_impl(
299 &mut self,
300 ctx: &CompletionContext,
301 variant: hir::EnumVariant,
302 local_name: Option<String>,
303 path: Option<ModPath>,
304 ) {
305 let is_deprecated = is_deprecated(variant, ctx.db);
306 let name = local_name.unwrap_or_else(|| variant.name(ctx.db).to_string());
307 let (qualified_name, short_qualified_name) = match &path {
308 Some(path) => {
309 let full = path.to_string();
310 let short =
311 path.segments[path.segments.len().saturating_sub(2)..].iter().join("::");
312 (full, short)
313 }
314 None => (name.to_string(), name.to_string()),
315 };
316 let detail_types = variant
317 .fields(ctx.db)
318 .into_iter()
319 .map(|field| (field.name(ctx.db), field.signature_ty(ctx.db)));
320 let variant_kind = variant.kind(ctx.db);
321 let detail = match variant_kind {
322 StructKind::Tuple | StructKind::Unit => format!(
323 "({})",
324 detail_types.map(|(_, t)| t.display(ctx.db).to_string()).format(", ")
325 ),
326 StructKind::Record => format!(
327 "{{ {} }}",
328 detail_types
329 .map(|(n, t)| format!("{}: {}", n, t.display(ctx.db).to_string()))
330 .format(", ")
331 ),
332 };
333 let mut res = CompletionItem::new(
334 CompletionKind::Reference,
335 ctx.source_range(),
336 qualified_name.clone(),
337 )
338 .kind(CompletionItemKind::EnumVariant)
339 .set_documentation(variant.docs(ctx.db))
340 .set_deprecated(is_deprecated)
341 .detail(detail);
342
343 if variant_kind == StructKind::Tuple {
344 mark::hit!(inserts_parens_for_tuple_enums);
345 let params = Params::Anonymous(variant.fields(ctx.db).len());
346 res = res.add_call_parens(ctx, short_qualified_name, params)
347 } else if path.is_some() {
348 res = res.lookup_by(short_qualified_name);
349 }
350
351 res.add_to(self);
352 }
353}
354
355fn compute_score_from_active(
356 active_type: &Type,
357 active_name: &str,
358 ty: &Type,
359 name: &str,
360) -> Option<CompletionScore> {
361 // Compute score
362 // For the same type
363 if active_type != ty {
364 return None;
365 }
366
367 let mut res = CompletionScore::TypeMatch;
368
369 // If same type + same name then go top position
370 if active_name == name {
371 res = CompletionScore::TypeAndNameMatch
372 }
373
374 Some(res)
375}
376fn refed_type_matches(
377 active_type: &Type,
378 active_name: &str,
379 ty: &Type,
380 name: &str,
381) -> Option<(Mutability, CompletionScore)> {
382 let derefed_active = active_type.remove_ref()?;
383 let score = compute_score_from_active(&derefed_active, &active_name, &ty, &name)?;
384 Some((
385 if active_type.is_mutable_reference() { Mutability::Mut } else { Mutability::Shared },
386 score,
387 ))
388}
389
390fn compute_score(ctx: &CompletionContext, ty: &Type, name: &str) -> Option<CompletionScore> {
391 let (active_name, active_type) = ctx.active_name_and_type()?;
392 compute_score_from_active(&active_type, &active_name, ty, name)
393}
394
395enum Params {
396 Named(Vec<String>),
397 Anonymous(usize),
398}
399
400impl Params {
401 fn len(&self) -> usize {
402 match self {
403 Params::Named(xs) => xs.len(),
404 Params::Anonymous(len) => *len,
405 }
406 }
407
408 fn is_empty(&self) -> bool {
409 self.len() == 0
410 }
411}
412
413impl Builder {
414 fn add_call_parens(mut self, ctx: &CompletionContext, name: String, params: Params) -> Builder {
415 if !ctx.config.add_call_parenthesis {
416 return self;
417 }
418 if ctx.use_item_syntax.is_some() {
419 mark::hit!(no_parens_in_use_item);
420 return self;
421 }
422 if ctx.is_pattern_call {
423 mark::hit!(dont_duplicate_pattern_parens);
424 return self;
425 }
426 if ctx.is_call {
427 return self;
428 }
429
430 // Don't add parentheses if the expected type is some function reference.
431 if let Some(ty) = &ctx.expected_type {
432 if ty.is_fn() {
433 mark::hit!(no_call_parens_if_fn_ptr_needed);
434 return self;
435 }
436 }
437
438 let cap = match ctx.config.snippet_cap {
439 Some(it) => it,
440 None => return self,
441 };
442 // If not an import, add parenthesis automatically.
443 mark::hit!(inserts_parens_for_function_calls);
444
445 let (snippet, label) = if params.is_empty() {
446 (format!("{}()$0", name), format!("{}()", name))
447 } else {
448 self = self.trigger_call_info();
449 let snippet = match (ctx.config.add_call_argument_snippets, params) {
450 (true, Params::Named(params)) => {
451 let function_params_snippet =
452 params.iter().enumerate().format_with(", ", |(index, param_name), f| {
453 f(&format_args!("${{{}:{}}}", index + 1, param_name))
454 });
455 format!("{}({})$0", name, function_params_snippet)
456 }
457 _ => {
458 mark::hit!(suppress_arg_snippets);
459 format!("{}($0)", name)
460 }
461 };
462
463 (snippet, format!("{}(…)", name))
464 };
465 self.lookup_by(name).label(label).insert_snippet(cap, snippet)
466 }
467}
468
469fn is_deprecated(node: impl HasAttrs, db: &RootDatabase) -> bool {
470 node.attrs(db).by_key("deprecated").exists()
471}
472
473fn guess_macro_braces(macro_name: &str, docs: &str) -> (&'static str, &'static str) {
474 let mut votes = [0, 0, 0];
475 for (idx, s) in docs.match_indices(&macro_name) {
476 let (before, after) = (&docs[..idx], &docs[idx + s.len()..]);
477 // Ensure to match the full word
478 if after.starts_with('!')
479 && !before.ends_with(|c: char| c == '_' || c.is_ascii_alphanumeric())
480 {
481 // It may have spaces before the braces like `foo! {}`
482 match after[1..].chars().find(|&c| !c.is_whitespace()) {
483 Some('{') => votes[0] += 1,
484 Some('[') => votes[1] += 1,
485 Some('(') => votes[2] += 1,
486 _ => {}
487 }
488 }
489 }
490
491 // Insert a space before `{}`.
492 // We prefer the last one when some votes equal.
493 let (_vote, (bra, ket)) = votes
494 .iter()
495 .zip(&[(" {", "}"), ("[", "]"), ("(", ")")])
496 .max_by_key(|&(&vote, _)| vote)
497 .unwrap();
498 (*bra, *ket)
499}
500
501#[cfg(test)]
502mod tests {
503 use std::cmp::Reverse;
504
505 use expect_test::{expect, Expect};
506 use test_utils::mark;
507
508 use crate::{
509 test_utils::{check_edit, check_edit_with_config, do_completion, get_all_completion_items},
510 CompletionConfig, CompletionKind, CompletionScore,
511 };
512
513 fn check(ra_fixture: &str, expect: Expect) {
514 let actual = do_completion(ra_fixture, CompletionKind::Reference);
515 expect.assert_debug_eq(&actual);
516 }
517
518 fn check_scores(ra_fixture: &str, expect: Expect) {
519 fn display_score(score: Option<CompletionScore>) -> &'static str {
520 match score {
521 Some(CompletionScore::TypeMatch) => "[type]",
522 Some(CompletionScore::TypeAndNameMatch) => "[type+name]",
523 None => "[]".into(),
524 }
525 }
526
527 let mut completions = get_all_completion_items(CompletionConfig::default(), ra_fixture);
528 completions.sort_by_key(|it| (Reverse(it.score()), it.label().to_string()));
529 let actual = completions
530 .into_iter()
531 .filter(|it| it.completion_kind == CompletionKind::Reference)
532 .map(|it| {
533 let tag = it.kind().unwrap().tag();
534 let score = display_score(it.score());
535 format!("{} {} {}\n", tag, it.label(), score)
536 })
537 .collect::<String>();
538 expect.assert_eq(&actual);
539 }
540
541 #[test]
542 fn enum_detail_includes_record_fields() {
543 check(
544 r#"
545enum Foo { Foo { x: i32, y: i32 } }
546
547fn main() { Foo::Fo<|> }
548"#,
549 expect![[r#"
550 [
551 CompletionItem {
552 label: "Foo",
553 source_range: 54..56,
554 delete: 54..56,
555 insert: "Foo",
556 kind: EnumVariant,
557 detail: "{ x: i32, y: i32 }",
558 },
559 ]
560 "#]],
561 );
562 }
563
564 #[test]
565 fn enum_detail_doesnt_include_tuple_fields() {
566 check(
567 r#"
568enum Foo { Foo (i32, i32) }
569
570fn main() { Foo::Fo<|> }
571"#,
572 expect![[r#"
573 [
574 CompletionItem {
575 label: "Foo(…)",
576 source_range: 46..48,
577 delete: 46..48,
578 insert: "Foo($0)",
579 kind: EnumVariant,
580 lookup: "Foo",
581 detail: "(i32, i32)",
582 trigger_call_info: true,
583 },
584 ]
585 "#]],
586 );
587 }
588
589 #[test]
590 fn enum_detail_just_parentheses_for_unit() {
591 check(
592 r#"
593enum Foo { Foo }
594
595fn main() { Foo::Fo<|> }
596"#,
597 expect![[r#"
598 [
599 CompletionItem {
600 label: "Foo",
601 source_range: 35..37,
602 delete: 35..37,
603 insert: "Foo",
604 kind: EnumVariant,
605 detail: "()",
606 },
607 ]
608 "#]],
609 );
610 }
611
612 #[test]
613 fn lookup_enums_by_two_qualifiers() {
614 check(
615 r#"
616mod m {
617 pub enum Spam { Foo, Bar(i32) }
618}
619fn main() { let _: m::Spam = S<|> }
620"#,
621 expect![[r#"
622 [
623 CompletionItem {
624 label: "Spam::Bar(…)",
625 source_range: 75..76,
626 delete: 75..76,
627 insert: "Spam::Bar($0)",
628 kind: EnumVariant,
629 lookup: "Spam::Bar",
630 detail: "(i32)",
631 trigger_call_info: true,
632 },
633 CompletionItem {
634 label: "m",
635 source_range: 75..76,
636 delete: 75..76,
637 insert: "m",
638 kind: Module,
639 },
640 CompletionItem {
641 label: "m::Spam::Foo",
642 source_range: 75..76,
643 delete: 75..76,
644 insert: "m::Spam::Foo",
645 kind: EnumVariant,
646 lookup: "Spam::Foo",
647 detail: "()",
648 },
649 CompletionItem {
650 label: "main()",
651 source_range: 75..76,
652 delete: 75..76,
653 insert: "main()$0",
654 kind: Function,
655 lookup: "main",
656 detail: "fn main()",
657 },
658 ]
659 "#]],
660 )
661 }
662
663 #[test]
664 fn sets_deprecated_flag_in_completion_items() {
665 check(
666 r#"
667#[deprecated]
668fn something_deprecated() {}
669#[deprecated(since = "1.0.0")]
670fn something_else_deprecated() {}
671
672fn main() { som<|> }
673"#,
674 expect![[r#"
675 [
676 CompletionItem {
677 label: "main()",
678 source_range: 121..124,
679 delete: 121..124,
680 insert: "main()$0",
681 kind: Function,
682 lookup: "main",
683 detail: "fn main()",
684 },
685 CompletionItem {
686 label: "something_deprecated()",
687 source_range: 121..124,
688 delete: 121..124,
689 insert: "something_deprecated()$0",
690 kind: Function,
691 lookup: "something_deprecated",
692 detail: "fn something_deprecated()",
693 deprecated: true,
694 },
695 CompletionItem {
696 label: "something_else_deprecated()",
697 source_range: 121..124,
698 delete: 121..124,
699 insert: "something_else_deprecated()$0",
700 kind: Function,
701 lookup: "something_else_deprecated",
702 detail: "fn something_else_deprecated()",
703 deprecated: true,
704 },
705 ]
706 "#]],
707 );
708
709 check(
710 r#"
711struct A { #[deprecated] the_field: u32 }
712fn foo() { A { the<|> } }
713"#,
714 expect![[r#"
715 [
716 CompletionItem {
717 label: "the_field",
718 source_range: 57..60,
719 delete: 57..60,
720 insert: "the_field",
721 kind: Field,
722 detail: "u32",
723 deprecated: true,
724 },
725 ]
726 "#]],
727 );
728 }
729
730 #[test]
731 fn renders_docs() {
732 check(
733 r#"
734struct S {
735 /// Field docs
736 foo:
737}
738impl S {
739 /// Method docs
740 fn bar(self) { self.<|> }
741}"#,
742 expect![[r#"
743 [
744 CompletionItem {
745 label: "bar()",
746 source_range: 94..94,
747 delete: 94..94,
748 insert: "bar()$0",
749 kind: Method,
750 lookup: "bar",
751 detail: "fn bar(self)",
752 documentation: Documentation(
753 "Method docs",
754 ),
755 },
756 CompletionItem {
757 label: "foo",
758 source_range: 94..94,
759 delete: 94..94,
760 insert: "foo",
761 kind: Field,
762 detail: "{unknown}",
763 documentation: Documentation(
764 "Field docs",
765 ),
766 },
767 ]
768 "#]],
769 );
770
771 check(
772 r#"
773use self::my<|>;
774
775/// mod docs
776mod my { }
777
778/// enum docs
779enum E {
780 /// variant docs
781 V
782}
783use self::E::*;
784"#,
785 expect![[r#"
786 [
787 CompletionItem {
788 label: "E",
789 source_range: 10..12,
790 delete: 10..12,
791 insert: "E",
792 kind: Enum,
793 documentation: Documentation(
794 "enum docs",
795 ),
796 },
797 CompletionItem {
798 label: "V",
799 source_range: 10..12,
800 delete: 10..12,
801 insert: "V",
802 kind: EnumVariant,
803 detail: "()",
804 documentation: Documentation(
805 "variant docs",
806 ),
807 },
808 CompletionItem {
809 label: "my",
810 source_range: 10..12,
811 delete: 10..12,
812 insert: "my",
813 kind: Module,
814 documentation: Documentation(
815 "mod docs",
816 ),
817 },
818 ]
819 "#]],
820 )
821 }
822
823 #[test]
824 fn dont_render_attrs() {
825 check(
826 r#"
827struct S;
828impl S {
829 #[inline]
830 fn the_method(&self) { }
831}
832fn foo(s: S) { s.<|> }
833"#,
834 expect![[r#"
835 [
836 CompletionItem {
837 label: "the_method()",
838 source_range: 81..81,
839 delete: 81..81,
840 insert: "the_method()$0",
841 kind: Method,
842 lookup: "the_method",
843 detail: "fn the_method(&self)",
844 },
845 ]
846 "#]],
847 )
848 }
849
850 #[test]
851 fn inserts_parens_for_function_calls() {
852 mark::check!(inserts_parens_for_function_calls);
853 check_edit(
854 "no_args",
855 r#"
856fn no_args() {}
857fn main() { no_<|> }
858"#,
859 r#"
860fn no_args() {}
861fn main() { no_args()$0 }
862"#,
863 );
864
865 check_edit(
866 "with_args",
867 r#"
868fn with_args(x: i32, y: String) {}
869fn main() { with_<|> }
870"#,
871 r#"
872fn with_args(x: i32, y: String) {}
873fn main() { with_args(${1:x}, ${2:y})$0 }
874"#,
875 );
876
877 check_edit(
878 "foo",
879 r#"
880struct S;
881impl S {
882 fn foo(&self) {}
883}
884fn bar(s: &S) { s.f<|> }
885"#,
886 r#"
887struct S;
888impl S {
889 fn foo(&self) {}
890}
891fn bar(s: &S) { s.foo()$0 }
892"#,
893 );
894
895 check_edit(
896 "foo",
897 r#"
898struct S {}
899impl S {
900 fn foo(&self, x: i32) {}
901}
902fn bar(s: &S) {
903 s.f<|>
904}
905"#,
906 r#"
907struct S {}
908impl S {
909 fn foo(&self, x: i32) {}
910}
911fn bar(s: &S) {
912 s.foo(${1:x})$0
913}
914"#,
915 );
916 }
917
918 #[test]
919 fn suppress_arg_snippets() {
920 mark::check!(suppress_arg_snippets);
921 check_edit_with_config(
922 CompletionConfig { add_call_argument_snippets: false, ..CompletionConfig::default() },
923 "with_args",
924 r#"
925fn with_args(x: i32, y: String) {}
926fn main() { with_<|> }
927"#,
928 r#"
929fn with_args(x: i32, y: String) {}
930fn main() { with_args($0) }
931"#,
932 );
933 }
934
935 #[test]
936 fn strips_underscores_from_args() {
937 check_edit(
938 "foo",
939 r#"
940fn foo(_foo: i32, ___bar: bool, ho_ge_: String) {}
941fn main() { f<|> }
942"#,
943 r#"
944fn foo(_foo: i32, ___bar: bool, ho_ge_: String) {}
945fn main() { foo(${1:foo}, ${2:bar}, ${3:ho_ge_})$0 }
946"#,
947 );
948 }
949
950 #[test]
951 fn insert_ref_when_matching_local_in_scope() {
952 check_edit(
953 "ref_arg",
954 r#"
955struct Foo {}
956fn ref_arg(x: &Foo) {}
957fn main() {
958 let x = Foo {};
959 ref_ar<|>
960}
961"#,
962 r#"
963struct Foo {}
964fn ref_arg(x: &Foo) {}
965fn main() {
966 let x = Foo {};
967 ref_arg(${1:&x})$0
968}
969"#,
970 );
971 }
972
973 #[test]
974 fn insert_mut_ref_when_matching_local_in_scope() {
975 check_edit(
976 "ref_arg",
977 r#"
978struct Foo {}
979fn ref_arg(x: &mut Foo) {}
980fn main() {
981 let x = Foo {};
982 ref_ar<|>
983}
984"#,
985 r#"
986struct Foo {}
987fn ref_arg(x: &mut Foo) {}
988fn main() {
989 let x = Foo {};
990 ref_arg(${1:&mut x})$0
991}
992"#,
993 );
994 }
995
996 #[test]
997 fn insert_ref_when_matching_local_in_scope_for_method() {
998 check_edit(
999 "apply_foo",
1000 r#"
1001struct Foo {}
1002struct Bar {}
1003impl Bar {
1004 fn apply_foo(&self, x: &Foo) {}
1005}
1006
1007fn main() {
1008 let x = Foo {};
1009 let y = Bar {};
1010 y.<|>
1011}
1012"#,
1013 r#"
1014struct Foo {}
1015struct Bar {}
1016impl Bar {
1017 fn apply_foo(&self, x: &Foo) {}
1018}
1019
1020fn main() {
1021 let x = Foo {};
1022 let y = Bar {};
1023 y.apply_foo(${1:&x})$0
1024}
1025"#,
1026 );
1027 }
1028
1029 #[test]
1030 fn trim_mut_keyword_in_func_completion() {
1031 check_edit(
1032 "take_mutably",
1033 r#"
1034fn take_mutably(mut x: &i32) {}
1035
1036fn main() {
1037 take_m<|>
1038}
1039"#,
1040 r#"
1041fn take_mutably(mut x: &i32) {}
1042
1043fn main() {
1044 take_mutably(${1:x})$0
1045}
1046"#,
1047 );
1048 }
1049
1050 #[test]
1051 fn inserts_parens_for_tuple_enums() {
1052 mark::check!(inserts_parens_for_tuple_enums);
1053 check_edit(
1054 "Some",
1055 r#"
1056enum Option<T> { Some(T), None }
1057use Option::*;
1058fn main() -> Option<i32> {
1059 Som<|>
1060}
1061"#,
1062 r#"
1063enum Option<T> { Some(T), None }
1064use Option::*;
1065fn main() -> Option<i32> {
1066 Some($0)
1067}
1068"#,
1069 );
1070 check_edit(
1071 "Some",
1072 r#"
1073enum Option<T> { Some(T), None }
1074use Option::*;
1075fn main(value: Option<i32>) {
1076 match value {
1077 Som<|>
1078 }
1079}
1080"#,
1081 r#"
1082enum Option<T> { Some(T), None }
1083use Option::*;
1084fn main(value: Option<i32>) {
1085 match value {
1086 Some($0)
1087 }
1088}
1089"#,
1090 );
1091 }
1092
1093 #[test]
1094 fn dont_duplicate_pattern_parens() {
1095 mark::check!(dont_duplicate_pattern_parens);
1096 check_edit(
1097 "Var",
1098 r#"
1099enum E { Var(i32) }
1100fn main() {
1101 match E::Var(92) {
1102 E::<|>(92) => (),
1103 }
1104}
1105"#,
1106 r#"
1107enum E { Var(i32) }
1108fn main() {
1109 match E::Var(92) {
1110 E::Var(92) => (),
1111 }
1112}
1113"#,
1114 );
1115 }
1116
1117 #[test]
1118 fn no_call_parens_if_fn_ptr_needed() {
1119 mark::check!(no_call_parens_if_fn_ptr_needed);
1120 check_edit(
1121 "foo",
1122 r#"
1123fn foo(foo: u8, bar: u8) {}
1124struct ManualVtable { f: fn(u8, u8) }
1125
1126fn main() -> ManualVtable {
1127 ManualVtable { f: f<|> }
1128}
1129"#,
1130 r#"
1131fn foo(foo: u8, bar: u8) {}
1132struct ManualVtable { f: fn(u8, u8) }
1133
1134fn main() -> ManualVtable {
1135 ManualVtable { f: foo }
1136}
1137"#,
1138 );
1139 }
1140
1141 #[test]
1142 fn no_parens_in_use_item() {
1143 mark::check!(no_parens_in_use_item);
1144 check_edit(
1145 "foo",
1146 r#"
1147mod m { pub fn foo() {} }
1148use crate::m::f<|>;
1149"#,
1150 r#"
1151mod m { pub fn foo() {} }
1152use crate::m::foo;
1153"#,
1154 );
1155 }
1156
1157 #[test]
1158 fn no_parens_in_call() {
1159 check_edit(
1160 "foo",
1161 r#"
1162fn foo(x: i32) {}
1163fn main() { f<|>(); }
1164"#,
1165 r#"
1166fn foo(x: i32) {}
1167fn main() { foo(); }
1168"#,
1169 );
1170 check_edit(
1171 "foo",
1172 r#"
1173struct Foo;
1174impl Foo { fn foo(&self){} }
1175fn f(foo: &Foo) { foo.f<|>(); }
1176"#,
1177 r#"
1178struct Foo;
1179impl Foo { fn foo(&self){} }
1180fn f(foo: &Foo) { foo.foo(); }
1181"#,
1182 );
1183 }
1184
1185 #[test]
1186 fn inserts_angle_brackets_for_generics() {
1187 mark::check!(inserts_angle_brackets_for_generics);
1188 check_edit(
1189 "Vec",
1190 r#"
1191struct Vec<T> {}
1192fn foo(xs: Ve<|>)
1193"#,
1194 r#"
1195struct Vec<T> {}
1196fn foo(xs: Vec<$0>)
1197"#,
1198 );
1199 check_edit(
1200 "Vec",
1201 r#"
1202type Vec<T> = (T,);
1203fn foo(xs: Ve<|>)
1204"#,
1205 r#"
1206type Vec<T> = (T,);
1207fn foo(xs: Vec<$0>)
1208"#,
1209 );
1210 check_edit(
1211 "Vec",
1212 r#"
1213struct Vec<T = i128> {}
1214fn foo(xs: Ve<|>)
1215"#,
1216 r#"
1217struct Vec<T = i128> {}
1218fn foo(xs: Vec)
1219"#,
1220 );
1221 check_edit(
1222 "Vec",
1223 r#"
1224struct Vec<T> {}
1225fn foo(xs: Ve<|><i128>)
1226"#,
1227 r#"
1228struct Vec<T> {}
1229fn foo(xs: Vec<i128>)
1230"#,
1231 );
1232 }
1233
1234 #[test]
1235 fn dont_insert_macro_call_parens_unncessary() {
1236 mark::check!(dont_insert_macro_call_parens_unncessary);
1237 check_edit(
1238 "frobnicate!",
1239 r#"
1240//- /main.rs crate:main deps:foo
1241use foo::<|>;
1242//- /foo/lib.rs crate:foo
1243#[macro_export]
1244macro_rules frobnicate { () => () }
1245"#,
1246 r#"
1247use foo::frobnicate;
1248"#,
1249 );
1250
1251 check_edit(
1252 "frobnicate!",
1253 r#"
1254macro_rules frobnicate { () => () }
1255fn main() { frob<|>!(); }
1256"#,
1257 r#"
1258macro_rules frobnicate { () => () }
1259fn main() { frobnicate!(); }
1260"#,
1261 );
1262 }
1263
1264 #[test]
1265 fn active_param_score() {
1266 mark::check!(active_param_type_match);
1267 check_scores(
1268 r#"
1269struct S { foo: i64, bar: u32, baz: u32 }
1270fn test(bar: u32) { }
1271fn foo(s: S) { test(s.<|>) }
1272"#,
1273 expect![[r#"
1274 fd bar [type+name]
1275 fd baz [type]
1276 fd foo []
1277 "#]],
1278 );
1279 }
1280
1281 #[test]
1282 fn record_field_scores() {
1283 mark::check!(record_field_type_match);
1284 check_scores(
1285 r#"
1286struct A { foo: i64, bar: u32, baz: u32 }
1287struct B { x: (), y: f32, bar: u32 }
1288fn foo(a: A) { B { bar: a.<|> }; }
1289"#,
1290 expect![[r#"
1291 fd bar [type+name]
1292 fd baz [type]
1293 fd foo []
1294 "#]],
1295 )
1296 }
1297
1298 #[test]
1299 fn record_field_and_call_scores() {
1300 check_scores(
1301 r#"
1302struct A { foo: i64, bar: u32, baz: u32 }
1303struct B { x: (), y: f32, bar: u32 }
1304fn f(foo: i64) { }
1305fn foo(a: A) { B { bar: f(a.<|>) }; }
1306"#,
1307 expect![[r#"
1308 fd foo [type+name]
1309 fd bar []
1310 fd baz []
1311 "#]],
1312 );
1313 check_scores(
1314 r#"
1315struct A { foo: i64, bar: u32, baz: u32 }
1316struct B { x: (), y: f32, bar: u32 }
1317fn f(foo: i64) { }
1318fn foo(a: A) { f(B { bar: a.<|> }); }
1319"#,
1320 expect![[r#"
1321 fd bar [type+name]
1322 fd baz [type]
1323 fd foo []
1324 "#]],
1325 );
1326 }
1327
1328 #[test]
1329 fn prioritize_exact_ref_match() {
1330 check_scores(
1331 r#"
1332struct WorldSnapshot { _f: () };
1333fn go(world: &WorldSnapshot) { go(w<|>) }
1334"#,
1335 expect![[r#"
1336 bn world [type+name]
1337 st WorldSnapshot []
1338 fn go(…) []
1339 "#]],
1340 );
1341 }
1342
1343 #[test]
1344 fn too_many_arguments() {
1345 check_scores(
1346 r#"
1347struct Foo;
1348fn f(foo: &Foo) { f(foo, w<|>) }
1349"#,
1350 expect![[r#"
1351 st Foo []
1352 fn f(…) []
1353 bn foo []
1354 "#]],
1355 );
1356 }
1357
1358 #[test]
1359 fn guesses_macro_braces() {
1360 check_edit(
1361 "vec!",
1362 r#"
1363/// Creates a [`Vec`] containing the arguments.
1364///
1365/// ```
1366/// let v = vec![1, 2, 3];
1367/// assert_eq!(v[0], 1);
1368/// assert_eq!(v[1], 2);
1369/// assert_eq!(v[2], 3);
1370/// ```
1371macro_rules! vec { () => {} }
1372
1373fn fn main() { v<|> }
1374"#,
1375 r#"
1376/// Creates a [`Vec`] containing the arguments.
1377///
1378/// ```
1379/// let v = vec![1, 2, 3];
1380/// assert_eq!(v[0], 1);
1381/// assert_eq!(v[1], 2);
1382/// assert_eq!(v[2], 3);
1383/// ```
1384macro_rules! vec { () => {} }
1385
1386fn fn main() { vec![$0] }
1387"#,
1388 );
1389
1390 check_edit(
1391 "foo!",
1392 r#"
1393/// Foo
1394///
1395/// Don't call `fooo!()` `fooo!()`, or `_foo![]` `_foo![]`,
1396/// call as `let _=foo! { hello world };`
1397macro_rules! foo { () => {} }
1398fn main() { <|> }
1399"#,
1400 r#"
1401/// Foo
1402///
1403/// Don't call `fooo!()` `fooo!()`, or `_foo![]` `_foo![]`,
1404/// call as `let _=foo! { hello world };`
1405macro_rules! foo { () => {} }
1406fn main() { foo! {$0} }
1407"#,
1408 )
1409 }
1410}