Skip to content

Commit 635b536

Browse files
committed
Make import sorting order follow 2024 edition style
1 parent 4e147e7 commit 635b536

File tree

5 files changed

+219
-50
lines changed

5 files changed

+219
-50
lines changed

crates/ide-assists/src/handlers/merge_imports.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -605,7 +605,7 @@ use foo::$0{
605605
",
606606
r"
607607
use foo::{
608-
bar::baz, FooBar
608+
FooBar, bar::baz,
609609
};
610610
",
611611
)

crates/ide-assists/src/handlers/normalize_import.rs

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -109,8 +109,8 @@ mod tests {
109109
#[test]
110110
fn test_order() {
111111
check_assist_variations!(
112-
"foo::{*, Qux, bar::{Quux, Bar}, baz, FOO_BAZ, self, Baz}",
113-
"foo::{self, bar::{Bar, Quux}, baz, Baz, Qux, FOO_BAZ, *}"
112+
"foo::{*, Qux, bar::{Quux, Bar}, baz, FOO_BAZ, self, Baz, v10, v9, r#aaa}",
113+
"foo::{self, Baz, FOO_BAZ, Qux, r#aaa, bar::{Bar, Quux}, baz, v9, v10, *}"
114114
);
115115
}
116116

@@ -145,29 +145,29 @@ fn main() {
145145

146146
#[test]
147147
fn test_redundant_braces() {
148-
check_assist_variations!("foo::{bar::{baz, Qux}}", "foo::bar::{baz, Qux}");
148+
check_assist_variations!("foo::{bar::{baz, Qux}}", "foo::bar::{Qux, baz}");
149149
check_assist_variations!("foo::{bar::{self}}", "foo::bar::{self}");
150150
check_assist_variations!("foo::{bar::{*}}", "foo::bar::*");
151151
check_assist_variations!("foo::{bar::{Qux as Quux}}", "foo::bar::Qux as Quux");
152152
check_assist_variations!(
153153
"foo::bar::{{FOO_BAZ, Qux, self}, {*, baz}}",
154-
"foo::bar::{self, baz, Qux, FOO_BAZ, *}"
154+
"foo::bar::{self, FOO_BAZ, Qux, baz, *}"
155155
);
156156
check_assist_variations!(
157157
"foo::bar::{{{FOO_BAZ}, {{Qux}, {self}}}, {{*}, {baz}}}",
158-
"foo::bar::{self, baz, Qux, FOO_BAZ, *}"
158+
"foo::bar::{self, FOO_BAZ, Qux, baz, *}"
159159
);
160160
}
161161

162162
#[test]
163163
fn test_merge() {
164164
check_assist_variations!(
165165
"foo::{*, bar, {FOO_BAZ, qux}, bar::{*, baz}, {Quux}}",
166-
"foo::{bar::{self, baz, *}, qux, Quux, FOO_BAZ, *}"
166+
"foo::{FOO_BAZ, Quux, bar::{self, baz, *}, qux, *}"
167167
);
168168
check_assist_variations!(
169169
"foo::{*, bar, {FOO_BAZ, qux}, bar::{*, baz}, {Quux, bar::{baz::Foo}}}",
170-
"foo::{bar::{self, baz::{self, Foo}, *}, qux, Quux, FOO_BAZ, *}"
170+
"foo::{FOO_BAZ, Quux, bar::{self, baz::{self, Foo}, *}, qux, *}"
171171
);
172172
}
173173

@@ -229,15 +229,15 @@ use {
229229
check_assist_not_applicable_variations!("foo::bar");
230230
check_assist_not_applicable_variations!("foo::bar::*");
231231
check_assist_not_applicable_variations!("foo::bar::Qux as Quux");
232-
check_assist_not_applicable_variations!("foo::bar::{self, baz, Qux, FOO_BAZ, *}");
232+
check_assist_not_applicable_variations!("foo::bar::{self, FOO_BAZ, Qux, baz, *}");
233233
check_assist_not_applicable_variations!(
234-
"foo::{self, bar::{Bar, Quux}, baz, Baz, Qux, FOO_BAZ, *}"
234+
"foo::{self, Baz, FOO_BAZ, Qux, bar::{Bar, Quux}, baz, *}"
235235
);
236236
check_assist_not_applicable_variations!(
237-
"foo::{bar::{self, baz, *}, qux, Quux, FOO_BAZ, *}"
237+
"foo::{FOO_BAZ, Quux, bar::{self, baz, *}, qux, *}"
238238
);
239239
check_assist_not_applicable_variations!(
240-
"foo::{bar::{self, baz::{self, Foo}, *}, qux, Quux, FOO_BAZ, *}"
240+
"foo::{bar::{self, FOO_BAZ, Quux, baz::{self, Foo}, *}, qux, *}"
241241
);
242242
}
243243
}

crates/ide-completion/src/tests/flyimport.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ fn main() {
114114
}
115115
"#,
116116
r#"
117-
use dep::{some_module::{SecondStruct, ThirdStruct}, FirstStruct};
117+
use dep::{FirstStruct, some_module::{SecondStruct, ThirdStruct}};
118118
119119
fn main() {
120120
ThirdStruct

crates/ide-db/src/imports/insert_use/tests.rs

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -782,18 +782,18 @@ fn merge_groups_long_last_list() {
782782
fn merge_groups_long_full_nested() {
783783
check_crate(
784784
"std::foo::bar::Baz",
785-
r"use std::foo::bar::{Qux, quux::{Fez, Fizz}};",
786-
r"use std::foo::bar::{quux::{Fez, Fizz}, Baz, Qux};",
785+
r"use std::foo::bar::{quux::{Fez, Fizz}, Qux};",
786+
r"use std::foo::bar::{Baz, Qux, quux::{Fez, Fizz}};",
787787
);
788788
check_crate(
789789
"std::foo::bar::r#Baz",
790-
r"use std::foo::bar::{Qux, quux::{Fez, Fizz}};",
791-
r"use std::foo::bar::{quux::{Fez, Fizz}, r#Baz, Qux};",
790+
r"use std::foo::bar::{quux::{Fez, Fizz}, Qux};",
791+
r"use std::foo::bar::{r#Baz, Qux, quux::{Fez, Fizz}};",
792792
);
793793
check_one(
794794
"std::foo::bar::Baz",
795-
r"use {std::foo::bar::{Qux, quux::{Fez, Fizz}}};",
796-
r"use {std::foo::bar::{quux::{Fez, Fizz}, Baz, Qux}};",
795+
r"use {std::foo::bar::{quux::{Fez, Fizz}}, Qux};",
796+
r"use {Qux, std::foo::bar::{Baz, quux::{Fez, Fizz}}};",
797797
);
798798
}
799799

@@ -811,13 +811,13 @@ use std::foo::bar::{Qux, quux::{Fez, Fizz}};",
811811
fn merge_groups_full_nested_deep() {
812812
check_crate(
813813
"std::foo::bar::quux::Baz",
814-
r"use std::foo::bar::{Qux, quux::{Fez, Fizz}};",
815-
r"use std::foo::bar::{quux::{Baz, Fez, Fizz}, Qux};",
814+
r"use std::foo::bar::{quux::{Fez, Fizz}, Qux};",
815+
r"use std::foo::bar::{Qux, quux::{Baz, Fez, Fizz}};",
816816
);
817817
check_one(
818818
"std::foo::bar::quux::Baz",
819-
r"use {std::foo::bar::{Qux, quux::{Fez, Fizz}}};",
820-
r"use {std::foo::bar::{quux::{Baz, Fez, Fizz}, Qux}};",
819+
r"use {std::foo::bar::{quux::{Fez, Fizz}}, Qux};",
820+
r"use {Qux, std::foo::bar::quux::{Baz, Fez, Fizz}};",
821821
);
822822
}
823823

@@ -988,8 +988,8 @@ use syntax::SyntaxKind::{self, *};",
988988
fn merge_glob_nested() {
989989
check_crate(
990990
"foo::bar::quux::Fez",
991-
r"use foo::bar::{Baz, quux::*};",
992-
r"use foo::bar::{quux::{Fez, *}, Baz};",
991+
r"use foo::bar::{quux::*, Baz};",
992+
r"use foo::bar::{Baz, quux::{Fez, *}};",
993993
)
994994
}
995995

@@ -998,7 +998,7 @@ fn merge_nested_considers_first_segments() {
998998
check_crate(
999999
"hir_ty::display::write_bounds_like_dyn_trait",
10001000
r"use hir_ty::{autoderef, display::{HirDisplayError, HirFormatter}, method_resolution};",
1001-
r"use hir_ty::{autoderef, display::{write_bounds_like_dyn_trait, HirDisplayError, HirFormatter}, method_resolution};",
1001+
r"use hir_ty::{autoderef, display::{HirDisplayError, HirFormatter, write_bounds_like_dyn_trait}, method_resolution};",
10021002
);
10031003
}
10041004

crates/ide-db/src/imports/merge_imports.rs

Lines changed: 193 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ use std::cmp::Ordering;
33

44
use itertools::{EitherOrBoth, Itertools};
55
use parser::T;
6-
use stdx::is_upper_snake_case;
76
use syntax::{
87
Direction, SyntaxElement, algo,
98
ast::{
@@ -543,12 +542,13 @@ fn use_tree_cmp_bin_search(lhs: &ast::UseTree, rhs: &ast::UseTree) -> Ordering {
543542
}
544543
}
545544

546-
/// Orders use trees following `rustfmt`'s algorithm for ordering imports, which is `self`, `super`
547-
/// and `crate` first, then identifier imports with lowercase ones first and upper snake case
548-
/// (e.g. UPPER_SNAKE_CASE) ones last, then glob imports, and at last list imports.
545+
/// Orders use trees following `rustfmt`'s version sorting algorithm for ordering imports.
549546
///
550-
/// Example: `foo::{self, baz, foo, Baz, Qux, FOO_BAZ, *, {Bar}}`
551-
/// Ref: <https://github.com/rust-lang/rustfmt/blob/6356fca675bd756d71f5c123cd053d17b16c573e/src/imports.rs#L83-L86>.
547+
/// Example: `foo::{self, Baz, FOO_BAZ, Qux, baz, foo, *, {Bar}}`
548+
///
549+
/// Ref:
550+
/// - <https://doc.rust-lang.org/style-guide/index.html#sorting>
551+
/// - <https://doc.rust-lang.org/edition-guide/rust-2024/rustfmt.html>
552552
pub(super) fn use_tree_cmp(a: &ast::UseTree, b: &ast::UseTree) -> Ordering {
553553
let a_is_simple_path = a.is_simple_path() && a.rename().is_none();
554554
let b_is_simple_path = b.is_simple_path() && b.rename().is_none();
@@ -613,26 +613,9 @@ fn path_segment_cmp(a: &ast::PathSegment, b: &ast::PathSegment) -> Ordering {
613613
(Some(_), None) => Ordering::Greater,
614614
(None, Some(_)) => Ordering::Less,
615615
(Some(a_name), Some(b_name)) => {
616-
// snake_case < UpperCamelCase < UPPER_SNAKE_CASE
617616
let a_text = a_name.as_str().trim_start_matches("r#");
618617
let b_text = b_name.as_str().trim_start_matches("r#");
619-
if a_text.starts_with(char::is_lowercase)
620-
&& b_text.starts_with(char::is_uppercase)
621-
{
622-
return Ordering::Less;
623-
}
624-
if a_text.starts_with(char::is_uppercase)
625-
&& b_text.starts_with(char::is_lowercase)
626-
{
627-
return Ordering::Greater;
628-
}
629-
if !is_upper_snake_case(a_text) && is_upper_snake_case(b_text) {
630-
return Ordering::Less;
631-
}
632-
if is_upper_snake_case(a_text) && !is_upper_snake_case(b_text) {
633-
return Ordering::Greater;
634-
}
635-
a_text.cmp(b_text)
618+
version_sort::version_sort(a_text, b_text)
636619
}
637620
}
638621
}
@@ -740,3 +723,189 @@ fn remove_subtree_if_only_self(use_tree: &ast::UseTree) {
740723
_ => (),
741724
}
742725
}
726+
727+
// Taken from rustfmt
728+
// https://github.com/rust-lang/rustfmt/blob/0332da01486508710f2a542111e40513bfb215aa/src/sort.rs
729+
mod version_sort {
730+
// Original rustfmt code contains some clippy lints.
731+
// Suppress them to minimize changes from upstream.
732+
#![allow(clippy::all)]
733+
734+
use std::cmp::Ordering;
735+
736+
use itertools::{EitherOrBoth, Itertools};
737+
738+
struct VersionChunkIter<'a> {
739+
ident: &'a str,
740+
start: usize,
741+
}
742+
743+
impl<'a> VersionChunkIter<'a> {
744+
pub(crate) fn new(ident: &'a str) -> Self {
745+
Self { ident, start: 0 }
746+
}
747+
748+
fn parse_numeric_chunk(
749+
&mut self,
750+
mut chars: std::str::CharIndices<'a>,
751+
) -> Option<VersionChunk<'a>> {
752+
let mut end = self.start;
753+
let mut is_end_of_chunk = false;
754+
755+
while let Some((idx, c)) = chars.next() {
756+
end = self.start + idx;
757+
758+
if c.is_ascii_digit() {
759+
continue;
760+
}
761+
762+
is_end_of_chunk = true;
763+
break;
764+
}
765+
766+
let source = if is_end_of_chunk {
767+
let value = &self.ident[self.start..end];
768+
self.start = end;
769+
value
770+
} else {
771+
let value = &self.ident[self.start..];
772+
self.start = self.ident.len();
773+
value
774+
};
775+
776+
let zeros = source.chars().take_while(|c| *c == '0').count();
777+
let value = source.parse::<usize>().ok()?;
778+
779+
Some(VersionChunk::Number { value, zeros, source })
780+
}
781+
782+
fn parse_str_chunk(
783+
&mut self,
784+
mut chars: std::str::CharIndices<'a>,
785+
) -> Option<VersionChunk<'a>> {
786+
let mut end = self.start;
787+
let mut is_end_of_chunk = false;
788+
789+
while let Some((idx, c)) = chars.next() {
790+
end = self.start + idx;
791+
792+
if c == '_' {
793+
is_end_of_chunk = true;
794+
break;
795+
}
796+
797+
if !c.is_ascii_digit() {
798+
continue;
799+
}
800+
801+
is_end_of_chunk = true;
802+
break;
803+
}
804+
805+
let source = if is_end_of_chunk {
806+
let value = &self.ident[self.start..end];
807+
self.start = end;
808+
value
809+
} else {
810+
let value = &self.ident[self.start..];
811+
self.start = self.ident.len();
812+
value
813+
};
814+
815+
Some(VersionChunk::Str(source))
816+
}
817+
}
818+
819+
impl<'a> Iterator for VersionChunkIter<'a> {
820+
type Item = VersionChunk<'a>;
821+
822+
fn next(&mut self) -> Option<Self::Item> {
823+
let mut chars = self.ident[self.start..].char_indices();
824+
let (_, next) = chars.next()?;
825+
826+
if next == '_' {
827+
self.start = self.start + next.len_utf8();
828+
return Some(VersionChunk::Underscore);
829+
}
830+
831+
if next.is_ascii_digit() {
832+
return self.parse_numeric_chunk(chars);
833+
}
834+
835+
self.parse_str_chunk(chars)
836+
}
837+
}
838+
839+
/// Represents a chunk in the version-sort algorithm
840+
#[derive(Debug, PartialEq, Eq)]
841+
enum VersionChunk<'a> {
842+
/// A single `_` in an identifier. Underscores are sorted before all other characters.
843+
Underscore,
844+
/// A &str chunk in the version sort.
845+
Str(&'a str),
846+
/// A numeric chunk in the version sort. Keeps track of the numeric value and leading zeros.
847+
Number { value: usize, zeros: usize, source: &'a str },
848+
}
849+
850+
/// Determine which side of the version-sort comparison had more leading zeros.
851+
#[derive(Debug, PartialEq, Eq)]
852+
enum MoreLeadingZeros {
853+
Left,
854+
Right,
855+
Equal,
856+
}
857+
858+
pub(super) fn version_sort(a: &str, b: &str) -> Ordering {
859+
let iter_a = VersionChunkIter::new(a);
860+
let iter_b = VersionChunkIter::new(b);
861+
let mut more_leading_zeros = MoreLeadingZeros::Equal;
862+
863+
for either_or_both in iter_a.zip_longest(iter_b) {
864+
match either_or_both {
865+
EitherOrBoth::Left(_) => return std::cmp::Ordering::Greater,
866+
EitherOrBoth::Right(_) => return std::cmp::Ordering::Less,
867+
EitherOrBoth::Both(a, b) => match (a, b) {
868+
(VersionChunk::Underscore, VersionChunk::Underscore) => {
869+
continue;
870+
}
871+
(VersionChunk::Underscore, _) => return std::cmp::Ordering::Less,
872+
(_, VersionChunk::Underscore) => return std::cmp::Ordering::Greater,
873+
(VersionChunk::Str(ca), VersionChunk::Str(cb))
874+
| (VersionChunk::Str(ca), VersionChunk::Number { source: cb, .. })
875+
| (VersionChunk::Number { source: ca, .. }, VersionChunk::Str(cb)) => {
876+
match ca.cmp(&cb) {
877+
std::cmp::Ordering::Equal => {
878+
continue;
879+
}
880+
order @ _ => return order,
881+
}
882+
}
883+
(
884+
VersionChunk::Number { value: va, zeros: lza, .. },
885+
VersionChunk::Number { value: vb, zeros: lzb, .. },
886+
) => match va.cmp(&vb) {
887+
std::cmp::Ordering::Equal => {
888+
if lza == lzb {
889+
continue;
890+
}
891+
892+
if more_leading_zeros == MoreLeadingZeros::Equal && lza > lzb {
893+
more_leading_zeros = MoreLeadingZeros::Left;
894+
} else if more_leading_zeros == MoreLeadingZeros::Equal && lza < lzb {
895+
more_leading_zeros = MoreLeadingZeros::Right;
896+
}
897+
continue;
898+
}
899+
order @ _ => return order,
900+
},
901+
},
902+
}
903+
}
904+
905+
match more_leading_zeros {
906+
MoreLeadingZeros::Equal => std::cmp::Ordering::Equal,
907+
MoreLeadingZeros::Left => std::cmp::Ordering::Less,
908+
MoreLeadingZeros::Right => std::cmp::Ordering::Greater,
909+
}
910+
}
911+
}

0 commit comments

Comments
 (0)