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