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