@@ -111,8 +111,17 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
111
111
// we aren't interested in different namespaces
112
112
proposed_imports. dedup_by ( |a, b| a. import_path == b. import_path ) ;
113
113
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
+
114
122
// 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 ( ) ) ) ) ;
116
125
117
126
for import in proposed_imports {
118
127
acc. add_group (
@@ -167,7 +176,11 @@ fn group_label(import_candidate: &ImportCandidate) -> GroupLabel {
167
176
168
177
/// Determine how relevant a given import is in the current context. Higher scores are more
169
178
/// 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 {
171
184
let mut score = 0 ;
172
185
173
186
let db = ctx. db ( ) ;
@@ -177,25 +190,11 @@ fn relevance_score(ctx: &AssistContext, import: &LocatedImport) -> i32 {
177
190
hir:: ItemInNs :: Macros ( makro) => Some ( makro. module ( db) ) ,
178
191
} ;
179
192
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 ;
199
198
}
200
199
201
200
// 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 {
206
205
}
207
206
208
207
/// 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
210
210
let mut current_path = current. path_to_root ( db) ;
211
211
let mut item_path = item. path_to_root ( db) ;
212
212
213
+ // we want paths going from the root to the item
213
214
current_path. reverse ( ) ;
214
215
item_path. reverse ( ) ;
215
216
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 ( ) ;
224
219
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 ;
227
222
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
+ } ;
231
231
232
- distinct_distance - common_prefix + crate_boundary_cost
232
+ distinct_length + crate_boundary_cost
233
233
}
234
234
235
235
#[ cfg( test) ]
@@ -266,14 +266,14 @@ mod tests {
266
266
#[ test]
267
267
fn prefer_shorter_paths ( ) {
268
268
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();
271
271
272
- //- /lib.rs crate:foo
273
- pub mod collections { pub struct HashMap; }
272
+ //- /lib.rs crate:foo
273
+ pub mod collections { pub struct HashMap; }
274
274
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; } }
277
277
" ;
278
278
279
279
check_auto_import_order (
@@ -285,17 +285,17 @@ mod tests {
285
285
#[ test]
286
286
fn prefer_same_crate ( ) {
287
287
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();
290
290
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
+ }
296
296
297
- //- /lib.rs crate:foo
298
- pub struct HashMap;
297
+ //- /lib.rs crate:foo
298
+ pub struct HashMap;
299
299
" ;
300
300
301
301
check_auto_import_order (
0 commit comments