Skip to content

Commit 073cc38

Browse files
MattXcalebcartwright
authored andcommitted
Option to create groups for std, external crates, and other imports
Backport of 17d90ca.
1 parent 580d826 commit 073cc38

12 files changed

+254
-22
lines changed

Diff for: Configurations.md

+50
Original file line numberDiff line numberDiff line change
@@ -1889,6 +1889,56 @@ use dolor;
18891889
use sit;
18901890
```
18911891

1892+
## `group_imports`
1893+
1894+
Controls the strategy for how imports are grouped together.
1895+
1896+
- **Default value**: `Preserve`
1897+
- **Possible values**: `Preserve`, `StdExternalCrate`
1898+
- **Stable**: No
1899+
1900+
#### `Preserve` (default):
1901+
1902+
Preserve the source file's import groups.
1903+
1904+
```rust
1905+
use super::update::convert_publish_payload;
1906+
use chrono::Utc;
1907+
1908+
use alloc::alloc::Layout;
1909+
use juniper::{FieldError, FieldResult};
1910+
use uuid::Uuid;
1911+
1912+
use std::sync::Arc;
1913+
1914+
use broker::database::PooledConnection;
1915+
1916+
use super::schema::{Context, Payload};
1917+
use crate::models::Event;
1918+
use core::f32;
1919+
```
1920+
1921+
#### `StdExternalCrate`:
1922+
1923+
Discard existing import groups, and create three groups for:
1924+
1. `std`, `core` and `alloc`,
1925+
2. external crates,
1926+
3. `self`, `super` and `crate` imports.
1927+
1928+
```rust
1929+
use alloc::alloc::Layout;
1930+
use core::f32;
1931+
use std::sync::Arc;
1932+
1933+
use broker::database::PooledConnection;
1934+
use chrono::Utc;
1935+
use juniper::{FieldError, FieldResult};
1936+
use uuid::Uuid;
1937+
1938+
use super::schema::{Context, Payload};
1939+
use super::update::convert_publish_payload;
1940+
use crate::models::Event;
1941+
```
18921942

18931943
## `reorder_modules`
18941944

Diff for: src/config/mod.rs

+3
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ create_config! {
6565
imports_indent: IndentStyle, IndentStyle::Block, false, "Indent of imports";
6666
imports_layout: ListTactic, ListTactic::Mixed, false, "Item layout inside a import block";
6767
merge_imports: bool, false, false, "Merge imports";
68+
group_imports: GroupImportsTactic, GroupImportsTactic::Preserve, false,
69+
"Controls the strategy for how imports are grouped together";
6870

6971
// Ordering
7072
reorder_imports: bool, true, true, "Reorder import and extern crate statements alphabetically";
@@ -528,6 +530,7 @@ where_single_line = false
528530
imports_indent = "Block"
529531
imports_layout = "Mixed"
530532
merge_imports = false
533+
group_imports = "Preserve"
531534
reorder_imports = true
532535
reorder_modules = true
533536
reorder_impl_items = false

Diff for: src/config/options.rs

+12
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,18 @@ impl Density {
9999
}
100100
}
101101

102+
#[config_type]
103+
/// Configuration for import groups, i.e. sets of imports separated by newlines.
104+
pub enum GroupImportsTactic {
105+
/// Keep groups as they are.
106+
Preserve,
107+
/// Discard existing groups, and create new groups for
108+
/// 1. `std` / `core` / `alloc` imports
109+
/// 2. other imports
110+
/// 3. `self` / `crate` / `super` imports
111+
StdExternalCrate,
112+
}
113+
102114
#[config_type]
103115
pub enum ReportTactic {
104116
Always,

Diff for: src/reorder.rs

+83-22
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ use std::cmp::{Ord, Ordering};
1111
use rustc_ast::ast;
1212
use rustc_span::{symbol::sym, Span};
1313

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};
1616
use crate::items::{is_mod_decl, rewrite_extern_crate, rewrite_mod};
1717
use crate::lists::{itemize_list, write_list, ListFormatting, ListItem};
1818
use crate::rewrite::RewriteContext;
@@ -74,9 +74,10 @@ fn rewrite_reorderable_item(
7474
}
7575
}
7676

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(
8081
context: &RewriteContext<'_>,
8182
reorderable_items: &[&ast::Item],
8283
shape: Shape,
@@ -109,19 +110,35 @@ fn rewrite_reorderable_items(
109110
if context.config.merge_imports() {
110111
normalized_items = merge_use_trees(normalized_items);
111112
}
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+
}
113122

114123
// 4 = "use ", 1 = ";"
115124
let nested_shape = shape.offset_left(4)?.sub_width(1)?;
116-
let item_vec: Vec<_> = normalized_items
125+
let item_vec: Vec<_> = regrouped_items
117126
.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)
121137
})
122-
.collect();
138+
.collect::<Option<Vec<_>>>()?;
123139

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))
125142
}
126143
_ => {
127144
let list_items = itemize_list(
@@ -150,6 +167,34 @@ fn contains_macro_use_attr(item: &ast::Item) -> bool {
150167
crate::attr::contains_name(&item.attrs, sym::macro_use)
151168
}
152169

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+
153198
/// A simplified version of `ast::ItemKind`.
154199
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
155200
enum ReorderableItemKind {
@@ -187,21 +232,29 @@ impl ReorderableItemKind {
187232
}
188233
}
189234

190-
fn in_group(self) -> bool {
235+
fn is_regroupable(self, config: &Config) -> bool {
191236
match self {
192237
ReorderableItemKind::ExternCrate
193238
| 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,
195248
ReorderableItemKind::Other => false,
196249
}
197250
}
198251
}
199252

200253
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(
205258
&mut self,
206259
items: &[&ast::Item],
207260
item_kind: ReorderableItemKind,
@@ -230,7 +283,12 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
230283
let lo = items.first().unwrap().span().lo();
231284
let hi = items.last().unwrap().span().hi();
232285
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+
);
234292
self.push_rewrite(span, rw);
235293
} else {
236294
for item in items {
@@ -249,9 +307,12 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
249307
// subsequent items that have the same item kind to be reordered within
250308
// `walk_reorderable_items`. Otherwise, just format the next item for output.
251309
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+
);
255316
let (_, rest) = items.split_at(visited_items_num);
256317
items = rest;
257318
} else {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// rustfmt-group_imports: StdExternalCrate
2+
// rustfmt-merge_imports: true
3+
use chrono::Utc;
4+
use super::update::convert_publish_payload;
5+
6+
use juniper::{FieldError, FieldResult};
7+
use uuid::Uuid;
8+
use alloc::alloc::Layout;
9+
10+
use std::sync::Arc;
11+
use alloc::vec::Vec;
12+
13+
use broker::database::PooledConnection;
14+
15+
use super::schema::{Context, Payload};
16+
use core::f32;
17+
use crate::models::Event;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// rustfmt-group_imports: StdExternalCrate
2+
mod test {
3+
use crate::foo::bar;
4+
use std::path;
5+
use crate::foo::bar2;
6+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// rustfmt-group_imports: StdExternalCrate
2+
// rustfmt-reorder_imports: false
3+
4+
use chrono::Utc;
5+
use super::update::convert_publish_payload;
6+
7+
use juniper::{FieldError, FieldResult};
8+
use uuid::Uuid;
9+
use alloc::alloc::Layout;
10+
11+
use std::sync::Arc;
12+
13+
use broker::database::PooledConnection;
14+
15+
use super::schema::{Context, Payload};
16+
use core::f32;
17+
use crate::models::Event;
+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// rustfmt-group_imports: StdExternalCrate
2+
use chrono::Utc;
3+
use super::update::convert_publish_payload;
4+
5+
use juniper::{FieldError, FieldResult};
6+
use uuid::Uuid;
7+
use alloc::alloc::Layout;
8+
9+
use std::sync::Arc;
10+
11+
use broker::database::PooledConnection;
12+
13+
use super::schema::{Context, Payload};
14+
use core::f32;
15+
use crate::models::Event;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// rustfmt-group_imports: StdExternalCrate
2+
// rustfmt-merge_imports: true
3+
use alloc::{alloc::Layout, vec::Vec};
4+
use core::f32;
5+
use std::sync::Arc;
6+
7+
use broker::database::PooledConnection;
8+
use chrono::Utc;
9+
use juniper::{FieldError, FieldResult};
10+
use uuid::Uuid;
11+
12+
use super::{
13+
schema::{Context, Payload},
14+
update::convert_publish_payload,
15+
};
16+
use crate::models::Event;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// rustfmt-group_imports: StdExternalCrate
2+
mod test {
3+
use std::path;
4+
5+
use crate::foo::bar;
6+
use crate::foo::bar2;
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// rustfmt-group_imports: StdExternalCrate
2+
// rustfmt-reorder_imports: false
3+
4+
use alloc::alloc::Layout;
5+
use std::sync::Arc;
6+
use core::f32;
7+
8+
use chrono::Utc;
9+
use juniper::{FieldError, FieldResult};
10+
use uuid::Uuid;
11+
use broker::database::PooledConnection;
12+
13+
use super::update::convert_publish_payload;
14+
use super::schema::{Context, Payload};
15+
use crate::models::Event;
+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// rustfmt-group_imports: StdExternalCrate
2+
use alloc::alloc::Layout;
3+
use core::f32;
4+
use std::sync::Arc;
5+
6+
use broker::database::PooledConnection;
7+
use chrono::Utc;
8+
use juniper::{FieldError, FieldResult};
9+
use uuid::Uuid;
10+
11+
use super::schema::{Context, Payload};
12+
use super::update::convert_publish_payload;
13+
use crate::models::Event;

0 commit comments

Comments
 (0)