aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide/src/completion
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ide/src/completion')
-rw-r--r--crates/ra_ide/src/completion/complete_dot.rs114
-rw-r--r--crates/ra_ide/src/completion/completion_item.rs62
-rw-r--r--crates/ra_ide/src/completion/presentation.rs2
-rw-r--r--crates/ra_ide/src/completion/test_utils.rs11
4 files changed, 85 insertions, 104 deletions
diff --git a/crates/ra_ide/src/completion/complete_dot.rs b/crates/ra_ide/src/completion/complete_dot.rs
index 2e228b638..174b39964 100644
--- a/crates/ra_ide/src/completion/complete_dot.rs
+++ b/crates/ra_ide/src/completion/complete_dot.rs
@@ -6,12 +6,11 @@ use hir::{
6 Type, 6 Type,
7}; 7};
8 8
9use crate::completion::completion_item::CompletionKind;
10use crate::{ 9use crate::{
11 call_info::call_info, 10 call_info::call_info,
12 completion::{ 11 completion::{
13 completion_context::CompletionContext, 12 completion_context::CompletionContext,
14 completion_item::{Completions, SortOption}, 13 completion_item::{CompletionKind, Completions, ScoreOption},
15 }, 14 },
16 // CallInfo, 15 // CallInfo,
17 CompletionItem, 16 CompletionItem,
@@ -49,40 +48,12 @@ fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: &Ty
49 for receiver in receiver.autoderef(ctx.db) { 48 for receiver in receiver.autoderef(ctx.db) {
50 let fields = receiver.fields(ctx.db); 49 let fields = receiver.fields(ctx.db);
51 50
52 // If we use this implementation we can delete call_info in the CompletionContext
53 if let Some(record_field) = &ctx.record_field_syntax { 51 if let Some(record_field) = &ctx.record_field_syntax {
54 acc.with_sort_option(SortOption::RecordField(record_field.clone())); 52 acc.with_score_option(ScoreOption::RecordField(record_field.clone()));
55 } else if let Some(call_info) = call_info(ctx.db, ctx.file_position) { 53 } else if let Some(call_info) = call_info(ctx.db, ctx.file_position) {
56 acc.with_sort_option(SortOption::CallFn(call_info)); 54 acc.with_score_option(ScoreOption::CallFn(call_info));
57 } 55 }
58 56
59 // // For Call Fn
60 // if let Some(call_info) = &ctx.call_info {
61 // if let Some(active_parameter_type) = call_info.active_parameter_type() {
62 // let active_parameter_name = call_info.active_parameter_name().unwrap();
63 // fields.sort_by(|a, b| {
64 // // For the same type
65 // if active_parameter_type == a.1.display(ctx.db).to_string() {
66 // // If same type + same name then go top position
67 // if active_parameter_name == a.0.name(ctx.db).to_string() {
68 // Ordering::Less
69 // } else {
70 // if active_parameter_type == b.1.display(ctx.db).to_string() {
71 // Ordering::Equal
72 // } else {
73 // Ordering::Less
74 // }
75 // }
76 // } else {
77 // Ordering::Greater
78 // }
79 // });
80 // }
81 // }
82
83 // For Lit struct fields
84 // ---
85
86 for (field, ty) in fields { 57 for (field, ty) in fields {
87 if ctx.scope().module().map_or(false, |m| !field.is_visible_from(ctx.db, m)) { 58 if ctx.scope().module().map_or(false, |m| !field.is_visible_from(ctx.db, m)) {
88 // Skip private field. FIXME: If the definition location of the 59 // Skip private field. FIXME: If the definition location of the
@@ -116,20 +87,13 @@ fn complete_methods(acc: &mut Completions, ctx: &CompletionContext, receiver: &T
116 87
117#[cfg(test)] 88#[cfg(test)]
118mod tests { 89mod tests {
119 use crate::completion::{ 90 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind};
120 test_utils::{do_completion, do_completion_without_sort},
121 CompletionItem, CompletionKind,
122 };
123 use insta::assert_debug_snapshot; 91 use insta::assert_debug_snapshot;
124 92
125 fn do_ref_completion(code: &str) -> Vec<CompletionItem> { 93 fn do_ref_completion(code: &str) -> Vec<CompletionItem> {
126 do_completion(code, CompletionKind::Reference) 94 do_completion(code, CompletionKind::Reference)
127 } 95 }
128 96
129 fn do_ref_completion_without_sort(code: &str) -> Vec<CompletionItem> {
130 do_completion_without_sort(code, CompletionKind::Reference)
131 }
132
133 #[test] 97 #[test]
134 fn test_struct_field_completion() { 98 fn test_struct_field_completion() {
135 assert_debug_snapshot!( 99 assert_debug_snapshot!(
@@ -159,7 +123,7 @@ mod tests {
159 #[test] 123 #[test]
160 fn test_struct_field_completion_in_func_call() { 124 fn test_struct_field_completion_in_func_call() {
161 assert_debug_snapshot!( 125 assert_debug_snapshot!(
162 do_ref_completion_without_sort( 126 do_ref_completion(
163 r" 127 r"
164 struct A { another_field: i64, the_field: u32, my_string: String } 128 struct A { another_field: i64, the_field: u32, my_string: String }
165 fn test(my_param: u32) -> u32 { my_param } 129 fn test(my_param: u32) -> u32 { my_param }
@@ -171,14 +135,6 @@ mod tests {
171 @r###" 135 @r###"
172 [ 136 [
173 CompletionItem { 137 CompletionItem {
174 label: "the_field",
175 source_range: [201; 201),
176 delete: [201; 201),
177 insert: "the_field",
178 kind: Field,
179 detail: "u32",
180 },
181 CompletionItem {
182 label: "another_field", 138 label: "another_field",
183 source_range: [201; 201), 139 source_range: [201; 201),
184 delete: [201; 201), 140 delete: [201; 201),
@@ -194,6 +150,15 @@ mod tests {
194 kind: Field, 150 kind: Field,
195 detail: "{unknown}", 151 detail: "{unknown}",
196 }, 152 },
153 CompletionItem {
154 label: "the_field",
155 source_range: [201; 201),
156 delete: [201; 201),
157 insert: "the_field",
158 kind: Field,
159 detail: "u32",
160 score: TypeMatch,
161 },
197 ] 162 ]
198 "### 163 "###
199 ); 164 );
@@ -202,7 +167,7 @@ mod tests {
202 #[test] 167 #[test]
203 fn test_struct_field_completion_in_func_call_with_type_and_name() { 168 fn test_struct_field_completion_in_func_call_with_type_and_name() {
204 assert_debug_snapshot!( 169 assert_debug_snapshot!(
205 do_ref_completion_without_sort( 170 do_ref_completion(
206 r" 171 r"
207 struct A { another_field: i64, another_good_type: u32, the_field: u32 } 172 struct A { another_field: i64, another_good_type: u32, the_field: u32 }
208 fn test(the_field: u32) -> u32 { the_field } 173 fn test(the_field: u32) -> u32 { the_field }
@@ -214,12 +179,12 @@ mod tests {
214 @r###" 179 @r###"
215 [ 180 [
216 CompletionItem { 181 CompletionItem {
217 label: "the_field", 182 label: "another_field",
218 source_range: [208; 208), 183 source_range: [208; 208),
219 delete: [208; 208), 184 delete: [208; 208),
220 insert: "the_field", 185 insert: "another_field",
221 kind: Field, 186 kind: Field,
222 detail: "u32", 187 detail: "i64",
223 }, 188 },
224 CompletionItem { 189 CompletionItem {
225 label: "another_good_type", 190 label: "another_good_type",
@@ -228,14 +193,16 @@ mod tests {
228 insert: "another_good_type", 193 insert: "another_good_type",
229 kind: Field, 194 kind: Field,
230 detail: "u32", 195 detail: "u32",
196 score: TypeMatch,
231 }, 197 },
232 CompletionItem { 198 CompletionItem {
233 label: "another_field", 199 label: "the_field",
234 source_range: [208; 208), 200 source_range: [208; 208),
235 delete: [208; 208), 201 delete: [208; 208),
236 insert: "another_field", 202 insert: "the_field",
237 kind: Field, 203 kind: Field,
238 detail: "i64", 204 detail: "u32",
205 score: TypeAndNameMatch,
239 }, 206 },
240 ] 207 ]
241 "### 208 "###
@@ -245,7 +212,7 @@ mod tests {
245 #[test] 212 #[test]
246 fn test_struct_field_completion_in_record_lit() { 213 fn test_struct_field_completion_in_record_lit() {
247 assert_debug_snapshot!( 214 assert_debug_snapshot!(
248 do_ref_completion_without_sort( 215 do_ref_completion(
249 r" 216 r"
250 struct A { another_field: i64, another_good_type: u32, the_field: u32 } 217 struct A { another_field: i64, another_good_type: u32, the_field: u32 }
251 struct B { my_string: String, my_vec: Vec<u32>, the_field: u32 } 218 struct B { my_string: String, my_vec: Vec<u32>, the_field: u32 }
@@ -259,12 +226,12 @@ mod tests {
259 @r###" 226 @r###"
260 [ 227 [
261 CompletionItem { 228 CompletionItem {
262 label: "the_field", 229 label: "another_field",
263 source_range: [270; 270), 230 source_range: [270; 270),
264 delete: [270; 270), 231 delete: [270; 270),
265 insert: "the_field", 232 insert: "another_field",
266 kind: Field, 233 kind: Field,
267 detail: "u32", 234 detail: "i64",
268 }, 235 },
269 CompletionItem { 236 CompletionItem {
270 label: "another_good_type", 237 label: "another_good_type",
@@ -273,14 +240,16 @@ mod tests {
273 insert: "another_good_type", 240 insert: "another_good_type",
274 kind: Field, 241 kind: Field,
275 detail: "u32", 242 detail: "u32",
243 score: TypeMatch,
276 }, 244 },
277 CompletionItem { 245 CompletionItem {
278 label: "another_field", 246 label: "the_field",
279 source_range: [270; 270), 247 source_range: [270; 270),
280 delete: [270; 270), 248 delete: [270; 270),
281 insert: "another_field", 249 insert: "the_field",
282 kind: Field, 250 kind: Field,
283 detail: "i64", 251 detail: "u32",
252 score: TypeAndNameMatch,
284 }, 253 },
285 ] 254 ]
286 "### 255 "###
@@ -290,7 +259,7 @@ mod tests {
290 #[test] 259 #[test]
291 fn test_struct_field_completion_in_record_lit_and_fn_call() { 260 fn test_struct_field_completion_in_record_lit_and_fn_call() {
292 assert_debug_snapshot!( 261 assert_debug_snapshot!(
293 do_ref_completion_without_sort( 262 do_ref_completion(
294 r" 263 r"
295 struct A { another_field: i64, another_good_type: u32, the_field: u32 } 264 struct A { another_field: i64, another_good_type: u32, the_field: u32 }
296 struct B { my_string: String, my_vec: Vec<u32>, the_field: u32 } 265 struct B { my_string: String, my_vec: Vec<u32>, the_field: u32 }
@@ -311,6 +280,7 @@ mod tests {
311 insert: "another_field", 280 insert: "another_field",
312 kind: Field, 281 kind: Field,
313 detail: "i64", 282 detail: "i64",
283 score: TypeMatch,
314 }, 284 },
315 CompletionItem { 285 CompletionItem {
316 label: "another_good_type", 286 label: "another_good_type",
@@ -336,7 +306,7 @@ mod tests {
336 #[test] 306 #[test]
337 fn test_struct_field_completion_in_fn_call_and_record_lit() { 307 fn test_struct_field_completion_in_fn_call_and_record_lit() {
338 assert_debug_snapshot!( 308 assert_debug_snapshot!(
339 do_ref_completion_without_sort( 309 do_ref_completion(
340 r" 310 r"
341 struct A { another_field: i64, another_good_type: u32, the_field: u32 } 311 struct A { another_field: i64, another_good_type: u32, the_field: u32 }
342 struct B { my_string: String, my_vec: Vec<u32>, the_field: u32 } 312 struct B { my_string: String, my_vec: Vec<u32>, the_field: u32 }
@@ -351,12 +321,12 @@ mod tests {
351 @r###" 321 @r###"
352 [ 322 [
353 CompletionItem { 323 CompletionItem {
354 label: "the_field", 324 label: "another_field",
355 source_range: [328; 328), 325 source_range: [328; 328),
356 delete: [328; 328), 326 delete: [328; 328),
357 insert: "the_field", 327 insert: "another_field",
358 kind: Field, 328 kind: Field,
359 detail: "u32", 329 detail: "i64",
360 }, 330 },
361 CompletionItem { 331 CompletionItem {
362 label: "another_good_type", 332 label: "another_good_type",
@@ -365,14 +335,16 @@ mod tests {
365 insert: "another_good_type", 335 insert: "another_good_type",
366 kind: Field, 336 kind: Field,
367 detail: "u32", 337 detail: "u32",
338 score: TypeMatch,
368 }, 339 },
369 CompletionItem { 340 CompletionItem {
370 label: "another_field", 341 label: "the_field",
371 source_range: [328; 328), 342 source_range: [328; 328),
372 delete: [328; 328), 343 delete: [328; 328),
373 insert: "another_field", 344 insert: "the_field",
374 kind: Field, 345 kind: Field,
375 detail: "i64", 346 detail: "u32",
347 score: TypeAndNameMatch,
376 }, 348 },
377 ] 349 ]
378 "### 350 "###
diff --git a/crates/ra_ide/src/completion/completion_item.rs b/crates/ra_ide/src/completion/completion_item.rs
index c9c3fdc0e..84d51bafe 100644
--- a/crates/ra_ide/src/completion/completion_item.rs
+++ b/crates/ra_ide/src/completion/completion_item.rs
@@ -53,6 +53,9 @@ pub struct CompletionItem {
53 /// 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
54 /// after completion. 54 /// after completion.
55 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>,
56} 59}
57 60
58// 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.
@@ -82,6 +85,9 @@ impl fmt::Debug for CompletionItem {
82 if self.deprecated { 85 if self.deprecated {
83 s.field("deprecated", &true); 86 s.field("deprecated", &true);
84 } 87 }
88 if let Some(score) = &self.score {
89 s.field("score", score);
90 }
85 if self.trigger_call_info { 91 if self.trigger_call_info {
86 s.field("trigger_call_info", &true); 92 s.field("trigger_call_info", &true);
87 } 93 }
@@ -149,6 +155,7 @@ impl CompletionItem {
149 text_edit: None, 155 text_edit: None,
150 deprecated: None, 156 deprecated: None,
151 trigger_call_info: None, 157 trigger_call_info: None,
158 score: None,
152 } 159 }
153 } 160 }
154 /// What user sees in pop-up in the UI. 161 /// What user sees in pop-up in the UI.
@@ -188,6 +195,10 @@ impl CompletionItem {
188 self.deprecated 195 self.deprecated
189 } 196 }
190 197
198 pub fn score(&self) -> Option<CompletionScore> {
199 self.score.clone()
200 }
201
191 pub fn trigger_call_info(&self) -> bool { 202 pub fn trigger_call_info(&self) -> bool {
192 self.trigger_call_info 203 self.trigger_call_info
193 } 204 }
@@ -208,6 +219,7 @@ pub(crate) struct Builder {
208 text_edit: Option<TextEdit>, 219 text_edit: Option<TextEdit>,
209 deprecated: Option<bool>, 220 deprecated: Option<bool>,
210 trigger_call_info: Option<bool>, 221 trigger_call_info: Option<bool>,
222 score: Option<CompletionScore>,
211} 223}
212 224
213impl Builder { 225impl Builder {
@@ -237,6 +249,7 @@ impl Builder {
237 completion_kind: self.completion_kind, 249 completion_kind: self.completion_kind,
238 deprecated: self.deprecated.unwrap_or(false), 250 deprecated: self.deprecated.unwrap_or(false),
239 trigger_call_info: self.trigger_call_info.unwrap_or(false), 251 trigger_call_info: self.trigger_call_info.unwrap_or(false),
252 score: self.score,
240 } 253 }
241 } 254 }
242 pub(crate) fn lookup_by(mut self, lookup: impl Into<String>) -> Builder { 255 pub(crate) fn lookup_by(mut self, lookup: impl Into<String>) -> Builder {
@@ -287,6 +300,10 @@ impl Builder {
287 self.deprecated = Some(deprecated); 300 self.deprecated = Some(deprecated);
288 self 301 self
289 } 302 }
303 pub(crate) fn set_score(mut self, score: CompletionScore) -> Builder {
304 self.score = Some(score);
305 self
306 }
290 pub(crate) fn trigger_call_info(mut self) -> Builder { 307 pub(crate) fn trigger_call_info(mut self) -> Builder {
291 self.trigger_call_info = Some(true); 308 self.trigger_call_info = Some(true);
292 self 309 self
@@ -300,16 +317,22 @@ impl<'a> Into<CompletionItem> for Builder {
300} 317}
301 318
302#[derive(Debug)] 319#[derive(Debug)]
303pub(crate) enum SortOption { 320pub(crate) enum ScoreOption {
304 CallFn(CallInfo), 321 CallFn(CallInfo),
305 RecordField(RecordField), 322 RecordField(RecordField),
306} 323}
307 324
325#[derive(Debug, Clone)]
326pub enum CompletionScore {
327 TypeMatch,
328 TypeAndNameMatch,
329}
330
308/// Represents an in-progress set of completions being built. 331/// Represents an in-progress set of completions being built.
309#[derive(Debug, Default)] 332#[derive(Debug, Default)]
310pub(crate) struct Completions { 333pub(crate) struct Completions {
311 buf: Vec<CompletionItem>, 334 buf: Vec<CompletionItem>,
312 sort_option: Option<SortOption>, 335 score_option: Option<ScoreOption>,
313} 336}
314 337
315impl Completions { 338impl Completions {
@@ -324,17 +347,17 @@ impl Completions {
324 items.into_iter().for_each(|item| self.add(item.into())) 347 items.into_iter().for_each(|item| self.add(item.into()))
325 } 348 }
326 349
327 pub(crate) fn with_sort_option(&mut self, sort_option: SortOption) { 350 pub(crate) fn with_score_option(&mut self, score_option: ScoreOption) {
328 self.sort_option = Some(sort_option); 351 self.score_option = Some(score_option);
329 } 352 }
330 353
331 pub(crate) fn sort(&mut self, ctx: &CompletionContext) { 354 pub(crate) fn compute_score(&mut self, ctx: &CompletionContext) {
332 if self.sort_option.is_none() { 355 if self.score_option.is_none() {
333 return; 356 return;
334 } 357 }
335 358
336 let (active_name, active_type) = match self.sort_option.as_ref().unwrap() { 359 let (active_name, active_type) = match self.score_option.as_ref().unwrap() {
337 SortOption::CallFn(call_info) => { 360 ScoreOption::CallFn(call_info) => {
338 if call_info.active_parameter_type().is_none() 361 if call_info.active_parameter_type().is_none()
339 || call_info.active_parameter_name().is_none() 362 || call_info.active_parameter_name().is_none()
340 { 363 {
@@ -345,7 +368,7 @@ impl Completions {
345 call_info.active_parameter_type().unwrap(), 368 call_info.active_parameter_type().unwrap(),
346 ) 369 )
347 } 370 }
348 SortOption::RecordField(record_field) => { 371 ScoreOption::RecordField(record_field) => {
349 if let Some((struct_field, _)) = ctx.sema.resolve_record_field(record_field) { 372 if let Some((struct_field, _)) = ctx.sema.resolve_record_field(record_field) {
350 ( 373 (
351 struct_field.name(ctx.db).to_string(), 374 struct_field.name(ctx.db).to_string(),
@@ -357,26 +380,19 @@ impl Completions {
357 } 380 }
358 }; 381 };
359 382
360 self.buf.sort_by(|a, b| { 383 for completion_item in &mut self.buf {
361 // For the same type 384 // For the same type
362 if let Some(a_parameter_type) = &a.detail { 385 if let Some(a_parameter_type) = &completion_item.detail {
363 if &active_type == a_parameter_type { 386 if &active_type == a_parameter_type {
364 // If same type + same name then go top position 387 // If same type + same name then go top position
365 if active_name != a.label { 388 if active_name == completion_item.label {
366 if let Some(b_parameter_type) = &b.detail { 389 completion_item.score = Some(CompletionScore::TypeAndNameMatch);
367 if &active_type == b_parameter_type { 390 } else {
368 return Ordering::Equal; 391 completion_item.score = Some(CompletionScore::TypeMatch);
369 }
370 }
371 } 392 }
372 Ordering::Less
373 } else {
374 Ordering::Greater
375 } 393 }
376 } else {
377 Ordering::Greater
378 } 394 }
379 }); 395 }
380 } 396 }
381} 397}
382 398
diff --git a/crates/ra_ide/src/completion/presentation.rs b/crates/ra_ide/src/completion/presentation.rs
index 8be2d02d0..55f75b15a 100644
--- a/crates/ra_ide/src/completion/presentation.rs
+++ b/crates/ra_ide/src/completion/presentation.rs
@@ -367,7 +367,7 @@ mod tests {
367 ra_fixture: &str, 367 ra_fixture: &str,
368 options: CompletionConfig, 368 options: CompletionConfig,
369 ) -> Vec<CompletionItem> { 369 ) -> Vec<CompletionItem> {
370 do_completion_with_options(ra_fixture, CompletionKind::Reference, &options, true) 370 do_completion_with_options(ra_fixture, CompletionKind::Reference, &options)
371 } 371 }
372 372
373 #[test] 373 #[test]
diff --git a/crates/ra_ide/src/completion/test_utils.rs b/crates/ra_ide/src/completion/test_utils.rs
index f54d15a90..eb90b5279 100644
--- a/crates/ra_ide/src/completion/test_utils.rs
+++ b/crates/ra_ide/src/completion/test_utils.rs
@@ -7,18 +7,13 @@ use crate::{
7}; 7};
8 8
9pub(crate) fn do_completion(code: &str, kind: CompletionKind) -> Vec<CompletionItem> { 9pub(crate) fn do_completion(code: &str, kind: CompletionKind) -> Vec<CompletionItem> {
10 do_completion_with_options(code, kind, &CompletionConfig::default(), true) 10 do_completion_with_options(code, kind, &CompletionConfig::default())
11}
12
13pub(crate) fn do_completion_without_sort(code: &str, kind: CompletionKind) -> Vec<CompletionItem> {
14 do_completion_with_options(code, kind, &CompletionConfig::default(), false)
15} 11}
16 12
17pub(crate) fn do_completion_with_options( 13pub(crate) fn do_completion_with_options(
18 code: &str, 14 code: &str,
19 kind: CompletionKind, 15 kind: CompletionKind,
20 options: &CompletionConfig, 16 options: &CompletionConfig,
21 sort_by_key: bool,
22) -> Vec<CompletionItem> { 17) -> Vec<CompletionItem> {
23 let (analysis, position) = if code.contains("//-") { 18 let (analysis, position) = if code.contains("//-") {
24 analysis_and_position(code) 19 analysis_and_position(code)
@@ -29,8 +24,6 @@ pub(crate) fn do_completion_with_options(
29 let completion_items: Vec<CompletionItem> = completions.into(); 24 let completion_items: Vec<CompletionItem> = completions.into();
30 let mut kind_completions: Vec<CompletionItem> = 25 let mut kind_completions: Vec<CompletionItem> =
31 completion_items.into_iter().filter(|c| c.completion_kind == kind).collect(); 26 completion_items.into_iter().filter(|c| c.completion_kind == kind).collect();
32 if sort_by_key { 27 kind_completions.sort_by_key(|c| c.label().to_owned());
33 kind_completions.sort_by_key(|c| c.label().to_owned());
34 }
35 kind_completions 28 kind_completions
36} 29}