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