add completion relevance score
This commit is contained in:
parent
c0e9530fd0
commit
3679821eea
@ -87,8 +87,8 @@ macro_rules! eprintln {
|
||||
pub use hir::{Documentation, Semantics};
|
||||
pub use ide_assists::{Assist, AssistConfig, AssistId, AssistKind};
|
||||
pub use ide_completion::{
|
||||
CompletionConfig, CompletionItem, CompletionItemKind, CompletionScore, ImportEdit,
|
||||
InsertTextFormat,
|
||||
CompletionConfig, CompletionItem, CompletionItemKind, CompletionRelevance, CompletionScore,
|
||||
ImportEdit, InsertTextFormat,
|
||||
};
|
||||
pub use ide_db::{
|
||||
base_db::{
|
||||
|
@ -70,7 +70,7 @@ pub struct CompletionItem {
|
||||
/// Note that Relevance ignores fuzzy match score. We compute Relevance for
|
||||
/// all possible items, and then separately build an ordered completion list
|
||||
/// based on relevance and fuzzy matching with the already typed identifier.
|
||||
relevance: Relevance,
|
||||
relevance: CompletionRelevance,
|
||||
|
||||
/// Indicates that a reference or mutable reference to this variable is a
|
||||
/// possible match.
|
||||
@ -107,9 +107,11 @@ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
if self.deprecated {
|
||||
s.field("deprecated", &true);
|
||||
}
|
||||
if self.relevance.is_relevant() {
|
||||
|
||||
if self.relevance != CompletionRelevance::default() {
|
||||
s.field("relevance", &self.relevance);
|
||||
}
|
||||
|
||||
if let Some(mutability) = &self.ref_match {
|
||||
s.field("ref_match", &format!("&{}", mutability.as_keyword_for_ref()));
|
||||
}
|
||||
@ -129,7 +131,7 @@ pub enum CompletionScore {
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Ord, PartialOrd, Eq, PartialEq, Default)]
|
||||
pub struct Relevance {
|
||||
pub struct CompletionRelevance {
|
||||
/// This is set in cases like these:
|
||||
///
|
||||
/// ```
|
||||
@ -152,9 +154,34 @@ pub struct Relevance {
|
||||
pub exact_type_match: bool,
|
||||
}
|
||||
|
||||
impl Relevance {
|
||||
impl CompletionRelevance {
|
||||
/// Provides a relevance score. Higher values are more relevant.
|
||||
///
|
||||
/// The absolute value of the relevance score is not meaningful, for
|
||||
/// example a value of 0 doesn't mean "not relevant", rather
|
||||
/// it means "least relevant". The score value should only be used
|
||||
/// for relative ordering.
|
||||
///
|
||||
/// See is_relevant if you need to make some judgement about score
|
||||
/// in an absolute sense.
|
||||
pub fn score(&self) -> u8 {
|
||||
let mut score = 0;
|
||||
|
||||
if self.exact_name_match {
|
||||
score += 1;
|
||||
}
|
||||
if self.exact_type_match {
|
||||
score += 1;
|
||||
}
|
||||
|
||||
score
|
||||
}
|
||||
|
||||
/// Returns true when the score for this threshold is above
|
||||
/// some threshold such that we think it is especially likely
|
||||
/// to be relevant.
|
||||
pub fn is_relevant(&self) -> bool {
|
||||
self != &Relevance::default()
|
||||
self.score() > 0
|
||||
}
|
||||
}
|
||||
|
||||
@ -249,7 +276,7 @@ pub(crate) fn new(
|
||||
text_edit: None,
|
||||
deprecated: false,
|
||||
trigger_call_info: None,
|
||||
relevance: Relevance::default(),
|
||||
relevance: CompletionRelevance::default(),
|
||||
ref_match: None,
|
||||
import_to_add: None,
|
||||
}
|
||||
@ -292,7 +319,7 @@ pub fn deprecated(&self) -> bool {
|
||||
self.deprecated
|
||||
}
|
||||
|
||||
pub fn relevance(&self) -> Relevance {
|
||||
pub fn relevance(&self) -> CompletionRelevance {
|
||||
self.relevance
|
||||
}
|
||||
|
||||
@ -300,8 +327,14 @@ pub fn trigger_call_info(&self) -> bool {
|
||||
self.trigger_call_info
|
||||
}
|
||||
|
||||
pub fn ref_match(&self) -> Option<Mutability> {
|
||||
self.ref_match
|
||||
pub fn ref_match(&self) -> Option<(Mutability, CompletionRelevance)> {
|
||||
// Relevance of the ref match should be the same as the original
|
||||
// match, but with exact type match set because self.ref_match
|
||||
// is only set if there is an exact type match.
|
||||
let mut relevance = self.relevance;
|
||||
relevance.exact_type_match = true;
|
||||
|
||||
self.ref_match.map(|mutability| (mutability, relevance))
|
||||
}
|
||||
|
||||
pub fn import_to_add(&self) -> Option<&ImportEdit> {
|
||||
@ -349,7 +382,7 @@ pub(crate) struct Builder {
|
||||
text_edit: Option<TextEdit>,
|
||||
deprecated: bool,
|
||||
trigger_call_info: Option<bool>,
|
||||
relevance: Relevance,
|
||||
relevance: CompletionRelevance,
|
||||
ref_match: Option<Mutability>,
|
||||
}
|
||||
|
||||
@ -457,7 +490,7 @@ pub(crate) fn set_deprecated(&mut self, deprecated: bool) -> &mut Builder {
|
||||
self.deprecated = deprecated;
|
||||
self
|
||||
}
|
||||
pub(crate) fn set_relevance(&mut self, relevance: Relevance) -> &mut Builder {
|
||||
pub(crate) fn set_relevance(&mut self, relevance: CompletionRelevance) -> &mut Builder {
|
||||
self.relevance = relevance;
|
||||
self
|
||||
}
|
||||
|
@ -24,8 +24,8 @@
|
||||
pub use crate::{
|
||||
config::CompletionConfig,
|
||||
item::{
|
||||
CompletionItem, CompletionItemKind, CompletionScore, ImportEdit, InsertTextFormat,
|
||||
Relevance,
|
||||
CompletionItem, CompletionItemKind, CompletionRelevance, CompletionScore, ImportEdit,
|
||||
InsertTextFormat,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -20,7 +20,7 @@
|
||||
use syntax::TextRange;
|
||||
|
||||
use crate::{
|
||||
item::{ImportEdit, Relevance},
|
||||
item::{CompletionRelevance, ImportEdit},
|
||||
CompletionContext, CompletionItem, CompletionItemKind, CompletionKind,
|
||||
};
|
||||
|
||||
@ -322,9 +322,9 @@ fn is_deprecated(&self, resolution: &ScopeDef) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
fn compute_relevance(ctx: &RenderContext, ty: &Type, name: &str) -> Option<Relevance> {
|
||||
fn compute_relevance(ctx: &RenderContext, ty: &Type, name: &str) -> Option<CompletionRelevance> {
|
||||
let (expected_name, expected_type) = ctx.expected_name_and_type()?;
|
||||
let mut res = Relevance::default();
|
||||
let mut res = CompletionRelevance::default();
|
||||
res.exact_type_match = ty == &expected_type;
|
||||
res.exact_name_match = name == &expected_name;
|
||||
Some(res)
|
||||
@ -338,7 +338,7 @@ mod tests {
|
||||
|
||||
use crate::{
|
||||
test_utils::{check_edit, do_completion, get_all_items, TEST_CONFIG},
|
||||
CompletionKind, Relevance,
|
||||
CompletionKind, CompletionRelevance,
|
||||
};
|
||||
|
||||
fn check(ra_fixture: &str, expect: Expect) {
|
||||
@ -347,12 +347,14 @@ fn check(ra_fixture: &str, expect: Expect) {
|
||||
}
|
||||
|
||||
fn check_relevance(ra_fixture: &str, expect: Expect) {
|
||||
fn display_relevance(relevance: Relevance) -> &'static str {
|
||||
fn display_relevance(relevance: CompletionRelevance) -> &'static str {
|
||||
match relevance {
|
||||
Relevance { exact_type_match: true, exact_name_match: true } => "[type+name]",
|
||||
Relevance { exact_type_match: true, exact_name_match: false } => "[type]",
|
||||
Relevance { exact_type_match: false, exact_name_match: true } => "[name]",
|
||||
Relevance { exact_type_match: false, exact_name_match: false } => "[]",
|
||||
CompletionRelevance { exact_type_match: true, exact_name_match: true } => {
|
||||
"[type+name]"
|
||||
}
|
||||
CompletionRelevance { exact_type_match: true, exact_name_match: false } => "[type]",
|
||||
CompletionRelevance { exact_type_match: false, exact_name_match: true } => "[name]",
|
||||
CompletionRelevance { exact_type_match: false, exact_name_match: false } => "[]",
|
||||
}
|
||||
}
|
||||
|
||||
@ -975,7 +977,7 @@ fn main() {
|
||||
Local,
|
||||
),
|
||||
detail: "S",
|
||||
relevance: Relevance {
|
||||
relevance: CompletionRelevance {
|
||||
exact_name_match: true,
|
||||
exact_type_match: false,
|
||||
},
|
||||
|
@ -6,9 +6,10 @@
|
||||
|
||||
use ide::{
|
||||
Annotation, AnnotationKind, Assist, AssistKind, CallInfo, CompletionItem, CompletionItemKind,
|
||||
Documentation, FileId, FileRange, FileSystemEdit, Fold, FoldKind, Highlight, HlMod, HlPunct,
|
||||
HlRange, HlTag, Indel, InlayHint, InlayKind, InsertTextFormat, Markup, NavigationTarget,
|
||||
ReferenceAccess, RenameError, Runnable, Severity, SourceChange, TextEdit, TextRange, TextSize,
|
||||
CompletionRelevance, Documentation, FileId, FileRange, FileSystemEdit, Fold, FoldKind,
|
||||
Highlight, HlMod, HlPunct, HlRange, HlTag, Indel, InlayHint, InlayKind, InsertTextFormat,
|
||||
Markup, NavigationTarget, ReferenceAccess, RenameError, Runnable, Severity, SourceChange,
|
||||
TextEdit, TextRange, TextSize,
|
||||
};
|
||||
use ide_db::SymbolKind;
|
||||
use itertools::Itertools;
|
||||
@ -213,12 +214,22 @@ pub(crate) fn completion_item(
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
if item.relevance().is_relevant() {
|
||||
lsp_item.preselect = Some(true);
|
||||
// HACK: sort preselect items first
|
||||
lsp_item.sort_text = Some(format!(" {}", item.label()));
|
||||
fn set_score(res: &mut lsp_types::CompletionItem, relevance: CompletionRelevance) {
|
||||
if relevance.is_relevant() {
|
||||
res.preselect = Some(true);
|
||||
}
|
||||
// The relevance needs to be inverted to come up with a sort score
|
||||
// because the client will sort ascending.
|
||||
let sort_score = relevance.score() ^ 0xFF;
|
||||
// Zero pad the string to ensure values are sorted numerically
|
||||
// even though the client is sorting alphabetically. Three
|
||||
// characters is enough to fit the largest u8, which is the
|
||||
// type of the relevance score.
|
||||
res.sort_text = Some(format!("{:03}", sort_score));
|
||||
}
|
||||
|
||||
set_score(&mut lsp_item, item.relevance());
|
||||
|
||||
if item.deprecated() {
|
||||
lsp_item.tags = Some(vec![lsp_types::CompletionItemTag::Deprecated])
|
||||
}
|
||||
@ -228,10 +239,9 @@ pub(crate) fn completion_item(
|
||||
}
|
||||
|
||||
let mut res = match item.ref_match() {
|
||||
Some(mutability) => {
|
||||
Some((mutability, relevance)) => {
|
||||
let mut lsp_item_with_ref = lsp_item.clone();
|
||||
lsp_item.preselect = Some(true);
|
||||
lsp_item.sort_text = Some(format!(" {}", item.label()));
|
||||
set_score(&mut lsp_item_with_ref, relevance);
|
||||
lsp_item_with_ref.label =
|
||||
format!("&{}{}", mutability.as_keyword_for_ref(), lsp_item_with_ref.label);
|
||||
if let Some(lsp_types::CompletionTextEdit::Edit(it)) = &mut lsp_item_with_ref.text_edit
|
||||
@ -1107,13 +1117,13 @@ fn main() {
|
||||
(
|
||||
"&arg",
|
||||
Some(
|
||||
" arg",
|
||||
"253",
|
||||
),
|
||||
),
|
||||
(
|
||||
"arg",
|
||||
Some(
|
||||
" arg",
|
||||
"254",
|
||||
),
|
||||
),
|
||||
]
|
||||
|
Loading…
Reference in New Issue
Block a user