@@ -5,7 +5,7 @@ use std::iter::Peekable;
5
5
6
6
use syntax:: source_map:: BytePos ;
7
7
8
- use crate :: comment:: { find_comment_end, rewrite_comment, FindUncommented } ;
8
+ use crate :: comment:: { find_comment_end, is_last_comment_block , rewrite_comment, FindUncommented } ;
9
9
use crate :: config:: lists:: * ;
10
10
use crate :: config:: { Config , IndentStyle } ;
11
11
use crate :: rewrite:: RewriteContext ;
@@ -32,6 +32,8 @@ pub(crate) struct ListFormatting<'a> {
32
32
// Whether comments should be visually aligned.
33
33
align_comments : bool ,
34
34
config : & ' a Config ,
35
+ item_on_newline : Vec < bool > ,
36
+ padding : bool ,
35
37
}
36
38
37
39
impl < ' a > ListFormatting < ' a > {
@@ -47,9 +49,21 @@ impl<'a> ListFormatting<'a> {
47
49
nested : false ,
48
50
align_comments : true ,
49
51
config,
52
+ item_on_newline : Vec :: new ( ) ,
53
+ padding : true ,
50
54
}
51
55
}
52
56
57
+ pub ( crate ) fn padding ( mut self , padding : bool ) -> Self {
58
+ self . padding = padding;
59
+ self
60
+ }
61
+
62
+ pub ( crate ) fn item_on_newline ( mut self , item_on_newline : Vec < bool > ) -> Self {
63
+ self . item_on_newline = item_on_newline;
64
+ self
65
+ }
66
+
53
67
pub ( crate ) fn tactic ( mut self , tactic : DefinitiveListTactic ) -> Self {
54
68
self . tactic = tactic;
55
69
self
@@ -281,6 +295,7 @@ where
281
295
let mut prev_item_is_nested_import = false ;
282
296
283
297
let mut line_len = 0 ;
298
+ let mut first_item_on_line = true ;
284
299
let indent_str = & formatting. shape . indent . to_string ( formatting. config ) ;
285
300
while let Some ( ( i, item) ) = iter. next ( ) {
286
301
let item = item. as_ref ( ) ;
@@ -310,7 +325,7 @@ where
310
325
}
311
326
312
327
match tactic {
313
- DefinitiveListTactic :: Horizontal if !first => {
328
+ DefinitiveListTactic :: Horizontal if !first && formatting . padding => {
314
329
result. push ( ' ' ) ;
315
330
}
316
331
DefinitiveListTactic :: SpecialMacro ( num_args_before) => {
@@ -328,25 +343,30 @@ where
328
343
DefinitiveListTactic :: Vertical
329
344
if !first && !inner_item. is_empty ( ) && !result. is_empty ( ) =>
330
345
{
346
+ first_item_on_line = true ;
331
347
result. push ( '\n' ) ;
332
348
result. push_str ( indent_str) ;
333
349
}
334
350
DefinitiveListTactic :: Mixed => {
335
351
let total_width = total_item_width ( item) + item_sep_len;
336
352
337
353
// 1 is space between separator and item.
338
- if ( line_len > 0 && line_len + 1 + total_width > formatting. shape . width )
339
- || prev_item_had_post_comment
340
- || ( formatting. nested
341
- && ( prev_item_is_nested_import || ( !first && inner_item. contains ( "::" ) ) ) )
354
+ if ( !formatting. item_on_newline . is_empty ( ) && formatting. item_on_newline [ i] )
355
+ || formatting. item_on_newline . is_empty ( )
356
+ && ( ( line_len > 0 && line_len + 1 + total_width > formatting. shape . width )
357
+ || prev_item_had_post_comment
358
+ || ( formatting. nested
359
+ && ( prev_item_is_nested_import
360
+ || ( !first && inner_item. contains ( "::" ) ) ) ) )
342
361
{
343
362
result. push ( '\n' ) ;
344
363
result. push_str ( indent_str) ;
345
364
line_len = 0 ;
365
+ first_item_on_line = true ;
346
366
if formatting. ends_with_newline {
347
367
trailing_separator = true ;
348
368
}
349
- } else if line_len > 0 {
369
+ } else if formatting . padding && line_len > 0 {
350
370
result. push ( ' ' ) ;
351
371
line_len += 1 ;
352
372
}
@@ -399,8 +419,14 @@ where
399
419
}
400
420
401
421
if separate && sep_place. is_front ( ) && !first {
402
- result. push_str ( formatting. separator . trim ( ) ) ;
403
- result. push ( ' ' ) ;
422
+ if formatting. padding {
423
+ result. push_str ( formatting. separator . trim ( ) ) ;
424
+ result. push ( ' ' ) ;
425
+ } else if first_item_on_line {
426
+ result. push_str ( formatting. separator . trim_start ( ) ) ;
427
+ } else {
428
+ result. push_str ( formatting. separator ) ;
429
+ }
404
430
}
405
431
result. push_str ( inner_item) ;
406
432
@@ -414,7 +440,12 @@ where
414
440
formatting. config ,
415
441
) ?;
416
442
417
- result. push ( ' ' ) ;
443
+ if is_last_comment_block ( & formatted_comment) {
444
+ result. push ( ' ' ) ;
445
+ } else {
446
+ result. push ( '\n' ) ;
447
+ result. push_str ( indent_str) ;
448
+ }
418
449
result. push_str ( & formatted_comment) ;
419
450
}
420
451
@@ -512,6 +543,7 @@ where
512
543
result. push ( '\n' ) ;
513
544
}
514
545
546
+ first_item_on_line = false ;
515
547
prev_item_had_post_comment = item. post_comment . is_some ( ) ;
516
548
prev_item_is_nested_import = inner_item. contains ( "::" ) ;
517
549
}
@@ -599,25 +631,20 @@ pub(crate) fn extract_pre_comment(pre_snippet: &str) -> (Option<String>, ListIte
599
631
}
600
632
}
601
633
602
- pub ( crate ) fn extract_post_comment (
603
- post_snippet : & str ,
604
- comment_end : usize ,
605
- separator : & str ,
606
- ) -> Option < String > {
634
+ fn extract_post_comment ( post_snippet : & str , comment_end : usize , separator : & str ) -> Option < String > {
607
635
let white_space: & [ _ ] = & [ ' ' , '\t' ] ;
608
636
609
637
// Cleanup post-comment: strip separators and whitespace.
610
638
let post_snippet = post_snippet[ ..comment_end] . trim ( ) ;
611
639
let post_snippet_trimmed = if post_snippet. starts_with ( |c| c == ',' || c == ':' ) {
612
640
post_snippet[ 1 ..] . trim_matches ( white_space)
641
+ } else if post_snippet. ends_with ( separator) {
642
+ // the separator is in front of the next item
643
+ post_snippet[ ..post_snippet. len ( ) - separator. len ( ) ] . trim_matches ( white_space)
613
644
} else if post_snippet. starts_with ( separator) {
614
645
post_snippet[ separator. len ( ) ..] . trim_matches ( white_space)
615
- }
616
- // not comment or over two lines
617
- else if post_snippet. ends_with ( ',' )
618
- && ( !post_snippet. trim ( ) . starts_with ( "//" ) || post_snippet. trim ( ) . contains ( '\n' ) )
619
- {
620
- post_snippet[ ..( post_snippet. len ( ) - 1 ) ] . trim_matches ( white_space)
646
+ } else if post_snippet. ends_with ( ',' ) && !post_snippet. trim_start ( ) . starts_with ( "//" ) {
647
+ post_snippet[ ..post_snippet. len ( ) - 1 ] . trim_matches ( white_space)
621
648
} else {
622
649
post_snippet
623
650
} ;
@@ -633,12 +660,7 @@ pub(crate) fn extract_post_comment(
633
660
}
634
661
}
635
662
636
- pub ( crate ) fn get_comment_end (
637
- post_snippet : & str ,
638
- separator : & str ,
639
- terminator : & str ,
640
- is_last : bool ,
641
- ) -> usize {
663
+ fn get_comment_end ( post_snippet : & str , separator : & str , terminator : & str , is_last : bool ) -> usize {
642
664
if is_last {
643
665
return post_snippet
644
666
. find_uncommented ( terminator)
@@ -686,7 +708,7 @@ pub(crate) fn get_comment_end(
686
708
687
709
// Account for extra whitespace between items. This is fiddly
688
710
// because of the way we divide pre- and post- comments.
689
- pub ( crate ) fn has_extra_newline ( post_snippet : & str , comment_end : usize ) -> bool {
711
+ fn has_extra_newline ( post_snippet : & str , comment_end : usize ) -> bool {
690
712
if post_snippet. is_empty ( ) || comment_end == 0 {
691
713
return false ;
692
714
}
@@ -767,7 +789,17 @@ where
767
789
}
768
790
769
791
#[ allow( clippy:: too_many_arguments) ]
770
- // Creates an iterator over a list's items with associated comments.
792
+ /// Creates an iterator over a list's items with associated comments.
793
+ ///
794
+ /// - inner is the iterator over items
795
+ /// - terminator is a string that closes the list, used to get comments after the last item
796
+ /// - separator is a string that separates the items
797
+ /// - get_lo is a closure to get the lower bound of an item's span
798
+ /// - get_hi is a closure to get the upper bound of an item's span
799
+ /// - get_item_string is a closure to get the rewritten item as a string
800
+ /// - prev_span_end is the BytePos before the first item
801
+ /// - next_span_start is the BytePos after the last item
802
+ /// - leave_last is a boolean whether or not to rewrite the last item
771
803
pub ( crate ) fn itemize_list < ' a , T , I , F1 , F2 , F3 > (
772
804
snippet_provider : & ' a SnippetProvider < ' _ > ,
773
805
inner : I ,
@@ -918,5 +950,57 @@ pub(crate) fn struct_lit_formatting<'a>(
918
950
nested : false ,
919
951
align_comments : true ,
920
952
config : context. config ,
953
+ item_on_newline : Vec :: new ( ) ,
954
+ padding : true ,
955
+ }
956
+ }
957
+
958
+ #[ cfg( test) ]
959
+ mod test {
960
+ use super :: * ;
961
+
962
+ #[ test]
963
+ fn test_extract_post_comment ( ) {
964
+ let data = [
965
+ (
966
+ ", // a comment" ,
967
+ ", // a comment" . len ( ) ,
968
+ "," ,
969
+ "// a comment" ,
970
+ ) ,
971
+ (
972
+ ": // a comment" ,
973
+ ": // a comment" . len ( ) ,
974
+ ":" ,
975
+ "// a comment" ,
976
+ ) ,
977
+ (
978
+ "// a comment\n +" ,
979
+ "// a comment\n +" . len ( ) ,
980
+ "+" ,
981
+ "// a comment\n " ,
982
+ ) ,
983
+ (
984
+ "+ // a comment\n " ,
985
+ "+ // a comment\n " . len ( ) ,
986
+ "+" ,
987
+ "// a comment" ,
988
+ ) ,
989
+ (
990
+ "/* a comment */ ," ,
991
+ "/* a comment */ ," . len ( ) ,
992
+ "+" ,
993
+ "/* a comment */" ,
994
+ ) ,
995
+ ] ;
996
+
997
+ for ( i, ( post_snippet, comment_end, separator, expected) ) in data. iter ( ) . enumerate ( ) {
998
+ assert_eq ! (
999
+ extract_post_comment( post_snippet, * comment_end, separator) ,
1000
+ Some ( expected. to_string( ) ) ,
1001
+ "Failed on input {}" ,
1002
+ i
1003
+ ) ;
1004
+ }
921
1005
}
922
1006
}
0 commit comments