Skip to content

Commit 8e5b318

Browse files
committed
Cleanup auto-import ordering
Addresses issues raised by @Veykril in rust-lang#12333
1 parent 068b138 commit 8e5b318

File tree

1 file changed

+51
-51
lines changed

1 file changed

+51
-51
lines changed

crates/ide-assists/src/handlers/auto_import.rs

+51-51
Original file line numberDiff line numberDiff line change
@@ -111,8 +111,17 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
111111
// we aren't interested in different namespaces
112112
proposed_imports.dedup_by(|a, b| a.import_path == b.import_path);
113113

114+
let current_node = match ctx.covering_element() {
115+
NodeOrToken::Node(node) => Some(node),
116+
NodeOrToken::Token(token) => token.parent(),
117+
};
118+
119+
let current_module =
120+
current_node.as_ref().and_then(|node| ctx.sema.scope(node)).map(|scope| scope.module());
121+
114122
// prioritize more relevant imports
115-
proposed_imports.sort_by_key(|import| Reverse(relevance_score(ctx, import)));
123+
proposed_imports
124+
.sort_by_key(|import| Reverse(relevance_score(ctx, import, current_module.as_ref())));
116125

117126
for import in proposed_imports {
118127
acc.add_group(
@@ -167,7 +176,11 @@ fn group_label(import_candidate: &ImportCandidate) -> GroupLabel {
167176

168177
/// Determine how relevant a given import is in the current context. Higher scores are more
169178
/// relevant.
170-
fn relevance_score(ctx: &AssistContext, import: &LocatedImport) -> i32 {
179+
fn relevance_score(
180+
ctx: &AssistContext,
181+
import: &LocatedImport,
182+
current_module: Option<&Module>,
183+
) -> i32 {
171184
let mut score = 0;
172185

173186
let db = ctx.db();
@@ -177,25 +190,11 @@ fn relevance_score(ctx: &AssistContext, import: &LocatedImport) -> i32 {
177190
hir::ItemInNs::Macros(makro) => Some(makro.module(db)),
178191
};
179192

180-
let current_node = match ctx.covering_element() {
181-
NodeOrToken::Node(node) => Some(node),
182-
NodeOrToken::Token(token) => token.parent(),
183-
};
184-
185-
if let Some(module) = item_module.as_ref() {
186-
if module.krate().is_builtin(db) {
187-
// prefer items builtin to the language
188-
score += 5;
189-
}
190-
}
191-
192-
let current_scope = current_node.as_ref().and_then(|node| ctx.sema.scope(node));
193-
194-
match item_module.zip(current_scope) {
195-
// get the distance between the modules (prefer items that are more local)
196-
Some((item_module, current_scope)) => {
197-
let current_module = current_scope.module();
198-
score -= module_distance_hueristic(&current_module, &item_module, db) as i32;
193+
match item_module.zip(current_module) {
194+
// get the distance between the imported path and the current module
195+
// (prefer items that are more local)
196+
Some((item_module, current_module)) => {
197+
score -= module_distance_hueristic(db, &current_module, &item_module) as i32;
199198
}
200199

201200
// 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 {
206205
}
207206

208207
/// A heuristic that gives a higher score to modules that are more separated.
209-
fn module_distance_hueristic(current: &Module, item: &Module, db: &dyn HirDatabase) -> usize {
208+
fn module_distance_hueristic(db: &dyn HirDatabase, current: &Module, item: &Module) -> usize {
209+
// get the path starting from the item to the respective crate roots
210210
let mut current_path = current.path_to_root(db);
211211
let mut item_path = item.path_to_root(db);
212212

213+
// we want paths going from the root to the item
213214
current_path.reverse();
214215
item_path.reverse();
215216

216-
let mut i = 0;
217-
while i < current_path.len() && i < item_path.len() {
218-
if current_path[i] == item_path[i] {
219-
i += 1
220-
} else {
221-
break;
222-
}
223-
}
217+
// length of the common prefix of the two paths
218+
let prefix_length = current_path.iter().zip(&item_path).take_while(|(a, b)| a == b).count();
224219

225-
let distinct_distance = current_path.len() + item_path.len();
226-
let common_prefix = 2 * i;
220+
// how many modules differ between the two paths (all modules, removing any duplicates)
221+
let distinct_length = current_path.len() + item_path.len() - 2 * prefix_length;
227222

228-
// prefer builtin crates and items within the same crate
229-
let crate_boundary_cost =
230-
if item.krate().is_builtin(db) || current.krate() == item.krate() { 0 } else { 4 };
223+
// cost of importing from another crate
224+
let crate_boundary_cost = if current.krate() == item.krate() {
225+
0
226+
} else if item.krate().is_builtin(db) {
227+
2
228+
} else {
229+
4
230+
};
231231

232-
distinct_distance - common_prefix + crate_boundary_cost
232+
distinct_length + crate_boundary_cost
233233
}
234234

235235
#[cfg(test)]
@@ -266,14 +266,14 @@ mod tests {
266266
#[test]
267267
fn prefer_shorter_paths() {
268268
let before = r"
269-
//- /main.rs crate:main deps:foo,bar
270-
HashMap$0::new();
269+
//- /main.rs crate:main deps:foo,bar
270+
HashMap$0::new();
271271
272-
//- /lib.rs crate:foo
273-
pub mod collections { pub struct HashMap; }
272+
//- /lib.rs crate:foo
273+
pub mod collections { pub struct HashMap; }
274274
275-
//- /lib.rs crate:bar
276-
pub mod collections { pub mod hash_map { pub struct HashMap; } }
275+
//- /lib.rs crate:bar
276+
pub mod collections { pub mod hash_map { pub struct HashMap; } }
277277
";
278278

279279
check_auto_import_order(
@@ -285,17 +285,17 @@ mod tests {
285285
#[test]
286286
fn prefer_same_crate() {
287287
let before = r"
288-
//- /main.rs crate:main deps:foo
289-
HashMap$0::new();
288+
//- /main.rs crate:main deps:foo
289+
HashMap$0::new();
290290
291-
mod collections {
292-
pub mod hash_map {
293-
pub struct HashMap;
294-
}
295-
}
291+
mod collections {
292+
pub mod hash_map {
293+
pub struct HashMap;
294+
}
295+
}
296296
297-
//- /lib.rs crate:foo
298-
pub struct HashMap;
297+
//- /lib.rs crate:foo
298+
pub struct HashMap;
299299
";
300300

301301
check_auto_import_order(

0 commit comments

Comments
 (0)