diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2021-05-31 14:21:31 +0100 |
---|---|---|
committer | GitHub <[email protected]> | 2021-05-31 14:21:31 +0100 |
commit | e6ec860363a140b8aa3dcaafaf6e9d7327838610 (patch) | |
tree | cacaf711d01ce64074ecf675287ab03c85487ec1 /crates/ide_completion/src/completions | |
parent | e9a797748daa7e25cde66927b8907b2d976201a5 (diff) | |
parent | fb7105a5801ab1d0ede830cd53bbc3ccbf0b5e2c (diff) |
Merge #9039
9039: feat: Complete fields and methods with `self.` prefixed when inside methods r=matklad a=Veykril
![w65NbjkZiG](https://user-images.githubusercontent.com/3757771/119984385-a0111700-bfc1-11eb-9dbf-52fdaa4d72b5.gif)
Closes #7173
Co-authored-by: Lukas Wirth <[email protected]>
Diffstat (limited to 'crates/ide_completion/src/completions')
-rw-r--r-- | crates/ide_completion/src/completions/dot.rs | 84 | ||||
-rw-r--r-- | crates/ide_completion/src/completions/record.rs | 2 | ||||
-rw-r--r-- | crates/ide_completion/src/completions/unqualified_path.rs | 2 |
3 files changed, 78 insertions, 10 deletions
diff --git a/crates/ide_completion/src/completions/dot.rs b/crates/ide_completion/src/completions/dot.rs index fd9738743..302c9ccbd 100644 --- a/crates/ide_completion/src/completions/dot.rs +++ b/crates/ide_completion/src/completions/dot.rs | |||
@@ -1,6 +1,7 @@ | |||
1 | //! Completes references after dot (fields and method calls). | 1 | //! Completes references after dot (fields and method calls). |
2 | 2 | ||
3 | use hir::{HasVisibility, Type}; | 3 | use either::Either; |
4 | use hir::{HasVisibility, ScopeDef}; | ||
4 | use rustc_hash::FxHashSet; | 5 | use rustc_hash::FxHashSet; |
5 | 6 | ||
6 | use crate::{context::CompletionContext, Completions}; | 7 | use crate::{context::CompletionContext, Completions}; |
@@ -9,7 +10,7 @@ use crate::{context::CompletionContext, Completions}; | |||
9 | pub(crate) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) { | 10 | pub(crate) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) { |
10 | let dot_receiver = match &ctx.dot_receiver { | 11 | let dot_receiver = match &ctx.dot_receiver { |
11 | Some(expr) => expr, | 12 | Some(expr) => expr, |
12 | _ => return, | 13 | _ => return complete_undotted_self(acc, ctx), |
13 | }; | 14 | }; |
14 | 15 | ||
15 | let receiver_ty = match ctx.sema.type_of_expr(&dot_receiver) { | 16 | let receiver_ty = match ctx.sema.type_of_expr(&dot_receiver) { |
@@ -20,12 +21,43 @@ pub(crate) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) { | |||
20 | if ctx.is_call { | 21 | if ctx.is_call { |
21 | cov_mark::hit!(test_no_struct_field_completion_for_method_call); | 22 | cov_mark::hit!(test_no_struct_field_completion_for_method_call); |
22 | } else { | 23 | } else { |
23 | complete_fields(acc, ctx, &receiver_ty); | 24 | complete_fields(ctx, &receiver_ty, |field, ty| match field { |
25 | Either::Left(field) => acc.add_field(ctx, None, field, &ty), | ||
26 | Either::Right(tuple_idx) => acc.add_tuple_field(ctx, None, tuple_idx, &ty), | ||
27 | }); | ||
24 | } | 28 | } |
25 | complete_methods(acc, ctx, &receiver_ty); | 29 | complete_methods(ctx, &receiver_ty, |func| acc.add_method(ctx, func, None, None)); |
30 | } | ||
31 | |||
32 | fn complete_undotted_self(acc: &mut Completions, ctx: &CompletionContext) { | ||
33 | if !ctx.is_trivial_path || !ctx.config.enable_self_on_the_fly { | ||
34 | return; | ||
35 | } | ||
36 | ctx.scope.process_all_names(&mut |name, def| { | ||
37 | if let ScopeDef::Local(local) = &def { | ||
38 | if local.is_self(ctx.db) { | ||
39 | let ty = local.ty(ctx.db); | ||
40 | complete_fields(ctx, &ty, |field, ty| match field { | ||
41 | either::Either::Left(field) => { | ||
42 | acc.add_field(ctx, Some(name.clone()), field, &ty) | ||
43 | } | ||
44 | either::Either::Right(tuple_idx) => { | ||
45 | acc.add_tuple_field(ctx, Some(name.clone()), tuple_idx, &ty) | ||
46 | } | ||
47 | }); | ||
48 | complete_methods(ctx, &ty, |func| { | ||
49 | acc.add_method(ctx, func, Some(name.clone()), None) | ||
50 | }); | ||
51 | } | ||
52 | } | ||
53 | }); | ||
26 | } | 54 | } |
27 | 55 | ||
28 | fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: &Type) { | 56 | fn complete_fields( |
57 | ctx: &CompletionContext, | ||
58 | receiver: &hir::Type, | ||
59 | mut f: impl FnMut(Either<hir::Field, usize>, hir::Type), | ||
60 | ) { | ||
29 | for receiver in receiver.autoderef(ctx.db) { | 61 | for receiver in receiver.autoderef(ctx.db) { |
30 | for (field, ty) in receiver.fields(ctx.db) { | 62 | for (field, ty) in receiver.fields(ctx.db) { |
31 | if ctx.scope.module().map_or(false, |m| !field.is_visible_from(ctx.db, m)) { | 63 | if ctx.scope.module().map_or(false, |m| !field.is_visible_from(ctx.db, m)) { |
@@ -33,16 +65,20 @@ fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: &Ty | |||
33 | // field is editable, we should show the completion | 65 | // field is editable, we should show the completion |
34 | continue; | 66 | continue; |
35 | } | 67 | } |
36 | acc.add_field(ctx, field, &ty); | 68 | f(Either::Left(field), ty); |
37 | } | 69 | } |
38 | for (i, ty) in receiver.tuple_fields(ctx.db).into_iter().enumerate() { | 70 | for (i, ty) in receiver.tuple_fields(ctx.db).into_iter().enumerate() { |
39 | // FIXME: Handle visibility | 71 | // FIXME: Handle visibility |
40 | acc.add_tuple_field(ctx, i, &ty); | 72 | f(Either::Right(i), ty); |
41 | } | 73 | } |
42 | } | 74 | } |
43 | } | 75 | } |
44 | 76 | ||
45 | fn complete_methods(acc: &mut Completions, ctx: &CompletionContext, receiver: &Type) { | 77 | fn complete_methods( |
78 | ctx: &CompletionContext, | ||
79 | receiver: &hir::Type, | ||
80 | mut f: impl FnMut(hir::Function), | ||
81 | ) { | ||
46 | if let Some(krate) = ctx.krate { | 82 | if let Some(krate) = ctx.krate { |
47 | let mut seen_methods = FxHashSet::default(); | 83 | let mut seen_methods = FxHashSet::default(); |
48 | let traits_in_scope = ctx.scope.traits_in_scope(); | 84 | let traits_in_scope = ctx.scope.traits_in_scope(); |
@@ -51,7 +87,7 @@ fn complete_methods(acc: &mut Completions, ctx: &CompletionContext, receiver: &T | |||
51 | && ctx.scope.module().map_or(true, |m| func.is_visible_from(ctx.db, m)) | 87 | && ctx.scope.module().map_or(true, |m| func.is_visible_from(ctx.db, m)) |
52 | && seen_methods.insert(func.name(ctx.db)) | 88 | && seen_methods.insert(func.name(ctx.db)) |
53 | { | 89 | { |
54 | acc.add_method(ctx, func, None); | 90 | f(func); |
55 | } | 91 | } |
56 | None::<()> | 92 | None::<()> |
57 | }); | 93 | }); |
@@ -484,4 +520,34 @@ impl S { | |||
484 | "#]], | 520 | "#]], |
485 | ); | 521 | ); |
486 | } | 522 | } |
523 | |||
524 | #[test] | ||
525 | fn completes_bare_fields_and_methods_in_methods() { | ||
526 | check( | ||
527 | r#" | ||
528 | struct Foo { field: i32 } | ||
529 | |||
530 | impl Foo { fn foo(&self) { $0 } }"#, | ||
531 | expect![[r#" | ||
532 | lc self &Foo | ||
533 | sp Self | ||
534 | st Foo | ||
535 | fd self.field i32 | ||
536 | me self.foo() fn(&self) | ||
537 | "#]], | ||
538 | ); | ||
539 | check( | ||
540 | r#" | ||
541 | struct Foo(i32); | ||
542 | |||
543 | impl Foo { fn foo(&mut self) { $0 } }"#, | ||
544 | expect![[r#" | ||
545 | lc self &mut Foo | ||
546 | sp Self | ||
547 | st Foo | ||
548 | fd self.0 i32 | ||
549 | me self.foo() fn(&mut self) | ||
550 | "#]], | ||
551 | ); | ||
552 | } | ||
487 | } | 553 | } |
diff --git a/crates/ide_completion/src/completions/record.rs b/crates/ide_completion/src/completions/record.rs index 227c08d01..0ac47cdbe 100644 --- a/crates/ide_completion/src/completions/record.rs +++ b/crates/ide_completion/src/completions/record.rs | |||
@@ -39,7 +39,7 @@ pub(crate) fn complete_record(acc: &mut Completions, ctx: &CompletionContext) -> | |||
39 | }; | 39 | }; |
40 | 40 | ||
41 | for (field, ty) in missing_fields { | 41 | for (field, ty) in missing_fields { |
42 | acc.add_field(ctx, field, &ty); | 42 | acc.add_field(ctx, None, field, &ty); |
43 | } | 43 | } |
44 | 44 | ||
45 | Some(()) | 45 | Some(()) |
diff --git a/crates/ide_completion/src/completions/unqualified_path.rs b/crates/ide_completion/src/completions/unqualified_path.rs index 9db8516d0..20188a7dd 100644 --- a/crates/ide_completion/src/completions/unqualified_path.rs +++ b/crates/ide_completion/src/completions/unqualified_path.rs | |||
@@ -11,6 +11,7 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC | |||
11 | if ctx.is_path_disallowed() || ctx.expects_item() { | 11 | if ctx.is_path_disallowed() || ctx.expects_item() { |
12 | return; | 12 | return; |
13 | } | 13 | } |
14 | |||
14 | if ctx.expects_assoc_item() { | 15 | if ctx.expects_assoc_item() { |
15 | ctx.scope.process_all_names(&mut |name, def| { | 16 | ctx.scope.process_all_names(&mut |name, def| { |
16 | if let ScopeDef::MacroDef(macro_def) = def { | 17 | if let ScopeDef::MacroDef(macro_def) = def { |
@@ -32,6 +33,7 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC | |||
32 | }); | 33 | }); |
33 | return; | 34 | return; |
34 | } | 35 | } |
36 | |||
35 | if let Some(hir::Adt::Enum(e)) = | 37 | if let Some(hir::Adt::Enum(e)) = |
36 | ctx.expected_type.as_ref().and_then(|ty| ty.strip_references().as_adt()) | 38 | ctx.expected_type.as_ref().and_then(|ty| ty.strip_references().as_adt()) |
37 | { | 39 | { |