@@ -4,13 +4,14 @@ use std::{
4
4
iter:: { self , successors} ,
5
5
} ;
6
6
7
- use ast:: {
8
- edit:: { AstNodeEdit , IndentLevel } ,
9
- PathSegmentKind , VisibilityOwner ,
10
- } ;
7
+ use itertools:: { EitherOrBoth , Itertools } ;
11
8
use syntax:: {
12
9
algo,
13
- ast:: { self , make, AstNode } ,
10
+ ast:: {
11
+ self ,
12
+ edit:: { AstNodeEdit , IndentLevel } ,
13
+ make, AstNode , PathSegmentKind , VisibilityOwner ,
14
+ } ,
14
15
InsertPosition , SyntaxElement , SyntaxNode ,
15
16
} ;
16
17
@@ -193,13 +194,13 @@ fn recursive_merge(
193
194
false => None ,
194
195
} )
195
196
. collect :: < Option < Vec < _ > > > ( ) ?;
196
- use_trees. sort_unstable_by ( |a, b| path_cmp_opt ( a. path ( ) , b. path ( ) ) ) ;
197
+ use_trees. sort_unstable_by ( |a, b| path_cmp_for_sort ( a. path ( ) , b. path ( ) ) ) ;
197
198
for rhs_t in rhs. use_tree_list ( ) . into_iter ( ) . flat_map ( |list| list. use_trees ( ) ) {
198
199
if !merge. is_tree_allowed ( & rhs_t) {
199
200
return None ;
200
201
}
201
202
let rhs_path = rhs_t. path ( ) ;
202
- match use_trees. binary_search_by ( |p| path_cmp_opt ( p. path ( ) , rhs_path. clone ( ) ) ) {
203
+ match use_trees. binary_search_by ( |p| path_cmp_bin_search ( p. path ( ) , rhs_path. clone ( ) ) ) {
203
204
Ok ( idx) => {
204
205
let lhs_t = & mut use_trees[ idx] ;
205
206
let lhs_path = lhs_t. path ( ) ?;
@@ -307,39 +308,77 @@ fn path_len(path: ast::Path) -> usize {
307
308
308
309
/// Orders paths in the following way:
309
310
/// the sole self token comes first, after that come uppercase identifiers, then lowercase identifiers
310
- // FIXME: rustfmt sort lowercase idents before uppercase, in general we want to have the same ordering rustfmt has
311
+ // FIXME: rustfmt sorts lowercase idents before uppercase, in general we want to have the same ordering rustfmt has
311
312
// which is `self` and `super` first, then identifier imports with lowercase ones first, then glob imports and at last list imports.
312
313
// Example foo::{self, foo, baz, Baz, Qux, *, {Bar}}
313
- fn path_cmp ( a : & ast:: Path , b : & ast:: Path ) -> Ordering {
314
- match ( path_is_self ( a) , path_is_self ( b) ) {
315
- ( true , true ) => Ordering :: Equal ,
316
- ( true , false ) => Ordering :: Less ,
317
- ( false , true ) => Ordering :: Greater ,
318
- ( false , false ) => {
319
- let a = segment_iter ( a) ;
320
- let b = segment_iter ( b) ;
321
- // cmp_by would be useful for us here but that is currently unstable
322
- // cmp doesnt work due the lifetimes on text's return type
323
- a. zip ( b)
324
- . flat_map ( |( seg, seg2) | seg. name_ref ( ) . zip ( seg2. name_ref ( ) ) )
325
- . find_map ( |( a, b) | match a. text ( ) . cmp ( b. text ( ) ) {
326
- ord @ Ordering :: Greater | ord @ Ordering :: Less => Some ( ord) ,
327
- Ordering :: Equal => None ,
328
- } )
329
- . unwrap_or ( Ordering :: Equal )
330
- }
314
+ fn path_cmp_for_sort ( a : Option < ast:: Path > , b : Option < ast:: Path > ) -> Ordering {
315
+ match ( a, b) {
316
+ ( None , None ) => Ordering :: Equal ,
317
+ ( None , Some ( _) ) => Ordering :: Less ,
318
+ ( Some ( _) , None ) => Ordering :: Greater ,
319
+ ( Some ( ref a) , Some ( ref b) ) => match ( path_is_self ( a) , path_is_self ( b) ) {
320
+ ( true , true ) => Ordering :: Equal ,
321
+ ( true , false ) => Ordering :: Less ,
322
+ ( false , true ) => Ordering :: Greater ,
323
+ ( false , false ) => path_cmp_short ( a, b) ,
324
+ } ,
331
325
}
332
326
}
333
327
334
- fn path_cmp_opt ( a : Option < ast:: Path > , b : Option < ast:: Path > ) -> Ordering {
335
- match ( a, b) {
328
+ /// Path comparison func for binary searching for merging.
329
+ fn path_cmp_bin_search ( lhs : Option < ast:: Path > , rhs : Option < ast:: Path > ) -> Ordering {
330
+ match ( lhs, rhs) {
336
331
( None , None ) => Ordering :: Equal ,
337
332
( None , Some ( _) ) => Ordering :: Less ,
338
333
( Some ( _) , None ) => Ordering :: Greater ,
339
- ( Some ( a) , Some ( b) ) => path_cmp ( & a, & b) ,
334
+ ( Some ( ref a) , Some ( ref b) ) => path_cmp_short ( a, b) ,
340
335
}
341
336
}
342
337
338
+ /// Short circuiting comparison, if both paths are equal until one of them ends they are considered
339
+ /// equal
340
+ fn path_cmp_short ( a : & ast:: Path , b : & ast:: Path ) -> Ordering {
341
+ let a = segment_iter ( a) ;
342
+ let b = segment_iter ( b) ;
343
+ // cmp_by would be useful for us here but that is currently unstable
344
+ // cmp doesnt work due the lifetimes on text's return type
345
+ a. zip ( b)
346
+ . find_map ( |( a, b) | match path_segment_cmp ( & a, & b) {
347
+ Ordering :: Equal => None ,
348
+ ord => Some ( ord) ,
349
+ } )
350
+ . unwrap_or ( Ordering :: Equal )
351
+ }
352
+
353
+ /// Compares to paths, if one ends earlier than the other the has_tl parameters decide which is
354
+ /// greater as a a path that has a tree list should be greater, while one that just ends without
355
+ /// a tree list should be considered less.
356
+ fn use_tree_path_cmp ( a : & ast:: Path , a_has_tl : bool , b : & ast:: Path , b_has_tl : bool ) -> Ordering {
357
+ let a_segments = segment_iter ( a) ;
358
+ let b_segments = segment_iter ( b) ;
359
+ // cmp_by would be useful for us here but that is currently unstable
360
+ // cmp doesnt work due the lifetimes on text's return type
361
+ a_segments
362
+ . zip_longest ( b_segments)
363
+ . find_map ( |zipped| match zipped {
364
+ EitherOrBoth :: Both ( ref a, ref b) => match path_segment_cmp ( a, b) {
365
+ Ordering :: Equal => None ,
366
+ ord => Some ( ord) ,
367
+ } ,
368
+ EitherOrBoth :: Left ( _) if !b_has_tl => Some ( Ordering :: Greater ) ,
369
+ EitherOrBoth :: Left ( _) => Some ( Ordering :: Less ) ,
370
+ EitherOrBoth :: Right ( _) if !a_has_tl => Some ( Ordering :: Less ) ,
371
+ EitherOrBoth :: Right ( _) => Some ( Ordering :: Greater ) ,
372
+ } )
373
+ . unwrap_or ( Ordering :: Equal )
374
+ }
375
+
376
+ fn path_segment_cmp ( a : & ast:: PathSegment , b : & ast:: PathSegment ) -> Ordering {
377
+ let a = a. name_ref ( ) ;
378
+ let b = b. name_ref ( ) ;
379
+ a. as_ref ( ) . map ( ast:: NameRef :: text) . cmp ( & b. as_ref ( ) . map ( ast:: NameRef :: text) )
380
+ }
381
+
343
382
/// What type of merges are allowed.
344
383
#[ derive( Copy , Clone , Debug , PartialEq , Eq ) ]
345
384
pub enum MergeBehaviour {
@@ -389,7 +428,6 @@ impl ImportGroup {
389
428
PathSegmentKind :: Name ( name) => match name. text ( ) . as_str ( ) {
390
429
"std" => ImportGroup :: Std ,
391
430
"core" => ImportGroup :: Std ,
392
- // FIXME: can be ThisModule as well
393
431
_ => ImportGroup :: ExternCrate ,
394
432
} ,
395
433
PathSegmentKind :: Type { .. } => unreachable ! ( ) ,
@@ -415,30 +453,30 @@ fn find_insert_position(
415
453
. as_syntax_node ( )
416
454
. children ( )
417
455
. filter_map ( |node| ast:: Use :: cast ( node. clone ( ) ) . zip ( Some ( node) ) )
418
- . flat_map ( |( use_, node) | use_. use_tree ( ) . and_then ( |tree| tree. path ( ) ) . zip ( Some ( node) ) ) ;
456
+ . flat_map ( |( use_, node) | {
457
+ let tree = use_. use_tree ( ) ?;
458
+ let path = tree. path ( ) ?;
459
+ let has_tl = tree. use_tree_list ( ) . is_some ( ) ;
460
+ Some ( ( path, has_tl, node) )
461
+ } ) ;
419
462
// Iterator that discards anything thats not in the required grouping
420
463
// This implementation allows the user to rearrange their import groups as this only takes the first group that fits
421
464
let group_iter = path_node_iter
422
465
. clone ( )
423
- . skip_while ( |( path, _ ) | ImportGroup :: new ( path) != group)
424
- . take_while ( |( path, _ ) | ImportGroup :: new ( path) == group) ;
466
+ . skip_while ( |( path, .. ) | ImportGroup :: new ( path) != group)
467
+ . take_while ( |( path, .. ) | ImportGroup :: new ( path) == group) ;
425
468
426
- let segments = segment_iter ( & insert_path) ;
427
469
// track the last element we iterated over, if this is still None after the iteration then that means we never iterated in the first place
428
470
let mut last = None ;
429
471
// find the element that would come directly after our new import
430
- let post_insert =
431
- group_iter. inspect ( |( _, node) | last = Some ( node. clone ( ) ) ) . find ( |( path, _) | {
432
- let check_segments = segment_iter ( & path) ;
433
- segments
434
- . clone ( )
435
- . zip ( check_segments)
436
- . flat_map ( |( seg, seg2) | seg. name_ref ( ) . zip ( seg2. name_ref ( ) ) )
437
- . all ( |( l, r) | l. text ( ) <= r. text ( ) )
438
- } ) ;
472
+ let post_insert = group_iter. inspect ( |( .., node) | last = Some ( node. clone ( ) ) ) . find (
473
+ |& ( ref path, has_tl, _) | {
474
+ use_tree_path_cmp ( & insert_path, false , path, has_tl) != Ordering :: Greater
475
+ } ,
476
+ ) ;
439
477
match post_insert {
440
478
// insert our import before that element
441
- Some ( ( _ , node) ) => ( InsertPosition :: Before ( node. into ( ) ) , AddBlankLine :: After ) ,
479
+ Some ( ( .. , node) ) => ( InsertPosition :: Before ( node. into ( ) ) , AddBlankLine :: After ) ,
442
480
// there is no element after our new import, so append it to the end of the group
443
481
None => match last {
444
482
Some ( node) => ( InsertPosition :: After ( node. into ( ) ) , AddBlankLine :: Before ) ,
@@ -448,10 +486,10 @@ fn find_insert_position(
448
486
let mut last = None ;
449
487
// find the group that comes after where we want to insert
450
488
let post_group = path_node_iter
451
- . inspect ( |( _ , node) | last = Some ( node. clone ( ) ) )
452
- . find ( |( p, _ ) | ImportGroup :: new ( p) > group) ;
489
+ . inspect ( |( .. , node) | last = Some ( node. clone ( ) ) )
490
+ . find ( |( p, .. ) | ImportGroup :: new ( p) > group) ;
453
491
match post_group {
454
- Some ( ( _ , node) ) => {
492
+ Some ( ( .. , node) ) => {
455
493
( InsertPosition :: Before ( node. into ( ) ) , AddBlankLine :: AfterTwice )
456
494
}
457
495
// there is no such group, so append after the last one
@@ -844,7 +882,6 @@ use foo::bar::baz::Qux;",
844
882
}
845
883
846
884
#[ test]
847
- #[ ignore] // FIXME: Order changes when switching lhs and rhs
848
885
fn skip_merge_last_too_long2 ( ) {
849
886
check_last (
850
887
"foo::bar::baz::Qux" ,
0 commit comments