aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2021-05-31 14:21:31 +0100
committerGitHub <[email protected]>2021-05-31 14:21:31 +0100
commite6ec860363a140b8aa3dcaafaf6e9d7327838610 (patch)
treecacaf711d01ce64074ecf675287ab03c85487ec1
parente9a797748daa7e25cde66927b8907b2d976201a5 (diff)
parentfb7105a5801ab1d0ede830cd53bbc3ccbf0b5e2c (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.rs17
-rw-r--r--crates/ide_completion/src/completions/dot.rs84
-rw-r--r--crates/ide_completion/src/completions/record.rs2
-rw-r--r--crates/ide_completion/src/completions/unqualified_path.rs2
-rw-r--r--crates/ide_completion/src/config.rs1
-rw-r--r--crates/ide_completion/src/render.rs30
-rw-r--r--crates/ide_completion/src/render/function.rs36
-rw-r--r--crates/ide_completion/src/test_utils.rs1
-rw-r--r--crates/rust-analyzer/src/config.rs4
-rw-r--r--crates/rust-analyzer/src/integrated_benchmarks.rs2
-rw-r--r--crates/rust-analyzer/src/to_proto.rs1
-rw-r--r--docs/dev/architecture.md5
-rw-r--r--docs/user/generated_config.adoc6
-rw-r--r--editors/code/package.json5
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
3use hir::{HasVisibility, Type}; 3use either::Either;
4use hir::{HasVisibility, ScopeDef};
4use rustc_hash::FxHashSet; 5use rustc_hash::FxHashSet;
5 6
6use crate::{context::CompletionContext, Completions}; 7use crate::{context::CompletionContext, Completions};
@@ -9,7 +10,7 @@ use crate::{context::CompletionContext, Completions};
9pub(crate) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) { 10pub(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
32fn 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
28fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: &Type) { 56fn 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
45fn complete_methods(acc: &mut Completions, ctx: &CompletionContext, receiver: &Type) { 77fn 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#"
528struct Foo { field: i32 }
529
530impl 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#"
541struct Foo(i32);
542
543impl 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};
10pub struct CompletionConfig { 10pub 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
26pub(crate) fn render_field<'a>( 26pub(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
34pub(crate) fn render_tuple_field<'a>( 35pub(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
42pub(crate) fn render_resolution<'a>( 44pub(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
26pub(crate) fn render_method<'a>( 26pub(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)]
37struct FunctionRender<'a> { 38struct 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> {
45impl<'a> FunctionRender<'a> { 47impl<'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#"
268struct S {}
269impl S {
270 fn foo(&self, x: i32) {
271 $0
272 }
273}
274"#,
275 r#"
276struct S {}
277impl 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};
19pub(crate) const TEST_CONFIG: CompletionConfig = CompletionConfig { 19pub(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
448Similarly, we save live object counting (`RA_COUNT=1`). 448Similarly, we save live object counting (`RA_COUNT=1`).
449It is not cheap enough to enable in prod, and this is a bug which should be fixed. 449It is not cheap enough to enable in prod, and this is a bug which should be fixed.
450
451### Configurability
452
453rust-analyzer strives to be as configurable as possible while offering reasonable defaults where no configuration exists yet.
454There 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.
136Toggles the additional completions that automatically add imports when completed. 136Toggles the additional completions that automatically add imports when completed.
137Note that your client must specify the `additionalTextEdits` LSP client capability to truly have this feature enabled. 137Note 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--
142Toggles the additional completions that automatically show method calls and field accesses
143with `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,