diff options
-rw-r--r-- | crates/ra_ide/src/completion.rs | 2 | ||||
-rw-r--r-- | crates/ra_ide/src/completion/complete_dot.rs | 237 | ||||
-rw-r--r-- | crates/ra_ide/src/completion/completion_context.rs | 12 | ||||
-rw-r--r-- | crates/ra_ide/src/completion/completion_item.rs | 30 | ||||
-rw-r--r-- | crates/ra_ide/src/completion/presentation.rs | 47 | ||||
-rw-r--r-- | crates/ra_ide/src/display/function_signature.rs | 69 | ||||
-rw-r--r-- | crates/ra_ide/src/lib.rs | 19 | ||||
-rw-r--r-- | crates/rust-analyzer/src/conv.rs | 29 | ||||
-rw-r--r-- | crates/rust-analyzer/src/main_loop/handlers.rs | 7 |
9 files changed, 412 insertions, 40 deletions
diff --git a/crates/ra_ide/src/completion.rs b/crates/ra_ide/src/completion.rs index 4a1a2a04a..19bc4321c 100644 --- a/crates/ra_ide/src/completion.rs +++ b/crates/ra_ide/src/completion.rs | |||
@@ -29,7 +29,7 @@ use crate::{ | |||
29 | }; | 29 | }; |
30 | 30 | ||
31 | pub use crate::completion::completion_item::{ | 31 | pub use crate::completion::completion_item::{ |
32 | CompletionItem, CompletionItemKind, InsertTextFormat, | 32 | CompletionItem, CompletionItemKind, CompletionScore, InsertTextFormat, |
33 | }; | 33 | }; |
34 | 34 | ||
35 | #[derive(Clone, Debug, PartialEq, Eq)] | 35 | #[derive(Clone, Debug, PartialEq, Eq)] |
diff --git a/crates/ra_ide/src/completion/complete_dot.rs b/crates/ra_ide/src/completion/complete_dot.rs index f433faef3..44288f92e 100644 --- a/crates/ra_ide/src/completion/complete_dot.rs +++ b/crates/ra_ide/src/completion/complete_dot.rs | |||
@@ -2,9 +2,11 @@ | |||
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 | }, | ||
8 | CompletionItem, | 10 | CompletionItem, |
9 | }; | 11 | }; |
10 | use rustc_hash::FxHashSet; | 12 | use rustc_hash::FxHashSet; |
@@ -104,6 +106,237 @@ mod tests { | |||
104 | } | 106 | } |
105 | 107 | ||
106 | #[test] | 108 | #[test] |
109 | fn test_struct_field_completion_in_func_call() { | ||
110 | assert_debug_snapshot!( | ||
111 | do_ref_completion( | ||
112 | r" | ||
113 | struct A { another_field: i64, the_field: u32, my_string: String } | ||
114 | fn test(my_param: u32) -> u32 { my_param } | ||
115 | fn foo(a: A) { | ||
116 | test(a.<|>) | ||
117 | } | ||
118 | ", | ||
119 | ), | ||
120 | @r###" | ||
121 | [ | ||
122 | CompletionItem { | ||
123 | label: "another_field", | ||
124 | source_range: [201; 201), | ||
125 | delete: [201; 201), | ||
126 | insert: "another_field", | ||
127 | kind: Field, | ||
128 | detail: "i64", | ||
129 | }, | ||
130 | CompletionItem { | ||
131 | label: "my_string", | ||
132 | source_range: [201; 201), | ||
133 | delete: [201; 201), | ||
134 | insert: "my_string", | ||
135 | kind: Field, | ||
136 | detail: "{unknown}", | ||
137 | }, | ||
138 | CompletionItem { | ||
139 | label: "the_field", | ||
140 | source_range: [201; 201), | ||
141 | delete: [201; 201), | ||
142 | insert: "the_field", | ||
143 | kind: Field, | ||
144 | detail: "u32", | ||
145 | score: TypeMatch, | ||
146 | }, | ||
147 | ] | ||
148 | "### | ||
149 | ); | ||
150 | } | ||
151 | |||
152 | #[test] | ||
153 | fn test_struct_field_completion_in_func_call_with_type_and_name() { | ||
154 | assert_debug_snapshot!( | ||
155 | do_ref_completion( | ||
156 | r" | ||
157 | struct A { another_field: i64, another_good_type: u32, the_field: u32 } | ||
158 | fn test(the_field: u32) -> u32 { the_field } | ||
159 | fn foo(a: A) { | ||
160 | test(a.<|>) | ||
161 | } | ||
162 | ", | ||
163 | ), | ||
164 | @r###" | ||
165 | [ | ||
166 | CompletionItem { | ||
167 | label: "another_field", | ||
168 | source_range: [208; 208), | ||
169 | delete: [208; 208), | ||
170 | insert: "another_field", | ||
171 | kind: Field, | ||
172 | detail: "i64", | ||
173 | }, | ||
174 | CompletionItem { | ||
175 | label: "another_good_type", | ||
176 | source_range: [208; 208), | ||
177 | delete: [208; 208), | ||
178 | insert: "another_good_type", | ||
179 | kind: Field, | ||
180 | detail: "u32", | ||
181 | score: TypeMatch, | ||
182 | }, | ||
183 | CompletionItem { | ||
184 | label: "the_field", | ||
185 | source_range: [208; 208), | ||
186 | delete: [208; 208), | ||
187 | insert: "the_field", | ||
188 | kind: Field, | ||
189 | detail: "u32", | ||
190 | score: TypeAndNameMatch, | ||
191 | }, | ||
192 | ] | ||
193 | "### | ||
194 | ); | ||
195 | } | ||
196 | |||
197 | #[test] | ||
198 | fn test_struct_field_completion_in_record_lit() { | ||
199 | assert_debug_snapshot!( | ||
200 | do_ref_completion( | ||
201 | r" | ||
202 | struct A { another_field: i64, another_good_type: u32, the_field: u32 } | ||
203 | struct B { my_string: String, my_vec: Vec<u32>, the_field: u32 } | ||
204 | fn foo(a: A) { | ||
205 | let b = B { | ||
206 | the_field: a.<|> | ||
207 | }; | ||
208 | } | ||
209 | ", | ||
210 | ), | ||
211 | @r###" | ||
212 | [ | ||
213 | CompletionItem { | ||
214 | label: "another_field", | ||
215 | source_range: [270; 270), | ||
216 | delete: [270; 270), | ||
217 | insert: "another_field", | ||
218 | kind: Field, | ||
219 | detail: "i64", | ||
220 | }, | ||
221 | CompletionItem { | ||
222 | label: "another_good_type", | ||
223 | source_range: [270; 270), | ||
224 | delete: [270; 270), | ||
225 | insert: "another_good_type", | ||
226 | kind: Field, | ||
227 | detail: "u32", | ||
228 | score: TypeMatch, | ||
229 | }, | ||
230 | CompletionItem { | ||
231 | label: "the_field", | ||
232 | source_range: [270; 270), | ||
233 | delete: [270; 270), | ||
234 | insert: "the_field", | ||
235 | kind: Field, | ||
236 | detail: "u32", | ||
237 | score: TypeAndNameMatch, | ||
238 | }, | ||
239 | ] | ||
240 | "### | ||
241 | ); | ||
242 | } | ||
243 | |||
244 | #[test] | ||
245 | fn test_struct_field_completion_in_record_lit_and_fn_call() { | ||
246 | assert_debug_snapshot!( | ||
247 | do_ref_completion( | ||
248 | r" | ||
249 | struct A { another_field: i64, another_good_type: u32, the_field: u32 } | ||
250 | struct B { my_string: String, my_vec: Vec<u32>, the_field: u32 } | ||
251 | fn test(the_field: i64) -> i64 { the_field } | ||
252 | fn foo(a: A) { | ||
253 | let b = B { | ||
254 | the_field: test(a.<|>) | ||
255 | }; | ||
256 | } | ||
257 | ", | ||
258 | ), | ||
259 | @r###" | ||
260 | [ | ||
261 | CompletionItem { | ||
262 | label: "another_field", | ||
263 | source_range: [336; 336), | ||
264 | delete: [336; 336), | ||
265 | insert: "another_field", | ||
266 | kind: Field, | ||
267 | detail: "i64", | ||
268 | score: TypeMatch, | ||
269 | }, | ||
270 | CompletionItem { | ||
271 | label: "another_good_type", | ||
272 | source_range: [336; 336), | ||
273 | delete: [336; 336), | ||
274 | insert: "another_good_type", | ||
275 | kind: Field, | ||
276 | detail: "u32", | ||
277 | }, | ||
278 | CompletionItem { | ||
279 | label: "the_field", | ||
280 | source_range: [336; 336), | ||
281 | delete: [336; 336), | ||
282 | insert: "the_field", | ||
283 | kind: Field, | ||
284 | detail: "u32", | ||
285 | }, | ||
286 | ] | ||
287 | "### | ||
288 | ); | ||
289 | } | ||
290 | |||
291 | #[test] | ||
292 | fn test_struct_field_completion_in_fn_call_and_record_lit() { | ||
293 | assert_debug_snapshot!( | ||
294 | do_ref_completion( | ||
295 | r" | ||
296 | struct A { another_field: i64, another_good_type: u32, the_field: u32 } | ||
297 | struct B { my_string: String, my_vec: Vec<u32>, the_field: u32 } | ||
298 | fn test(the_field: i64) -> i64 { the_field } | ||
299 | fn foo(a: A) { | ||
300 | test(B { | ||
301 | the_field: a.<|> | ||
302 | }); | ||
303 | } | ||
304 | ", | ||
305 | ), | ||
306 | @r###" | ||
307 | [ | ||
308 | CompletionItem { | ||
309 | label: "another_field", | ||
310 | source_range: [328; 328), | ||
311 | delete: [328; 328), | ||
312 | insert: "another_field", | ||
313 | kind: Field, | ||
314 | detail: "i64", | ||
315 | }, | ||
316 | CompletionItem { | ||
317 | label: "another_good_type", | ||
318 | source_range: [328; 328), | ||
319 | delete: [328; 328), | ||
320 | insert: "another_good_type", | ||
321 | kind: Field, | ||
322 | detail: "u32", | ||
323 | score: TypeMatch, | ||
324 | }, | ||
325 | CompletionItem { | ||
326 | label: "the_field", | ||
327 | source_range: [328; 328), | ||
328 | delete: [328; 328), | ||
329 | insert: "the_field", | ||
330 | kind: Field, | ||
331 | detail: "u32", | ||
332 | score: TypeAndNameMatch, | ||
333 | }, | ||
334 | ] | ||
335 | "### | ||
336 | ); | ||
337 | } | ||
338 | |||
339 | #[test] | ||
107 | fn test_struct_field_completion_self() { | 340 | fn test_struct_field_completion_self() { |
108 | assert_debug_snapshot!( | 341 | assert_debug_snapshot!( |
109 | do_ref_completion( | 342 | do_ref_completion( |
diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs index cfc5c34df..dd7c8a873 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, |
@@ -279,6 +283,14 @@ impl<'a> CompletionContext<'a> { | |||
279 | .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE) | 283 | .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE) |
280 | .find_map(ast::FnDef::cast); | 284 | .find_map(ast::FnDef::cast); |
281 | 285 | ||
286 | self.record_field_syntax = self | ||
287 | .sema | ||
288 | .ancestors_with_macros(self.token.parent()) | ||
289 | .take_while(|it| { | ||
290 | it.kind() != SOURCE_FILE && it.kind() != MODULE && it.kind() != CALL_EXPR | ||
291 | }) | ||
292 | .find_map(ast::RecordField::cast); | ||
293 | |||
282 | let parent = match name_ref.syntax().parent() { | 294 | let parent = match name_ref.syntax().parent() { |
283 | Some(it) => it, | 295 | Some(it) => it, |
284 | None => return, | 296 | None => return, |
diff --git a/crates/ra_ide/src/completion/completion_item.rs b/crates/ra_ide/src/completion/completion_item.rs index bc0f1aff5..e17586aa5 100644 --- a/crates/ra_ide/src/completion/completion_item.rs +++ b/crates/ra_ide/src/completion/completion_item.rs | |||
@@ -51,6 +51,9 @@ pub struct CompletionItem { | |||
51 | /// If completing a function call, ask the editor to show parameter popup | 51 | /// If completing a function call, ask the editor to show parameter popup |
52 | /// after completion. | 52 | /// after completion. |
53 | trigger_call_info: bool, | 53 | trigger_call_info: bool, |
54 | |||
55 | /// Score is usefull to pre select or display in better order completion items | ||
56 | score: Option<CompletionScore>, | ||
54 | } | 57 | } |
55 | 58 | ||
56 | // We use custom debug for CompletionItem to make `insta`'s diffs more readable. | 59 | // We use custom debug for CompletionItem to make `insta`'s diffs more readable. |
@@ -80,6 +83,9 @@ impl fmt::Debug for CompletionItem { | |||
80 | if self.deprecated { | 83 | if self.deprecated { |
81 | s.field("deprecated", &true); | 84 | s.field("deprecated", &true); |
82 | } | 85 | } |
86 | if let Some(score) = &self.score { | ||
87 | s.field("score", score); | ||
88 | } | ||
83 | if self.trigger_call_info { | 89 | if self.trigger_call_info { |
84 | s.field("trigger_call_info", &true); | 90 | s.field("trigger_call_info", &true); |
85 | } | 91 | } |
@@ -147,6 +153,7 @@ impl CompletionItem { | |||
147 | text_edit: None, | 153 | text_edit: None, |
148 | deprecated: None, | 154 | deprecated: None, |
149 | trigger_call_info: None, | 155 | trigger_call_info: None, |
156 | score: None, | ||
150 | } | 157 | } |
151 | } | 158 | } |
152 | /// What user sees in pop-up in the UI. | 159 | /// What user sees in pop-up in the UI. |
@@ -186,6 +193,14 @@ impl CompletionItem { | |||
186 | self.deprecated | 193 | self.deprecated |
187 | } | 194 | } |
188 | 195 | ||
196 | pub fn score(&self) -> Option<CompletionScore> { | ||
197 | self.score.clone() | ||
198 | } | ||
199 | |||
200 | pub fn set_score(&mut self, score: CompletionScore) { | ||
201 | self.score = Some(score); | ||
202 | } | ||
203 | |||
189 | pub fn trigger_call_info(&self) -> bool { | 204 | pub fn trigger_call_info(&self) -> bool { |
190 | self.trigger_call_info | 205 | self.trigger_call_info |
191 | } | 206 | } |
@@ -206,6 +221,7 @@ pub(crate) struct Builder { | |||
206 | text_edit: Option<TextEdit>, | 221 | text_edit: Option<TextEdit>, |
207 | deprecated: Option<bool>, | 222 | deprecated: Option<bool>, |
208 | trigger_call_info: Option<bool>, | 223 | trigger_call_info: Option<bool>, |
224 | score: Option<CompletionScore>, | ||
209 | } | 225 | } |
210 | 226 | ||
211 | impl Builder { | 227 | impl Builder { |
@@ -235,6 +251,7 @@ impl Builder { | |||
235 | completion_kind: self.completion_kind, | 251 | completion_kind: self.completion_kind, |
236 | deprecated: self.deprecated.unwrap_or(false), | 252 | deprecated: self.deprecated.unwrap_or(false), |
237 | trigger_call_info: self.trigger_call_info.unwrap_or(false), | 253 | trigger_call_info: self.trigger_call_info.unwrap_or(false), |
254 | score: self.score, | ||
238 | } | 255 | } |
239 | } | 256 | } |
240 | pub(crate) fn lookup_by(mut self, lookup: impl Into<String>) -> Builder { | 257 | pub(crate) fn lookup_by(mut self, lookup: impl Into<String>) -> Builder { |
@@ -285,6 +302,11 @@ impl Builder { | |||
285 | self.deprecated = Some(deprecated); | 302 | self.deprecated = Some(deprecated); |
286 | self | 303 | self |
287 | } | 304 | } |
305 | #[allow(unused)] | ||
306 | pub(crate) fn set_score(mut self, score: CompletionScore) -> Builder { | ||
307 | self.score = Some(score); | ||
308 | self | ||
309 | } | ||
288 | pub(crate) fn trigger_call_info(mut self) -> Builder { | 310 | pub(crate) fn trigger_call_info(mut self) -> Builder { |
289 | self.trigger_call_info = Some(true); | 311 | self.trigger_call_info = Some(true); |
290 | self | 312 | self |
@@ -297,6 +319,14 @@ impl<'a> Into<CompletionItem> for Builder { | |||
297 | } | 319 | } |
298 | } | 320 | } |
299 | 321 | ||
322 | #[derive(Debug, Clone)] | ||
323 | pub enum CompletionScore { | ||
324 | /// If only type match | ||
325 | TypeMatch, | ||
326 | /// If type and name match | ||
327 | TypeAndNameMatch, | ||
328 | } | ||
329 | |||
300 | /// Represents an in-progress set of completions being built. | 330 | /// Represents an in-progress set of completions being built. |
301 | #[derive(Debug, Default)] | 331 | #[derive(Debug, Default)] |
302 | pub(crate) struct Completions { | 332 | pub(crate) struct Completions { |
diff --git a/crates/ra_ide/src/completion/presentation.rs b/crates/ra_ide/src/completion/presentation.rs index 2189cef65..f8dac1d54 100644 --- a/crates/ra_ide/src/completion/presentation.rs +++ b/crates/ra_ide/src/completion/presentation.rs | |||
@@ -6,12 +6,13 @@ use stdx::SepBy; | |||
6 | use test_utils::tested_by; | 6 | use test_utils::tested_by; |
7 | 7 | ||
8 | use crate::{ | 8 | use crate::{ |
9 | call_info::call_info, | ||
9 | completion::{ | 10 | completion::{ |
10 | completion_item::Builder, CompletionContext, CompletionItem, CompletionItemKind, | 11 | completion_item::Builder, CompletionContext, CompletionItem, CompletionItemKind, |
11 | CompletionKind, Completions, | 12 | CompletionKind, Completions, |
12 | }, | 13 | }, |
13 | display::{const_label, macro_label, type_label, FunctionSignature}, | 14 | display::{const_label, macro_label, type_label, FunctionSignature}, |
14 | RootDatabase, | 15 | CompletionScore, RootDatabase, |
15 | }; | 16 | }; |
16 | 17 | ||
17 | impl Completions { | 18 | impl Completions { |
@@ -22,7 +23,7 @@ impl Completions { | |||
22 | ty: &Type, | 23 | ty: &Type, |
23 | ) { | 24 | ) { |
24 | let is_deprecated = is_deprecated(field, ctx.db); | 25 | let is_deprecated = is_deprecated(field, ctx.db); |
25 | CompletionItem::new( | 26 | let mut completion_item = CompletionItem::new( |
26 | CompletionKind::Reference, | 27 | CompletionKind::Reference, |
27 | ctx.source_range(), | 28 | ctx.source_range(), |
28 | field.name(ctx.db).to_string(), | 29 | field.name(ctx.db).to_string(), |
@@ -31,7 +32,11 @@ impl Completions { | |||
31 | .detail(ty.display(ctx.db).to_string()) | 32 | .detail(ty.display(ctx.db).to_string()) |
32 | .set_documentation(field.docs(ctx.db)) | 33 | .set_documentation(field.docs(ctx.db)) |
33 | .set_deprecated(is_deprecated) | 34 | .set_deprecated(is_deprecated) |
34 | .add_to(self); | 35 | .build(); |
36 | |||
37 | compute_score(&mut completion_item, ctx); | ||
38 | |||
39 | self.add(completion_item); | ||
35 | } | 40 | } |
36 | 41 | ||
37 | pub(crate) fn add_tuple_field(&mut self, ctx: &CompletionContext, field: usize, ty: &Type) { | 42 | pub(crate) fn add_tuple_field(&mut self, ctx: &CompletionContext, field: usize, ty: &Type) { |
@@ -300,6 +305,42 @@ impl Completions { | |||
300 | } | 305 | } |
301 | } | 306 | } |
302 | 307 | ||
308 | pub(crate) fn compute_score(completion_item: &mut CompletionItem, ctx: &CompletionContext) { | ||
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; | ||
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 | (call_info.active_parameter_name().unwrap(), call_info.active_parameter_type().unwrap()) | ||
323 | } else { | ||
324 | return; | ||
325 | } | ||
326 | } else { | ||
327 | return; | ||
328 | }; | ||
329 | |||
330 | // Compute score | ||
331 | // For the same type | ||
332 | if let Some(a_parameter_type) = completion_item.detail() { | ||
333 | if &active_type == a_parameter_type { | ||
334 | // If same type + same name then go top position | ||
335 | if active_name == completion_item.label() { | ||
336 | completion_item.set_score(CompletionScore::TypeAndNameMatch); | ||
337 | } else { | ||
338 | completion_item.set_score(CompletionScore::TypeMatch); | ||
339 | } | ||
340 | } | ||
341 | } | ||
342 | } | ||
343 | |||
303 | enum Params { | 344 | enum Params { |
304 | Named(Vec<String>), | 345 | Named(Vec<String>), |
305 | Anonymous(usize), | 346 | Anonymous(usize), |
diff --git a/crates/ra_ide/src/display/function_signature.rs b/crates/ra_ide/src/display/function_signature.rs index b967a6816..b5e2785fe 100644 --- a/crates/ra_ide/src/display/function_signature.rs +++ b/crates/ra_ide/src/display/function_signature.rs | |||
@@ -36,6 +36,8 @@ pub struct FunctionSignature { | |||
36 | pub parameters: Vec<String>, | 36 | pub parameters: Vec<String>, |
37 | /// Parameter names of the function | 37 | /// Parameter names of the function |
38 | pub parameter_names: Vec<String>, | 38 | pub parameter_names: Vec<String>, |
39 | /// Parameter types of the function | ||
40 | pub parameter_types: Vec<String>, | ||
39 | /// Optional return type | 41 | /// Optional return type |
40 | pub ret_type: Option<String>, | 42 | pub ret_type: Option<String>, |
41 | /// Where predicates | 43 | /// Where predicates |
@@ -62,14 +64,20 @@ impl FunctionSignature { | |||
62 | return None; | 64 | return None; |
63 | }; | 65 | }; |
64 | 66 | ||
65 | let params = st | 67 | let mut params = vec![]; |
66 | .fields(db) | 68 | let mut parameter_types = vec![]; |
67 | .into_iter() | 69 | for field in st.fields(db).into_iter() { |
68 | .map(|field: hir::StructField| { | 70 | let ty = field.signature_ty(db); |
69 | let ty = field.signature_ty(db); | 71 | let raw_param = format!("{}", ty.display(db)); |
70 | format!("{}", ty.display(db)) | 72 | |
71 | }) | 73 | if let Some(param_type) = raw_param.split(':').nth(1) { |
72 | .collect(); | 74 | parameter_types.push(param_type[1..].to_string()); |
75 | } else { | ||
76 | // useful when you have tuple struct | ||
77 | parameter_types.push(raw_param.clone()); | ||
78 | } | ||
79 | params.push(raw_param); | ||
80 | } | ||
73 | 81 | ||
74 | Some( | 82 | Some( |
75 | FunctionSignature { | 83 | FunctionSignature { |
@@ -79,6 +87,7 @@ impl FunctionSignature { | |||
79 | ret_type: node.name().map(|n| n.text().to_string()), | 87 | ret_type: node.name().map(|n| n.text().to_string()), |
80 | parameters: params, | 88 | parameters: params, |
81 | parameter_names: vec![], | 89 | parameter_names: vec![], |
90 | parameter_types, | ||
82 | generic_parameters: generic_parameters(&node), | 91 | generic_parameters: generic_parameters(&node), |
83 | where_predicates: where_predicates(&node), | 92 | where_predicates: where_predicates(&node), |
84 | doc: None, | 93 | doc: None, |
@@ -99,15 +108,21 @@ impl FunctionSignature { | |||
99 | 108 | ||
100 | let name = format!("{}::{}", parent_name, variant.name(db)); | 109 | let name = format!("{}::{}", parent_name, variant.name(db)); |
101 | 110 | ||
102 | let params = variant | 111 | let mut params = vec![]; |
103 | .fields(db) | 112 | let mut parameter_types = vec![]; |
104 | .into_iter() | 113 | for field in variant.fields(db).into_iter() { |
105 | .map(|field: hir::StructField| { | 114 | let ty = field.signature_ty(db); |
106 | let name = field.name(db); | 115 | let raw_param = format!("{}", ty.display(db)); |
107 | let ty = field.signature_ty(db); | 116 | if let Some(param_type) = raw_param.split(':').nth(1) { |
108 | format!("{}: {}", name, ty.display(db)) | 117 | parameter_types.push(param_type[1..].to_string()); |
109 | }) | 118 | } else { |
110 | .collect(); | 119 | // The unwrap_or_else is useful when you have tuple |
120 | parameter_types.push(raw_param); | ||
121 | } | ||
122 | let name = field.name(db); | ||
123 | |||
124 | params.push(format!("{}: {}", name, ty.display(db))); | ||
125 | } | ||
111 | 126 | ||
112 | Some( | 127 | Some( |
113 | FunctionSignature { | 128 | FunctionSignature { |
@@ -117,6 +132,7 @@ impl FunctionSignature { | |||
117 | ret_type: None, | 132 | ret_type: None, |
118 | parameters: params, | 133 | parameters: params, |
119 | parameter_names: vec![], | 134 | parameter_names: vec![], |
135 | parameter_types, | ||
120 | generic_parameters: vec![], | 136 | generic_parameters: vec![], |
121 | where_predicates: vec![], | 137 | where_predicates: vec![], |
122 | doc: None, | 138 | doc: None, |
@@ -139,6 +155,7 @@ impl FunctionSignature { | |||
139 | ret_type: None, | 155 | ret_type: None, |
140 | parameters: params, | 156 | parameters: params, |
141 | parameter_names: vec![], | 157 | parameter_names: vec![], |
158 | parameter_types: vec![], | ||
142 | generic_parameters: vec![], | 159 | generic_parameters: vec![], |
143 | where_predicates: vec![], | 160 | where_predicates: vec![], |
144 | doc: None, | 161 | doc: None, |
@@ -151,18 +168,27 @@ impl FunctionSignature { | |||
151 | 168 | ||
152 | impl From<&'_ ast::FnDef> for FunctionSignature { | 169 | impl From<&'_ ast::FnDef> for FunctionSignature { |
153 | fn from(node: &ast::FnDef) -> FunctionSignature { | 170 | fn from(node: &ast::FnDef) -> FunctionSignature { |
154 | fn param_list(node: &ast::FnDef) -> (bool, Vec<String>) { | 171 | fn param_list(node: &ast::FnDef) -> (bool, Vec<String>, Vec<String>) { |
155 | let mut res = vec![]; | 172 | let mut res = vec![]; |
173 | let mut res_types = vec![]; | ||
156 | let mut has_self_param = false; | 174 | let mut has_self_param = false; |
157 | if let Some(param_list) = node.param_list() { | 175 | if let Some(param_list) = node.param_list() { |
158 | if let Some(self_param) = param_list.self_param() { | 176 | if let Some(self_param) = param_list.self_param() { |
159 | has_self_param = true; | 177 | has_self_param = true; |
160 | res.push(self_param.syntax().text().to_string()) | 178 | let raw_param = self_param.syntax().text().to_string(); |
179 | |||
180 | res_types.push( | ||
181 | raw_param.split(':').nth(1).unwrap_or_else(|| " Self")[1..].to_string(), | ||
182 | ); | ||
183 | res.push(raw_param); | ||
161 | } | 184 | } |
162 | 185 | ||
163 | res.extend(param_list.params().map(|param| param.syntax().text().to_string())); | 186 | res.extend(param_list.params().map(|param| param.syntax().text().to_string())); |
187 | res_types.extend(param_list.params().map(|param| { | ||
188 | param.syntax().text().to_string().split(':').nth(1).unwrap()[1..].to_string() | ||
189 | })); | ||
164 | } | 190 | } |
165 | (has_self_param, res) | 191 | (has_self_param, res, res_types) |
166 | } | 192 | } |
167 | 193 | ||
168 | fn param_name_list(node: &ast::FnDef) -> Vec<String> { | 194 | fn param_name_list(node: &ast::FnDef) -> Vec<String> { |
@@ -192,7 +218,7 @@ impl From<&'_ ast::FnDef> for FunctionSignature { | |||
192 | res | 218 | res |
193 | } | 219 | } |
194 | 220 | ||
195 | let (has_self_param, parameters) = param_list(node); | 221 | let (has_self_param, parameters, parameter_types) = param_list(node); |
196 | 222 | ||
197 | FunctionSignature { | 223 | FunctionSignature { |
198 | kind: CallableKind::Function, | 224 | kind: CallableKind::Function, |
@@ -204,6 +230,7 @@ impl From<&'_ ast::FnDef> for FunctionSignature { | |||
204 | .map(|n| n.syntax().text().to_string()), | 230 | .map(|n| n.syntax().text().to_string()), |
205 | parameters, | 231 | parameters, |
206 | parameter_names: param_name_list(node), | 232 | parameter_names: param_name_list(node), |
233 | parameter_types, | ||
207 | generic_parameters: generic_parameters(node), | 234 | generic_parameters: generic_parameters(node), |
208 | where_predicates: where_predicates(node), | 235 | where_predicates: where_predicates(node), |
209 | // docs are processed separately | 236 | // docs are processed separately |
diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs index 5599f143f..ddaa30a16 100644 --- a/crates/ra_ide/src/lib.rs +++ b/crates/ra_ide/src/lib.rs | |||
@@ -67,7 +67,9 @@ use crate::display::ToNav; | |||
67 | pub use crate::{ | 67 | pub use crate::{ |
68 | assists::{Assist, AssistId}, | 68 | assists::{Assist, AssistId}, |
69 | call_hierarchy::CallItem, | 69 | call_hierarchy::CallItem, |
70 | completion::{CompletionConfig, CompletionItem, CompletionItemKind, InsertTextFormat}, | 70 | completion::{ |
71 | CompletionConfig, CompletionItem, CompletionItemKind, CompletionScore, InsertTextFormat, | ||
72 | }, | ||
71 | diagnostics::Severity, | 73 | diagnostics::Severity, |
72 | display::{file_structure, FunctionSignature, NavigationTarget, StructureNode}, | 74 | display::{file_structure, FunctionSignature, NavigationTarget, StructureNode}, |
73 | expand_macro::ExpandedMacro, | 75 | expand_macro::ExpandedMacro, |
@@ -127,6 +129,21 @@ pub struct CallInfo { | |||
127 | pub active_parameter: Option<usize>, | 129 | pub active_parameter: Option<usize>, |
128 | } | 130 | } |
129 | 131 | ||
132 | impl CallInfo { | ||
133 | pub fn active_parameter_type(&self) -> Option<String> { | ||
134 | if let Some(id) = self.active_parameter { | ||
135 | return self.signature.parameter_types.get(id).map(|param_ty| param_ty.clone()); | ||
136 | } | ||
137 | None | ||
138 | } | ||
139 | pub fn active_parameter_name(&self) -> Option<String> { | ||
140 | if let Some(id) = self.active_parameter { | ||
141 | return self.signature.parameter_names.get(id).map(|param_ty| param_ty.clone()); | ||
142 | } | ||
143 | None | ||
144 | } | ||
145 | } | ||
146 | |||
130 | /// `AnalysisHost` stores the current state of the world. | 147 | /// `AnalysisHost` stores the current state of the world. |
131 | #[derive(Debug)] | 148 | #[derive(Debug)] |
132 | pub struct AnalysisHost { | 149 | pub struct AnalysisHost { |
diff --git a/crates/rust-analyzer/src/conv.rs b/crates/rust-analyzer/src/conv.rs index 8d2360cc8..7e30956cc 100644 --- a/crates/rust-analyzer/src/conv.rs +++ b/crates/rust-analyzer/src/conv.rs | |||
@@ -9,10 +9,10 @@ use lsp_types::{ | |||
9 | TextDocumentPositionParams, Url, VersionedTextDocumentIdentifier, WorkspaceEdit, | 9 | TextDocumentPositionParams, Url, VersionedTextDocumentIdentifier, WorkspaceEdit, |
10 | }; | 10 | }; |
11 | use ra_ide::{ | 11 | use ra_ide::{ |
12 | translate_offset_with_edit, CompletionItem, CompletionItemKind, FileId, FilePosition, | 12 | translate_offset_with_edit, CompletionItem, CompletionItemKind, CompletionScore, FileId, |
13 | FileRange, FileSystemEdit, Fold, FoldKind, Highlight, HighlightModifier, HighlightTag, | 13 | FilePosition, FileRange, FileSystemEdit, Fold, FoldKind, Highlight, HighlightModifier, |
14 | InlayHint, InlayKind, InsertTextFormat, LineCol, LineIndex, NavigationTarget, RangeInfo, | 14 | HighlightTag, InlayHint, InlayKind, InsertTextFormat, LineCol, LineIndex, NavigationTarget, |
15 | ReferenceAccess, Severity, SourceChange, SourceFileEdit, | 15 | RangeInfo, ReferenceAccess, Severity, SourceChange, SourceFileEdit, |
16 | }; | 16 | }; |
17 | use ra_syntax::{SyntaxKind, TextRange, TextUnit}; | 17 | use ra_syntax::{SyntaxKind, TextRange, TextUnit}; |
18 | use ra_text_edit::{AtomTextEdit, TextEdit}; | 18 | use ra_text_edit::{AtomTextEdit, TextEdit}; |
@@ -114,10 +114,10 @@ impl Conv for Severity { | |||
114 | } | 114 | } |
115 | } | 115 | } |
116 | 116 | ||
117 | impl ConvWith<(&LineIndex, LineEndings)> for CompletionItem { | 117 | impl ConvWith<(&LineIndex, LineEndings, &mut usize)> for CompletionItem { |
118 | type Output = ::lsp_types::CompletionItem; | 118 | type Output = ::lsp_types::CompletionItem; |
119 | 119 | ||
120 | fn conv_with(self, ctx: (&LineIndex, LineEndings)) -> ::lsp_types::CompletionItem { | 120 | fn conv_with(self, ctx: (&LineIndex, LineEndings, &mut usize)) -> ::lsp_types::CompletionItem { |
121 | let mut additional_text_edits = Vec::new(); | 121 | let mut additional_text_edits = Vec::new(); |
122 | let mut text_edit = None; | 122 | let mut text_edit = None; |
123 | // LSP does not allow arbitrary edits in completion, so we have to do a | 123 | // LSP does not allow arbitrary edits in completion, so we have to do a |
@@ -125,7 +125,7 @@ impl ConvWith<(&LineIndex, LineEndings)> for CompletionItem { | |||
125 | for atom_edit in self.text_edit().as_atoms() { | 125 | for atom_edit in self.text_edit().as_atoms() { |
126 | if self.source_range().is_subrange(&atom_edit.delete) { | 126 | if self.source_range().is_subrange(&atom_edit.delete) { |
127 | text_edit = Some(if atom_edit.delete == self.source_range() { | 127 | text_edit = Some(if atom_edit.delete == self.source_range() { |
128 | atom_edit.conv_with(ctx) | 128 | atom_edit.conv_with((ctx.0, ctx.1)) |
129 | } else { | 129 | } else { |
130 | assert!(self.source_range().end() == atom_edit.delete.end()); | 130 | assert!(self.source_range().end() == atom_edit.delete.end()); |
131 | let range1 = | 131 | let range1 = |
@@ -133,12 +133,12 @@ impl ConvWith<(&LineIndex, LineEndings)> for CompletionItem { | |||
133 | let range2 = self.source_range(); | 133 | let range2 = self.source_range(); |
134 | let edit1 = AtomTextEdit::replace(range1, String::new()); | 134 | let edit1 = AtomTextEdit::replace(range1, String::new()); |
135 | let edit2 = AtomTextEdit::replace(range2, atom_edit.insert.clone()); | 135 | let edit2 = AtomTextEdit::replace(range2, atom_edit.insert.clone()); |
136 | additional_text_edits.push(edit1.conv_with(ctx)); | 136 | additional_text_edits.push(edit1.conv_with((ctx.0, ctx.1))); |
137 | edit2.conv_with(ctx) | 137 | edit2.conv_with((ctx.0, ctx.1)) |
138 | }) | 138 | }) |
139 | } else { | 139 | } else { |
140 | assert!(self.source_range().intersection(&atom_edit.delete).is_none()); | 140 | assert!(self.source_range().intersection(&atom_edit.delete).is_none()); |
141 | additional_text_edits.push(atom_edit.conv_with(ctx)); | 141 | additional_text_edits.push(atom_edit.conv_with((ctx.0, ctx.1))); |
142 | } | 142 | } |
143 | } | 143 | } |
144 | let text_edit = text_edit.unwrap(); | 144 | let text_edit = text_edit.unwrap(); |
@@ -165,6 +165,15 @@ impl ConvWith<(&LineIndex, LineEndings)> for CompletionItem { | |||
165 | ..Default::default() | 165 | ..Default::default() |
166 | }; | 166 | }; |
167 | 167 | ||
168 | if let Some(score) = self.score() { | ||
169 | match score { | ||
170 | CompletionScore::TypeAndNameMatch => res.preselect = Some(true), | ||
171 | CompletionScore::TypeMatch => {} | ||
172 | } | ||
173 | res.sort_text = Some(format!("{:02}", *ctx.2)); | ||
174 | *ctx.2 += 1; | ||
175 | } | ||
176 | |||
168 | if self.deprecated() { | 177 | if self.deprecated() { |
169 | res.tags = Some(vec![lsp_types::CompletionItemTag::Deprecated]) | 178 | res.tags = Some(vec![lsp_types::CompletionItemTag::Deprecated]) |
170 | } | 179 | } |
diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs index 41d9fe344..ee669f383 100644 --- a/crates/rust-analyzer/src/main_loop/handlers.rs +++ b/crates/rust-analyzer/src/main_loop/handlers.rs | |||
@@ -423,8 +423,11 @@ pub fn handle_completion( | |||
423 | }; | 423 | }; |
424 | let line_index = world.analysis().file_line_index(position.file_id)?; | 424 | let line_index = world.analysis().file_line_index(position.file_id)?; |
425 | let line_endings = world.file_line_endings(position.file_id); | 425 | let line_endings = world.file_line_endings(position.file_id); |
426 | let items: Vec<CompletionItem> = | 426 | let mut count_sort_text_item = 0usize; |
427 | items.into_iter().map(|item| item.conv_with((&line_index, line_endings))).collect(); | 427 | let items: Vec<CompletionItem> = items |
428 | .into_iter() | ||
429 | .map(|item| item.conv_with((&line_index, line_endings, &mut count_sort_text_item))) | ||
430 | .collect(); | ||
428 | 431 | ||
429 | Ok(Some(items.into())) | 432 | Ok(Some(items.into())) |
430 | } | 433 | } |