@@ -9,7 +9,7 @@ use syntax::{
9
9
algo,
10
10
ast:: {
11
11
self , edit_in_place:: Removable , make, AstNode , HasAttrs , HasModuleItem , HasVisibility ,
12
- PathSegmentKind , UseTree ,
12
+ PathSegmentKind ,
13
13
} ,
14
14
ted, Direction , NodeOrToken , SyntaxKind , SyntaxNode ,
15
15
} ;
@@ -26,14 +26,18 @@ pub use hir::PrefixKind;
26
26
/// How imports should be grouped into use statements.
27
27
#[ derive( Copy , Clone , Debug , PartialEq , Eq ) ]
28
28
pub enum ImportGranularity {
29
- /// Do not change the granularity of any imports and preserve the original structure written by the developer.
29
+ /// Do not change the granularity of any imports and preserve the original structure written
30
+ /// by the developer.
30
31
Preserve ,
31
32
/// Merge imports from the same crate into a single use statement.
32
33
Crate ,
33
34
/// Merge imports from the same module into a single use statement.
34
35
Module ,
35
36
/// Flatten imports so that each has its own use statement.
36
37
Item ,
38
+ /// Merge all imports into a single use statement as long as they have the same visibility
39
+ /// and attributes.
40
+ One ,
37
41
}
38
42
39
43
#[ derive( Clone , Copy , Debug , PartialEq , Eq ) ]
@@ -167,7 +171,7 @@ pub fn insert_use_as_alias(scope: &ImportScope, path: ast::Path, cfg: &InsertUse
167
171
. tree ( )
168
172
. syntax ( )
169
173
. descendants ( )
170
- . find_map ( UseTree :: cast)
174
+ . find_map ( ast :: UseTree :: cast)
171
175
. expect ( "Failed to make ast node `Rename`" ) ;
172
176
let alias = node. rename ( ) ;
173
177
@@ -184,6 +188,7 @@ fn insert_use_with_alias_option(
184
188
let mut mb = match cfg. granularity {
185
189
ImportGranularity :: Crate => Some ( MergeBehavior :: Crate ) ,
186
190
ImportGranularity :: Module => Some ( MergeBehavior :: Module ) ,
191
+ ImportGranularity :: One => Some ( MergeBehavior :: One ) ,
187
192
ImportGranularity :: Item | ImportGranularity :: Preserve => None ,
188
193
} ;
189
194
if !cfg. enforce_granularity {
@@ -195,11 +200,16 @@ fn insert_use_with_alias_option(
195
200
ImportGranularityGuess :: ModuleOrItem => mb. and ( Some ( MergeBehavior :: Module ) ) ,
196
201
ImportGranularityGuess :: Crate => Some ( MergeBehavior :: Crate ) ,
197
202
ImportGranularityGuess :: CrateOrModule => mb. or ( Some ( MergeBehavior :: Crate ) ) ,
203
+ ImportGranularityGuess :: One => Some ( MergeBehavior :: One ) ,
198
204
} ;
199
205
}
200
206
201
- let use_item =
202
- make:: use_ ( None , make:: use_tree ( path. clone ( ) , None , alias, false ) ) . clone_for_update ( ) ;
207
+ let mut use_tree = make:: use_tree ( path. clone ( ) , None , alias, false ) ;
208
+ if mb == Some ( MergeBehavior :: One ) && use_tree. path ( ) . is_some ( ) {
209
+ use_tree = use_tree. clone_for_update ( ) ;
210
+ use_tree. wrap_in_tree_list ( ) ;
211
+ }
212
+ let use_item = make:: use_ ( None , use_tree) . clone_for_update ( ) ;
203
213
204
214
// merge into existing imports if possible
205
215
if let Some ( mb) = mb {
@@ -216,7 +226,7 @@ fn insert_use_with_alias_option(
216
226
217
227
// either we weren't allowed to merge or there is no import that fits the merge conditions
218
228
// so look for the place we have to insert to
219
- insert_use_ ( scope, & path , cfg. group , use_item ) ;
229
+ insert_use_ ( scope, use_item , cfg. group ) ;
220
230
}
221
231
222
232
pub fn ast_to_remove_for_path_in_use_stmt ( path : & ast:: Path ) -> Option < Box < dyn Removable > > {
@@ -248,15 +258,18 @@ enum ImportGroup {
248
258
ThisCrate ,
249
259
ThisModule ,
250
260
SuperModule ,
261
+ One ,
251
262
}
252
263
253
264
impl ImportGroup {
254
- fn new ( path : & ast:: Path ) -> ImportGroup {
255
- let default = ImportGroup :: ExternCrate ;
265
+ fn new ( use_tree : & ast:: UseTree ) -> ImportGroup {
266
+ if use_tree. path ( ) . is_none ( ) && use_tree. use_tree_list ( ) . is_some ( ) {
267
+ return ImportGroup :: One ;
268
+ }
256
269
257
- let first_segment = match path. first_segment ( ) {
258
- Some ( it ) => it ,
259
- None => return default ,
270
+ let Some ( first_segment) = use_tree . path ( ) . as_ref ( ) . and_then ( ast :: Path :: first_segment )
271
+ else {
272
+ return ImportGroup :: ExternCrate ;
260
273
} ;
261
274
262
275
let kind = first_segment. kind ( ) . unwrap_or ( PathSegmentKind :: SelfKw ) ;
@@ -284,6 +297,7 @@ enum ImportGranularityGuess {
284
297
ModuleOrItem ,
285
298
Crate ,
286
299
CrateOrModule ,
300
+ One ,
287
301
}
288
302
289
303
fn guess_granularity_from_scope ( scope : & ImportScope ) -> ImportGranularityGuess {
@@ -303,12 +317,24 @@ fn guess_granularity_from_scope(scope: &ImportScope) -> ImportGranularityGuess {
303
317
}
304
318
. filter_map ( use_stmt) ;
305
319
let mut res = ImportGranularityGuess :: Unknown ;
306
- let ( mut prev, mut prev_vis, mut prev_attrs) = match use_stmts. next ( ) {
307
- Some ( it) => it,
308
- None => return res,
309
- } ;
320
+ let Some ( ( mut prev, mut prev_vis, mut prev_attrs) ) = use_stmts. next ( ) else { return res } ;
321
+
322
+ let is_tree_one_style =
323
+ |use_tree : & ast:: UseTree | use_tree. path ( ) . is_none ( ) && use_tree. use_tree_list ( ) . is_some ( ) ;
324
+ let mut seen_one_style_groups = Vec :: new ( ) ;
325
+
310
326
loop {
311
- if let Some ( use_tree_list) = prev. use_tree_list ( ) {
327
+ if is_tree_one_style ( & prev) {
328
+ if res != ImportGranularityGuess :: One {
329
+ if res != ImportGranularityGuess :: Unknown {
330
+ // This scope has a mix of one-style and other style imports.
331
+ break ImportGranularityGuess :: Unknown ;
332
+ }
333
+
334
+ res = ImportGranularityGuess :: One ;
335
+ seen_one_style_groups. push ( ( prev_vis. clone ( ) , prev_attrs. clone ( ) ) ) ;
336
+ }
337
+ } else if let Some ( use_tree_list) = prev. use_tree_list ( ) {
312
338
if use_tree_list. use_trees ( ) . any ( |tree| tree. use_tree_list ( ) . is_some ( ) ) {
313
339
// Nested tree lists can only occur in crate style, or with no proper style being enforced in the file.
314
340
break ImportGranularityGuess :: Crate ;
@@ -318,11 +344,22 @@ fn guess_granularity_from_scope(scope: &ImportScope) -> ImportGranularityGuess {
318
344
}
319
345
}
320
346
321
- let ( curr, curr_vis, curr_attrs) = match use_stmts. next ( ) {
322
- Some ( it) => it,
323
- None => break res,
324
- } ;
325
- if eq_visibility ( prev_vis, curr_vis. clone ( ) ) && eq_attrs ( prev_attrs, curr_attrs. clone ( ) ) {
347
+ let Some ( ( curr, curr_vis, curr_attrs) ) = use_stmts. next ( ) else { break res } ;
348
+ if is_tree_one_style ( & curr) {
349
+ if res != ImportGranularityGuess :: One
350
+ || seen_one_style_groups. iter ( ) . any ( |( prev_vis, prev_attrs) | {
351
+ eq_visibility ( prev_vis. clone ( ) , curr_vis. clone ( ) )
352
+ && eq_attrs ( prev_attrs. clone ( ) , curr_attrs. clone ( ) )
353
+ } )
354
+ {
355
+ // This scope has either a mix of one-style and other style imports or
356
+ // multiple one-style imports with the same visibility and attributes.
357
+ break ImportGranularityGuess :: Unknown ;
358
+ }
359
+ seen_one_style_groups. push ( ( curr_vis. clone ( ) , curr_attrs. clone ( ) ) ) ;
360
+ } else if eq_visibility ( prev_vis, curr_vis. clone ( ) )
361
+ && eq_attrs ( prev_attrs, curr_attrs. clone ( ) )
362
+ {
326
363
if let Some ( ( prev_path, curr_path) ) = prev. path ( ) . zip ( curr. path ( ) ) {
327
364
if let Some ( ( prev_prefix, _) ) = common_prefix ( & prev_path, & curr_path) {
328
365
if prev. use_tree_list ( ) . is_none ( ) && curr. use_tree_list ( ) . is_none ( ) {
@@ -350,39 +387,33 @@ fn guess_granularity_from_scope(scope: &ImportScope) -> ImportGranularityGuess {
350
387
}
351
388
}
352
389
353
- fn insert_use_ (
354
- scope : & ImportScope ,
355
- insert_path : & ast:: Path ,
356
- group_imports : bool ,
357
- use_item : ast:: Use ,
358
- ) {
390
+ fn insert_use_ ( scope : & ImportScope , use_item : ast:: Use , group_imports : bool ) {
359
391
let scope_syntax = scope. as_syntax_node ( ) ;
360
392
let insert_use_tree =
361
393
use_item. use_tree ( ) . expect ( "`use_item` should have a use tree for `insert_path`" ) ;
362
- let group = ImportGroup :: new ( insert_path ) ;
394
+ let group = ImportGroup :: new ( & insert_use_tree ) ;
363
395
let path_node_iter = scope_syntax
364
396
. children ( )
365
397
. filter_map ( |node| ast:: Use :: cast ( node. clone ( ) ) . zip ( Some ( node) ) )
366
398
. flat_map ( |( use_, node) | {
367
399
let tree = use_. use_tree ( ) ?;
368
- let path = tree. path ( ) ?;
369
- Some ( ( path, tree, node) )
400
+ Some ( ( tree, node) )
370
401
} ) ;
371
402
372
403
if group_imports {
373
- // Iterator that discards anything thats not in the required grouping
404
+ // Iterator that discards anything that's not in the required grouping
374
405
// This implementation allows the user to rearrange their import groups as this only takes the first group that fits
375
406
let group_iter = path_node_iter
376
407
. clone ( )
377
- . skip_while ( |( path , ..) | ImportGroup :: new ( path ) != group)
378
- . take_while ( |( path , ..) | ImportGroup :: new ( path ) == group) ;
408
+ . skip_while ( |( use_tree , ..) | ImportGroup :: new ( use_tree ) != group)
409
+ . take_while ( |( use_tree , ..) | ImportGroup :: new ( use_tree ) == group) ;
379
410
380
411
// track the last element we iterated over, if this is still None after the iteration then that means we never iterated in the first place
381
412
let mut last = None ;
382
413
// find the element that would come directly after our new import
383
- let post_insert: Option < ( _ , _ , SyntaxNode ) > = group_iter
414
+ let post_insert: Option < ( _ , SyntaxNode ) > = group_iter
384
415
. inspect ( |( .., node) | last = Some ( node. clone ( ) ) )
385
- . find ( |( _ , use_tree, _) | use_tree_cmp ( & insert_use_tree, use_tree) != Ordering :: Greater ) ;
416
+ . find ( |( use_tree, _) | use_tree_cmp ( & insert_use_tree, use_tree) != Ordering :: Greater ) ;
386
417
387
418
if let Some ( ( .., node) ) = post_insert {
388
419
cov_mark:: hit!( insert_group) ;
@@ -401,7 +432,7 @@ fn insert_use_(
401
432
// find the group that comes after where we want to insert
402
433
let post_group = path_node_iter
403
434
. inspect ( |( .., node) | last = Some ( node. clone ( ) ) )
404
- . find ( |( p , ..) | ImportGroup :: new ( p ) > group) ;
435
+ . find ( |( use_tree , ..) | ImportGroup :: new ( use_tree ) > group) ;
405
436
if let Some ( ( .., node) ) = post_group {
406
437
cov_mark:: hit!( insert_group_new_group) ;
407
438
ted:: insert ( ted:: Position :: before ( & node) , use_item. syntax ( ) ) ;
@@ -419,7 +450,7 @@ fn insert_use_(
419
450
}
420
451
} else {
421
452
// There exists a group, so append to the end of it
422
- if let Some ( ( _, _ , node) ) = path_node_iter. last ( ) {
453
+ if let Some ( ( _, node) ) = path_node_iter. last ( ) {
423
454
cov_mark:: hit!( insert_no_grouping_last) ;
424
455
ted:: insert ( ted:: Position :: after ( node) , use_item. syntax ( ) ) ;
425
456
return ;
0 commit comments