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