aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide/src/completion
diff options
context:
space:
mode:
authorBenjamin Coenen <[email protected]>2020-04-11 21:54:18 +0100
committerBenjamin Coenen <[email protected]>2020-04-11 21:54:18 +0100
commitd42346fed61f706d68fe888631a41ea5f2752d7f (patch)
treeb9e7eac005c4c6200d6a95f191e00cb738ad31b0 /crates/ra_ide/src/completion
parentc1317d692321ba5ba8f138067ebefbb9559d098d (diff)
Improve autocompletion by looking on the type and name
Signed-off-by: Benjamin Coenen <[email protected]>
Diffstat (limited to 'crates/ra_ide/src/completion')
-rw-r--r--crates/ra_ide/src/completion/complete_dot.rs125
-rw-r--r--crates/ra_ide/src/completion/completion_context.rs14
-rw-r--r--crates/ra_ide/src/completion/presentation.rs2
-rw-r--r--crates/ra_ide/src/completion/test_utils.rs11
4 files changed, 143 insertions, 9 deletions
diff --git a/crates/ra_ide/src/completion/complete_dot.rs b/crates/ra_ide/src/completion/complete_dot.rs
index f433faef3..358b041aa 100644
--- a/crates/ra_ide/src/completion/complete_dot.rs
+++ b/crates/ra_ide/src/completion/complete_dot.rs
@@ -1,6 +1,6 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use hir::{HasVisibility, Type}; 3use hir::{HasVisibility, HirDisplay, Type};
4 4
5use crate::completion::completion_item::CompletionKind; 5use crate::completion::completion_item::CompletionKind;
6use crate::{ 6use crate::{
@@ -8,6 +8,7 @@ use crate::{
8 CompletionItem, 8 CompletionItem,
9}; 9};
10use rustc_hash::FxHashSet; 10use rustc_hash::FxHashSet;
11use std::cmp::Ordering;
11 12
12/// Complete dot accesses, i.e. fields or methods (and .await syntax). 13/// Complete dot accesses, i.e. fields or methods (and .await syntax).
13pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) { 14pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) {
@@ -37,7 +38,31 @@ pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) {
37 38
38fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: &Type) { 39fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: &Type) {
39 for receiver in receiver.autoderef(ctx.db) { 40 for receiver in receiver.autoderef(ctx.db) {
40 for (field, ty) in receiver.fields(ctx.db) { 41 let mut fields = receiver.fields(ctx.db);
42 if let Some(call_info) = &ctx.call_info {
43 if let Some(active_parameter_type) = call_info.active_parameter_type() {
44 let active_parameter_name = call_info.active_parameter_name().unwrap();
45 fields.sort_by(|a, b| {
46 // For the same type
47 if active_parameter_type == a.1.display(ctx.db).to_string() {
48 // If same type + same name then go top position
49 if active_parameter_name == a.0.name(ctx.db).to_string() {
50 Ordering::Less
51 } else {
52 if active_parameter_type == b.1.display(ctx.db).to_string() {
53 Ordering::Equal
54 } else {
55 Ordering::Less
56 }
57 }
58 } else {
59 Ordering::Greater
60 }
61 });
62 }
63 }
64
65 for (field, ty) in fields {
41 if ctx.scope().module().map_or(false, |m| !field.is_visible_from(ctx.db, m)) { 66 if ctx.scope().module().map_or(false, |m| !field.is_visible_from(ctx.db, m)) {
42 // Skip private field. FIXME: If the definition location of the 67 // Skip private field. FIXME: If the definition location of the
43 // field is editable, we should show the completion 68 // field is editable, we should show the completion
@@ -47,6 +72,7 @@ fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: &Ty
47 } 72 }
48 for (i, ty) in receiver.tuple_fields(ctx.db).into_iter().enumerate() { 73 for (i, ty) in receiver.tuple_fields(ctx.db).into_iter().enumerate() {
49 // FIXME: Handle visibility 74 // FIXME: Handle visibility
75 // TODO: add the same behavior with type ?
50 acc.add_tuple_field(ctx, i, &ty); 76 acc.add_tuple_field(ctx, i, &ty);
51 } 77 }
52 } 78 }
@@ -70,13 +96,20 @@ fn complete_methods(acc: &mut Completions, ctx: &CompletionContext, receiver: &T
70 96
71#[cfg(test)] 97#[cfg(test)]
72mod tests { 98mod tests {
73 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind}; 99 use crate::completion::{
100 test_utils::{do_completion, do_completion_without_sort},
101 CompletionItem, CompletionKind,
102 };
74 use insta::assert_debug_snapshot; 103 use insta::assert_debug_snapshot;
75 104
76 fn do_ref_completion(code: &str) -> Vec<CompletionItem> { 105 fn do_ref_completion(code: &str) -> Vec<CompletionItem> {
77 do_completion(code, CompletionKind::Reference) 106 do_completion(code, CompletionKind::Reference)
78 } 107 }
79 108
109 fn do_ref_completion_without_sort(code: &str) -> Vec<CompletionItem> {
110 do_completion_without_sort(code, CompletionKind::Reference)
111 }
112
80 #[test] 113 #[test]
81 fn test_struct_field_completion() { 114 fn test_struct_field_completion() {
82 assert_debug_snapshot!( 115 assert_debug_snapshot!(
@@ -104,6 +137,92 @@ mod tests {
104 } 137 }
105 138
106 #[test] 139 #[test]
140 fn test_struct_field_completion_in_func_call() {
141 assert_debug_snapshot!(
142 do_ref_completion_without_sort(
143 r"
144 struct A { another_field: i64, the_field: u32, my_string: String }
145 fn test(my_param: u32) -> u32 { my_param }
146 fn foo(a: A) {
147 test(a.<|>)
148 }
149 ",
150 ),
151 @r###"
152 [
153 CompletionItem {
154 label: "the_field",
155 source_range: [201; 201),
156 delete: [201; 201),
157 insert: "the_field",
158 kind: Field,
159 detail: "u32",
160 },
161 CompletionItem {
162 label: "another_field",
163 source_range: [201; 201),
164 delete: [201; 201),
165 insert: "another_field",
166 kind: Field,
167 detail: "i64",
168 },
169 CompletionItem {
170 label: "my_string",
171 source_range: [201; 201),
172 delete: [201; 201),
173 insert: "my_string",
174 kind: Field,
175 detail: "{unknown}",
176 },
177 ]
178 "###
179 );
180 }
181
182 #[test]
183 fn test_struct_field_completion_in_func_call_with_type_and_name() {
184 assert_debug_snapshot!(
185 do_ref_completion_without_sort(
186 r"
187 struct A { another_field: i64, another_good_type: u32, the_field: u32 }
188 fn test(the_field: u32) -> u32 { the_field }
189 fn foo(a: A) {
190 test(a.<|>)
191 }
192 ",
193 ),
194 @r###"
195 [
196 CompletionItem {
197 label: "the_field",
198 source_range: [208; 208),
199 delete: [208; 208),
200 insert: "the_field",
201 kind: Field,
202 detail: "u32",
203 },
204 CompletionItem {
205 label: "another_good_type",
206 source_range: [208; 208),
207 delete: [208; 208),
208 insert: "another_good_type",
209 kind: Field,
210 detail: "u32",
211 },
212 CompletionItem {
213 label: "another_field",
214 source_range: [208; 208),
215 delete: [208; 208),
216 insert: "another_field",
217 kind: Field,
218 detail: "i64",
219 },
220 ]
221 "###
222 );
223 }
224
225 #[test]
107 fn test_struct_field_completion_self() { 226 fn test_struct_field_completion_self() {
108 assert_debug_snapshot!( 227 assert_debug_snapshot!(
109 do_ref_completion( 228 do_ref_completion(
diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs
index f833d2a9a..fddaf21e4 100644
--- a/crates/ra_ide/src/completion/completion_context.rs
+++ b/crates/ra_ide/src/completion/completion_context.rs
@@ -1,17 +1,19 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use hir::{Semantics, SemanticsScope}; 3use hir::{db::HirDatabase, Semantics, SemanticsScope};
4use ra_db::SourceDatabase; 4use ra_db::SourceDatabase;
5use ra_ide_db::RootDatabase; 5use ra_ide_db::RootDatabase;
6use ra_syntax::{ 6use ra_syntax::{
7 algo::{find_covering_element, find_node_at_offset}, 7 algo::{find_covering_element, find_node_at_offset},
8 ast, AstNode, 8 ast,
9 ast::ArgListOwner,
10 AstNode,
9 SyntaxKind::*, 11 SyntaxKind::*,
10 SyntaxNode, SyntaxToken, TextRange, TextUnit, 12 SyntaxNode, SyntaxToken, TextRange, TextUnit,
11}; 13};
12use ra_text_edit::AtomTextEdit; 14use ra_text_edit::AtomTextEdit;
13 15
14use crate::{completion::CompletionConfig, FilePosition}; 16use crate::{call_info::call_info, completion::CompletionConfig, CallInfo, FilePosition};
15 17
16/// `CompletionContext` is created early during completion to figure out, where 18/// `CompletionContext` is created early during completion to figure out, where
17/// exactly is the cursor, syntax-wise. 19/// exactly is the cursor, syntax-wise.
@@ -21,6 +23,7 @@ pub(crate) struct CompletionContext<'a> {
21 pub(super) db: &'a RootDatabase, 23 pub(super) db: &'a RootDatabase,
22 pub(super) config: &'a CompletionConfig, 24 pub(super) config: &'a CompletionConfig,
23 pub(super) offset: TextUnit, 25 pub(super) offset: TextUnit,
26 pub(super) file_position: FilePosition,
24 /// The token before the cursor, in the original file. 27 /// The token before the cursor, in the original file.
25 pub(super) original_token: SyntaxToken, 28 pub(super) original_token: SyntaxToken,
26 /// The token before the cursor, in the macro-expanded file. 29 /// The token before the cursor, in the macro-expanded file.
@@ -32,6 +35,7 @@ pub(crate) struct CompletionContext<'a> {
32 pub(super) record_lit_syntax: Option<ast::RecordLit>, 35 pub(super) record_lit_syntax: Option<ast::RecordLit>,
33 pub(super) record_lit_pat: Option<ast::RecordPat>, 36 pub(super) record_lit_pat: Option<ast::RecordPat>,
34 pub(super) impl_def: Option<ast::ImplDef>, 37 pub(super) impl_def: Option<ast::ImplDef>,
38 pub(super) call_info: Option<CallInfo>,
35 pub(super) is_param: bool, 39 pub(super) is_param: bool,
36 /// If a name-binding or reference to a const in a pattern. 40 /// If a name-binding or reference to a const in a pattern.
37 /// Irrefutable patterns (like let) are excluded. 41 /// Irrefutable patterns (like let) are excluded.
@@ -88,9 +92,11 @@ impl<'a> CompletionContext<'a> {
88 original_token, 92 original_token,
89 token, 93 token,
90 offset: position.offset, 94 offset: position.offset,
95 file_position: position,
91 krate, 96 krate,
92 name_ref_syntax: None, 97 name_ref_syntax: None,
93 function_syntax: None, 98 function_syntax: None,
99 call_info: None,
94 use_item_syntax: None, 100 use_item_syntax: None,
95 record_lit_syntax: None, 101 record_lit_syntax: None,
96 record_lit_pat: None, 102 record_lit_pat: None,
@@ -253,6 +259,8 @@ impl<'a> CompletionContext<'a> {
253 self.use_item_syntax = 259 self.use_item_syntax =
254 self.sema.ancestors_with_macros(self.token.parent()).find_map(ast::UseItem::cast); 260 self.sema.ancestors_with_macros(self.token.parent()).find_map(ast::UseItem::cast);
255 261
262 self.call_info = call_info(self.db, self.file_position);
263
256 self.function_syntax = self 264 self.function_syntax = self
257 .sema 265 .sema
258 .ancestors_with_macros(self.token.parent()) 266 .ancestors_with_macros(self.token.parent())
diff --git a/crates/ra_ide/src/completion/presentation.rs b/crates/ra_ide/src/completion/presentation.rs
index 55f75b15a..8be2d02d0 100644
--- a/crates/ra_ide/src/completion/presentation.rs
+++ b/crates/ra_ide/src/completion/presentation.rs
@@ -367,7 +367,7 @@ mod tests {
367 ra_fixture: &str, 367 ra_fixture: &str,
368 options: CompletionConfig, 368 options: CompletionConfig,
369 ) -> Vec<CompletionItem> { 369 ) -> Vec<CompletionItem> {
370 do_completion_with_options(ra_fixture, CompletionKind::Reference, &options) 370 do_completion_with_options(ra_fixture, CompletionKind::Reference, &options, true)
371 } 371 }
372 372
373 #[test] 373 #[test]
diff --git a/crates/ra_ide/src/completion/test_utils.rs b/crates/ra_ide/src/completion/test_utils.rs
index eb90b5279..f54d15a90 100644
--- a/crates/ra_ide/src/completion/test_utils.rs
+++ b/crates/ra_ide/src/completion/test_utils.rs
@@ -7,13 +7,18 @@ use crate::{
7}; 7};
8 8
9pub(crate) fn do_completion(code: &str, kind: CompletionKind) -> Vec<CompletionItem> { 9pub(crate) fn do_completion(code: &str, kind: CompletionKind) -> Vec<CompletionItem> {
10 do_completion_with_options(code, kind, &CompletionConfig::default()) 10 do_completion_with_options(code, kind, &CompletionConfig::default(), true)
11}
12
13pub(crate) fn do_completion_without_sort(code: &str, kind: CompletionKind) -> Vec<CompletionItem> {
14 do_completion_with_options(code, kind, &CompletionConfig::default(), false)
11} 15}
12 16
13pub(crate) fn do_completion_with_options( 17pub(crate) fn do_completion_with_options(
14 code: &str, 18 code: &str,
15 kind: CompletionKind, 19 kind: CompletionKind,
16 options: &CompletionConfig, 20 options: &CompletionConfig,
21 sort_by_key: bool,
17) -> Vec<CompletionItem> { 22) -> Vec<CompletionItem> {
18 let (analysis, position) = if code.contains("//-") { 23 let (analysis, position) = if code.contains("//-") {
19 analysis_and_position(code) 24 analysis_and_position(code)
@@ -24,6 +29,8 @@ pub(crate) fn do_completion_with_options(
24 let completion_items: Vec<CompletionItem> = completions.into(); 29 let completion_items: Vec<CompletionItem> = completions.into();
25 let mut kind_completions: Vec<CompletionItem> = 30 let mut kind_completions: Vec<CompletionItem> =
26 completion_items.into_iter().filter(|c| c.completion_kind == kind).collect(); 31 completion_items.into_iter().filter(|c| c.completion_kind == kind).collect();
27 kind_completions.sort_by_key(|c| c.label().to_owned()); 32 if sort_by_key {
33 kind_completions.sort_by_key(|c| c.label().to_owned());
34 }
28 kind_completions 35 kind_completions
29} 36}