Skip to content

Commit 3679821

Browse files
committed
add completion relevance score
1 parent c0e9530 commit 3679821

File tree

5 files changed

+82
-37
lines changed

5 files changed

+82
-37
lines changed

crates/ide/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,8 @@ pub use crate::{
8787
pub use hir::{Documentation, Semantics};
8888
pub use ide_assists::{Assist, AssistConfig, AssistId, AssistKind};
8989
pub use ide_completion::{
90-
CompletionConfig, CompletionItem, CompletionItemKind, CompletionScore, ImportEdit,
91-
InsertTextFormat,
90+
CompletionConfig, CompletionItem, CompletionItemKind, CompletionRelevance, CompletionScore,
91+
ImportEdit, InsertTextFormat,
9292
};
9393
pub use ide_db::{
9494
base_db::{

crates/ide_completion/src/item.rs

Lines changed: 44 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ pub struct CompletionItem {
7070
/// Note that Relevance ignores fuzzy match score. We compute Relevance for
7171
/// all possible items, and then separately build an ordered completion list
7272
/// based on relevance and fuzzy matching with the already typed identifier.
73-
relevance: Relevance,
73+
relevance: CompletionRelevance,
7474

7575
/// Indicates that a reference or mutable reference to this variable is a
7676
/// possible match.
@@ -107,9 +107,11 @@ impl fmt::Debug for CompletionItem {
107107
if self.deprecated {
108108
s.field("deprecated", &true);
109109
}
110-
if self.relevance.is_relevant() {
110+
111+
if self.relevance != CompletionRelevance::default() {
111112
s.field("relevance", &self.relevance);
112113
}
114+
113115
if let Some(mutability) = &self.ref_match {
114116
s.field("ref_match", &format!("&{}", mutability.as_keyword_for_ref()));
115117
}
@@ -129,7 +131,7 @@ pub enum CompletionScore {
129131
}
130132

131133
#[derive(Debug, Clone, Copy, Ord, PartialOrd, Eq, PartialEq, Default)]
132-
pub struct Relevance {
134+
pub struct CompletionRelevance {
133135
/// This is set in cases like these:
134136
///
135137
/// ```
@@ -152,9 +154,34 @@ pub struct Relevance {
152154
pub exact_type_match: bool,
153155
}
154156

155-
impl Relevance {
157+
impl CompletionRelevance {
158+
/// Provides a relevance score. Higher values are more relevant.
159+
///
160+
/// The absolute value of the relevance score is not meaningful, for
161+
/// example a value of 0 doesn't mean "not relevant", rather
162+
/// it means "least relevant". The score value should only be used
163+
/// for relative ordering.
164+
///
165+
/// See is_relevant if you need to make some judgement about score
166+
/// in an absolute sense.
167+
pub fn score(&self) -> u8 {
168+
let mut score = 0;
169+
170+
if self.exact_name_match {
171+
score += 1;
172+
}
173+
if self.exact_type_match {
174+
score += 1;
175+
}
176+
177+
score
178+
}
179+
180+
/// Returns true when the score for this threshold is above
181+
/// some threshold such that we think it is especially likely
182+
/// to be relevant.
156183
pub fn is_relevant(&self) -> bool {
157-
self != &Relevance::default()
184+
self.score() > 0
158185
}
159186
}
160187

@@ -249,7 +276,7 @@ impl CompletionItem {
249276
text_edit: None,
250277
deprecated: false,
251278
trigger_call_info: None,
252-
relevance: Relevance::default(),
279+
relevance: CompletionRelevance::default(),
253280
ref_match: None,
254281
import_to_add: None,
255282
}
@@ -292,16 +319,22 @@ impl CompletionItem {
292319
self.deprecated
293320
}
294321

295-
pub fn relevance(&self) -> Relevance {
322+
pub fn relevance(&self) -> CompletionRelevance {
296323
self.relevance
297324
}
298325

299326
pub fn trigger_call_info(&self) -> bool {
300327
self.trigger_call_info
301328
}
302329

303-
pub fn ref_match(&self) -> Option<Mutability> {
304-
self.ref_match
330+
pub fn ref_match(&self) -> Option<(Mutability, CompletionRelevance)> {
331+
// Relevance of the ref match should be the same as the original
332+
// match, but with exact type match set because self.ref_match
333+
// is only set if there is an exact type match.
334+
let mut relevance = self.relevance;
335+
relevance.exact_type_match = true;
336+
337+
self.ref_match.map(|mutability| (mutability, relevance))
305338
}
306339

307340
pub fn import_to_add(&self) -> Option<&ImportEdit> {
@@ -349,7 +382,7 @@ pub(crate) struct Builder {
349382
text_edit: Option<TextEdit>,
350383
deprecated: bool,
351384
trigger_call_info: Option<bool>,
352-
relevance: Relevance,
385+
relevance: CompletionRelevance,
353386
ref_match: Option<Mutability>,
354387
}
355388

@@ -457,7 +490,7 @@ impl Builder {
457490
self.deprecated = deprecated;
458491
self
459492
}
460-
pub(crate) fn set_relevance(&mut self, relevance: Relevance) -> &mut Builder {
493+
pub(crate) fn set_relevance(&mut self, relevance: CompletionRelevance) -> &mut Builder {
461494
self.relevance = relevance;
462495
self
463496
}

crates/ide_completion/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ use crate::{completions::Completions, context::CompletionContext, item::Completi
2424
pub use crate::{
2525
config::CompletionConfig,
2626
item::{
27-
CompletionItem, CompletionItemKind, CompletionScore, ImportEdit, InsertTextFormat,
28-
Relevance,
27+
CompletionItem, CompletionItemKind, CompletionRelevance, CompletionScore, ImportEdit,
28+
InsertTextFormat,
2929
},
3030
};
3131

crates/ide_completion/src/render.rs

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use ide_db::{
2020
use syntax::TextRange;
2121

2222
use crate::{
23-
item::{ImportEdit, Relevance},
23+
item::{CompletionRelevance, ImportEdit},
2424
CompletionContext, CompletionItem, CompletionItemKind, CompletionKind,
2525
};
2626

@@ -322,9 +322,9 @@ impl<'a> Render<'a> {
322322
}
323323
}
324324

325-
fn compute_relevance(ctx: &RenderContext, ty: &Type, name: &str) -> Option<Relevance> {
325+
fn compute_relevance(ctx: &RenderContext, ty: &Type, name: &str) -> Option<CompletionRelevance> {
326326
let (expected_name, expected_type) = ctx.expected_name_and_type()?;
327-
let mut res = Relevance::default();
327+
let mut res = CompletionRelevance::default();
328328
res.exact_type_match = ty == &expected_type;
329329
res.exact_name_match = name == &expected_name;
330330
Some(res)
@@ -338,7 +338,7 @@ mod tests {
338338

339339
use crate::{
340340
test_utils::{check_edit, do_completion, get_all_items, TEST_CONFIG},
341-
CompletionKind, Relevance,
341+
CompletionKind, CompletionRelevance,
342342
};
343343

344344
fn check(ra_fixture: &str, expect: Expect) {
@@ -347,12 +347,14 @@ mod tests {
347347
}
348348

349349
fn check_relevance(ra_fixture: &str, expect: Expect) {
350-
fn display_relevance(relevance: Relevance) -> &'static str {
350+
fn display_relevance(relevance: CompletionRelevance) -> &'static str {
351351
match relevance {
352-
Relevance { exact_type_match: true, exact_name_match: true } => "[type+name]",
353-
Relevance { exact_type_match: true, exact_name_match: false } => "[type]",
354-
Relevance { exact_type_match: false, exact_name_match: true } => "[name]",
355-
Relevance { exact_type_match: false, exact_name_match: false } => "[]",
352+
CompletionRelevance { exact_type_match: true, exact_name_match: true } => {
353+
"[type+name]"
354+
}
355+
CompletionRelevance { exact_type_match: true, exact_name_match: false } => "[type]",
356+
CompletionRelevance { exact_type_match: false, exact_name_match: true } => "[name]",
357+
CompletionRelevance { exact_type_match: false, exact_name_match: false } => "[]",
356358
}
357359
}
358360

@@ -975,7 +977,7 @@ fn main() {
975977
Local,
976978
),
977979
detail: "S",
978-
relevance: Relevance {
980+
relevance: CompletionRelevance {
979981
exact_name_match: true,
980982
exact_type_match: false,
981983
},

crates/rust-analyzer/src/to_proto.rs

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,10 @@ use std::{
66

77
use ide::{
88
Annotation, AnnotationKind, Assist, AssistKind, CallInfo, CompletionItem, CompletionItemKind,
9-
Documentation, FileId, FileRange, FileSystemEdit, Fold, FoldKind, Highlight, HlMod, HlPunct,
10-
HlRange, HlTag, Indel, InlayHint, InlayKind, InsertTextFormat, Markup, NavigationTarget,
11-
ReferenceAccess, RenameError, Runnable, Severity, SourceChange, TextEdit, TextRange, TextSize,
9+
CompletionRelevance, Documentation, FileId, FileRange, FileSystemEdit, Fold, FoldKind,
10+
Highlight, HlMod, HlPunct, HlRange, HlTag, Indel, InlayHint, InlayKind, InsertTextFormat,
11+
Markup, NavigationTarget, ReferenceAccess, RenameError, Runnable, Severity, SourceChange,
12+
TextEdit, TextRange, TextSize,
1213
};
1314
use ide_db::SymbolKind;
1415
use itertools::Itertools;
@@ -213,12 +214,22 @@ pub(crate) fn completion_item(
213214
..Default::default()
214215
};
215216

216-
if item.relevance().is_relevant() {
217-
lsp_item.preselect = Some(true);
218-
// HACK: sort preselect items first
219-
lsp_item.sort_text = Some(format!(" {}", item.label()));
217+
fn set_score(res: &mut lsp_types::CompletionItem, relevance: CompletionRelevance) {
218+
if relevance.is_relevant() {
219+
res.preselect = Some(true);
220+
}
221+
// The relevance needs to be inverted to come up with a sort score
222+
// because the client will sort ascending.
223+
let sort_score = relevance.score() ^ 0xFF;
224+
// Zero pad the string to ensure values are sorted numerically
225+
// even though the client is sorting alphabetically. Three
226+
// characters is enough to fit the largest u8, which is the
227+
// type of the relevance score.
228+
res.sort_text = Some(format!("{:03}", sort_score));
220229
}
221230

231+
set_score(&mut lsp_item, item.relevance());
232+
222233
if item.deprecated() {
223234
lsp_item.tags = Some(vec![lsp_types::CompletionItemTag::Deprecated])
224235
}
@@ -228,10 +239,9 @@ pub(crate) fn completion_item(
228239
}
229240

230241
let mut res = match item.ref_match() {
231-
Some(mutability) => {
242+
Some((mutability, relevance)) => {
232243
let mut lsp_item_with_ref = lsp_item.clone();
233-
lsp_item.preselect = Some(true);
234-
lsp_item.sort_text = Some(format!(" {}", item.label()));
244+
set_score(&mut lsp_item_with_ref, relevance);
235245
lsp_item_with_ref.label =
236246
format!("&{}{}", mutability.as_keyword_for_ref(), lsp_item_with_ref.label);
237247
if let Some(lsp_types::CompletionTextEdit::Edit(it)) = &mut lsp_item_with_ref.text_edit
@@ -1107,13 +1117,13 @@ mod tests {
11071117
(
11081118
"&arg",
11091119
Some(
1110-
" arg",
1120+
"253",
11111121
),
11121122
),
11131123
(
11141124
"arg",
11151125
Some(
1116-
" arg",
1126+
"254",
11171127
),
11181128
),
11191129
]

0 commit comments

Comments
 (0)