feat: Deprioritize ops methods in completion

This commit is contained in:
Lukas Wirth 2022-01-06 13:31:36 +01:00
parent 41a0e95d61
commit ca0633c808
6 changed files with 106 additions and 30 deletions

View File

@ -1610,6 +1610,12 @@ pub struct Trait {
}
impl Trait {
pub fn lang(db: &dyn HirDatabase, krate: Crate, name: &Name) -> Option<Trait> {
db.lang_item(krate.into(), name.to_smol_str())
.and_then(LangItemTarget::as_trait)
.map(Into::into)
}
pub fn module(self, db: &dyn HirDatabase) -> Module {
Module { id: self.id.lookup(db.upcast()).container }
}

View File

@ -144,8 +144,8 @@ impl LangItems {
let _p = profile::span("lang_item_query");
let lang_items = db.crate_lang_items(start_crate);
let start_crate_target = lang_items.items.get(&item);
if let Some(target) = start_crate_target {
return Some(*target);
if let Some(&target) = start_crate_target {
return Some(target);
}
db.crate_graph()[start_crate]
.dependencies

View File

@ -309,26 +309,6 @@ pub mod known {
wrapping_mul,
wrapping_sub,
// known methods of lang items
add,
mul,
sub,
div,
rem,
shl,
shr,
bitxor,
bitor,
bitand,
add_assign,
mul_assign,
sub_assign,
div_assign,
rem_assign,
shl_assign,
shr_assign,
bitxor_assign,
bitor_assign,
bitand_assign,
eq,
ne,
ge,
@ -336,12 +316,38 @@ pub mod known {
le,
lt,
// lang items
not,
neg,
add_assign,
add,
bitand_assign,
bitand,
bitor_assign,
bitor,
bitxor_assign,
bitxor,
deref_mut,
deref,
div_assign,
div,
fn_mut,
fn_once,
future_trait,
owned_box,
index,
partial_ord
index_mut,
mul_assign,
mul,
neg,
not,
owned_box,
partial_ord,
r#fn,
rem_assign,
rem,
shl_assign,
shl,
shr_assign,
shr,
sub_assign,
sub,
);
// self/Self cannot be used as an identifier

View File

@ -3,13 +3,14 @@
use std::iter;
use base_db::SourceDatabaseExt;
use hir::{Local, Name, ScopeDef, Semantics, SemanticsScope, Type, TypeInfo};
use hir::{known, Local, Name, ScopeDef, Semantics, SemanticsScope, Type, TypeInfo};
use ide_db::{
active_parameter::ActiveParameter,
base_db::{FilePosition, SourceDatabase},
helpers::FamousDefs,
RootDatabase,
};
use rustc_hash::FxHashSet;
use syntax::{
algo::find_node_at_offset,
ast::{self, HasName, NameOrNameRef},
@ -85,6 +86,7 @@ pub(crate) enum ParamKind {
Function,
Closure,
}
/// `CompletionContext` is created early during completion to figure out, where
/// exactly is the cursor, syntax-wise.
#[derive(Debug)]
@ -120,7 +122,10 @@ pub(crate) struct CompletionContext<'a> {
pub(super) lifetime_ctx: Option<LifetimeContext>,
pub(super) pattern_ctx: Option<PatternContext>,
pub(super) path_context: Option<PathCompletionContext>,
pub(super) locals: Vec<(Name, Local)>,
/// Operator traits defined in the project
pub(super) ops_traits: FxHashSet<hir::Trait>,
no_completion_required: bool,
}
@ -308,6 +313,11 @@ impl<'a> CompletionContext<'a> {
self.token.kind() == BANG && self.token.parent().map_or(false, |it| it.kind() == MACRO_CALL)
}
/// Whether the given trait is an operator trait or not.
pub(crate) fn is_ops_trait(&self, trait_: hir::Trait) -> bool {
self.ops_traits.contains(&trait_)
}
/// A version of [`SemanticsScope::process_all_names`] that filters out `#[doc(hidden)]` items.
pub(crate) fn process_all_names(&self, f: &mut dyn FnMut(Name, ScopeDef)) {
let _p = profile::span("CompletionContext::process_all_names");
@ -388,6 +398,17 @@ impl<'a> CompletionContext<'a> {
locals.push((name, local));
}
});
let mut ops_traits =
FxHashSet::with_capacity_and_hasher(OP_TRAIT_LANG_NAMES.len(), Default::default());
if let Some(krate) = krate {
let _p = profile::span("CompletionContext::new ops");
for trait_ in
OP_TRAIT_LANG_NAMES.iter().filter_map(|item| hir::Trait::lang(db, krate, item))
{
ops_traits.insert(trait_);
}
}
let mut ctx = CompletionContext {
sema,
scope,
@ -413,6 +434,7 @@ impl<'a> CompletionContext<'a> {
locals,
incomplete_let: false,
no_completion_required: false,
ops_traits,
};
ctx.expand_and_fill(
original_file.syntax().clone(),
@ -889,6 +911,7 @@ fn pattern_context_for(pat: ast::Pat) -> PatternContext {
});
PatternContext { refutability, is_param, has_type_ascription }
}
fn find_node_with_range<N: AstNode>(syntax: &SyntaxNode, range: TextRange) -> Option<N> {
syntax.covering_element(range).ancestors().find_map(N::cast)
}
@ -915,6 +938,37 @@ fn has_ref(token: &SyntaxToken) -> bool {
token.kind() == T![&]
}
const OP_TRAIT_LANG_NAMES: &[hir::Name] = &[
known::add_assign,
known::add,
known::bitand_assign,
known::bitand,
known::bitor_assign,
known::bitor,
known::bitxor_assign,
known::bitxor,
known::deref_mut,
known::deref,
known::div_assign,
known::div,
known::fn_mut,
known::fn_once,
known::r#fn,
known::index_mut,
known::index,
known::mul_assign,
known::mul,
known::neg,
known::not,
known::rem_assign,
known::rem,
known::shl_assign,
known::shl,
known::shr_assign,
known::shr,
known::sub,
known::sub,
];
#[cfg(test)]
mod tests {
use expect_test::{expect, Expect};

View File

@ -139,6 +139,8 @@ pub struct CompletionRelevance {
/// }
/// ```
pub is_local: bool,
/// Set for method completions of the `core::ops` family.
pub is_op_method: bool,
/// This is set in cases like these:
///
/// ```
@ -198,6 +200,9 @@ impl CompletionRelevance {
if self.is_local {
score += 1;
}
if self.is_op_method {
score -= 1;
}
if self.exact_postfix_snippet_match {
score += 100;
}
@ -588,10 +593,8 @@ mod tests {
..CompletionRelevance::default()
}],
vec![CompletionRelevance {
exact_name_match: false,
type_match: None,
is_local: false,
exact_postfix_snippet_match: true,
..CompletionRelevance::default()
}],
];

View File

@ -70,6 +70,13 @@ fn render(
item.set_relevance(CompletionRelevance {
type_match: compute_type_match(completion, &ret_type),
exact_name_match: compute_exact_name_match(completion, &call),
is_op_method: match func_type {
FuncType::Method(_) => func
.as_assoc_item(ctx.db())
.and_then(|trait_| trait_.containing_trait_or_trait_impl(ctx.db()))
.map_or(false, |trait_| completion.is_ops_trait(trait_)),
_ => false,
},
..CompletionRelevance::default()
});