diff options
Diffstat (limited to 'crates/completion/src/render.rs')
-rw-r--r-- | crates/completion/src/render.rs | 848 |
1 files changed, 848 insertions, 0 deletions
diff --git a/crates/completion/src/render.rs b/crates/completion/src/render.rs new file mode 100644 index 000000000..1fa02c375 --- /dev/null +++ b/crates/completion/src/render.rs | |||
@@ -0,0 +1,848 @@ | |||
1 | //! `render` module provides utilities for rendering completion suggestions | ||
2 | //! into code pieces that will be presented to user. | ||
3 | |||
4 | pub(crate) mod macro_; | ||
5 | pub(crate) mod function; | ||
6 | pub(crate) mod enum_variant; | ||
7 | pub(crate) mod const_; | ||
8 | pub(crate) mod type_alias; | ||
9 | |||
10 | mod builder_ext; | ||
11 | |||
12 | use hir::{Documentation, HasAttrs, HirDisplay, Mutability, ScopeDef, Type}; | ||
13 | use ide_db::RootDatabase; | ||
14 | use syntax::TextRange; | ||
15 | use test_utils::mark; | ||
16 | |||
17 | use crate::{ | ||
18 | config::SnippetCap, CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, | ||
19 | CompletionScore, | ||
20 | }; | ||
21 | |||
22 | use crate::render::{enum_variant::render_enum_variant, function::render_fn, macro_::render_macro}; | ||
23 | |||
24 | pub(crate) fn render_field<'a>( | ||
25 | ctx: RenderContext<'a>, | ||
26 | field: hir::Field, | ||
27 | ty: &Type, | ||
28 | ) -> CompletionItem { | ||
29 | Render::new(ctx).add_field(field, ty) | ||
30 | } | ||
31 | |||
32 | pub(crate) fn render_tuple_field<'a>( | ||
33 | ctx: RenderContext<'a>, | ||
34 | field: usize, | ||
35 | ty: &Type, | ||
36 | ) -> CompletionItem { | ||
37 | Render::new(ctx).add_tuple_field(field, ty) | ||
38 | } | ||
39 | |||
40 | pub(crate) fn render_resolution<'a>( | ||
41 | ctx: RenderContext<'a>, | ||
42 | local_name: String, | ||
43 | resolution: &ScopeDef, | ||
44 | ) -> Option<CompletionItem> { | ||
45 | Render::new(ctx).render_resolution(local_name, resolution) | ||
46 | } | ||
47 | |||
48 | /// Interface for data and methods required for items rendering. | ||
49 | #[derive(Debug)] | ||
50 | pub(crate) struct RenderContext<'a> { | ||
51 | completion: &'a CompletionContext<'a>, | ||
52 | } | ||
53 | |||
54 | impl<'a> RenderContext<'a> { | ||
55 | pub(crate) fn new(completion: &'a CompletionContext<'a>) -> RenderContext<'a> { | ||
56 | RenderContext { completion } | ||
57 | } | ||
58 | |||
59 | fn snippet_cap(&self) -> Option<SnippetCap> { | ||
60 | self.completion.config.snippet_cap.clone() | ||
61 | } | ||
62 | |||
63 | fn db(&self) -> &'a RootDatabase { | ||
64 | &self.completion.db | ||
65 | } | ||
66 | |||
67 | fn source_range(&self) -> TextRange { | ||
68 | self.completion.source_range() | ||
69 | } | ||
70 | |||
71 | fn is_deprecated(&self, node: impl HasAttrs) -> bool { | ||
72 | node.attrs(self.db()).by_key("deprecated").exists() | ||
73 | } | ||
74 | |||
75 | fn docs(&self, node: impl HasAttrs) -> Option<Documentation> { | ||
76 | node.docs(self.db()) | ||
77 | } | ||
78 | |||
79 | fn active_name_and_type(&self) -> Option<(String, Type)> { | ||
80 | if let Some(record_field) = &self.completion.record_field_syntax { | ||
81 | mark::hit!(record_field_type_match); | ||
82 | let (struct_field, _local) = self.completion.sema.resolve_record_field(record_field)?; | ||
83 | Some((struct_field.name(self.db()).to_string(), struct_field.signature_ty(self.db()))) | ||
84 | } else if let Some(active_parameter) = &self.completion.active_parameter { | ||
85 | mark::hit!(active_param_type_match); | ||
86 | Some((active_parameter.name.clone(), active_parameter.ty.clone())) | ||
87 | } else { | ||
88 | None | ||
89 | } | ||
90 | } | ||
91 | } | ||
92 | |||
93 | /// Generic renderer for completion items. | ||
94 | #[derive(Debug)] | ||
95 | struct Render<'a> { | ||
96 | ctx: RenderContext<'a>, | ||
97 | } | ||
98 | |||
99 | impl<'a> Render<'a> { | ||
100 | fn new(ctx: RenderContext<'a>) -> Render<'a> { | ||
101 | Render { ctx } | ||
102 | } | ||
103 | |||
104 | fn add_field(&mut self, field: hir::Field, ty: &Type) -> CompletionItem { | ||
105 | let is_deprecated = self.ctx.is_deprecated(field); | ||
106 | let name = field.name(self.ctx.db()); | ||
107 | let mut item = CompletionItem::new( | ||
108 | CompletionKind::Reference, | ||
109 | self.ctx.source_range(), | ||
110 | name.to_string(), | ||
111 | ) | ||
112 | .kind(CompletionItemKind::Field) | ||
113 | .detail(ty.display(self.ctx.db()).to_string()) | ||
114 | .set_documentation(field.docs(self.ctx.db())) | ||
115 | .set_deprecated(is_deprecated); | ||
116 | |||
117 | if let Some(score) = compute_score(&self.ctx, &ty, &name.to_string()) { | ||
118 | item = item.set_score(score); | ||
119 | } | ||
120 | |||
121 | item.build() | ||
122 | } | ||
123 | |||
124 | fn add_tuple_field(&mut self, field: usize, ty: &Type) -> CompletionItem { | ||
125 | CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), field.to_string()) | ||
126 | .kind(CompletionItemKind::Field) | ||
127 | .detail(ty.display(self.ctx.db()).to_string()) | ||
128 | .build() | ||
129 | } | ||
130 | |||
131 | fn render_resolution( | ||
132 | self, | ||
133 | local_name: String, | ||
134 | resolution: &ScopeDef, | ||
135 | ) -> Option<CompletionItem> { | ||
136 | use hir::ModuleDef::*; | ||
137 | |||
138 | let completion_kind = match resolution { | ||
139 | ScopeDef::ModuleDef(BuiltinType(..)) => CompletionKind::BuiltinType, | ||
140 | _ => CompletionKind::Reference, | ||
141 | }; | ||
142 | |||
143 | let kind = match resolution { | ||
144 | ScopeDef::ModuleDef(Function(func)) => { | ||
145 | let item = render_fn(self.ctx, Some(local_name), *func); | ||
146 | return Some(item); | ||
147 | } | ||
148 | ScopeDef::ModuleDef(EnumVariant(var)) => { | ||
149 | let item = render_enum_variant(self.ctx, Some(local_name), *var, None); | ||
150 | return Some(item); | ||
151 | } | ||
152 | ScopeDef::MacroDef(mac) => { | ||
153 | let item = render_macro(self.ctx, local_name, *mac); | ||
154 | return item; | ||
155 | } | ||
156 | |||
157 | ScopeDef::ModuleDef(Module(..)) => CompletionItemKind::Module, | ||
158 | ScopeDef::ModuleDef(Adt(hir::Adt::Struct(_))) => CompletionItemKind::Struct, | ||
159 | // FIXME: add CompletionItemKind::Union | ||
160 | ScopeDef::ModuleDef(Adt(hir::Adt::Union(_))) => CompletionItemKind::Struct, | ||
161 | ScopeDef::ModuleDef(Adt(hir::Adt::Enum(_))) => CompletionItemKind::Enum, | ||
162 | ScopeDef::ModuleDef(Const(..)) => CompletionItemKind::Const, | ||
163 | ScopeDef::ModuleDef(Static(..)) => CompletionItemKind::Static, | ||
164 | ScopeDef::ModuleDef(Trait(..)) => CompletionItemKind::Trait, | ||
165 | ScopeDef::ModuleDef(TypeAlias(..)) => CompletionItemKind::TypeAlias, | ||
166 | ScopeDef::ModuleDef(BuiltinType(..)) => CompletionItemKind::BuiltinType, | ||
167 | ScopeDef::GenericParam(..) => CompletionItemKind::TypeParam, | ||
168 | ScopeDef::Local(..) => CompletionItemKind::Binding, | ||
169 | // (does this need its own kind?) | ||
170 | ScopeDef::AdtSelfType(..) | ScopeDef::ImplSelfType(..) => CompletionItemKind::TypeParam, | ||
171 | ScopeDef::Unknown => { | ||
172 | let item = CompletionItem::new( | ||
173 | CompletionKind::Reference, | ||
174 | self.ctx.source_range(), | ||
175 | local_name, | ||
176 | ) | ||
177 | .kind(CompletionItemKind::UnresolvedReference) | ||
178 | .build(); | ||
179 | return Some(item); | ||
180 | } | ||
181 | }; | ||
182 | |||
183 | let docs = self.docs(resolution); | ||
184 | |||
185 | let mut item = | ||
186 | CompletionItem::new(completion_kind, self.ctx.source_range(), local_name.clone()); | ||
187 | if let ScopeDef::Local(local) = resolution { | ||
188 | let ty = local.ty(self.ctx.db()); | ||
189 | if !ty.is_unknown() { | ||
190 | item = item.detail(ty.display(self.ctx.db()).to_string()); | ||
191 | } | ||
192 | }; | ||
193 | |||
194 | let mut ref_match = None; | ||
195 | if let ScopeDef::Local(local) = resolution { | ||
196 | if let Some((active_name, active_type)) = self.ctx.active_name_and_type() { | ||
197 | let ty = local.ty(self.ctx.db()); | ||
198 | if let Some(score) = | ||
199 | compute_score_from_active(&active_type, &active_name, &ty, &local_name) | ||
200 | { | ||
201 | item = item.set_score(score); | ||
202 | } | ||
203 | ref_match = refed_type_matches(&active_type, &active_name, &ty, &local_name); | ||
204 | } | ||
205 | } | ||
206 | |||
207 | // Add `<>` for generic types | ||
208 | if self.ctx.completion.is_path_type | ||
209 | && !self.ctx.completion.has_type_args | ||
210 | && self.ctx.completion.config.add_call_parenthesis | ||
211 | { | ||
212 | if let Some(cap) = self.ctx.snippet_cap() { | ||
213 | let has_non_default_type_params = match resolution { | ||
214 | ScopeDef::ModuleDef(Adt(it)) => it.has_non_default_type_params(self.ctx.db()), | ||
215 | ScopeDef::ModuleDef(TypeAlias(it)) => { | ||
216 | it.has_non_default_type_params(self.ctx.db()) | ||
217 | } | ||
218 | _ => false, | ||
219 | }; | ||
220 | if has_non_default_type_params { | ||
221 | mark::hit!(inserts_angle_brackets_for_generics); | ||
222 | item = item | ||
223 | .lookup_by(local_name.clone()) | ||
224 | .label(format!("{}<…>", local_name)) | ||
225 | .insert_snippet(cap, format!("{}<$0>", local_name)); | ||
226 | } | ||
227 | } | ||
228 | } | ||
229 | |||
230 | let item = item.kind(kind).set_documentation(docs).set_ref_match(ref_match).build(); | ||
231 | Some(item) | ||
232 | } | ||
233 | |||
234 | fn docs(&self, resolution: &ScopeDef) -> Option<Documentation> { | ||
235 | use hir::ModuleDef::*; | ||
236 | match resolution { | ||
237 | ScopeDef::ModuleDef(Module(it)) => it.docs(self.ctx.db()), | ||
238 | ScopeDef::ModuleDef(Adt(it)) => it.docs(self.ctx.db()), | ||
239 | ScopeDef::ModuleDef(EnumVariant(it)) => it.docs(self.ctx.db()), | ||
240 | ScopeDef::ModuleDef(Const(it)) => it.docs(self.ctx.db()), | ||
241 | ScopeDef::ModuleDef(Static(it)) => it.docs(self.ctx.db()), | ||
242 | ScopeDef::ModuleDef(Trait(it)) => it.docs(self.ctx.db()), | ||
243 | ScopeDef::ModuleDef(TypeAlias(it)) => it.docs(self.ctx.db()), | ||
244 | _ => None, | ||
245 | } | ||
246 | } | ||
247 | } | ||
248 | |||
249 | fn compute_score_from_active( | ||
250 | active_type: &Type, | ||
251 | active_name: &str, | ||
252 | ty: &Type, | ||
253 | name: &str, | ||
254 | ) -> Option<CompletionScore> { | ||
255 | // Compute score | ||
256 | // For the same type | ||
257 | if active_type != ty { | ||
258 | return None; | ||
259 | } | ||
260 | |||
261 | let mut res = CompletionScore::TypeMatch; | ||
262 | |||
263 | // If same type + same name then go top position | ||
264 | if active_name == name { | ||
265 | res = CompletionScore::TypeAndNameMatch | ||
266 | } | ||
267 | |||
268 | Some(res) | ||
269 | } | ||
270 | fn refed_type_matches( | ||
271 | active_type: &Type, | ||
272 | active_name: &str, | ||
273 | ty: &Type, | ||
274 | name: &str, | ||
275 | ) -> Option<(Mutability, CompletionScore)> { | ||
276 | let derefed_active = active_type.remove_ref()?; | ||
277 | let score = compute_score_from_active(&derefed_active, &active_name, &ty, &name)?; | ||
278 | Some(( | ||
279 | if active_type.is_mutable_reference() { Mutability::Mut } else { Mutability::Shared }, | ||
280 | score, | ||
281 | )) | ||
282 | } | ||
283 | |||
284 | fn compute_score(ctx: &RenderContext, ty: &Type, name: &str) -> Option<CompletionScore> { | ||
285 | let (active_name, active_type) = ctx.active_name_and_type()?; | ||
286 | compute_score_from_active(&active_type, &active_name, ty, name) | ||
287 | } | ||
288 | |||
289 | #[cfg(test)] | ||
290 | mod tests { | ||
291 | use std::cmp::Reverse; | ||
292 | |||
293 | use expect_test::{expect, Expect}; | ||
294 | use test_utils::mark; | ||
295 | |||
296 | use crate::{ | ||
297 | test_utils::{check_edit, do_completion, get_all_items}, | ||
298 | CompletionConfig, CompletionKind, CompletionScore, | ||
299 | }; | ||
300 | |||
301 | fn check(ra_fixture: &str, expect: Expect) { | ||
302 | let actual = do_completion(ra_fixture, CompletionKind::Reference); | ||
303 | expect.assert_debug_eq(&actual); | ||
304 | } | ||
305 | |||
306 | fn check_scores(ra_fixture: &str, expect: Expect) { | ||
307 | fn display_score(score: Option<CompletionScore>) -> &'static str { | ||
308 | match score { | ||
309 | Some(CompletionScore::TypeMatch) => "[type]", | ||
310 | Some(CompletionScore::TypeAndNameMatch) => "[type+name]", | ||
311 | None => "[]".into(), | ||
312 | } | ||
313 | } | ||
314 | |||
315 | let mut completions = get_all_items(CompletionConfig::default(), ra_fixture); | ||
316 | completions.sort_by_key(|it| (Reverse(it.score()), it.label().to_string())); | ||
317 | let actual = completions | ||
318 | .into_iter() | ||
319 | .filter(|it| it.completion_kind == CompletionKind::Reference) | ||
320 | .map(|it| { | ||
321 | let tag = it.kind().unwrap().tag(); | ||
322 | let score = display_score(it.score()); | ||
323 | format!("{} {} {}\n", tag, it.label(), score) | ||
324 | }) | ||
325 | .collect::<String>(); | ||
326 | expect.assert_eq(&actual); | ||
327 | } | ||
328 | |||
329 | #[test] | ||
330 | fn enum_detail_includes_record_fields() { | ||
331 | check( | ||
332 | r#" | ||
333 | enum Foo { Foo { x: i32, y: i32 } } | ||
334 | |||
335 | fn main() { Foo::Fo<|> } | ||
336 | "#, | ||
337 | expect![[r#" | ||
338 | [ | ||
339 | CompletionItem { | ||
340 | label: "Foo", | ||
341 | source_range: 54..56, | ||
342 | delete: 54..56, | ||
343 | insert: "Foo", | ||
344 | kind: EnumVariant, | ||
345 | detail: "{ x: i32, y: i32 }", | ||
346 | }, | ||
347 | ] | ||
348 | "#]], | ||
349 | ); | ||
350 | } | ||
351 | |||
352 | #[test] | ||
353 | fn enum_detail_doesnt_include_tuple_fields() { | ||
354 | check( | ||
355 | r#" | ||
356 | enum Foo { Foo (i32, i32) } | ||
357 | |||
358 | fn main() { Foo::Fo<|> } | ||
359 | "#, | ||
360 | expect![[r#" | ||
361 | [ | ||
362 | CompletionItem { | ||
363 | label: "Foo(…)", | ||
364 | source_range: 46..48, | ||
365 | delete: 46..48, | ||
366 | insert: "Foo($0)", | ||
367 | kind: EnumVariant, | ||
368 | lookup: "Foo", | ||
369 | detail: "(i32, i32)", | ||
370 | trigger_call_info: true, | ||
371 | }, | ||
372 | ] | ||
373 | "#]], | ||
374 | ); | ||
375 | } | ||
376 | |||
377 | #[test] | ||
378 | fn enum_detail_just_parentheses_for_unit() { | ||
379 | check( | ||
380 | r#" | ||
381 | enum Foo { Foo } | ||
382 | |||
383 | fn main() { Foo::Fo<|> } | ||
384 | "#, | ||
385 | expect![[r#" | ||
386 | [ | ||
387 | CompletionItem { | ||
388 | label: "Foo", | ||
389 | source_range: 35..37, | ||
390 | delete: 35..37, | ||
391 | insert: "Foo", | ||
392 | kind: EnumVariant, | ||
393 | detail: "()", | ||
394 | }, | ||
395 | ] | ||
396 | "#]], | ||
397 | ); | ||
398 | } | ||
399 | |||
400 | #[test] | ||
401 | fn lookup_enums_by_two_qualifiers() { | ||
402 | check( | ||
403 | r#" | ||
404 | mod m { | ||
405 | pub enum Spam { Foo, Bar(i32) } | ||
406 | } | ||
407 | fn main() { let _: m::Spam = S<|> } | ||
408 | "#, | ||
409 | expect![[r#" | ||
410 | [ | ||
411 | CompletionItem { | ||
412 | label: "Spam::Bar(…)", | ||
413 | source_range: 75..76, | ||
414 | delete: 75..76, | ||
415 | insert: "Spam::Bar($0)", | ||
416 | kind: EnumVariant, | ||
417 | lookup: "Spam::Bar", | ||
418 | detail: "(i32)", | ||
419 | trigger_call_info: true, | ||
420 | }, | ||
421 | CompletionItem { | ||
422 | label: "m", | ||
423 | source_range: 75..76, | ||
424 | delete: 75..76, | ||
425 | insert: "m", | ||
426 | kind: Module, | ||
427 | }, | ||
428 | CompletionItem { | ||
429 | label: "m::Spam::Foo", | ||
430 | source_range: 75..76, | ||
431 | delete: 75..76, | ||
432 | insert: "m::Spam::Foo", | ||
433 | kind: EnumVariant, | ||
434 | lookup: "Spam::Foo", | ||
435 | detail: "()", | ||
436 | }, | ||
437 | CompletionItem { | ||
438 | label: "main()", | ||
439 | source_range: 75..76, | ||
440 | delete: 75..76, | ||
441 | insert: "main()$0", | ||
442 | kind: Function, | ||
443 | lookup: "main", | ||
444 | detail: "fn main()", | ||
445 | }, | ||
446 | ] | ||
447 | "#]], | ||
448 | ) | ||
449 | } | ||
450 | |||
451 | #[test] | ||
452 | fn sets_deprecated_flag_in_items() { | ||
453 | check( | ||
454 | r#" | ||
455 | #[deprecated] | ||
456 | fn something_deprecated() {} | ||
457 | #[deprecated(since = "1.0.0")] | ||
458 | fn something_else_deprecated() {} | ||
459 | |||
460 | fn main() { som<|> } | ||
461 | "#, | ||
462 | expect![[r#" | ||
463 | [ | ||
464 | CompletionItem { | ||
465 | label: "main()", | ||
466 | source_range: 121..124, | ||
467 | delete: 121..124, | ||
468 | insert: "main()$0", | ||
469 | kind: Function, | ||
470 | lookup: "main", | ||
471 | detail: "fn main()", | ||
472 | }, | ||
473 | CompletionItem { | ||
474 | label: "something_deprecated()", | ||
475 | source_range: 121..124, | ||
476 | delete: 121..124, | ||
477 | insert: "something_deprecated()$0", | ||
478 | kind: Function, | ||
479 | lookup: "something_deprecated", | ||
480 | detail: "fn something_deprecated()", | ||
481 | deprecated: true, | ||
482 | }, | ||
483 | CompletionItem { | ||
484 | label: "something_else_deprecated()", | ||
485 | source_range: 121..124, | ||
486 | delete: 121..124, | ||
487 | insert: "something_else_deprecated()$0", | ||
488 | kind: Function, | ||
489 | lookup: "something_else_deprecated", | ||
490 | detail: "fn something_else_deprecated()", | ||
491 | deprecated: true, | ||
492 | }, | ||
493 | ] | ||
494 | "#]], | ||
495 | ); | ||
496 | |||
497 | check( | ||
498 | r#" | ||
499 | struct A { #[deprecated] the_field: u32 } | ||
500 | fn foo() { A { the<|> } } | ||
501 | "#, | ||
502 | expect![[r#" | ||
503 | [ | ||
504 | CompletionItem { | ||
505 | label: "the_field", | ||
506 | source_range: 57..60, | ||
507 | delete: 57..60, | ||
508 | insert: "the_field", | ||
509 | kind: Field, | ||
510 | detail: "u32", | ||
511 | deprecated: true, | ||
512 | }, | ||
513 | ] | ||
514 | "#]], | ||
515 | ); | ||
516 | } | ||
517 | |||
518 | #[test] | ||
519 | fn renders_docs() { | ||
520 | check( | ||
521 | r#" | ||
522 | struct S { | ||
523 | /// Field docs | ||
524 | foo: | ||
525 | } | ||
526 | impl S { | ||
527 | /// Method docs | ||
528 | fn bar(self) { self.<|> } | ||
529 | }"#, | ||
530 | expect![[r#" | ||
531 | [ | ||
532 | CompletionItem { | ||
533 | label: "bar()", | ||
534 | source_range: 94..94, | ||
535 | delete: 94..94, | ||
536 | insert: "bar()$0", | ||
537 | kind: Method, | ||
538 | lookup: "bar", | ||
539 | detail: "fn bar(self)", | ||
540 | documentation: Documentation( | ||
541 | "Method docs", | ||
542 | ), | ||
543 | }, | ||
544 | CompletionItem { | ||
545 | label: "foo", | ||
546 | source_range: 94..94, | ||
547 | delete: 94..94, | ||
548 | insert: "foo", | ||
549 | kind: Field, | ||
550 | detail: "{unknown}", | ||
551 | documentation: Documentation( | ||
552 | "Field docs", | ||
553 | ), | ||
554 | }, | ||
555 | ] | ||
556 | "#]], | ||
557 | ); | ||
558 | |||
559 | check( | ||
560 | r#" | ||
561 | use self::my<|>; | ||
562 | |||
563 | /// mod docs | ||
564 | mod my { } | ||
565 | |||
566 | /// enum docs | ||
567 | enum E { | ||
568 | /// variant docs | ||
569 | V | ||
570 | } | ||
571 | use self::E::*; | ||
572 | "#, | ||
573 | expect![[r#" | ||
574 | [ | ||
575 | CompletionItem { | ||
576 | label: "E", | ||
577 | source_range: 10..12, | ||
578 | delete: 10..12, | ||
579 | insert: "E", | ||
580 | kind: Enum, | ||
581 | documentation: Documentation( | ||
582 | "enum docs", | ||
583 | ), | ||
584 | }, | ||
585 | CompletionItem { | ||
586 | label: "V", | ||
587 | source_range: 10..12, | ||
588 | delete: 10..12, | ||
589 | insert: "V", | ||
590 | kind: EnumVariant, | ||
591 | detail: "()", | ||
592 | documentation: Documentation( | ||
593 | "variant docs", | ||
594 | ), | ||
595 | }, | ||
596 | CompletionItem { | ||
597 | label: "my", | ||
598 | source_range: 10..12, | ||
599 | delete: 10..12, | ||
600 | insert: "my", | ||
601 | kind: Module, | ||
602 | documentation: Documentation( | ||
603 | "mod docs", | ||
604 | ), | ||
605 | }, | ||
606 | ] | ||
607 | "#]], | ||
608 | ) | ||
609 | } | ||
610 | |||
611 | #[test] | ||
612 | fn dont_render_attrs() { | ||
613 | check( | ||
614 | r#" | ||
615 | struct S; | ||
616 | impl S { | ||
617 | #[inline] | ||
618 | fn the_method(&self) { } | ||
619 | } | ||
620 | fn foo(s: S) { s.<|> } | ||
621 | "#, | ||
622 | expect![[r#" | ||
623 | [ | ||
624 | CompletionItem { | ||
625 | label: "the_method()", | ||
626 | source_range: 81..81, | ||
627 | delete: 81..81, | ||
628 | insert: "the_method()$0", | ||
629 | kind: Method, | ||
630 | lookup: "the_method", | ||
631 | detail: "fn the_method(&self)", | ||
632 | }, | ||
633 | ] | ||
634 | "#]], | ||
635 | ) | ||
636 | } | ||
637 | |||
638 | #[test] | ||
639 | fn no_call_parens_if_fn_ptr_needed() { | ||
640 | mark::check!(no_call_parens_if_fn_ptr_needed); | ||
641 | check_edit( | ||
642 | "foo", | ||
643 | r#" | ||
644 | fn foo(foo: u8, bar: u8) {} | ||
645 | struct ManualVtable { f: fn(u8, u8) } | ||
646 | |||
647 | fn main() -> ManualVtable { | ||
648 | ManualVtable { f: f<|> } | ||
649 | } | ||
650 | "#, | ||
651 | r#" | ||
652 | fn foo(foo: u8, bar: u8) {} | ||
653 | struct ManualVtable { f: fn(u8, u8) } | ||
654 | |||
655 | fn main() -> ManualVtable { | ||
656 | ManualVtable { f: foo } | ||
657 | } | ||
658 | "#, | ||
659 | ); | ||
660 | } | ||
661 | |||
662 | #[test] | ||
663 | fn no_parens_in_use_item() { | ||
664 | mark::check!(no_parens_in_use_item); | ||
665 | check_edit( | ||
666 | "foo", | ||
667 | r#" | ||
668 | mod m { pub fn foo() {} } | ||
669 | use crate::m::f<|>; | ||
670 | "#, | ||
671 | r#" | ||
672 | mod m { pub fn foo() {} } | ||
673 | use crate::m::foo; | ||
674 | "#, | ||
675 | ); | ||
676 | } | ||
677 | |||
678 | #[test] | ||
679 | fn no_parens_in_call() { | ||
680 | check_edit( | ||
681 | "foo", | ||
682 | r#" | ||
683 | fn foo(x: i32) {} | ||
684 | fn main() { f<|>(); } | ||
685 | "#, | ||
686 | r#" | ||
687 | fn foo(x: i32) {} | ||
688 | fn main() { foo(); } | ||
689 | "#, | ||
690 | ); | ||
691 | check_edit( | ||
692 | "foo", | ||
693 | r#" | ||
694 | struct Foo; | ||
695 | impl Foo { fn foo(&self){} } | ||
696 | fn f(foo: &Foo) { foo.f<|>(); } | ||
697 | "#, | ||
698 | r#" | ||
699 | struct Foo; | ||
700 | impl Foo { fn foo(&self){} } | ||
701 | fn f(foo: &Foo) { foo.foo(); } | ||
702 | "#, | ||
703 | ); | ||
704 | } | ||
705 | |||
706 | #[test] | ||
707 | fn inserts_angle_brackets_for_generics() { | ||
708 | mark::check!(inserts_angle_brackets_for_generics); | ||
709 | check_edit( | ||
710 | "Vec", | ||
711 | r#" | ||
712 | struct Vec<T> {} | ||
713 | fn foo(xs: Ve<|>) | ||
714 | "#, | ||
715 | r#" | ||
716 | struct Vec<T> {} | ||
717 | fn foo(xs: Vec<$0>) | ||
718 | "#, | ||
719 | ); | ||
720 | check_edit( | ||
721 | "Vec", | ||
722 | r#" | ||
723 | type Vec<T> = (T,); | ||
724 | fn foo(xs: Ve<|>) | ||
725 | "#, | ||
726 | r#" | ||
727 | type Vec<T> = (T,); | ||
728 | fn foo(xs: Vec<$0>) | ||
729 | "#, | ||
730 | ); | ||
731 | check_edit( | ||
732 | "Vec", | ||
733 | r#" | ||
734 | struct Vec<T = i128> {} | ||
735 | fn foo(xs: Ve<|>) | ||
736 | "#, | ||
737 | r#" | ||
738 | struct Vec<T = i128> {} | ||
739 | fn foo(xs: Vec) | ||
740 | "#, | ||
741 | ); | ||
742 | check_edit( | ||
743 | "Vec", | ||
744 | r#" | ||
745 | struct Vec<T> {} | ||
746 | fn foo(xs: Ve<|><i128>) | ||
747 | "#, | ||
748 | r#" | ||
749 | struct Vec<T> {} | ||
750 | fn foo(xs: Vec<i128>) | ||
751 | "#, | ||
752 | ); | ||
753 | } | ||
754 | |||
755 | #[test] | ||
756 | fn active_param_score() { | ||
757 | mark::check!(active_param_type_match); | ||
758 | check_scores( | ||
759 | r#" | ||
760 | struct S { foo: i64, bar: u32, baz: u32 } | ||
761 | fn test(bar: u32) { } | ||
762 | fn foo(s: S) { test(s.<|>) } | ||
763 | "#, | ||
764 | expect![[r#" | ||
765 | fd bar [type+name] | ||
766 | fd baz [type] | ||
767 | fd foo [] | ||
768 | "#]], | ||
769 | ); | ||
770 | } | ||
771 | |||
772 | #[test] | ||
773 | fn record_field_scores() { | ||
774 | mark::check!(record_field_type_match); | ||
775 | check_scores( | ||
776 | r#" | ||
777 | struct A { foo: i64, bar: u32, baz: u32 } | ||
778 | struct B { x: (), y: f32, bar: u32 } | ||
779 | fn foo(a: A) { B { bar: a.<|> }; } | ||
780 | "#, | ||
781 | expect![[r#" | ||
782 | fd bar [type+name] | ||
783 | fd baz [type] | ||
784 | fd foo [] | ||
785 | "#]], | ||
786 | ) | ||
787 | } | ||
788 | |||
789 | #[test] | ||
790 | fn record_field_and_call_scores() { | ||
791 | check_scores( | ||
792 | r#" | ||
793 | struct A { foo: i64, bar: u32, baz: u32 } | ||
794 | struct B { x: (), y: f32, bar: u32 } | ||
795 | fn f(foo: i64) { } | ||
796 | fn foo(a: A) { B { bar: f(a.<|>) }; } | ||
797 | "#, | ||
798 | expect![[r#" | ||
799 | fd foo [type+name] | ||
800 | fd bar [] | ||
801 | fd baz [] | ||
802 | "#]], | ||
803 | ); | ||
804 | check_scores( | ||
805 | r#" | ||
806 | struct A { foo: i64, bar: u32, baz: u32 } | ||
807 | struct B { x: (), y: f32, bar: u32 } | ||
808 | fn f(foo: i64) { } | ||
809 | fn foo(a: A) { f(B { bar: a.<|> }); } | ||
810 | "#, | ||
811 | expect![[r#" | ||
812 | fd bar [type+name] | ||
813 | fd baz [type] | ||
814 | fd foo [] | ||
815 | "#]], | ||
816 | ); | ||
817 | } | ||
818 | |||
819 | #[test] | ||
820 | fn prioritize_exact_ref_match() { | ||
821 | check_scores( | ||
822 | r#" | ||
823 | struct WorldSnapshot { _f: () }; | ||
824 | fn go(world: &WorldSnapshot) { go(w<|>) } | ||
825 | "#, | ||
826 | expect![[r#" | ||
827 | bn world [type+name] | ||
828 | st WorldSnapshot [] | ||
829 | fn go(…) [] | ||
830 | "#]], | ||
831 | ); | ||
832 | } | ||
833 | |||
834 | #[test] | ||
835 | fn too_many_arguments() { | ||
836 | check_scores( | ||
837 | r#" | ||
838 | struct Foo; | ||
839 | fn f(foo: &Foo) { f(foo, w<|>) } | ||
840 | "#, | ||
841 | expect![[r#" | ||
842 | st Foo [] | ||
843 | fn f(…) [] | ||
844 | bn foo [] | ||
845 | "#]], | ||
846 | ); | ||
847 | } | ||
848 | } | ||