diff options
Diffstat (limited to 'crates/ra_ide/src/completion/completion_item.rs')
-rw-r--r-- | crates/ra_ide/src/completion/completion_item.rs | 62 |
1 files changed, 39 insertions, 23 deletions
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 | ||
213 | impl Builder { | 225 | impl 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)] |
303 | pub(crate) enum SortOption { | 320 | pub(crate) enum ScoreOption { |
304 | CallFn(CallInfo), | 321 | CallFn(CallInfo), |
305 | RecordField(RecordField), | 322 | RecordField(RecordField), |
306 | } | 323 | } |
307 | 324 | ||
325 | #[derive(Debug, Clone)] | ||
326 | pub 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)] |
310 | pub(crate) struct Completions { | 333 | pub(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 | ||
315 | impl Completions { | 338 | impl 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 | ||