diff options
Diffstat (limited to 'crates/ra_ide/src/completion')
-rw-r--r-- | crates/ra_ide/src/completion/complete_dot.rs | 239 | ||||
-rw-r--r-- | crates/ra_ide/src/completion/completion_context.rs | 12 | ||||
-rw-r--r-- | crates/ra_ide/src/completion/completion_item.rs | 72 | ||||
-rw-r--r-- | crates/ra_ide/src/completion/presentation.rs | 1 |
4 files changed, 321 insertions, 3 deletions
diff --git a/crates/ra_ide/src/completion/complete_dot.rs b/crates/ra_ide/src/completion/complete_dot.rs index f433faef3..c16357a7e 100644 --- a/crates/ra_ide/src/completion/complete_dot.rs +++ b/crates/ra_ide/src/completion/complete_dot.rs | |||
@@ -2,12 +2,16 @@ | |||
2 | 2 | ||
3 | use hir::{HasVisibility, Type}; | 3 | use hir::{HasVisibility, Type}; |
4 | 4 | ||
5 | use crate::completion::completion_item::CompletionKind; | ||
6 | use crate::{ | 5 | use crate::{ |
7 | completion::{completion_context::CompletionContext, completion_item::Completions}, | 6 | completion::{ |
7 | completion_context::CompletionContext, | ||
8 | completion_item::{CompletionKind, Completions}, | ||
9 | }, | ||
10 | // CallInfo, | ||
8 | CompletionItem, | 11 | CompletionItem, |
9 | }; | 12 | }; |
10 | use rustc_hash::FxHashSet; | 13 | use rustc_hash::FxHashSet; |
14 | // use std::cmp::Ordering; | ||
11 | 15 | ||
12 | /// Complete dot accesses, i.e. fields or methods (and .await syntax). | 16 | /// Complete dot accesses, i.e. fields or methods (and .await syntax). |
13 | pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) { | 17 | pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) { |
@@ -104,6 +108,237 @@ mod tests { | |||
104 | } | 108 | } |
105 | 109 | ||
106 | #[test] | 110 | #[test] |
111 | fn test_struct_field_completion_in_func_call() { | ||
112 | assert_debug_snapshot!( | ||
113 | do_ref_completion( | ||
114 | r" | ||
115 | struct A { another_field: i64, the_field: u32, my_string: String } | ||
116 | fn test(my_param: u32) -> u32 { my_param } | ||
117 | fn foo(a: A) { | ||
118 | test(a.<|>) | ||
119 | } | ||
120 | ", | ||
121 | ), | ||
122 | @r###" | ||
123 | [ | ||
124 | CompletionItem { | ||
125 | label: "another_field", | ||
126 | source_range: [201; 201), | ||
127 | delete: [201; 201), | ||
128 | insert: "another_field", | ||
129 | kind: Field, | ||
130 | detail: "i64", | ||
131 | }, | ||
132 | CompletionItem { | ||
133 | label: "my_string", | ||
134 | source_range: [201; 201), | ||
135 | delete: [201; 201), | ||
136 | insert: "my_string", | ||
137 | kind: Field, | ||
138 | detail: "{unknown}", | ||
139 | }, | ||
140 | CompletionItem { | ||
141 | label: "the_field", | ||
142 | source_range: [201; 201), | ||
143 | delete: [201; 201), | ||
144 | insert: "the_field", | ||
145 | kind: Field, | ||
146 | detail: "u32", | ||
147 | score: TypeMatch, | ||
148 | }, | ||
149 | ] | ||
150 | "### | ||
151 | ); | ||
152 | } | ||
153 | |||
154 | #[test] | ||
155 | fn test_struct_field_completion_in_func_call_with_type_and_name() { | ||
156 | assert_debug_snapshot!( | ||
157 | do_ref_completion( | ||
158 | r" | ||
159 | struct A { another_field: i64, another_good_type: u32, the_field: u32 } | ||
160 | fn test(the_field: u32) -> u32 { the_field } | ||
161 | fn foo(a: A) { | ||
162 | test(a.<|>) | ||
163 | } | ||
164 | ", | ||
165 | ), | ||
166 | @r###" | ||
167 | [ | ||
168 | CompletionItem { | ||
169 | label: "another_field", | ||
170 | source_range: [208; 208), | ||
171 | delete: [208; 208), | ||
172 | insert: "another_field", | ||
173 | kind: Field, | ||
174 | detail: "i64", | ||
175 | }, | ||
176 | CompletionItem { | ||
177 | label: "another_good_type", | ||
178 | source_range: [208; 208), | ||
179 | delete: [208; 208), | ||
180 | insert: "another_good_type", | ||
181 | kind: Field, | ||
182 | detail: "u32", | ||
183 | score: TypeMatch, | ||
184 | }, | ||
185 | CompletionItem { | ||
186 | label: "the_field", | ||
187 | source_range: [208; 208), | ||
188 | delete: [208; 208), | ||
189 | insert: "the_field", | ||
190 | kind: Field, | ||
191 | detail: "u32", | ||
192 | score: TypeAndNameMatch, | ||
193 | }, | ||
194 | ] | ||
195 | "### | ||
196 | ); | ||
197 | } | ||
198 | |||
199 | #[test] | ||
200 | fn test_struct_field_completion_in_record_lit() { | ||
201 | assert_debug_snapshot!( | ||
202 | do_ref_completion( | ||
203 | r" | ||
204 | struct A { another_field: i64, another_good_type: u32, the_field: u32 } | ||
205 | struct B { my_string: String, my_vec: Vec<u32>, the_field: u32 } | ||
206 | fn foo(a: A) { | ||
207 | let b = B { | ||
208 | the_field: a.<|> | ||
209 | }; | ||
210 | } | ||
211 | ", | ||
212 | ), | ||
213 | @r###" | ||
214 | [ | ||
215 | CompletionItem { | ||
216 | label: "another_field", | ||
217 | source_range: [270; 270), | ||
218 | delete: [270; 270), | ||
219 | insert: "another_field", | ||
220 | kind: Field, | ||
221 | detail: "i64", | ||
222 | }, | ||
223 | CompletionItem { | ||
224 | label: "another_good_type", | ||
225 | source_range: [270; 270), | ||
226 | delete: [270; 270), | ||
227 | insert: "another_good_type", | ||
228 | kind: Field, | ||
229 | detail: "u32", | ||
230 | score: TypeMatch, | ||
231 | }, | ||
232 | CompletionItem { | ||
233 | label: "the_field", | ||
234 | source_range: [270; 270), | ||
235 | delete: [270; 270), | ||
236 | insert: "the_field", | ||
237 | kind: Field, | ||
238 | detail: "u32", | ||
239 | score: TypeAndNameMatch, | ||
240 | }, | ||
241 | ] | ||
242 | "### | ||
243 | ); | ||
244 | } | ||
245 | |||
246 | #[test] | ||
247 | fn test_struct_field_completion_in_record_lit_and_fn_call() { | ||
248 | assert_debug_snapshot!( | ||
249 | do_ref_completion( | ||
250 | r" | ||
251 | struct A { another_field: i64, another_good_type: u32, the_field: u32 } | ||
252 | struct B { my_string: String, my_vec: Vec<u32>, the_field: u32 } | ||
253 | fn test(the_field: i64) -> i64 { the_field } | ||
254 | fn foo(a: A) { | ||
255 | let b = B { | ||
256 | the_field: test(a.<|>) | ||
257 | }; | ||
258 | } | ||
259 | ", | ||
260 | ), | ||
261 | @r###" | ||
262 | [ | ||
263 | CompletionItem { | ||
264 | label: "another_field", | ||
265 | source_range: [336; 336), | ||
266 | delete: [336; 336), | ||
267 | insert: "another_field", | ||
268 | kind: Field, | ||
269 | detail: "i64", | ||
270 | score: TypeMatch, | ||
271 | }, | ||
272 | CompletionItem { | ||
273 | label: "another_good_type", | ||
274 | source_range: [336; 336), | ||
275 | delete: [336; 336), | ||
276 | insert: "another_good_type", | ||
277 | kind: Field, | ||
278 | detail: "u32", | ||
279 | }, | ||
280 | CompletionItem { | ||
281 | label: "the_field", | ||
282 | source_range: [336; 336), | ||
283 | delete: [336; 336), | ||
284 | insert: "the_field", | ||
285 | kind: Field, | ||
286 | detail: "u32", | ||
287 | }, | ||
288 | ] | ||
289 | "### | ||
290 | ); | ||
291 | } | ||
292 | |||
293 | #[test] | ||
294 | fn test_struct_field_completion_in_fn_call_and_record_lit() { | ||
295 | assert_debug_snapshot!( | ||
296 | do_ref_completion( | ||
297 | r" | ||
298 | struct A { another_field: i64, another_good_type: u32, the_field: u32 } | ||
299 | struct B { my_string: String, my_vec: Vec<u32>, the_field: u32 } | ||
300 | fn test(the_field: i64) -> i64 { the_field } | ||
301 | fn foo(a: A) { | ||
302 | test(B { | ||
303 | the_field: a.<|> | ||
304 | }); | ||
305 | } | ||
306 | ", | ||
307 | ), | ||
308 | @r###" | ||
309 | [ | ||
310 | CompletionItem { | ||
311 | label: "another_field", | ||
312 | source_range: [328; 328), | ||
313 | delete: [328; 328), | ||
314 | insert: "another_field", | ||
315 | kind: Field, | ||
316 | detail: "i64", | ||
317 | }, | ||
318 | CompletionItem { | ||
319 | label: "another_good_type", | ||
320 | source_range: [328; 328), | ||
321 | delete: [328; 328), | ||
322 | insert: "another_good_type", | ||
323 | kind: Field, | ||
324 | detail: "u32", | ||
325 | score: TypeMatch, | ||
326 | }, | ||
327 | CompletionItem { | ||
328 | label: "the_field", | ||
329 | source_range: [328; 328), | ||
330 | delete: [328; 328), | ||
331 | insert: "the_field", | ||
332 | kind: Field, | ||
333 | detail: "u32", | ||
334 | score: TypeAndNameMatch, | ||
335 | }, | ||
336 | ] | ||
337 | "### | ||
338 | ); | ||
339 | } | ||
340 | |||
341 | #[test] | ||
107 | fn test_struct_field_completion_self() { | 342 | fn test_struct_field_completion_self() { |
108 | assert_debug_snapshot!( | 343 | assert_debug_snapshot!( |
109 | do_ref_completion( | 344 | do_ref_completion( |
diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs index 8b3401595..46e243192 100644 --- a/crates/ra_ide/src/completion/completion_context.rs +++ b/crates/ra_ide/src/completion/completion_context.rs | |||
@@ -21,6 +21,7 @@ pub(crate) struct CompletionContext<'a> { | |||
21 | pub(super) db: &'a RootDatabase, | 21 | pub(super) db: &'a RootDatabase, |
22 | pub(super) config: &'a CompletionConfig, | 22 | pub(super) config: &'a CompletionConfig, |
23 | pub(super) offset: TextUnit, | 23 | pub(super) offset: TextUnit, |
24 | pub(super) file_position: FilePosition, | ||
24 | /// The token before the cursor, in the original file. | 25 | /// The token before the cursor, in the original file. |
25 | pub(super) original_token: SyntaxToken, | 26 | pub(super) original_token: SyntaxToken, |
26 | /// The token before the cursor, in the macro-expanded file. | 27 | /// The token before the cursor, in the macro-expanded file. |
@@ -31,6 +32,7 @@ pub(crate) struct CompletionContext<'a> { | |||
31 | pub(super) use_item_syntax: Option<ast::UseItem>, | 32 | pub(super) use_item_syntax: Option<ast::UseItem>, |
32 | pub(super) record_lit_syntax: Option<ast::RecordLit>, | 33 | pub(super) record_lit_syntax: Option<ast::RecordLit>, |
33 | pub(super) record_pat_syntax: Option<ast::RecordPat>, | 34 | pub(super) record_pat_syntax: Option<ast::RecordPat>, |
35 | pub(super) record_field_syntax: Option<ast::RecordField>, | ||
34 | pub(super) impl_def: Option<ast::ImplDef>, | 36 | pub(super) impl_def: Option<ast::ImplDef>, |
35 | pub(super) is_param: bool, | 37 | pub(super) is_param: bool, |
36 | /// If a name-binding or reference to a const in a pattern. | 38 | /// If a name-binding or reference to a const in a pattern. |
@@ -88,12 +90,14 @@ impl<'a> CompletionContext<'a> { | |||
88 | original_token, | 90 | original_token, |
89 | token, | 91 | token, |
90 | offset: position.offset, | 92 | offset: position.offset, |
93 | file_position: position, | ||
91 | krate, | 94 | krate, |
92 | name_ref_syntax: None, | 95 | name_ref_syntax: None, |
93 | function_syntax: None, | 96 | function_syntax: None, |
94 | use_item_syntax: None, | 97 | use_item_syntax: None, |
95 | record_lit_syntax: None, | 98 | record_lit_syntax: None, |
96 | record_pat_syntax: None, | 99 | record_pat_syntax: None, |
100 | record_field_syntax: None, | ||
97 | impl_def: None, | 101 | impl_def: None, |
98 | is_param: false, | 102 | is_param: false, |
99 | is_pat_binding_or_const: false, | 103 | is_pat_binding_or_const: false, |
@@ -268,6 +272,14 @@ impl<'a> CompletionContext<'a> { | |||
268 | .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE) | 272 | .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE) |
269 | .find_map(ast::FnDef::cast); | 273 | .find_map(ast::FnDef::cast); |
270 | 274 | ||
275 | self.record_field_syntax = self | ||
276 | .sema | ||
277 | .ancestors_with_macros(self.token.parent()) | ||
278 | .take_while(|it| { | ||
279 | it.kind() != SOURCE_FILE && it.kind() != MODULE && it.kind() != CALL_EXPR | ||
280 | }) | ||
281 | .find_map(ast::RecordField::cast); | ||
282 | |||
271 | let parent = match name_ref.syntax().parent() { | 283 | let parent = match name_ref.syntax().parent() { |
272 | Some(it) => it, | 284 | Some(it) => it, |
273 | None => return, | 285 | None => return, |
diff --git a/crates/ra_ide/src/completion/completion_item.rs b/crates/ra_ide/src/completion/completion_item.rs index bc0f1aff5..a3ae9c86b 100644 --- a/crates/ra_ide/src/completion/completion_item.rs +++ b/crates/ra_ide/src/completion/completion_item.rs | |||
@@ -2,7 +2,9 @@ | |||
2 | 2 | ||
3 | use std::fmt; | 3 | use std::fmt; |
4 | 4 | ||
5 | use hir::Documentation; | 5 | use super::completion_context::CompletionContext; |
6 | use crate::call_info::call_info; | ||
7 | use hir::{Documentation, HirDisplay}; | ||
6 | use ra_syntax::TextRange; | 8 | use ra_syntax::TextRange; |
7 | use ra_text_edit::TextEdit; | 9 | use ra_text_edit::TextEdit; |
8 | 10 | ||
@@ -51,6 +53,9 @@ pub struct CompletionItem { | |||
51 | /// If completing a function call, ask the editor to show parameter popup | 53 | /// If completing a function call, ask the editor to show parameter popup |
52 | /// after completion. | 54 | /// after completion. |
53 | trigger_call_info: bool, | 55 | trigger_call_info: bool, |
56 | |||
57 | /// Score is usefull to pre select or display in better order completion items | ||
58 | score: Option<CompletionScore>, | ||
54 | } | 59 | } |
55 | 60 | ||
56 | // We use custom debug for CompletionItem to make `insta`'s diffs more readable. | 61 | // We use custom debug for CompletionItem to make `insta`'s diffs more readable. |
@@ -80,6 +85,9 @@ impl fmt::Debug for CompletionItem { | |||
80 | if self.deprecated { | 85 | if self.deprecated { |
81 | s.field("deprecated", &true); | 86 | s.field("deprecated", &true); |
82 | } | 87 | } |
88 | if let Some(score) = &self.score { | ||
89 | s.field("score", score); | ||
90 | } | ||
83 | if self.trigger_call_info { | 91 | if self.trigger_call_info { |
84 | s.field("trigger_call_info", &true); | 92 | s.field("trigger_call_info", &true); |
85 | } | 93 | } |
@@ -147,6 +155,7 @@ impl CompletionItem { | |||
147 | text_edit: None, | 155 | text_edit: None, |
148 | deprecated: None, | 156 | deprecated: None, |
149 | trigger_call_info: None, | 157 | trigger_call_info: None, |
158 | score: None, | ||
150 | } | 159 | } |
151 | } | 160 | } |
152 | /// What user sees in pop-up in the UI. | 161 | /// What user sees in pop-up in the UI. |
@@ -186,6 +195,14 @@ impl CompletionItem { | |||
186 | self.deprecated | 195 | self.deprecated |
187 | } | 196 | } |
188 | 197 | ||
198 | pub fn score(&self) -> Option<CompletionScore> { | ||
199 | self.score.clone() | ||
200 | } | ||
201 | |||
202 | pub fn set_score(&mut self, score: CompletionScore) { | ||
203 | self.score = Some(score); | ||
204 | } | ||
205 | |||
189 | pub fn trigger_call_info(&self) -> bool { | 206 | pub fn trigger_call_info(&self) -> bool { |
190 | self.trigger_call_info | 207 | self.trigger_call_info |
191 | } | 208 | } |
@@ -206,6 +223,7 @@ pub(crate) struct Builder { | |||
206 | text_edit: Option<TextEdit>, | 223 | text_edit: Option<TextEdit>, |
207 | deprecated: Option<bool>, | 224 | deprecated: Option<bool>, |
208 | trigger_call_info: Option<bool>, | 225 | trigger_call_info: Option<bool>, |
226 | score: Option<CompletionScore>, | ||
209 | } | 227 | } |
210 | 228 | ||
211 | impl Builder { | 229 | impl Builder { |
@@ -235,6 +253,7 @@ impl Builder { | |||
235 | completion_kind: self.completion_kind, | 253 | completion_kind: self.completion_kind, |
236 | deprecated: self.deprecated.unwrap_or(false), | 254 | deprecated: self.deprecated.unwrap_or(false), |
237 | trigger_call_info: self.trigger_call_info.unwrap_or(false), | 255 | trigger_call_info: self.trigger_call_info.unwrap_or(false), |
256 | score: self.score, | ||
238 | } | 257 | } |
239 | } | 258 | } |
240 | pub(crate) fn lookup_by(mut self, lookup: impl Into<String>) -> Builder { | 259 | pub(crate) fn lookup_by(mut self, lookup: impl Into<String>) -> Builder { |
@@ -285,6 +304,51 @@ impl Builder { | |||
285 | self.deprecated = Some(deprecated); | 304 | self.deprecated = Some(deprecated); |
286 | self | 305 | self |
287 | } | 306 | } |
307 | #[allow(unused)] | ||
308 | pub(crate) fn compute_score(mut self, ctx: &CompletionContext) -> Builder { | ||
309 | let (active_name, active_type) = if let Some(record_field) = &ctx.record_field_syntax { | ||
310 | if let Some((struct_field, _)) = ctx.sema.resolve_record_field(record_field) { | ||
311 | ( | ||
312 | struct_field.name(ctx.db).to_string(), | ||
313 | struct_field.signature_ty(ctx.db).display(ctx.db).to_string(), | ||
314 | ) | ||
315 | } else { | ||
316 | return self; | ||
317 | } | ||
318 | } else if let Some(call_info) = call_info(ctx.db, ctx.file_position) { | ||
319 | if call_info.active_parameter_type().is_some() | ||
320 | && call_info.active_parameter_name().is_some() | ||
321 | { | ||
322 | ( | ||
323 | call_info.active_parameter_name().unwrap(), | ||
324 | call_info.active_parameter_type().unwrap(), | ||
325 | ) | ||
326 | } else { | ||
327 | return self; | ||
328 | } | ||
329 | } else { | ||
330 | return self; | ||
331 | }; | ||
332 | |||
333 | // Compute score | ||
334 | // For the same type | ||
335 | if let Some(a_parameter_type) = &self.detail { | ||
336 | if &active_type == a_parameter_type { | ||
337 | // If same type + same name then go top position | ||
338 | if active_name == self.label { | ||
339 | return self.set_score(CompletionScore::TypeAndNameMatch); | ||
340 | } else { | ||
341 | return self.set_score(CompletionScore::TypeMatch); | ||
342 | } | ||
343 | } | ||
344 | } | ||
345 | |||
346 | self | ||
347 | } | ||
348 | pub(crate) fn set_score(mut self, score: CompletionScore) -> Builder { | ||
349 | self.score = Some(score); | ||
350 | self | ||
351 | } | ||
288 | pub(crate) fn trigger_call_info(mut self) -> Builder { | 352 | pub(crate) fn trigger_call_info(mut self) -> Builder { |
289 | self.trigger_call_info = Some(true); | 353 | self.trigger_call_info = Some(true); |
290 | self | 354 | self |
@@ -297,6 +361,12 @@ impl<'a> Into<CompletionItem> for Builder { | |||
297 | } | 361 | } |
298 | } | 362 | } |
299 | 363 | ||
364 | #[derive(Debug, Clone)] | ||
365 | pub enum CompletionScore { | ||
366 | TypeMatch, | ||
367 | TypeAndNameMatch, | ||
368 | } | ||
369 | |||
300 | /// Represents an in-progress set of completions being built. | 370 | /// Represents an in-progress set of completions being built. |
301 | #[derive(Debug, Default)] | 371 | #[derive(Debug, Default)] |
302 | pub(crate) struct Completions { | 372 | pub(crate) struct Completions { |
diff --git a/crates/ra_ide/src/completion/presentation.rs b/crates/ra_ide/src/completion/presentation.rs index 55f75b15a..5c3360ce4 100644 --- a/crates/ra_ide/src/completion/presentation.rs +++ b/crates/ra_ide/src/completion/presentation.rs | |||
@@ -31,6 +31,7 @@ impl Completions { | |||
31 | .detail(ty.display(ctx.db).to_string()) | 31 | .detail(ty.display(ctx.db).to_string()) |
32 | .set_documentation(field.docs(ctx.db)) | 32 | .set_documentation(field.docs(ctx.db)) |
33 | .set_deprecated(is_deprecated) | 33 | .set_deprecated(is_deprecated) |
34 | .compute_score(ctx) | ||
34 | .add_to(self); | 35 | .add_to(self); |
35 | } | 36 | } |
36 | 37 | ||