mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-28 04:44:57 +00:00
completion relevance distinguish between exact type match and could unify
This commit is contained in:
parent
957939292e
commit
0e31ae2cef
5 changed files with 87 additions and 50 deletions
|
@ -122,7 +122,7 @@ impl fmt::Debug for CompletionItem {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Ord, PartialOrd, Eq, PartialEq, Default)]
|
#[derive(Debug, Clone, Copy, Eq, PartialEq, Default)]
|
||||||
pub struct CompletionRelevance {
|
pub struct CompletionRelevance {
|
||||||
/// This is set in cases like these:
|
/// This is set in cases like these:
|
||||||
///
|
///
|
||||||
|
@ -134,6 +134,31 @@ pub struct CompletionRelevance {
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
pub exact_name_match: bool,
|
pub exact_name_match: bool,
|
||||||
|
/// See CompletionRelevanceTypeMatch doc comments for cases where this is set.
|
||||||
|
pub type_match: Option<CompletionRelevanceTypeMatch>,
|
||||||
|
/// This is set in cases like these:
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// fn foo(a: u32) {
|
||||||
|
/// let b = 0;
|
||||||
|
/// $0 // `a` and `b` are local
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
pub is_local: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
||||||
|
pub enum CompletionRelevanceTypeMatch {
|
||||||
|
/// This is set in cases like these:
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// enum Option<T> { Some(T), None }
|
||||||
|
/// fn f(a: Option<u32>) {}
|
||||||
|
/// fn main {
|
||||||
|
/// f(Option::N$0) // type `Option<T>` could unify with `Option<u32>`
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
CouldUnify,
|
||||||
/// This is set in cases like these:
|
/// This is set in cases like these:
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
|
@ -143,22 +168,7 @@ pub struct CompletionRelevance {
|
||||||
/// f($0) // type of local matches the type of param
|
/// f($0) // type of local matches the type of param
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
pub exact_type_match: bool,
|
Exact,
|
||||||
/// This is set in cases like these:
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// fn foo(bar: u32) {
|
|
||||||
/// $0 // `bar` is local
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// fn foo() {
|
|
||||||
/// let bar = 0;
|
|
||||||
/// $0 // `bar` is local
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
pub is_local: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CompletionRelevance {
|
impl CompletionRelevance {
|
||||||
|
@ -177,9 +187,11 @@ impl CompletionRelevance {
|
||||||
if self.exact_name_match {
|
if self.exact_name_match {
|
||||||
score += 1;
|
score += 1;
|
||||||
}
|
}
|
||||||
if self.exact_type_match {
|
score += match self.type_match {
|
||||||
score += 3;
|
Some(CompletionRelevanceTypeMatch::Exact) => 4,
|
||||||
}
|
Some(CompletionRelevanceTypeMatch::CouldUnify) => 3,
|
||||||
|
None => 0,
|
||||||
|
};
|
||||||
if self.is_local {
|
if self.is_local {
|
||||||
score += 1;
|
score += 1;
|
||||||
}
|
}
|
||||||
|
@ -342,7 +354,7 @@ impl CompletionItem {
|
||||||
// match, but with exact type match set because self.ref_match
|
// match, but with exact type match set because self.ref_match
|
||||||
// is only set if there is an exact type match.
|
// is only set if there is an exact type match.
|
||||||
let mut relevance = self.relevance;
|
let mut relevance = self.relevance;
|
||||||
relevance.exact_type_match = true;
|
relevance.type_match = Some(CompletionRelevanceTypeMatch::Exact);
|
||||||
|
|
||||||
self.ref_match.map(|mutability| (mutability, relevance))
|
self.ref_match.map(|mutability| (mutability, relevance))
|
||||||
}
|
}
|
||||||
|
@ -523,7 +535,7 @@ mod tests {
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use test_utils::assert_eq_text;
|
use test_utils::assert_eq_text;
|
||||||
|
|
||||||
use super::CompletionRelevance;
|
use super::{CompletionRelevance, CompletionRelevanceTypeMatch};
|
||||||
|
|
||||||
/// Check that these are CompletionRelevance are sorted in ascending order
|
/// Check that these are CompletionRelevance are sorted in ascending order
|
||||||
/// by their relevance score.
|
/// by their relevance score.
|
||||||
|
@ -576,15 +588,22 @@ mod tests {
|
||||||
is_local: true,
|
is_local: true,
|
||||||
..CompletionRelevance::default()
|
..CompletionRelevance::default()
|
||||||
}],
|
}],
|
||||||
vec![CompletionRelevance { exact_type_match: true, ..CompletionRelevance::default() }],
|
|
||||||
vec![CompletionRelevance {
|
vec![CompletionRelevance {
|
||||||
exact_name_match: true,
|
type_match: Some(CompletionRelevanceTypeMatch::CouldUnify),
|
||||||
exact_type_match: true,
|
..CompletionRelevance::default()
|
||||||
|
}],
|
||||||
|
vec![CompletionRelevance {
|
||||||
|
type_match: Some(CompletionRelevanceTypeMatch::Exact),
|
||||||
..CompletionRelevance::default()
|
..CompletionRelevance::default()
|
||||||
}],
|
}],
|
||||||
vec![CompletionRelevance {
|
vec![CompletionRelevance {
|
||||||
exact_name_match: true,
|
exact_name_match: true,
|
||||||
exact_type_match: true,
|
type_match: Some(CompletionRelevanceTypeMatch::Exact),
|
||||||
|
..CompletionRelevance::default()
|
||||||
|
}],
|
||||||
|
vec![CompletionRelevance {
|
||||||
|
exact_name_match: true,
|
||||||
|
type_match: Some(CompletionRelevanceTypeMatch::Exact),
|
||||||
is_local: true,
|
is_local: true,
|
||||||
}],
|
}],
|
||||||
];
|
];
|
||||||
|
|
|
@ -20,8 +20,8 @@ use ide_db::{
|
||||||
use syntax::TextRange;
|
use syntax::TextRange;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
item::ImportEdit, CompletionContext, CompletionItem, CompletionItemKind, CompletionKind,
|
item::{CompletionRelevanceTypeMatch, ImportEdit},
|
||||||
CompletionRelevance,
|
CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, CompletionRelevance,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::render::{enum_variant::render_variant, function::render_fn, macro_::render_macro};
|
use crate::render::{enum_variant::render_variant, function::render_fn, macro_::render_macro};
|
||||||
|
@ -143,7 +143,7 @@ impl<'a> Render<'a> {
|
||||||
.set_deprecated(is_deprecated);
|
.set_deprecated(is_deprecated);
|
||||||
|
|
||||||
item.set_relevance(CompletionRelevance {
|
item.set_relevance(CompletionRelevance {
|
||||||
exact_type_match: compute_exact_type_match(self.ctx.completion, ty),
|
type_match: compute_type_match(self.ctx.completion, ty),
|
||||||
exact_name_match: compute_exact_name_match(self.ctx.completion, name.to_string()),
|
exact_name_match: compute_exact_name_match(self.ctx.completion, name.to_string()),
|
||||||
..CompletionRelevance::default()
|
..CompletionRelevance::default()
|
||||||
});
|
});
|
||||||
|
@ -245,7 +245,7 @@ impl<'a> Render<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
item.set_relevance(CompletionRelevance {
|
item.set_relevance(CompletionRelevance {
|
||||||
exact_type_match: compute_exact_type_match(self.ctx.completion, &ty),
|
type_match: compute_type_match(self.ctx.completion, &ty),
|
||||||
exact_name_match: compute_exact_name_match(self.ctx.completion, &local_name),
|
exact_name_match: compute_exact_name_match(self.ctx.completion, &local_name),
|
||||||
is_local: true,
|
is_local: true,
|
||||||
..CompletionRelevance::default()
|
..CompletionRelevance::default()
|
||||||
|
@ -309,15 +309,24 @@ impl<'a> Render<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compute_exact_type_match(ctx: &CompletionContext, completion_ty: &hir::Type) -> bool {
|
fn compute_type_match(
|
||||||
match ctx.expected_type.as_ref() {
|
ctx: &CompletionContext,
|
||||||
Some(expected_type) => {
|
completion_ty: &hir::Type,
|
||||||
// We don't ever consider unit type to be an exact type match, since
|
) -> Option<CompletionRelevanceTypeMatch> {
|
||||||
// nearly always this is not meaningful to the user.
|
let expected_type = ctx.expected_type.as_ref()?;
|
||||||
(completion_ty == expected_type || expected_type.could_unify_with(completion_ty))
|
|
||||||
&& !expected_type.is_unit()
|
// We don't ever consider unit type to be an exact type match, since
|
||||||
}
|
// nearly always this is not meaningful to the user.
|
||||||
None => false,
|
if expected_type.is_unit() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
if completion_ty == expected_type {
|
||||||
|
Some(CompletionRelevanceTypeMatch::Exact)
|
||||||
|
} else if expected_type.could_unify_with(completion_ty) {
|
||||||
|
Some(CompletionRelevanceTypeMatch::CouldUnify)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -349,6 +358,7 @@ mod tests {
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
item::CompletionRelevanceTypeMatch,
|
||||||
test_utils::{check_edit, do_completion, get_all_items, TEST_CONFIG},
|
test_utils::{check_edit, do_completion, get_all_items, TEST_CONFIG},
|
||||||
CompletionKind, CompletionRelevance,
|
CompletionKind, CompletionRelevance,
|
||||||
};
|
};
|
||||||
|
@ -361,7 +371,11 @@ mod tests {
|
||||||
fn check_relevance(ra_fixture: &str, expect: Expect) {
|
fn check_relevance(ra_fixture: &str, expect: Expect) {
|
||||||
fn display_relevance(relevance: CompletionRelevance) -> String {
|
fn display_relevance(relevance: CompletionRelevance) -> String {
|
||||||
let relevance_factors = vec![
|
let relevance_factors = vec![
|
||||||
(relevance.exact_type_match, "type"),
|
(relevance.type_match == Some(CompletionRelevanceTypeMatch::Exact), "type"),
|
||||||
|
(
|
||||||
|
relevance.type_match == Some(CompletionRelevanceTypeMatch::CouldUnify),
|
||||||
|
"type_could_unify",
|
||||||
|
),
|
||||||
(relevance.exact_name_match, "name"),
|
(relevance.exact_name_match, "name"),
|
||||||
(relevance.is_local, "local"),
|
(relevance.is_local, "local"),
|
||||||
]
|
]
|
||||||
|
@ -534,7 +548,9 @@ fn main() { let _: m::Spam = S$0 }
|
||||||
detail: "(i32)",
|
detail: "(i32)",
|
||||||
relevance: CompletionRelevance {
|
relevance: CompletionRelevance {
|
||||||
exact_name_match: false,
|
exact_name_match: false,
|
||||||
exact_type_match: true,
|
type_match: Some(
|
||||||
|
Exact,
|
||||||
|
),
|
||||||
is_local: false,
|
is_local: false,
|
||||||
},
|
},
|
||||||
trigger_call_info: true,
|
trigger_call_info: true,
|
||||||
|
@ -560,7 +576,9 @@ fn main() { let _: m::Spam = S$0 }
|
||||||
detail: "()",
|
detail: "()",
|
||||||
relevance: CompletionRelevance {
|
relevance: CompletionRelevance {
|
||||||
exact_name_match: false,
|
exact_name_match: false,
|
||||||
exact_type_match: true,
|
type_match: Some(
|
||||||
|
Exact,
|
||||||
|
),
|
||||||
is_local: false,
|
is_local: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -1109,7 +1127,7 @@ fn main() {
|
||||||
detail: "S",
|
detail: "S",
|
||||||
relevance: CompletionRelevance {
|
relevance: CompletionRelevance {
|
||||||
exact_name_match: true,
|
exact_name_match: true,
|
||||||
exact_type_match: false,
|
type_match: None,
|
||||||
is_local: true,
|
is_local: true,
|
||||||
},
|
},
|
||||||
ref_match: "&mut ",
|
ref_match: "&mut ",
|
||||||
|
@ -1374,8 +1392,8 @@ fn foo() {
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
ev Foo::A(…) [type]
|
ev Foo::A(…) [type_could_unify]
|
||||||
ev Foo::B [type]
|
ev Foo::B [type_could_unify]
|
||||||
lc foo [type+local]
|
lc foo [type+local]
|
||||||
en Foo []
|
en Foo []
|
||||||
fn baz() []
|
fn baz() []
|
||||||
|
|
|
@ -6,7 +6,7 @@ use itertools::Itertools;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
item::{CompletionItem, CompletionKind, ImportEdit},
|
item::{CompletionItem, CompletionKind, ImportEdit},
|
||||||
render::{builder_ext::Params, compute_exact_type_match, compute_ref_match, RenderContext},
|
render::{builder_ext::Params, compute_ref_match, compute_type_match, RenderContext},
|
||||||
CompletionRelevance,
|
CompletionRelevance,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -77,7 +77,7 @@ impl<'a> EnumRender<'a> {
|
||||||
|
|
||||||
let ty = self.variant.parent_enum(self.ctx.completion.db).ty(self.ctx.completion.db);
|
let ty = self.variant.parent_enum(self.ctx.completion.db).ty(self.ctx.completion.db);
|
||||||
item.set_relevance(CompletionRelevance {
|
item.set_relevance(CompletionRelevance {
|
||||||
exact_type_match: compute_exact_type_match(self.ctx.completion, &ty),
|
type_match: compute_type_match(self.ctx.completion, &ty),
|
||||||
..CompletionRelevance::default()
|
..CompletionRelevance::default()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ use syntax::ast::Fn;
|
||||||
use crate::{
|
use crate::{
|
||||||
item::{CompletionItem, CompletionItemKind, CompletionKind, CompletionRelevance, ImportEdit},
|
item::{CompletionItem, CompletionItemKind, CompletionKind, CompletionRelevance, ImportEdit},
|
||||||
render::{
|
render::{
|
||||||
builder_ext::Params, compute_exact_name_match, compute_exact_type_match, compute_ref_match,
|
builder_ext::Params, compute_exact_name_match, compute_ref_match, compute_type_match,
|
||||||
RenderContext,
|
RenderContext,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -73,7 +73,7 @@ impl<'a> FunctionRender<'a> {
|
||||||
|
|
||||||
let ret_type = self.func.ret_type(self.ctx.db());
|
let ret_type = self.func.ret_type(self.ctx.db());
|
||||||
item.set_relevance(CompletionRelevance {
|
item.set_relevance(CompletionRelevance {
|
||||||
exact_type_match: compute_exact_type_match(self.ctx.completion, &ret_type),
|
type_match: compute_type_match(self.ctx.completion, &ret_type),
|
||||||
exact_name_match: compute_exact_name_match(self.ctx.completion, self.name.clone()),
|
exact_name_match: compute_exact_name_match(self.ctx.completion, self.name.clone()),
|
||||||
..CompletionRelevance::default()
|
..CompletionRelevance::default()
|
||||||
});
|
});
|
||||||
|
|
|
@ -1138,7 +1138,7 @@ mod tests {
|
||||||
(
|
(
|
||||||
"&arg",
|
"&arg",
|
||||||
Some(
|
Some(
|
||||||
"fffffffa",
|
"fffffff9",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue