@@ -11,7 +11,8 @@ use std::cmp::{Ord, Ordering};
11
11
use rustc_ast:: ast;
12
12
use rustc_span:: { symbol:: sym, Span } ;
13
13
14
- use crate :: config:: Config ;
14
+ use crate :: config:: { Config , GroupImportsTactic } ;
15
+ use crate :: formatting:: imports:: UseSegment ;
15
16
use crate :: formatting:: modules:: { get_mod_inner_attrs, FileModMap } ;
16
17
use crate :: formatting:: {
17
18
imports:: { merge_use_trees, UseTree } ,
@@ -192,9 +193,10 @@ fn rewrite_reorderable_item(
192
193
}
193
194
}
194
195
195
- /// Rewrite a list of items with reordering. Every item in `items` must have
196
- /// the same `ast::ItemKind`.
197
- fn rewrite_reorderable_items (
196
+ /// Rewrite a list of items with reordering and/or regrouping. Every item
197
+ /// in `items` must have the same `ast::ItemKind`. Whether reordering, regrouping,
198
+ /// or both are done is determined from the `context`.
199
+ fn rewrite_reorderable_or_regroupable_items (
198
200
context : & RewriteContext < ' _ > ,
199
201
reorderable_items : & [ & ast:: Item ] ,
200
202
shape : Shape ,
@@ -227,19 +229,35 @@ fn rewrite_reorderable_items(
227
229
if context. config . merge_imports ( ) {
228
230
normalized_items = merge_use_trees ( normalized_items) ;
229
231
}
230
- normalized_items. sort ( ) ;
232
+
233
+ let mut regrouped_items = match context. config . group_imports ( ) {
234
+ GroupImportsTactic :: Preserve => vec ! [ normalized_items] ,
235
+ GroupImportsTactic :: StdExternalCrate => group_imports ( normalized_items) ,
236
+ } ;
237
+
238
+ if context. config . reorder_imports ( ) {
239
+ regrouped_items. iter_mut ( ) . for_each ( |items| items. sort ( ) )
240
+ }
231
241
232
242
// 4 = "use ", 1 = ";"
233
243
let nested_shape = shape. offset_left ( 4 ) ?. sub_width ( 1 ) ?;
234
- let item_vec: Vec < _ > = normalized_items
244
+ let item_vec: Vec < _ > = regrouped_items
235
245
. into_iter ( )
236
- . map ( |use_tree| ListItem {
237
- item : use_tree. rewrite_top_level ( context, nested_shape) ,
238
- ..use_tree. list_item . unwrap_or_else ( ListItem :: empty)
246
+ . filter ( |use_group| !use_group. is_empty ( ) )
247
+ . map ( |use_group| {
248
+ let item_vec: Vec < _ > = use_group
249
+ . into_iter ( )
250
+ . map ( |use_tree| ListItem {
251
+ item : use_tree. rewrite_top_level ( context, nested_shape) ,
252
+ ..use_tree. list_item . unwrap_or_else ( ListItem :: empty)
253
+ } )
254
+ . collect ( ) ;
255
+ wrap_reorderable_items ( context, & item_vec, nested_shape)
239
256
} )
240
- . collect ( ) ;
257
+ . collect :: < Option < Vec < _ > > > ( ) ? ;
241
258
242
- wrap_reorderable_items ( context, & item_vec, nested_shape)
259
+ let join_string = format ! ( "\n \n {}" , shape. indent. to_string( context. config) ) ;
260
+ Some ( item_vec. join ( & join_string) )
243
261
}
244
262
_ => {
245
263
let list_items = itemize_list (
@@ -268,6 +286,34 @@ fn contains_macro_use_attr(attrs: &[ast::Attribute]) -> bool {
268
286
crate :: formatting:: attr:: contains_name ( attrs, sym:: macro_use)
269
287
}
270
288
289
+ /// Divides imports into three groups, corresponding to standard, external
290
+ /// and local imports. Sorts each subgroup.
291
+ fn group_imports ( uts : Vec < UseTree > ) -> Vec < Vec < UseTree > > {
292
+ let mut std_imports = Vec :: new ( ) ;
293
+ let mut external_imports = Vec :: new ( ) ;
294
+ let mut local_imports = Vec :: new ( ) ;
295
+
296
+ for ut in uts. into_iter ( ) {
297
+ if ut. path . is_empty ( ) {
298
+ external_imports. push ( ut) ;
299
+ continue ;
300
+ }
301
+ match & ut. path [ 0 ] {
302
+ UseSegment :: Ident ( id, _) => match id. as_ref ( ) {
303
+ "std" | "alloc" | "core" => std_imports. push ( ut) ,
304
+ _ => external_imports. push ( ut) ,
305
+ } ,
306
+ UseSegment :: Slf ( _) | UseSegment :: Super ( _) | UseSegment :: Crate ( _) => {
307
+ local_imports. push ( ut)
308
+ }
309
+ // These are probably illegal here
310
+ UseSegment :: Glob | UseSegment :: List ( _) => external_imports. push ( ut) ,
311
+ }
312
+ }
313
+
314
+ vec ! [ std_imports, external_imports, local_imports]
315
+ }
316
+
271
317
/// A simplified version of `ast::ItemKind`.
272
318
#[ derive( Debug , PartialEq , Eq , Copy , Clone ) ]
273
319
enum ReorderableItemKind {
@@ -311,21 +357,29 @@ impl ReorderableItemKind {
311
357
}
312
358
}
313
359
314
- fn in_group ( self ) -> bool {
360
+ fn is_regroupable ( self , config : & Config ) -> bool {
315
361
match self {
316
362
ReorderableItemKind :: ExternCrate
317
363
| ReorderableItemKind :: Mod
318
- | ReorderableItemKind :: Use => true ,
364
+ | ReorderableItemKind :: Other => false ,
365
+ ReorderableItemKind :: Use => config. group_imports ( ) != GroupImportsTactic :: Preserve ,
366
+ }
367
+ }
368
+
369
+ fn in_group ( self , config : & Config ) -> bool {
370
+ match self {
371
+ ReorderableItemKind :: ExternCrate | ReorderableItemKind :: Mod => true ,
372
+ ReorderableItemKind :: Use => config. group_imports ( ) == GroupImportsTactic :: Preserve ,
319
373
ReorderableItemKind :: Other => false ,
320
374
}
321
375
}
322
376
}
323
377
324
378
impl < ' b , ' a : ' b > FmtVisitor < ' a > {
325
- /// Format items with the same item kind and reorder them. If `in_group` is
326
- /// ` true`, then the items separated by an empty line will not be reordered
327
- /// together.
328
- fn walk_reorderable_items (
379
+ /// Format items with the same item kind and reorder them, regroup them, or
380
+ /// both. If `in_group` is ` true`, then the items separated by an empty line
381
+ /// will not be reordered together.
382
+ fn walk_reorderable_or_regroupable_items (
329
383
& mut self ,
330
384
items : & [ & ast:: Item ] ,
331
385
item_kind : ReorderableItemKind ,
@@ -355,7 +409,12 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
355
409
let lo = items. first ( ) . unwrap ( ) . span ( ) . lo ( ) ;
356
410
let hi = items. last ( ) . unwrap ( ) . span ( ) . hi ( ) ;
357
411
let span = mk_sp ( lo, hi) ;
358
- let rw = rewrite_reorderable_items ( & self . get_context ( ) , items, self . shape ( ) , span) ;
412
+ let rw = rewrite_reorderable_or_regroupable_items (
413
+ & self . get_context ( ) ,
414
+ items,
415
+ self . shape ( ) ,
416
+ span,
417
+ ) ;
359
418
self . push_rewrite ( span, rw) ;
360
419
} else {
361
420
for item in items {
@@ -374,9 +433,12 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
374
433
// subsequent items that have the same item kind to be reordered within
375
434
// `walk_reorderable_items`. Otherwise, just format the next item for output.
376
435
let item_kind = ReorderableItemKind :: from ( items[ 0 ] , self . file_mod_map ) ;
377
- if item_kind. is_reorderable ( self . config ) {
378
- let visited_items_num =
379
- self . walk_reorderable_items ( items, item_kind, item_kind. in_group ( ) ) ;
436
+ if item_kind. is_reorderable ( self . config ) || item_kind. is_regroupable ( self . config ) {
437
+ let visited_items_num = self . walk_reorderable_or_regroupable_items (
438
+ items,
439
+ item_kind,
440
+ item_kind. in_group ( self . config ) ,
441
+ ) ;
380
442
let ( _, rest) = items. split_at ( visited_items_num) ;
381
443
items = rest;
382
444
} else {
0 commit comments