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 | |
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]>
-rw-r--r-- | crates/ide_completion/src/completions.rs | 17 | ||||
-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 | ||||
-rw-r--r-- | crates/ide_completion/src/config.rs | 1 | ||||
-rw-r--r-- | crates/ide_completion/src/render.rs | 30 | ||||
-rw-r--r-- | crates/ide_completion/src/render/function.rs | 36 | ||||
-rw-r--r-- | crates/ide_completion/src/test_utils.rs | 1 | ||||
-rw-r--r-- | crates/rust-analyzer/src/config.rs | 4 | ||||
-rw-r--r-- | crates/rust-analyzer/src/integrated_benchmarks.rs | 2 | ||||
-rw-r--r-- | crates/rust-analyzer/src/to_proto.rs | 1 | ||||
-rw-r--r-- | docs/dev/architecture.md | 5 | ||||
-rw-r--r-- | docs/user/generated_config.adoc | 6 | ||||
-rw-r--r-- | editors/code/package.json | 5 |
14 files changed, 170 insertions, 26 deletions
diff --git a/crates/ide_completion/src/completions.rs b/crates/ide_completion/src/completions.rs index 151bf3783..ffdcdc930 100644 --- a/crates/ide_completion/src/completions.rs +++ b/crates/ide_completion/src/completions.rs | |||
@@ -69,18 +69,25 @@ impl Completions { | |||
69 | items.into_iter().for_each(|item| self.add(item.into())) | 69 | items.into_iter().for_each(|item| self.add(item.into())) |
70 | } | 70 | } |
71 | 71 | ||
72 | pub(crate) fn add_field(&mut self, ctx: &CompletionContext, field: hir::Field, ty: &hir::Type) { | 72 | pub(crate) fn add_field( |
73 | let item = render_field(RenderContext::new(ctx), field, ty); | 73 | &mut self, |
74 | ctx: &CompletionContext, | ||
75 | receiver: Option<hir::Name>, | ||
76 | field: hir::Field, | ||
77 | ty: &hir::Type, | ||
78 | ) { | ||
79 | let item = render_field(RenderContext::new(ctx), receiver, field, ty); | ||
74 | self.add(item); | 80 | self.add(item); |
75 | } | 81 | } |
76 | 82 | ||
77 | pub(crate) fn add_tuple_field( | 83 | pub(crate) fn add_tuple_field( |
78 | &mut self, | 84 | &mut self, |
79 | ctx: &CompletionContext, | 85 | ctx: &CompletionContext, |
86 | receiver: Option<hir::Name>, | ||
80 | field: usize, | 87 | field: usize, |
81 | ty: &hir::Type, | 88 | ty: &hir::Type, |
82 | ) { | 89 | ) { |
83 | let item = render_tuple_field(RenderContext::new(ctx), field, ty); | 90 | let item = render_tuple_field(RenderContext::new(ctx), receiver, field, ty); |
84 | self.add(item); | 91 | self.add(item); |
85 | } | 92 | } |
86 | 93 | ||
@@ -132,9 +139,11 @@ impl Completions { | |||
132 | &mut self, | 139 | &mut self, |
133 | ctx: &CompletionContext, | 140 | ctx: &CompletionContext, |
134 | func: hir::Function, | 141 | func: hir::Function, |
142 | receiver: Option<hir::Name>, | ||
135 | local_name: Option<hir::Name>, | 143 | local_name: Option<hir::Name>, |
136 | ) { | 144 | ) { |
137 | if let Some(item) = render_method(RenderContext::new(ctx), None, local_name, func) { | 145 | if let Some(item) = render_method(RenderContext::new(ctx), None, receiver, local_name, func) |
146 | { | ||
138 | self.add(item) | 147 | self.add(item) |
139 | } | 148 | } |
140 | } | 149 | } |
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 | { |
diff --git a/crates/ide_completion/src/config.rs b/crates/ide_completion/src/config.rs index d70ed6c1c..c300ce887 100644 --- a/crates/ide_completion/src/config.rs +++ b/crates/ide_completion/src/config.rs | |||
@@ -10,6 +10,7 @@ use ide_db::helpers::{insert_use::InsertUseConfig, SnippetCap}; | |||
10 | pub struct CompletionConfig { | 10 | pub struct CompletionConfig { |
11 | pub enable_postfix_completions: bool, | 11 | pub enable_postfix_completions: bool, |
12 | pub enable_imports_on_the_fly: bool, | 12 | pub enable_imports_on_the_fly: bool, |
13 | pub enable_self_on_the_fly: bool, | ||
13 | pub add_call_parenthesis: bool, | 14 | pub add_call_parenthesis: bool, |
14 | pub add_call_argument_snippets: bool, | 15 | pub add_call_argument_snippets: bool, |
15 | pub snippet_cap: Option<SnippetCap>, | 16 | pub snippet_cap: Option<SnippetCap>, |
diff --git a/crates/ide_completion/src/render.rs b/crates/ide_completion/src/render.rs index 425dd0247..a49a60711 100644 --- a/crates/ide_completion/src/render.rs +++ b/crates/ide_completion/src/render.rs | |||
@@ -25,18 +25,20 @@ use crate::{ | |||
25 | 25 | ||
26 | pub(crate) fn render_field<'a>( | 26 | pub(crate) fn render_field<'a>( |
27 | ctx: RenderContext<'a>, | 27 | ctx: RenderContext<'a>, |
28 | receiver: Option<hir::Name>, | ||
28 | field: hir::Field, | 29 | field: hir::Field, |
29 | ty: &hir::Type, | 30 | ty: &hir::Type, |
30 | ) -> CompletionItem { | 31 | ) -> CompletionItem { |
31 | Render::new(ctx).render_field(field, ty) | 32 | Render::new(ctx).render_field(receiver, field, ty) |
32 | } | 33 | } |
33 | 34 | ||
34 | pub(crate) fn render_tuple_field<'a>( | 35 | pub(crate) fn render_tuple_field<'a>( |
35 | ctx: RenderContext<'a>, | 36 | ctx: RenderContext<'a>, |
37 | receiver: Option<hir::Name>, | ||
36 | field: usize, | 38 | field: usize, |
37 | ty: &hir::Type, | 39 | ty: &hir::Type, |
38 | ) -> CompletionItem { | 40 | ) -> CompletionItem { |
39 | Render::new(ctx).render_tuple_field(field, ty) | 41 | Render::new(ctx).render_tuple_field(receiver, field, ty) |
40 | } | 42 | } |
41 | 43 | ||
42 | pub(crate) fn render_resolution<'a>( | 44 | pub(crate) fn render_resolution<'a>( |
@@ -126,11 +128,19 @@ impl<'a> Render<'a> { | |||
126 | Render { ctx } | 128 | Render { ctx } |
127 | } | 129 | } |
128 | 130 | ||
129 | fn render_field(&self, field: hir::Field, ty: &hir::Type) -> CompletionItem { | 131 | fn render_field( |
132 | &self, | ||
133 | receiver: Option<hir::Name>, | ||
134 | field: hir::Field, | ||
135 | ty: &hir::Type, | ||
136 | ) -> CompletionItem { | ||
130 | let is_deprecated = self.ctx.is_deprecated(field); | 137 | let is_deprecated = self.ctx.is_deprecated(field); |
131 | let name = field.name(self.ctx.db()).to_string(); | 138 | let name = field.name(self.ctx.db()).to_string(); |
132 | let mut item = | 139 | let mut item = CompletionItem::new( |
133 | CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), name.clone()); | 140 | CompletionKind::Reference, |
141 | self.ctx.source_range(), | ||
142 | receiver.map_or_else(|| name.clone(), |receiver| format!("{}.{}", receiver, name)), | ||
143 | ); | ||
134 | item.kind(SymbolKind::Field) | 144 | item.kind(SymbolKind::Field) |
135 | .detail(ty.display(self.ctx.db()).to_string()) | 145 | .detail(ty.display(self.ctx.db()).to_string()) |
136 | .set_documentation(field.docs(self.ctx.db())) | 146 | .set_documentation(field.docs(self.ctx.db())) |
@@ -151,11 +161,17 @@ impl<'a> Render<'a> { | |||
151 | item.build() | 161 | item.build() |
152 | } | 162 | } |
153 | 163 | ||
154 | fn render_tuple_field(&self, field: usize, ty: &hir::Type) -> CompletionItem { | 164 | fn render_tuple_field( |
165 | &self, | ||
166 | receiver: Option<hir::Name>, | ||
167 | field: usize, | ||
168 | ty: &hir::Type, | ||
169 | ) -> CompletionItem { | ||
155 | let mut item = CompletionItem::new( | 170 | let mut item = CompletionItem::new( |
156 | CompletionKind::Reference, | 171 | CompletionKind::Reference, |
157 | self.ctx.source_range(), | 172 | self.ctx.source_range(), |
158 | field.to_string(), | 173 | receiver |
174 | .map_or_else(|| field.to_string(), |receiver| format!("{}.{}", receiver, field)), | ||
159 | ); | 175 | ); |
160 | 176 | ||
161 | item.kind(SymbolKind::Field).detail(ty.display(self.ctx.db()).to_string()); | 177 | item.kind(SymbolKind::Field).detail(ty.display(self.ctx.db()).to_string()); |
diff --git a/crates/ide_completion/src/render/function.rs b/crates/ide_completion/src/render/function.rs index 63bd66926..3ec77ca0f 100644 --- a/crates/ide_completion/src/render/function.rs +++ b/crates/ide_completion/src/render/function.rs | |||
@@ -20,23 +20,25 @@ pub(crate) fn render_fn<'a>( | |||
20 | fn_: hir::Function, | 20 | fn_: hir::Function, |
21 | ) -> Option<CompletionItem> { | 21 | ) -> Option<CompletionItem> { |
22 | let _p = profile::span("render_fn"); | 22 | let _p = profile::span("render_fn"); |
23 | Some(FunctionRender::new(ctx, local_name, fn_, false)?.render(import_to_add)) | 23 | Some(FunctionRender::new(ctx, None, local_name, fn_, false)?.render(import_to_add)) |
24 | } | 24 | } |
25 | 25 | ||
26 | pub(crate) fn render_method<'a>( | 26 | pub(crate) fn render_method<'a>( |
27 | ctx: RenderContext<'a>, | 27 | ctx: RenderContext<'a>, |
28 | import_to_add: Option<ImportEdit>, | 28 | import_to_add: Option<ImportEdit>, |
29 | receiver: Option<hir::Name>, | ||
29 | local_name: Option<hir::Name>, | 30 | local_name: Option<hir::Name>, |
30 | fn_: hir::Function, | 31 | fn_: hir::Function, |
31 | ) -> Option<CompletionItem> { | 32 | ) -> Option<CompletionItem> { |
32 | let _p = profile::span("render_method"); | 33 | let _p = profile::span("render_method"); |
33 | Some(FunctionRender::new(ctx, local_name, fn_, true)?.render(import_to_add)) | 34 | Some(FunctionRender::new(ctx, receiver, local_name, fn_, true)?.render(import_to_add)) |
34 | } | 35 | } |
35 | 36 | ||
36 | #[derive(Debug)] | 37 | #[derive(Debug)] |
37 | struct FunctionRender<'a> { | 38 | struct FunctionRender<'a> { |
38 | ctx: RenderContext<'a>, | 39 | ctx: RenderContext<'a>, |
39 | name: String, | 40 | name: String, |
41 | receiver: Option<hir::Name>, | ||
40 | func: hir::Function, | 42 | func: hir::Function, |
41 | ast_node: Fn, | 43 | ast_node: Fn, |
42 | is_method: bool, | 44 | is_method: bool, |
@@ -45,6 +47,7 @@ struct FunctionRender<'a> { | |||
45 | impl<'a> FunctionRender<'a> { | 47 | impl<'a> FunctionRender<'a> { |
46 | fn new( | 48 | fn new( |
47 | ctx: RenderContext<'a>, | 49 | ctx: RenderContext<'a>, |
50 | receiver: Option<hir::Name>, | ||
48 | local_name: Option<hir::Name>, | 51 | local_name: Option<hir::Name>, |
49 | fn_: hir::Function, | 52 | fn_: hir::Function, |
50 | is_method: bool, | 53 | is_method: bool, |
@@ -52,11 +55,14 @@ impl<'a> FunctionRender<'a> { | |||
52 | let name = local_name.unwrap_or_else(|| fn_.name(ctx.db())).to_string(); | 55 | let name = local_name.unwrap_or_else(|| fn_.name(ctx.db())).to_string(); |
53 | let ast_node = fn_.source(ctx.db())?.value; | 56 | let ast_node = fn_.source(ctx.db())?.value; |
54 | 57 | ||
55 | Some(FunctionRender { ctx, name, func: fn_, ast_node, is_method }) | 58 | Some(FunctionRender { ctx, name, receiver, func: fn_, ast_node, is_method }) |
56 | } | 59 | } |
57 | 60 | ||
58 | fn render(self, import_to_add: Option<ImportEdit>) -> CompletionItem { | 61 | fn render(mut self, import_to_add: Option<ImportEdit>) -> CompletionItem { |
59 | let params = self.params(); | 62 | let params = self.params(); |
63 | if let Some(receiver) = &self.receiver { | ||
64 | self.name = format!("{}.{}", receiver, &self.name) | ||
65 | } | ||
60 | let mut item = CompletionItem::new( | 66 | let mut item = CompletionItem::new( |
61 | CompletionKind::Reference, | 67 | CompletionKind::Reference, |
62 | self.ctx.source_range(), | 68 | self.ctx.source_range(), |
@@ -148,7 +154,7 @@ impl<'a> FunctionRender<'a> { | |||
148 | }; | 154 | }; |
149 | 155 | ||
150 | let mut params_pats = Vec::new(); | 156 | let mut params_pats = Vec::new(); |
151 | let params_ty = if self.ctx.completion.dot_receiver.is_some() { | 157 | let params_ty = if self.ctx.completion.dot_receiver.is_some() || self.receiver.is_some() { |
152 | self.func.method_params(self.ctx.db()).unwrap_or_default() | 158 | self.func.method_params(self.ctx.db()).unwrap_or_default() |
153 | } else { | 159 | } else { |
154 | if let Some(s) = ast_params.self_param() { | 160 | if let Some(s) = ast_params.self_param() { |
@@ -255,6 +261,26 @@ fn bar(s: &S) { | |||
255 | } | 261 | } |
256 | "#, | 262 | "#, |
257 | ); | 263 | ); |
264 | |||
265 | check_edit( | ||
266 | "self.foo", | ||
267 | r#" | ||
268 | struct S {} | ||
269 | impl S { | ||
270 | fn foo(&self, x: i32) { | ||
271 | $0 | ||
272 | } | ||
273 | } | ||
274 | "#, | ||
275 | r#" | ||
276 | struct S {} | ||
277 | impl S { | ||
278 | fn foo(&self, x: i32) { | ||
279 | self.foo(${1:x})$0 | ||
280 | } | ||
281 | } | ||
282 | "#, | ||
283 | ); | ||
258 | } | 284 | } |
259 | 285 | ||
260 | #[test] | 286 | #[test] |
diff --git a/crates/ide_completion/src/test_utils.rs b/crates/ide_completion/src/test_utils.rs index 93c7c872c..b0a4b2026 100644 --- a/crates/ide_completion/src/test_utils.rs +++ b/crates/ide_completion/src/test_utils.rs | |||
@@ -19,6 +19,7 @@ use crate::{item::CompletionKind, CompletionConfig, CompletionItem}; | |||
19 | pub(crate) const TEST_CONFIG: CompletionConfig = CompletionConfig { | 19 | pub(crate) const TEST_CONFIG: CompletionConfig = CompletionConfig { |
20 | enable_postfix_completions: true, | 20 | enable_postfix_completions: true, |
21 | enable_imports_on_the_fly: true, | 21 | enable_imports_on_the_fly: true, |
22 | enable_self_on_the_fly: true, | ||
22 | add_call_parenthesis: true, | 23 | add_call_parenthesis: true, |
23 | add_call_argument_snippets: true, | 24 | add_call_argument_snippets: true, |
24 | snippet_cap: SnippetCap::new(true), | 25 | snippet_cap: SnippetCap::new(true), |
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index a67b0bb25..ae78fd4f6 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs | |||
@@ -100,6 +100,9 @@ config_data! { | |||
100 | /// Toggles the additional completions that automatically add imports when completed. | 100 | /// Toggles the additional completions that automatically add imports when completed. |
101 | /// Note that your client must specify the `additionalTextEdits` LSP client capability to truly have this feature enabled. | 101 | /// Note that your client must specify the `additionalTextEdits` LSP client capability to truly have this feature enabled. |
102 | completion_autoimport_enable: bool = "true", | 102 | completion_autoimport_enable: bool = "true", |
103 | /// Toggles the additional completions that automatically show method calls and field accesses | ||
104 | /// with `self` prefixed to them when inside a method. | ||
105 | completion_autoself_enable: bool = "true", | ||
103 | 106 | ||
104 | /// Whether to show native rust-analyzer diagnostics. | 107 | /// Whether to show native rust-analyzer diagnostics. |
105 | diagnostics_enable: bool = "true", | 108 | diagnostics_enable: bool = "true", |
@@ -666,6 +669,7 @@ impl Config { | |||
666 | enable_postfix_completions: self.data.completion_postfix_enable, | 669 | enable_postfix_completions: self.data.completion_postfix_enable, |
667 | enable_imports_on_the_fly: self.data.completion_autoimport_enable | 670 | enable_imports_on_the_fly: self.data.completion_autoimport_enable |
668 | && completion_item_edit_resolve(&self.caps), | 671 | && completion_item_edit_resolve(&self.caps), |
672 | enable_self_on_the_fly: self.data.completion_autoself_enable, | ||
669 | add_call_parenthesis: self.data.completion_addCallParenthesis, | 673 | add_call_parenthesis: self.data.completion_addCallParenthesis, |
670 | add_call_argument_snippets: self.data.completion_addCallArgumentSnippets, | 674 | add_call_argument_snippets: self.data.completion_addCallArgumentSnippets, |
671 | insert_use: self.insert_use_config(), | 675 | insert_use: self.insert_use_config(), |
diff --git a/crates/rust-analyzer/src/integrated_benchmarks.rs b/crates/rust-analyzer/src/integrated_benchmarks.rs index 781073fe5..ec36a5f5c 100644 --- a/crates/rust-analyzer/src/integrated_benchmarks.rs +++ b/crates/rust-analyzer/src/integrated_benchmarks.rs | |||
@@ -132,6 +132,7 @@ fn integrated_completion_benchmark() { | |||
132 | let config = CompletionConfig { | 132 | let config = CompletionConfig { |
133 | enable_postfix_completions: true, | 133 | enable_postfix_completions: true, |
134 | enable_imports_on_the_fly: true, | 134 | enable_imports_on_the_fly: true, |
135 | enable_self_on_the_fly: true, | ||
135 | add_call_parenthesis: true, | 136 | add_call_parenthesis: true, |
136 | add_call_argument_snippets: true, | 137 | add_call_argument_snippets: true, |
137 | snippet_cap: SnippetCap::new(true), | 138 | snippet_cap: SnippetCap::new(true), |
@@ -166,6 +167,7 @@ fn integrated_completion_benchmark() { | |||
166 | let config = CompletionConfig { | 167 | let config = CompletionConfig { |
167 | enable_postfix_completions: true, | 168 | enable_postfix_completions: true, |
168 | enable_imports_on_the_fly: true, | 169 | enable_imports_on_the_fly: true, |
170 | enable_self_on_the_fly: true, | ||
169 | add_call_parenthesis: true, | 171 | add_call_parenthesis: true, |
170 | add_call_argument_snippets: true, | 172 | add_call_argument_snippets: true, |
171 | snippet_cap: SnippetCap::new(true), | 173 | snippet_cap: SnippetCap::new(true), |
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs index 85898f495..7428a3043 100644 --- a/crates/rust-analyzer/src/to_proto.rs +++ b/crates/rust-analyzer/src/to_proto.rs | |||
@@ -1178,6 +1178,7 @@ mod tests { | |||
1178 | &ide::CompletionConfig { | 1178 | &ide::CompletionConfig { |
1179 | enable_postfix_completions: true, | 1179 | enable_postfix_completions: true, |
1180 | enable_imports_on_the_fly: true, | 1180 | enable_imports_on_the_fly: true, |
1181 | enable_self_on_the_fly: true, | ||
1181 | add_call_parenthesis: true, | 1182 | add_call_parenthesis: true, |
1182 | add_call_argument_snippets: true, | 1183 | add_call_argument_snippets: true, |
1183 | snippet_cap: SnippetCap::new(true), | 1184 | snippet_cap: SnippetCap::new(true), |
diff --git a/docs/dev/architecture.md b/docs/dev/architecture.md index 39edf9e19..2624069a5 100644 --- a/docs/dev/architecture.md +++ b/docs/dev/architecture.md | |||
@@ -447,3 +447,8 @@ This is cheap enough to enable in production. | |||
447 | 447 | ||
448 | Similarly, we save live object counting (`RA_COUNT=1`). | 448 | Similarly, we save live object counting (`RA_COUNT=1`). |
449 | It is not cheap enough to enable in prod, and this is a bug which should be fixed. | 449 | It is not cheap enough to enable in prod, and this is a bug which should be fixed. |
450 | |||
451 | ### Configurability | ||
452 | |||
453 | rust-analyzer strives to be as configurable as possible while offering reasonable defaults where no configuration exists yet. | ||
454 | There will always be features that some people find more annoying than helpful, so giving the users the ability to tweak or disable these is a big part of offering a good user experience. | ||
diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc index 4a5782a57..dbd9a3503 100644 --- a/docs/user/generated_config.adoc +++ b/docs/user/generated_config.adoc | |||
@@ -136,6 +136,12 @@ Whether to show postfix snippets like `dbg`, `if`, `not`, etc. | |||
136 | Toggles the additional completions that automatically add imports when completed. | 136 | Toggles the additional completions that automatically add imports when completed. |
137 | Note that your client must specify the `additionalTextEdits` LSP client capability to truly have this feature enabled. | 137 | Note that your client must specify the `additionalTextEdits` LSP client capability to truly have this feature enabled. |
138 | -- | 138 | -- |
139 | [[rust-analyzer.completion.autoself.enable]]rust-analyzer.completion.autoself.enable (default: `true`):: | ||
140 | + | ||
141 | -- | ||
142 | Toggles the additional completions that automatically show method calls and field accesses | ||
143 | with `self` prefixed to them when inside a method. | ||
144 | -- | ||
139 | [[rust-analyzer.diagnostics.enable]]rust-analyzer.diagnostics.enable (default: `true`):: | 145 | [[rust-analyzer.diagnostics.enable]]rust-analyzer.diagnostics.enable (default: `true`):: |
140 | + | 146 | + |
141 | -- | 147 | -- |
diff --git a/editors/code/package.json b/editors/code/package.json index 5b80cc1f9..42a06e137 100644 --- a/editors/code/package.json +++ b/editors/code/package.json | |||
@@ -572,6 +572,11 @@ | |||
572 | "default": true, | 572 | "default": true, |
573 | "type": "boolean" | 573 | "type": "boolean" |
574 | }, | 574 | }, |
575 | "rust-analyzer.completion.autoself.enable": { | ||
576 | "markdownDescription": "Toggles the additional completions that automatically show method calls and field accesses\nwith `self` prefixed to them when inside a method.", | ||
577 | "default": true, | ||
578 | "type": "boolean" | ||
579 | }, | ||
575 | "rust-analyzer.diagnostics.enable": { | 580 | "rust-analyzer.diagnostics.enable": { |
576 | "markdownDescription": "Whether to show native rust-analyzer diagnostics.", | 581 | "markdownDescription": "Whether to show native rust-analyzer diagnostics.", |
577 | "default": true, | 582 | "default": true, |