aboutsummaryrefslogtreecommitdiff
path: root/crates/ide_completion/src/item.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide_completion/src/item.rs')
-rw-r--r--crates/ide_completion/src/item.rs188
1 files changed, 153 insertions, 35 deletions
diff --git a/crates/ide_completion/src/item.rs b/crates/ide_completion/src/item.rs
index 14afec603..9a4b5217a 100644
--- a/crates/ide_completion/src/item.rs
+++ b/crates/ide_completion/src/item.rs
@@ -70,7 +70,7 @@ pub struct CompletionItem {
70 /// Note that Relevance ignores fuzzy match score. We compute Relevance for 70 /// Note that Relevance ignores fuzzy match score. We compute Relevance for
71 /// all possible items, and then separately build an ordered completion list 71 /// all possible items, and then separately build an ordered completion list
72 /// based on relevance and fuzzy matching with the already typed identifier. 72 /// based on relevance and fuzzy matching with the already typed identifier.
73 relevance: Relevance, 73 relevance: CompletionRelevance,
74 74
75 /// Indicates that a reference or mutable reference to this variable is a 75 /// Indicates that a reference or mutable reference to this variable is a
76 /// possible match. 76 /// possible match.
@@ -107,9 +107,11 @@ impl fmt::Debug for CompletionItem {
107 if self.deprecated { 107 if self.deprecated {
108 s.field("deprecated", &true); 108 s.field("deprecated", &true);
109 } 109 }
110 if self.relevance.is_relevant() { 110
111 if self.relevance != CompletionRelevance::default() {
111 s.field("relevance", &self.relevance); 112 s.field("relevance", &self.relevance);
112 } 113 }
114
113 if let Some(mutability) = &self.ref_match { 115 if let Some(mutability) = &self.ref_match {
114 s.field("ref_match", &format!("&{}", mutability.as_keyword_for_ref())); 116 s.field("ref_match", &format!("&{}", mutability.as_keyword_for_ref()));
115 } 117 }
@@ -120,16 +122,8 @@ impl fmt::Debug for CompletionItem {
120 } 122 }
121} 123}
122 124
123#[derive(Debug, Clone, Copy, Ord, PartialOrd, Eq, PartialEq)]
124pub enum CompletionScore {
125 /// If only type match
126 TypeMatch,
127 /// If type and name match
128 TypeAndNameMatch,
129}
130
131#[derive(Debug, Clone, Copy, Ord, PartialOrd, Eq, PartialEq, Default)] 125#[derive(Debug, Clone, Copy, Ord, PartialOrd, Eq, PartialEq, Default)]
132pub struct Relevance { 126pub struct CompletionRelevance {
133 /// This is set in cases like these: 127 /// This is set in cases like these:
134 /// 128 ///
135 /// ``` 129 /// ```
@@ -150,11 +144,54 @@ pub struct Relevance {
150 /// } 144 /// }
151 /// ``` 145 /// ```
152 pub exact_type_match: bool, 146 pub exact_type_match: bool,
147 /// This is set in cases like these:
148 ///
149 /// ```
150 /// fn foo(bar: u32) {
151 /// $0 // `bar` is local
152 /// }
153 /// ```
154 ///
155 /// ```
156 /// fn foo() {
157 /// let bar = 0;
158 /// $0 // `bar` is local
159 /// }
160 /// ```
161 pub is_local: bool,
153} 162}
154 163
155impl Relevance { 164impl CompletionRelevance {
165 /// Provides a relevance score. Higher values are more relevant.
166 ///
167 /// The absolute value of the relevance score is not meaningful, for
168 /// example a value of 0 doesn't mean "not relevant", rather
169 /// it means "least relevant". The score value should only be used
170 /// for relative ordering.
171 ///
172 /// See is_relevant if you need to make some judgement about score
173 /// in an absolute sense.
174 pub fn score(&self) -> u32 {
175 let mut score = 0;
176
177 if self.exact_name_match {
178 score += 1;
179 }
180 if self.exact_type_match {
181 score += 3;
182 }
183 if self.is_local {
184 score += 1;
185 }
186
187 score
188 }
189
190 /// Returns true when the score for this threshold is above
191 /// some threshold such that we think it is especially likely
192 /// to be relevant.
156 pub fn is_relevant(&self) -> bool { 193 pub fn is_relevant(&self) -> bool {
157 self != &Relevance::default() 194 self.score() > 0
158 } 195 }
159} 196}
160 197
@@ -249,7 +286,7 @@ impl CompletionItem {
249 text_edit: None, 286 text_edit: None,
250 deprecated: false, 287 deprecated: false,
251 trigger_call_info: None, 288 trigger_call_info: None,
252 relevance: Relevance::default(), 289 relevance: CompletionRelevance::default(),
253 ref_match: None, 290 ref_match: None,
254 import_to_add: None, 291 import_to_add: None,
255 } 292 }
@@ -292,7 +329,7 @@ impl CompletionItem {
292 self.deprecated 329 self.deprecated
293 } 330 }
294 331
295 pub fn relevance(&self) -> Relevance { 332 pub fn relevance(&self) -> CompletionRelevance {
296 self.relevance 333 self.relevance
297 } 334 }
298 335
@@ -300,8 +337,14 @@ impl CompletionItem {
300 self.trigger_call_info 337 self.trigger_call_info
301 } 338 }
302 339
303 pub fn ref_match(&self) -> Option<Mutability> { 340 pub fn ref_match(&self) -> Option<(Mutability, CompletionRelevance)> {
304 self.ref_match 341 // Relevance of the ref match should be the same as the original
342 // match, but with exact type match set because self.ref_match
343 // is only set if there is an exact type match.
344 let mut relevance = self.relevance;
345 relevance.exact_type_match = true;
346
347 self.ref_match.map(|mutability| (mutability, relevance))
305 } 348 }
306 349
307 pub fn import_to_add(&self) -> Option<&ImportEdit> { 350 pub fn import_to_add(&self) -> Option<&ImportEdit> {
@@ -349,7 +392,7 @@ pub(crate) struct Builder {
349 text_edit: Option<TextEdit>, 392 text_edit: Option<TextEdit>,
350 deprecated: bool, 393 deprecated: bool,
351 trigger_call_info: Option<bool>, 394 trigger_call_info: Option<bool>,
352 relevance: Relevance, 395 relevance: CompletionRelevance,
353 ref_match: Option<Mutability>, 396 ref_match: Option<Mutability>,
354} 397}
355 398
@@ -401,42 +444,42 @@ impl Builder {
401 import_to_add: self.import_to_add, 444 import_to_add: self.import_to_add,
402 } 445 }
403 } 446 }
404 pub(crate) fn lookup_by(mut self, lookup: impl Into<String>) -> Builder { 447 pub(crate) fn lookup_by(&mut self, lookup: impl Into<String>) -> &mut Builder {
405 self.lookup = Some(lookup.into()); 448 self.lookup = Some(lookup.into());
406 self 449 self
407 } 450 }
408 pub(crate) fn label(mut self, label: impl Into<String>) -> Builder { 451 pub(crate) fn label(&mut self, label: impl Into<String>) -> &mut Builder {
409 self.label = label.into(); 452 self.label = label.into();
410 self 453 self
411 } 454 }
412 pub(crate) fn insert_text(mut self, insert_text: impl Into<String>) -> Builder { 455 pub(crate) fn insert_text(&mut self, insert_text: impl Into<String>) -> &mut Builder {
413 self.insert_text = Some(insert_text.into()); 456 self.insert_text = Some(insert_text.into());
414 self 457 self
415 } 458 }
416 pub(crate) fn insert_snippet( 459 pub(crate) fn insert_snippet(
417 mut self, 460 &mut self,
418 _cap: SnippetCap, 461 _cap: SnippetCap,
419 snippet: impl Into<String>, 462 snippet: impl Into<String>,
420 ) -> Builder { 463 ) -> &mut Builder {
421 self.insert_text_format = InsertTextFormat::Snippet; 464 self.insert_text_format = InsertTextFormat::Snippet;
422 self.insert_text(snippet) 465 self.insert_text(snippet)
423 } 466 }
424 pub(crate) fn kind(mut self, kind: impl Into<CompletionItemKind>) -> Builder { 467 pub(crate) fn kind(&mut self, kind: impl Into<CompletionItemKind>) -> &mut Builder {
425 self.kind = Some(kind.into()); 468 self.kind = Some(kind.into());
426 self 469 self
427 } 470 }
428 pub(crate) fn text_edit(mut self, edit: TextEdit) -> Builder { 471 pub(crate) fn text_edit(&mut self, edit: TextEdit) -> &mut Builder {
429 self.text_edit = Some(edit); 472 self.text_edit = Some(edit);
430 self 473 self
431 } 474 }
432 pub(crate) fn snippet_edit(mut self, _cap: SnippetCap, edit: TextEdit) -> Builder { 475 pub(crate) fn snippet_edit(&mut self, _cap: SnippetCap, edit: TextEdit) -> &mut Builder {
433 self.insert_text_format = InsertTextFormat::Snippet; 476 self.insert_text_format = InsertTextFormat::Snippet;
434 self.text_edit(edit) 477 self.text_edit(edit)
435 } 478 }
436 pub(crate) fn detail(self, detail: impl Into<String>) -> Builder { 479 pub(crate) fn detail(&mut self, detail: impl Into<String>) -> &mut Builder {
437 self.set_detail(Some(detail)) 480 self.set_detail(Some(detail))
438 } 481 }
439 pub(crate) fn set_detail(mut self, detail: Option<impl Into<String>>) -> Builder { 482 pub(crate) fn set_detail(&mut self, detail: Option<impl Into<String>>) -> &mut Builder {
440 self.detail = detail.map(Into::into); 483 self.detail = detail.map(Into::into);
441 if let Some(detail) = &self.detail { 484 if let Some(detail) = &self.detail {
442 if never!(detail.contains('\n'), "multiline detail:\n{}", detail) { 485 if never!(detail.contains('\n'), "multiline detail:\n{}", detail) {
@@ -446,31 +489,106 @@ impl Builder {
446 self 489 self
447 } 490 }
448 #[allow(unused)] 491 #[allow(unused)]
449 pub(crate) fn documentation(self, docs: Documentation) -> Builder { 492 pub(crate) fn documentation(&mut self, docs: Documentation) -> &mut Builder {
450 self.set_documentation(Some(docs)) 493 self.set_documentation(Some(docs))
451 } 494 }
452 pub(crate) fn set_documentation(mut self, docs: Option<Documentation>) -> Builder { 495 pub(crate) fn set_documentation(&mut self, docs: Option<Documentation>) -> &mut Builder {
453 self.documentation = docs.map(Into::into); 496 self.documentation = docs.map(Into::into);
454 self 497 self
455 } 498 }
456 pub(crate) fn set_deprecated(mut self, deprecated: bool) -> Builder { 499 pub(crate) fn set_deprecated(&mut self, deprecated: bool) -> &mut Builder {
457 self.deprecated = deprecated; 500 self.deprecated = deprecated;
458 self 501 self
459 } 502 }
460 pub(crate) fn set_relevance(mut self, relevance: Relevance) -> Builder { 503 pub(crate) fn set_relevance(&mut self, relevance: CompletionRelevance) -> &mut Builder {
461 self.relevance = relevance; 504 self.relevance = relevance;
462 self 505 self
463 } 506 }
464 pub(crate) fn trigger_call_info(mut self) -> Builder { 507 pub(crate) fn trigger_call_info(&mut self) -> &mut Builder {
465 self.trigger_call_info = Some(true); 508 self.trigger_call_info = Some(true);
466 self 509 self
467 } 510 }
468 pub(crate) fn add_import(mut self, import_to_add: Option<ImportEdit>) -> Builder { 511 pub(crate) fn add_import(&mut self, import_to_add: Option<ImportEdit>) -> &mut Builder {
469 self.import_to_add = import_to_add; 512 self.import_to_add = import_to_add;
470 self 513 self
471 } 514 }
472 pub(crate) fn ref_match(mut self, mutability: Mutability) -> Builder { 515 pub(crate) fn ref_match(&mut self, mutability: Mutability) -> &mut Builder {
473 self.ref_match = Some(mutability); 516 self.ref_match = Some(mutability);
474 self 517 self
475 } 518 }
476} 519}
520
521#[cfg(test)]
522mod tests {
523 use itertools::Itertools;
524 use test_utils::assert_eq_text;
525
526 use super::CompletionRelevance;
527
528 /// Check that these are CompletionRelevance are sorted in ascending order
529 /// by their relevance score.
530 ///
531 /// We want to avoid making assertions about the absolute score of any
532 /// item, but we do want to assert whether each is >, <, or == to the
533 /// others.
534 ///
535 /// If provided vec![vec![a], vec![b, c], vec![d]], then this will assert:
536 /// a.score < b.score == c.score < d.score
537 fn check_relevance_score_ordered(expected_relevance_order: Vec<Vec<CompletionRelevance>>) {
538 let expected = format!("{:#?}", &expected_relevance_order);
539
540 let actual_relevance_order = expected_relevance_order
541 .into_iter()
542 .flatten()
543 .map(|r| (r.score(), r))
544 .sorted_by_key(|(score, _r)| *score)
545 .fold(
546 (u32::MIN, vec![vec![]]),
547 |(mut currently_collecting_score, mut out), (score, r)| {
548 if currently_collecting_score == score {
549 out.last_mut().unwrap().push(r);
550 } else {
551 currently_collecting_score = score;
552 out.push(vec![r]);
553 }
554 (currently_collecting_score, out)
555 },
556 )
557 .1;
558
559 let actual = format!("{:#?}", &actual_relevance_order);
560
561 assert_eq_text!(&expected, &actual);
562 }
563
564 #[test]
565 fn relevance_score() {
566 // This test asserts that the relevance score for these items is ascending, and
567 // that any items in the same vec have the same score.
568 let expected_relevance_order = vec![
569 vec![CompletionRelevance::default()],
570 vec![
571 CompletionRelevance { exact_name_match: true, ..CompletionRelevance::default() },
572 CompletionRelevance { is_local: true, ..CompletionRelevance::default() },
573 ],
574 vec![CompletionRelevance {
575 exact_name_match: true,
576 is_local: true,
577 ..CompletionRelevance::default()
578 }],
579 vec![CompletionRelevance { exact_type_match: true, ..CompletionRelevance::default() }],
580 vec![CompletionRelevance {
581 exact_name_match: true,
582 exact_type_match: true,
583 ..CompletionRelevance::default()
584 }],
585 vec![CompletionRelevance {
586 exact_name_match: true,
587 exact_type_match: true,
588 is_local: true,
589 }],
590 ];
591
592 check_relevance_score_ordered(expected_relevance_order);
593 }
594}