Implement One
option for imports_granularity (#4669)
This option merges all imports into a single `use` statement as long as they have the same visibility.
This commit is contained in:
parent
e81c393663
commit
ca9b050bbf
6 changed files with 288 additions and 19 deletions
|
@ -1679,7 +1679,7 @@ pub enum Foo {}
|
||||||
How imports should be grouped into `use` statements. Imports will be merged or split to the configured level of granularity.
|
How imports should be grouped into `use` statements. Imports will be merged or split to the configured level of granularity.
|
||||||
|
|
||||||
- **Default value**: `Preserve`
|
- **Default value**: `Preserve`
|
||||||
- **Possible values**: `Preserve`, `Crate`, `Module`, `Item`
|
- **Possible values**: `Preserve`, `Crate`, `Module`, `Item`, `One`
|
||||||
- **Stable**: No
|
- **Stable**: No
|
||||||
|
|
||||||
#### `Preserve` (default):
|
#### `Preserve` (default):
|
||||||
|
@ -1733,6 +1733,23 @@ use qux::h;
|
||||||
use qux::i;
|
use qux::i;
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### `One`:
|
||||||
|
|
||||||
|
Merge all imports into a single `use` statement as long as they have the same visibility.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
pub use foo::{x, y};
|
||||||
|
use {
|
||||||
|
bar::{
|
||||||
|
a,
|
||||||
|
b::{self, f, g},
|
||||||
|
c,
|
||||||
|
d::e,
|
||||||
|
},
|
||||||
|
qux::{h, i},
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
## `merge_imports`
|
## `merge_imports`
|
||||||
|
|
||||||
This option is deprecated. Use `imports_granularity = "Crate"` instead.
|
This option is deprecated. Use `imports_granularity = "Crate"` instead.
|
||||||
|
|
|
@ -125,6 +125,8 @@ pub enum ImportGranularity {
|
||||||
Module,
|
Module,
|
||||||
/// Use one `use` statement per imported item.
|
/// Use one `use` statement per imported item.
|
||||||
Item,
|
Item,
|
||||||
|
/// Use one `use` statement including all items.
|
||||||
|
One,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[config_type]
|
#[config_type]
|
||||||
|
|
146
src/imports.rs
146
src/imports.rs
|
@ -138,6 +138,29 @@ impl UseSegment {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if self == other with their aliases removed.
|
||||||
|
fn equal_except_alias(&self, other: &Self) -> bool {
|
||||||
|
match (self, other) {
|
||||||
|
(UseSegment::Ident(ref s1, _), UseSegment::Ident(ref s2, _)) => s1 == s2,
|
||||||
|
(UseSegment::Slf(_), UseSegment::Slf(_))
|
||||||
|
| (UseSegment::Super(_), UseSegment::Super(_))
|
||||||
|
| (UseSegment::Crate(_), UseSegment::Crate(_))
|
||||||
|
| (UseSegment::Glob, UseSegment::Glob) => true,
|
||||||
|
(UseSegment::List(ref list1), UseSegment::List(ref list2)) => list1 == list2,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_alias(&self) -> Option<&str> {
|
||||||
|
match self {
|
||||||
|
UseSegment::Ident(_, a)
|
||||||
|
| UseSegment::Slf(a)
|
||||||
|
| UseSegment::Super(a)
|
||||||
|
| UseSegment::Crate(a) => a.as_deref(),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn from_path_segment(
|
fn from_path_segment(
|
||||||
context: &RewriteContext<'_>,
|
context: &RewriteContext<'_>,
|
||||||
path_seg: &ast::PathSegment,
|
path_seg: &ast::PathSegment,
|
||||||
|
@ -558,6 +581,7 @@ impl UseTree {
|
||||||
SharedPrefix::Module => {
|
SharedPrefix::Module => {
|
||||||
self.path[..self.path.len() - 1] == other.path[..other.path.len() - 1]
|
self.path[..self.path.len() - 1] == other.path[..other.path.len() - 1]
|
||||||
}
|
}
|
||||||
|
SharedPrefix::One => true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -598,7 +622,7 @@ impl UseTree {
|
||||||
fn merge(&mut self, other: &UseTree, merge_by: SharedPrefix) {
|
fn merge(&mut self, other: &UseTree, merge_by: SharedPrefix) {
|
||||||
let mut prefix = 0;
|
let mut prefix = 0;
|
||||||
for (a, b) in self.path.iter().zip(other.path.iter()) {
|
for (a, b) in self.path.iter().zip(other.path.iter()) {
|
||||||
if *a == *b {
|
if a.equal_except_alias(b) {
|
||||||
prefix += 1;
|
prefix += 1;
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
|
@ -633,14 +657,20 @@ fn merge_rest(
|
||||||
return Some(new_path);
|
return Some(new_path);
|
||||||
}
|
}
|
||||||
} else if len == 1 {
|
} else if len == 1 {
|
||||||
let rest = if a.len() == len { &b[1..] } else { &a[1..] };
|
let (common, rest) = if a.len() == len {
|
||||||
return Some(vec![
|
(&a[0], &b[1..])
|
||||||
b[0].clone(),
|
} else {
|
||||||
UseSegment::List(vec![
|
(&b[0], &a[1..])
|
||||||
UseTree::from_path(vec![UseSegment::Slf(None)], DUMMY_SP),
|
};
|
||||||
UseTree::from_path(rest.to_vec(), DUMMY_SP),
|
let mut list = vec![UseTree::from_path(
|
||||||
]),
|
vec![UseSegment::Slf(common.get_alias().map(ToString::to_string))],
|
||||||
]);
|
DUMMY_SP,
|
||||||
|
)];
|
||||||
|
match rest {
|
||||||
|
[UseSegment::List(rest_list)] => list.extend(rest_list.clone()),
|
||||||
|
_ => list.push(UseTree::from_path(rest.to_vec(), DUMMY_SP)),
|
||||||
|
}
|
||||||
|
return Some(vec![b[0].clone(), UseSegment::List(list)]);
|
||||||
} else {
|
} else {
|
||||||
len -= 1;
|
len -= 1;
|
||||||
}
|
}
|
||||||
|
@ -655,18 +685,54 @@ fn merge_rest(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn merge_use_trees_inner(trees: &mut Vec<UseTree>, use_tree: UseTree, merge_by: SharedPrefix) {
|
fn merge_use_trees_inner(trees: &mut Vec<UseTree>, use_tree: UseTree, merge_by: SharedPrefix) {
|
||||||
let similar_trees = trees
|
struct SimilarTree<'a> {
|
||||||
.iter_mut()
|
similarity: usize,
|
||||||
.filter(|tree| tree.share_prefix(&use_tree, merge_by));
|
path_len: usize,
|
||||||
|
tree: &'a mut UseTree,
|
||||||
|
}
|
||||||
|
|
||||||
|
let similar_trees = trees.iter_mut().filter_map(|tree| {
|
||||||
|
if tree.share_prefix(&use_tree, merge_by) {
|
||||||
|
// In the case of `SharedPrefix::One`, `similarity` is used for deciding with which
|
||||||
|
// tree `use_tree` should be merge.
|
||||||
|
// In other cases `similarity` won't be used, so set it to `0` as a dummy value.
|
||||||
|
let similarity = if merge_by == SharedPrefix::One {
|
||||||
|
tree.path
|
||||||
|
.iter()
|
||||||
|
.zip(&use_tree.path)
|
||||||
|
.take_while(|(a, b)| a.equal_except_alias(b))
|
||||||
|
.count()
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
let path_len = tree.path.len();
|
||||||
|
Some(SimilarTree {
|
||||||
|
similarity,
|
||||||
|
tree,
|
||||||
|
path_len,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
if use_tree.path.len() == 1 && merge_by == SharedPrefix::Crate {
|
if use_tree.path.len() == 1 && merge_by == SharedPrefix::Crate {
|
||||||
if let Some(tree) = similar_trees.min_by_key(|tree| tree.path.len()) {
|
if let Some(tree) = similar_trees.min_by_key(|tree| tree.path_len) {
|
||||||
if tree.path.len() == 1 {
|
if tree.path_len == 1 {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if let Some(tree) = similar_trees.max_by_key(|tree| tree.path.len()) {
|
} else if merge_by == SharedPrefix::One {
|
||||||
if tree.path.len() > 1 {
|
if let Some(sim_tree) = similar_trees.max_by_key(|tree| tree.similarity) {
|
||||||
tree.merge(&use_tree, merge_by);
|
if sim_tree.similarity > 0 {
|
||||||
|
sim_tree.tree.merge(&use_tree, merge_by);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if let Some(sim_tree) = similar_trees.max_by_key(|tree| tree.path_len) {
|
||||||
|
if sim_tree.path_len > 1 {
|
||||||
|
sim_tree.tree.merge(&use_tree, merge_by);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -880,6 +946,7 @@ impl Rewrite for UseTree {
|
||||||
pub(crate) enum SharedPrefix {
|
pub(crate) enum SharedPrefix {
|
||||||
Crate,
|
Crate,
|
||||||
Module,
|
Module,
|
||||||
|
One,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -904,7 +971,7 @@ mod test {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eat(&mut self, c: char) {
|
fn eat(&mut self, c: char) {
|
||||||
assert!(self.input.next().unwrap() == c);
|
assert_eq!(self.input.next().unwrap(), c);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn push_segment(
|
fn push_segment(
|
||||||
|
@ -1094,6 +1161,49 @@ mod test {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_use_tree_merge_one() {
|
||||||
|
test_merge!(One, ["a", "b"], ["{a, b}"]);
|
||||||
|
|
||||||
|
test_merge!(One, ["a::{aa, ab}", "b", "a"], ["{a::{self, aa, ab}, b}"]);
|
||||||
|
|
||||||
|
test_merge!(One, ["a as x", "b as y"], ["{a as x, b as y}"]);
|
||||||
|
|
||||||
|
test_merge!(
|
||||||
|
One,
|
||||||
|
["a::{aa as xa, ab}", "b", "a"],
|
||||||
|
["{a::{self, aa as xa, ab}, b}"]
|
||||||
|
);
|
||||||
|
|
||||||
|
test_merge!(
|
||||||
|
One,
|
||||||
|
["a", "a::{aa, ab::{aba, abb}}"],
|
||||||
|
["a::{self, aa, ab::{aba, abb}}"]
|
||||||
|
);
|
||||||
|
|
||||||
|
test_merge!(One, ["a", "b::{ba, *}"], ["{a, b::{ba, *}}"]);
|
||||||
|
|
||||||
|
test_merge!(One, ["a", "b", "a::aa"], ["{a::{self, aa}, b}"]);
|
||||||
|
|
||||||
|
test_merge!(
|
||||||
|
One,
|
||||||
|
["a::aa::aaa", "a::ac::aca", "a::aa::*"],
|
||||||
|
["a::{aa::{aaa, *}, ac::aca}"]
|
||||||
|
);
|
||||||
|
|
||||||
|
test_merge!(
|
||||||
|
One,
|
||||||
|
["a", "b::{ba, bb}", "a::{aa::*, ab::aba}"],
|
||||||
|
["{a::{self, aa::*, ab::aba}, b::{ba, bb}}"]
|
||||||
|
);
|
||||||
|
|
||||||
|
test_merge!(
|
||||||
|
One,
|
||||||
|
["b", "a::ac::{aca, acb}", "a::{aa::*, ab}"],
|
||||||
|
["{a::{aa::*, ab, ac::{aca, acb}}, b}"]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_flatten_use_trees() {
|
fn test_flatten_use_trees() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
|
|
@ -113,6 +113,7 @@ fn rewrite_reorderable_or_regroupable_items(
|
||||||
merge_use_trees(normalized_items, SharedPrefix::Module)
|
merge_use_trees(normalized_items, SharedPrefix::Module)
|
||||||
}
|
}
|
||||||
ImportGranularity::Item => flatten_use_trees(normalized_items),
|
ImportGranularity::Item => flatten_use_trees(normalized_items),
|
||||||
|
ImportGranularity::One => merge_use_trees(normalized_items, SharedPrefix::One),
|
||||||
ImportGranularity::Preserve => normalized_items,
|
ImportGranularity::Preserve => normalized_items,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
60
tests/source/imports_granularity_one.rs
Normal file
60
tests/source/imports_granularity_one.rs
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
// rustfmt-imports_granularity: One
|
||||||
|
|
||||||
|
use b;
|
||||||
|
use a::ac::{aca, acb};
|
||||||
|
use a::{aa::*, ab};
|
||||||
|
|
||||||
|
use a as x;
|
||||||
|
use b::ba;
|
||||||
|
use a::{aa, ab};
|
||||||
|
|
||||||
|
use a::aa::aaa;
|
||||||
|
use a::ab::aba as x;
|
||||||
|
use a::aa::*;
|
||||||
|
|
||||||
|
use a::aa;
|
||||||
|
use a::ad::ada;
|
||||||
|
#[cfg(test)]
|
||||||
|
use a::{ab, ac::aca};
|
||||||
|
use b;
|
||||||
|
#[cfg(test)]
|
||||||
|
use b::{
|
||||||
|
ba, bb,
|
||||||
|
bc::bca::{bcaa, bcab},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub use a::aa;
|
||||||
|
pub use a::ae;
|
||||||
|
use a::{ab, ac, ad};
|
||||||
|
use b::ba;
|
||||||
|
pub use b::{bb, bc::bca};
|
||||||
|
|
||||||
|
use a::aa::aaa;
|
||||||
|
use a::ac::{aca, acb};
|
||||||
|
use a::{aa::*, ab};
|
||||||
|
use b::{
|
||||||
|
ba,
|
||||||
|
bb::{self, bba},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::a;
|
||||||
|
use crate::b::ba;
|
||||||
|
use c::ca;
|
||||||
|
|
||||||
|
use super::a;
|
||||||
|
use c::ca;
|
||||||
|
use super::b::ba;
|
||||||
|
|
||||||
|
use crate::a;
|
||||||
|
use super::b;
|
||||||
|
use c::{self, ca};
|
||||||
|
|
||||||
|
use a::{
|
||||||
|
// some comment
|
||||||
|
aa::{aaa, aab},
|
||||||
|
ab,
|
||||||
|
// another comment
|
||||||
|
ac::aca,
|
||||||
|
};
|
||||||
|
use b as x;
|
||||||
|
use a::ad::ada;
|
79
tests/target/imports_granularity_one.rs
Normal file
79
tests/target/imports_granularity_one.rs
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
// rustfmt-imports_granularity: One
|
||||||
|
|
||||||
|
use {
|
||||||
|
a::{
|
||||||
|
aa::*,
|
||||||
|
ab,
|
||||||
|
ac::{aca, acb},
|
||||||
|
},
|
||||||
|
b,
|
||||||
|
};
|
||||||
|
|
||||||
|
use {
|
||||||
|
a::{self as x, aa, ab},
|
||||||
|
b::ba,
|
||||||
|
};
|
||||||
|
|
||||||
|
use a::{
|
||||||
|
aa::{aaa, *},
|
||||||
|
ab::aba as x,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
use a::{ab, ac::aca};
|
||||||
|
#[cfg(test)]
|
||||||
|
use b::{
|
||||||
|
ba, bb,
|
||||||
|
bc::bca::{bcaa, bcab},
|
||||||
|
};
|
||||||
|
use {
|
||||||
|
a::{aa, ad::ada},
|
||||||
|
b,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub use {
|
||||||
|
a::{aa, ae},
|
||||||
|
b::{bb, bc::bca},
|
||||||
|
};
|
||||||
|
use {
|
||||||
|
a::{ab, ac, ad},
|
||||||
|
b::ba,
|
||||||
|
};
|
||||||
|
|
||||||
|
use {
|
||||||
|
a::{
|
||||||
|
aa::{aaa, *},
|
||||||
|
ab,
|
||||||
|
ac::{aca, acb},
|
||||||
|
},
|
||||||
|
b::{
|
||||||
|
ba,
|
||||||
|
bb::{self, bba},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
use {
|
||||||
|
crate::{a, b::ba},
|
||||||
|
c::ca,
|
||||||
|
};
|
||||||
|
|
||||||
|
use {
|
||||||
|
super::{a, b::ba},
|
||||||
|
c::ca,
|
||||||
|
};
|
||||||
|
|
||||||
|
use {
|
||||||
|
super::b,
|
||||||
|
crate::a,
|
||||||
|
c::{self, ca},
|
||||||
|
};
|
||||||
|
|
||||||
|
use {
|
||||||
|
a::{
|
||||||
|
aa::{aaa, aab},
|
||||||
|
ab,
|
||||||
|
ac::aca,
|
||||||
|
ad::ada,
|
||||||
|
},
|
||||||
|
b as x,
|
||||||
|
};
|
Loading…
Reference in a new issue