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