aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ra_ide/src/completion.rs2
-rw-r--r--crates/ra_ide/src/completion/complete_dot.rs239
-rw-r--r--crates/ra_ide/src/completion/completion_context.rs18
-rw-r--r--crates/ra_ide/src/completion/completion_item.rs72
-rw-r--r--crates/ra_ide/src/completion/presentation.rs1
-rw-r--r--crates/ra_ide/src/display/function_signature.rs70
-rw-r--r--crates/ra_ide/src/lib.rs19
-rw-r--r--crates/rust-analyzer/src/conv.rs28
-rw-r--r--crates/rust-analyzer/src/main_loop/handlers.rs7
-rw-r--r--xtask/tests/tidy-tests/main.rs6
10 files changed, 420 insertions, 42 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..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
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 },
10 // CallInfo,
8 CompletionItem, 11 CompletionItem,
9}; 12};
10use rustc_hash::FxHashSet; 13use 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).
13pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) { 17pub(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..dd9cc7daa 100644
--- a/crates/ra_ide/src/completion/completion_context.rs
+++ b/crates/ra_ide/src/completion/completion_context.rs
@@ -11,7 +11,7 @@ use ra_syntax::{
11}; 11};
12use ra_text_edit::AtomTextEdit; 12use ra_text_edit::AtomTextEdit;
13 13
14use crate::{completion::CompletionConfig, FilePosition}; 14use crate::{call_info::call_info, completion::CompletionConfig, CallInfo, FilePosition};
15 15
16/// `CompletionContext` is created early during completion to figure out, where 16/// `CompletionContext` is created early during completion to figure out, where
17/// exactly is the cursor, syntax-wise. 17/// exactly is the cursor, syntax-wise.
@@ -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,7 +32,9 @@ 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>,
37 pub(super) call_info: Option<CallInfo>,
35 pub(super) is_param: bool, 38 pub(super) is_param: bool,
36 /// If a name-binding or reference to a const in a pattern. 39 /// If a name-binding or reference to a const in a pattern.
37 /// Irrefutable patterns (like let) are excluded. 40 /// Irrefutable patterns (like let) are excluded.
@@ -88,12 +91,15 @@ impl<'a> CompletionContext<'a> {
88 original_token, 91 original_token,
89 token, 92 token,
90 offset: position.offset, 93 offset: position.offset,
94 file_position: position,
91 krate, 95 krate,
92 name_ref_syntax: None, 96 name_ref_syntax: None,
93 function_syntax: None, 97 function_syntax: None,
98 call_info: None,
94 use_item_syntax: None, 99 use_item_syntax: None,
95 record_lit_syntax: None, 100 record_lit_syntax: None,
96 record_pat_syntax: None, 101 record_pat_syntax: None,
102 record_field_syntax: None,
97 impl_def: None, 103 impl_def: None,
98 is_param: false, 104 is_param: false,
99 is_pat_binding_or_const: false, 105 is_pat_binding_or_const: false,
@@ -262,12 +268,22 @@ impl<'a> CompletionContext<'a> {
262 self.use_item_syntax = 268 self.use_item_syntax =
263 self.sema.ancestors_with_macros(self.token.parent()).find_map(ast::UseItem::cast); 269 self.sema.ancestors_with_macros(self.token.parent()).find_map(ast::UseItem::cast);
264 270
271 self.call_info = call_info(self.db, self.file_position);
272
265 self.function_syntax = self 273 self.function_syntax = self
266 .sema 274 .sema
267 .ancestors_with_macros(self.token.parent()) 275 .ancestors_with_macros(self.token.parent())
268 .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE) 276 .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE)
269 .find_map(ast::FnDef::cast); 277 .find_map(ast::FnDef::cast);
270 278
279 self.record_field_syntax = self
280 .sema
281 .ancestors_with_macros(self.token.parent())
282 .take_while(|it| {
283 it.kind() != SOURCE_FILE && it.kind() != MODULE && it.kind() != CALL_EXPR
284 })
285 .find_map(ast::RecordField::cast);
286
271 let parent = match name_ref.syntax().parent() { 287 let parent = match name_ref.syntax().parent() {
272 Some(it) => it, 288 Some(it) => it,
273 None => return, 289 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
3use std::fmt; 3use std::fmt;
4 4
5use hir::Documentation; 5use super::completion_context::CompletionContext;
6use crate::call_info::call_info;
7use hir::{Documentation, HirDisplay};
6use ra_syntax::TextRange; 8use ra_syntax::TextRange;
7use ra_text_edit::TextEdit; 9use 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
211impl Builder { 229impl 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)]
365pub 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)]
302pub(crate) struct Completions { 372pub(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
diff --git a/crates/ra_ide/src/display/function_signature.rs b/crates/ra_ide/src/display/function_signature.rs
index b967a6816..2d175882b 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 // The unwrap_or_else is 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,28 @@ 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 // FIXME: better solution ?
181 res_types.push(
182 raw_param.split(':').nth(1).unwrap_or_else(|| " Self")[1..].to_string(),
183 );
184 res.push(raw_param);
161 } 185 }
162 186
163 res.extend(param_list.params().map(|param| param.syntax().text().to_string())); 187 res.extend(param_list.params().map(|param| param.syntax().text().to_string()));
188 res_types.extend(param_list.params().map(|param| {
189 param.syntax().text().to_string().split(':').nth(1).unwrap()[1..].to_string()
190 }));
164 } 191 }
165 (has_self_param, res) 192 (has_self_param, res, res_types)
166 } 193 }
167 194
168 fn param_name_list(node: &ast::FnDef) -> Vec<String> { 195 fn param_name_list(node: &ast::FnDef) -> Vec<String> {
@@ -192,7 +219,7 @@ impl From<&'_ ast::FnDef> for FunctionSignature {
192 res 219 res
193 } 220 }
194 221
195 let (has_self_param, parameters) = param_list(node); 222 let (has_self_param, parameters, parameter_types) = param_list(node);
196 223
197 FunctionSignature { 224 FunctionSignature {
198 kind: CallableKind::Function, 225 kind: CallableKind::Function,
@@ -204,6 +231,7 @@ impl From<&'_ ast::FnDef> for FunctionSignature {
204 .map(|n| n.syntax().text().to_string()), 231 .map(|n| n.syntax().text().to_string()),
205 parameters, 232 parameters,
206 parameter_names: param_name_list(node), 233 parameter_names: param_name_list(node),
234 parameter_types,
207 generic_parameters: generic_parameters(node), 235 generic_parameters: generic_parameters(node),
208 where_predicates: where_predicates(node), 236 where_predicates: where_predicates(node),
209 // docs are processed separately 237 // 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 b2b1cb625..f47d931fd 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};
@@ -112,10 +112,10 @@ impl Conv for Severity {
112 } 112 }
113} 113}
114 114
115impl ConvWith<(&LineIndex, LineEndings)> for CompletionItem { 115impl ConvWith<(&LineIndex, LineEndings, usize)> for CompletionItem {
116 type Output = ::lsp_types::CompletionItem; 116 type Output = ::lsp_types::CompletionItem;
117 117
118 fn conv_with(self, ctx: (&LineIndex, LineEndings)) -> ::lsp_types::CompletionItem { 118 fn conv_with(self, ctx: (&LineIndex, LineEndings, usize)) -> ::lsp_types::CompletionItem {
119 let mut additional_text_edits = Vec::new(); 119 let mut additional_text_edits = Vec::new();
120 let mut text_edit = None; 120 let mut text_edit = None;
121 // LSP does not allow arbitrary edits in completion, so we have to do a 121 // LSP does not allow arbitrary edits in completion, so we have to do a
@@ -123,7 +123,7 @@ impl ConvWith<(&LineIndex, LineEndings)> for CompletionItem {
123 for atom_edit in self.text_edit().as_atoms() { 123 for atom_edit in self.text_edit().as_atoms() {
124 if self.source_range().is_subrange(&atom_edit.delete) { 124 if self.source_range().is_subrange(&atom_edit.delete) {
125 text_edit = Some(if atom_edit.delete == self.source_range() { 125 text_edit = Some(if atom_edit.delete == self.source_range() {
126 atom_edit.conv_with(ctx) 126 atom_edit.conv_with((ctx.0, ctx.1))
127 } else { 127 } else {
128 assert!(self.source_range().end() == atom_edit.delete.end()); 128 assert!(self.source_range().end() == atom_edit.delete.end());
129 let range1 = 129 let range1 =
@@ -131,12 +131,12 @@ impl ConvWith<(&LineIndex, LineEndings)> for CompletionItem {
131 let range2 = self.source_range(); 131 let range2 = self.source_range();
132 let edit1 = AtomTextEdit::replace(range1, String::new()); 132 let edit1 = AtomTextEdit::replace(range1, String::new());
133 let edit2 = AtomTextEdit::replace(range2, atom_edit.insert.clone()); 133 let edit2 = AtomTextEdit::replace(range2, atom_edit.insert.clone());
134 additional_text_edits.push(edit1.conv_with(ctx)); 134 additional_text_edits.push(edit1.conv_with((ctx.0, ctx.1)));
135 edit2.conv_with(ctx) 135 edit2.conv_with((ctx.0, ctx.1))
136 }) 136 })
137 } else { 137 } else {
138 assert!(self.source_range().intersection(&atom_edit.delete).is_none()); 138 assert!(self.source_range().intersection(&atom_edit.delete).is_none());
139 additional_text_edits.push(atom_edit.conv_with(ctx)); 139 additional_text_edits.push(atom_edit.conv_with((ctx.0, ctx.1)));
140 } 140 }
141 } 141 }
142 let text_edit = text_edit.unwrap(); 142 let text_edit = text_edit.unwrap();
@@ -163,6 +163,14 @@ impl ConvWith<(&LineIndex, LineEndings)> for CompletionItem {
163 ..Default::default() 163 ..Default::default()
164 }; 164 };
165 165
166 if let Some(score) = self.score() {
167 match score {
168 CompletionScore::TypeAndNameMatch => res.preselect = Some(true),
169 CompletionScore::TypeMatch => {}
170 }
171 res.sort_text = Some(format!("{:02}", ctx.2));
172 }
173
166 if self.deprecated() { 174 if self.deprecated() {
167 res.tags = Some(vec![lsp_types::CompletionItemTag::Deprecated]) 175 res.tags = Some(vec![lsp_types::CompletionItemTag::Deprecated])
168 } 176 }
diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs
index b207f0764..d18d2de34 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 items: Vec<CompletionItem> = items
427 items.into_iter().map(|item| item.conv_with((&line_index, line_endings))).collect(); 427 .into_iter()
428 .enumerate()
429 .map(|(idx, item)| item.conv_with((&line_index, line_endings, idx)))
430 .collect();
428 431
429 Ok(Some(items.into())) 432 Ok(Some(items.into()))
430} 433}
diff --git a/xtask/tests/tidy-tests/main.rs b/xtask/tests/tidy-tests/main.rs
index 101ae19bd..ead642acc 100644
--- a/xtask/tests/tidy-tests/main.rs
+++ b/xtask/tests/tidy-tests/main.rs
@@ -35,7 +35,7 @@ fn check_todo(path: &Path, text: &str) {
35 } 35 }
36 if text.contains("TODO") || text.contains("TOOD") || text.contains("todo!") { 36 if text.contains("TODO") || text.contains("TOOD") || text.contains("todo!") {
37 panic!( 37 panic!(
38 "\nTODO markers should not be committed to the master branch,\n\ 38 "\nTODO markers or todo! macros should not be committed to the master branch,\n\
39 use FIXME instead\n\ 39 use FIXME instead\n\
40 {}\n", 40 {}\n",
41 path.display(), 41 path.display(),
@@ -47,9 +47,9 @@ fn check_trailing_ws(path: &Path, text: &str) {
47 if is_exclude_dir(path, &["test_data"]) { 47 if is_exclude_dir(path, &["test_data"]) {
48 return; 48 return;
49 } 49 }
50 for line in text.lines() { 50 for (line_number, line) in text.lines().enumerate() {
51 if line.chars().last().map(char::is_whitespace) == Some(true) { 51 if line.chars().last().map(char::is_whitespace) == Some(true) {
52 panic!("Trailing whitespace in {}", path.display()) 52 panic!("Trailing whitespace in {} at line {}", path.display(), line_number)
53 } 53 }
54 } 54 }
55} 55}