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