@@ -3,7 +3,6 @@ use std::cmp::Ordering;
33
44use itertools:: { EitherOrBoth , Itertools } ;
55use parser:: T ;
6- use stdx:: is_upper_snake_case;
76use 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>
552552pub ( 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