Cleanup auto-import ordering
Addresses issues raised by @Veykril in #12333
This commit is contained in:
parent
068b138c87
commit
8e5b318d99
@ -111,8 +111,17 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
|
|||||||
// we aren't interested in different namespaces
|
// we aren't interested in different namespaces
|
||||||
proposed_imports.dedup_by(|a, b| a.import_path == b.import_path);
|
proposed_imports.dedup_by(|a, b| a.import_path == b.import_path);
|
||||||
|
|
||||||
|
let current_node = match ctx.covering_element() {
|
||||||
|
NodeOrToken::Node(node) => Some(node),
|
||||||
|
NodeOrToken::Token(token) => token.parent(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let current_module =
|
||||||
|
current_node.as_ref().and_then(|node| ctx.sema.scope(node)).map(|scope| scope.module());
|
||||||
|
|
||||||
// prioritize more relevant imports
|
// prioritize more relevant imports
|
||||||
proposed_imports.sort_by_key(|import| Reverse(relevance_score(ctx, import)));
|
proposed_imports
|
||||||
|
.sort_by_key(|import| Reverse(relevance_score(ctx, import, current_module.as_ref())));
|
||||||
|
|
||||||
for import in proposed_imports {
|
for import in proposed_imports {
|
||||||
acc.add_group(
|
acc.add_group(
|
||||||
@ -167,7 +176,11 @@ fn group_label(import_candidate: &ImportCandidate) -> GroupLabel {
|
|||||||
|
|
||||||
/// Determine how relevant a given import is in the current context. Higher scores are more
|
/// Determine how relevant a given import is in the current context. Higher scores are more
|
||||||
/// relevant.
|
/// relevant.
|
||||||
fn relevance_score(ctx: &AssistContext, import: &LocatedImport) -> i32 {
|
fn relevance_score(
|
||||||
|
ctx: &AssistContext,
|
||||||
|
import: &LocatedImport,
|
||||||
|
current_module: Option<&Module>,
|
||||||
|
) -> i32 {
|
||||||
let mut score = 0;
|
let mut score = 0;
|
||||||
|
|
||||||
let db = ctx.db();
|
let db = ctx.db();
|
||||||
@ -177,25 +190,11 @@ fn relevance_score(ctx: &AssistContext, import: &LocatedImport) -> i32 {
|
|||||||
hir::ItemInNs::Macros(makro) => Some(makro.module(db)),
|
hir::ItemInNs::Macros(makro) => Some(makro.module(db)),
|
||||||
};
|
};
|
||||||
|
|
||||||
let current_node = match ctx.covering_element() {
|
match item_module.zip(current_module) {
|
||||||
NodeOrToken::Node(node) => Some(node),
|
// get the distance between the imported path and the current module
|
||||||
NodeOrToken::Token(token) => token.parent(),
|
// (prefer items that are more local)
|
||||||
};
|
Some((item_module, current_module)) => {
|
||||||
|
score -= module_distance_hueristic(db, ¤t_module, &item_module) as i32;
|
||||||
if let Some(module) = item_module.as_ref() {
|
|
||||||
if module.krate().is_builtin(db) {
|
|
||||||
// prefer items builtin to the language
|
|
||||||
score += 5;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let current_scope = current_node.as_ref().and_then(|node| ctx.sema.scope(node));
|
|
||||||
|
|
||||||
match item_module.zip(current_scope) {
|
|
||||||
// get the distance between the modules (prefer items that are more local)
|
|
||||||
Some((item_module, current_scope)) => {
|
|
||||||
let current_module = current_scope.module();
|
|
||||||
score -= module_distance_hueristic(¤t_module, &item_module, db) as i32;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// could not find relevant modules, so just use the length of the path as an estimate
|
// could not find relevant modules, so just use the length of the path as an estimate
|
||||||
@ -206,30 +205,31 @@ fn relevance_score(ctx: &AssistContext, import: &LocatedImport) -> i32 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// A heuristic that gives a higher score to modules that are more separated.
|
/// A heuristic that gives a higher score to modules that are more separated.
|
||||||
fn module_distance_hueristic(current: &Module, item: &Module, db: &dyn HirDatabase) -> usize {
|
fn module_distance_hueristic(db: &dyn HirDatabase, current: &Module, item: &Module) -> usize {
|
||||||
|
// get the path starting from the item to the respective crate roots
|
||||||
let mut current_path = current.path_to_root(db);
|
let mut current_path = current.path_to_root(db);
|
||||||
let mut item_path = item.path_to_root(db);
|
let mut item_path = item.path_to_root(db);
|
||||||
|
|
||||||
|
// we want paths going from the root to the item
|
||||||
current_path.reverse();
|
current_path.reverse();
|
||||||
item_path.reverse();
|
item_path.reverse();
|
||||||
|
|
||||||
let mut i = 0;
|
// length of the common prefix of the two paths
|
||||||
while i < current_path.len() && i < item_path.len() {
|
let prefix_length = current_path.iter().zip(&item_path).take_while(|(a, b)| a == b).count();
|
||||||
if current_path[i] == item_path[i] {
|
|
||||||
i += 1
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let distinct_distance = current_path.len() + item_path.len();
|
// how many modules differ between the two paths (all modules, removing any duplicates)
|
||||||
let common_prefix = 2 * i;
|
let distinct_length = current_path.len() + item_path.len() - 2 * prefix_length;
|
||||||
|
|
||||||
// prefer builtin crates and items within the same crate
|
// cost of importing from another crate
|
||||||
let crate_boundary_cost =
|
let crate_boundary_cost = if current.krate() == item.krate() {
|
||||||
if item.krate().is_builtin(db) || current.krate() == item.krate() { 0 } else { 4 };
|
0
|
||||||
|
} else if item.krate().is_builtin(db) {
|
||||||
|
2
|
||||||
|
} else {
|
||||||
|
4
|
||||||
|
};
|
||||||
|
|
||||||
distinct_distance - common_prefix + crate_boundary_cost
|
distinct_length + crate_boundary_cost
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@ -266,14 +266,14 @@ fn check_auto_import_order(before: &str, order: &[&str]) {
|
|||||||
#[test]
|
#[test]
|
||||||
fn prefer_shorter_paths() {
|
fn prefer_shorter_paths() {
|
||||||
let before = r"
|
let before = r"
|
||||||
//- /main.rs crate:main deps:foo,bar
|
//- /main.rs crate:main deps:foo,bar
|
||||||
HashMap$0::new();
|
HashMap$0::new();
|
||||||
|
|
||||||
//- /lib.rs crate:foo
|
//- /lib.rs crate:foo
|
||||||
pub mod collections { pub struct HashMap; }
|
pub mod collections { pub struct HashMap; }
|
||||||
|
|
||||||
//- /lib.rs crate:bar
|
//- /lib.rs crate:bar
|
||||||
pub mod collections { pub mod hash_map { pub struct HashMap; } }
|
pub mod collections { pub mod hash_map { pub struct HashMap; } }
|
||||||
";
|
";
|
||||||
|
|
||||||
check_auto_import_order(
|
check_auto_import_order(
|
||||||
@ -285,17 +285,17 @@ fn prefer_shorter_paths() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn prefer_same_crate() {
|
fn prefer_same_crate() {
|
||||||
let before = r"
|
let before = r"
|
||||||
//- /main.rs crate:main deps:foo
|
//- /main.rs crate:main deps:foo
|
||||||
HashMap$0::new();
|
HashMap$0::new();
|
||||||
|
|
||||||
mod collections {
|
mod collections {
|
||||||
pub mod hash_map {
|
pub mod hash_map {
|
||||||
pub struct HashMap;
|
pub struct HashMap;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//- /lib.rs crate:foo
|
//- /lib.rs crate:foo
|
||||||
pub struct HashMap;
|
pub struct HashMap;
|
||||||
";
|
";
|
||||||
|
|
||||||
check_auto_import_order(
|
check_auto_import_order(
|
||||||
|
Loading…
Reference in New Issue
Block a user