@@ -11,8 +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 ;
15
- use crate :: imports:: { merge_use_trees, UseTree } ;
14
+ use crate :: config:: { Config , GroupImportsTactic } ;
15
+ use crate :: imports:: { merge_use_trees, UseSegment , UseTree } ;
16
16
use crate :: items:: { is_mod_decl, rewrite_extern_crate, rewrite_mod} ;
17
17
use crate :: lists:: { itemize_list, write_list, ListFormatting , ListItem } ;
18
18
use crate :: rewrite:: RewriteContext ;
@@ -74,9 +74,10 @@ fn rewrite_reorderable_item(
74
74
}
75
75
}
76
76
77
- /// Rewrite a list of items with reordering. Every item in `items` must have
78
- /// the same `ast::ItemKind`.
79
- fn rewrite_reorderable_items (
77
+ /// Rewrite a list of items with reordering and/or regrouping. Every item
78
+ /// in `items` must have the same `ast::ItemKind`. Whether reordering, regrouping,
79
+ /// or both are done is determined from the `context`.
80
+ fn rewrite_reorderable_or_regroupable_items (
80
81
context : & RewriteContext < ' _ > ,
81
82
reorderable_items : & [ & ast:: Item ] ,
82
83
shape : Shape ,
@@ -109,19 +110,35 @@ fn rewrite_reorderable_items(
109
110
if context. config . merge_imports ( ) {
110
111
normalized_items = merge_use_trees ( normalized_items) ;
111
112
}
112
- normalized_items. sort ( ) ;
113
+
114
+ let mut regrouped_items = match context. config . group_imports ( ) {
115
+ GroupImportsTactic :: Preserve => vec ! [ normalized_items] ,
116
+ GroupImportsTactic :: StdExternalCrate => group_imports ( normalized_items) ,
117
+ } ;
118
+
119
+ if context. config . reorder_imports ( ) {
120
+ regrouped_items. iter_mut ( ) . for_each ( |items| items. sort ( ) )
121
+ }
113
122
114
123
// 4 = "use ", 1 = ";"
115
124
let nested_shape = shape. offset_left ( 4 ) ?. sub_width ( 1 ) ?;
116
- let item_vec: Vec < _ > = normalized_items
125
+ let item_vec: Vec < _ > = regrouped_items
117
126
. into_iter ( )
118
- . map ( |use_tree| ListItem {
119
- item : use_tree. rewrite_top_level ( context, nested_shape) ,
120
- ..use_tree. list_item . unwrap_or_else ( ListItem :: empty)
127
+ . filter ( |use_group| !use_group. is_empty ( ) )
128
+ . map ( |use_group| {
129
+ let item_vec: Vec < _ > = use_group
130
+ . into_iter ( )
131
+ . map ( |use_tree| ListItem {
132
+ item : use_tree. rewrite_top_level ( context, nested_shape) ,
133
+ ..use_tree. list_item . unwrap_or_else ( ListItem :: empty)
134
+ } )
135
+ . collect ( ) ;
136
+ wrap_reorderable_items ( context, & item_vec, nested_shape)
121
137
} )
122
- . collect ( ) ;
138
+ . collect :: < Option < Vec < _ > > > ( ) ? ;
123
139
124
- wrap_reorderable_items ( context, & item_vec, nested_shape)
140
+ let join_string = format ! ( "\n \n {}" , shape. indent. to_string( context. config) ) ;
141
+ Some ( item_vec. join ( & join_string) )
125
142
}
126
143
_ => {
127
144
let list_items = itemize_list (
@@ -150,6 +167,34 @@ fn contains_macro_use_attr(item: &ast::Item) -> bool {
150
167
crate :: attr:: contains_name ( & item. attrs , sym:: macro_use)
151
168
}
152
169
170
+ /// Divides imports into three groups, corresponding to standard, external
171
+ /// and local imports. Sorts each subgroup.
172
+ fn group_imports ( uts : Vec < UseTree > ) -> Vec < Vec < UseTree > > {
173
+ let mut std_imports = Vec :: new ( ) ;
174
+ let mut external_imports = Vec :: new ( ) ;
175
+ let mut local_imports = Vec :: new ( ) ;
176
+
177
+ for ut in uts. into_iter ( ) {
178
+ if ut. path . is_empty ( ) {
179
+ external_imports. push ( ut) ;
180
+ continue ;
181
+ }
182
+ match & ut. path [ 0 ] {
183
+ UseSegment :: Ident ( id, _) => match id. as_ref ( ) {
184
+ "std" | "alloc" | "core" => std_imports. push ( ut) ,
185
+ _ => external_imports. push ( ut) ,
186
+ } ,
187
+ UseSegment :: Slf ( _) | UseSegment :: Super ( _) | UseSegment :: Crate ( _) => {
188
+ local_imports. push ( ut)
189
+ }
190
+ // These are probably illegal here
191
+ UseSegment :: Glob | UseSegment :: List ( _) => external_imports. push ( ut) ,
192
+ }
193
+ }
194
+
195
+ vec ! [ std_imports, external_imports, local_imports]
196
+ }
197
+
153
198
/// A simplified version of `ast::ItemKind`.
154
199
#[ derive( Debug , PartialEq , Eq , Copy , Clone ) ]
155
200
enum ReorderableItemKind {
@@ -187,21 +232,29 @@ impl ReorderableItemKind {
187
232
}
188
233
}
189
234
190
- fn in_group ( self ) -> bool {
235
+ fn is_regroupable ( self , config : & Config ) -> bool {
191
236
match self {
192
237
ReorderableItemKind :: ExternCrate
193
238
| ReorderableItemKind :: Mod
194
- | ReorderableItemKind :: Use => true ,
239
+ | ReorderableItemKind :: Other => false ,
240
+ ReorderableItemKind :: Use => config. group_imports ( ) != GroupImportsTactic :: Preserve ,
241
+ }
242
+ }
243
+
244
+ fn in_group ( self , config : & Config ) -> bool {
245
+ match self {
246
+ ReorderableItemKind :: ExternCrate | ReorderableItemKind :: Mod => true ,
247
+ ReorderableItemKind :: Use => config. group_imports ( ) == GroupImportsTactic :: Preserve ,
195
248
ReorderableItemKind :: Other => false ,
196
249
}
197
250
}
198
251
}
199
252
200
253
impl < ' b , ' a : ' b > FmtVisitor < ' a > {
201
- /// Format items with the same item kind and reorder them. If `in_group` is
202
- /// ` true`, then the items separated by an empty line will not be reordered
203
- /// together.
204
- fn walk_reorderable_items (
254
+ /// Format items with the same item kind and reorder them, regroup them, or
255
+ /// both. If `in_group` is ` true`, then the items separated by an empty line
256
+ /// will not be reordered together.
257
+ fn walk_reorderable_or_regroupable_items (
205
258
& mut self ,
206
259
items : & [ & ast:: Item ] ,
207
260
item_kind : ReorderableItemKind ,
@@ -230,7 +283,12 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
230
283
let lo = items. first ( ) . unwrap ( ) . span ( ) . lo ( ) ;
231
284
let hi = items. last ( ) . unwrap ( ) . span ( ) . hi ( ) ;
232
285
let span = mk_sp ( lo, hi) ;
233
- let rw = rewrite_reorderable_items ( & self . get_context ( ) , items, self . shape ( ) , span) ;
286
+ let rw = rewrite_reorderable_or_regroupable_items (
287
+ & self . get_context ( ) ,
288
+ items,
289
+ self . shape ( ) ,
290
+ span,
291
+ ) ;
234
292
self . push_rewrite ( span, rw) ;
235
293
} else {
236
294
for item in items {
@@ -249,9 +307,12 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
249
307
// subsequent items that have the same item kind to be reordered within
250
308
// `walk_reorderable_items`. Otherwise, just format the next item for output.
251
309
let item_kind = ReorderableItemKind :: from ( items[ 0 ] ) ;
252
- if item_kind. is_reorderable ( self . config ) {
253
- let visited_items_num =
254
- self . walk_reorderable_items ( items, item_kind, item_kind. in_group ( ) ) ;
310
+ if item_kind. is_reorderable ( self . config ) || item_kind. is_regroupable ( self . config ) {
311
+ let visited_items_num = self . walk_reorderable_or_regroupable_items (
312
+ items,
313
+ item_kind,
314
+ item_kind. in_group ( self . config ) ,
315
+ ) ;
255
316
let ( _, rest) = items. split_at ( visited_items_num) ;
256
317
items = rest;
257
318
} else {
0 commit comments