aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2020-04-23 22:48:45 +0100
committerGitHub <[email protected]>2020-04-23 22:48:45 +0100
commite833e037834102c829dadeff0069b9219efc047b (patch)
tree9f264b1ddd2811da0a3a0a6fec595b7b50f94284 /crates
parent01f1f10feccb3f4bc25d5c39b60c0d4a59e17b52 (diff)
parentda6b136ea5b0e37d3dae9b78f3dbff2f18a9e5ea (diff)
Merge #3954
3954: Improve autocompletion by looking on the type and name r=matklad a=bnjjj This tweet (https://twitter.com/tjholowaychuk/status/1248918374731714560) gaves me the idea to implement that in rust-analyzer. Basically for this first example I made some examples when we are in a function call definition. I look on the parameter list to prioritize autocompletions for the same types and if it's the same type + the same name then it's displayed first in the completion list. So here is a draft, first step to open a discussion and know what you think about the implementation. It works (cf tests) but maybe I can make a better implementation at some places. Be careful the code needs some refactoring to be better and concise. PS: It was lot of fun writing this haha Co-authored-by: Benjamin Coenen <[email protected]>
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_ide/src/completion.rs2
-rw-r--r--crates/ra_ide/src/completion/complete_dot.rs237
-rw-r--r--crates/ra_ide/src/completion/completion_context.rs12
-rw-r--r--crates/ra_ide/src/completion/completion_item.rs30
-rw-r--r--crates/ra_ide/src/completion/presentation.rs47
-rw-r--r--crates/ra_ide/src/display/function_signature.rs69
-rw-r--r--crates/ra_ide/src/lib.rs19
-rw-r--r--crates/rust-analyzer/src/conv.rs29
-rw-r--r--crates/rust-analyzer/src/main_loop/handlers.rs7
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
31pub use crate::completion::completion_item::{ 31pub 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
3use hir::{HasVisibility, Type}; 3use hir::{HasVisibility, Type};
4 4
5use crate::completion::completion_item::CompletionKind;
6use crate::{ 5use 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};
10use rustc_hash::FxHashSet; 12use 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
211impl Builder { 227impl 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)]
323pub 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)]
302pub(crate) struct Completions { 332pub(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;
6use test_utils::tested_by; 6use test_utils::tested_by;
7 7
8use crate::{ 8use 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
17impl Completions { 18impl 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
308pub(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
303enum Params { 344enum 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
152impl From<&'_ ast::FnDef> for FunctionSignature { 169impl 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;
67pub use crate::{ 67pub 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
132impl 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)]
132pub struct AnalysisHost { 149pub 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};
11use ra_ide::{ 11use 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};
17use ra_syntax::{SyntaxKind, TextRange, TextUnit}; 17use ra_syntax::{SyntaxKind, TextRange, TextUnit};
18use ra_text_edit::{AtomTextEdit, TextEdit}; 18use ra_text_edit::{AtomTextEdit, TextEdit};
@@ -114,10 +114,10 @@ impl Conv for Severity {
114 } 114 }
115} 115}
116 116
117impl ConvWith<(&LineIndex, LineEndings)> for CompletionItem { 117impl 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}