aboutsummaryrefslogtreecommitdiff
path: root/crates/ide_completion
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 /crates/ide_completion
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]>
Diffstat (limited to 'crates/ide_completion')
-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
8 files changed, 147 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),