@@ -275,33 +275,31 @@ where
275
275
}
276
276
}
277
277
278
- struct ItemState {
278
+ /// Stores the state of the ongoing stringification of a list item.
279
+ struct WriteListItemState {
280
+ /// The offset of the item in the list.
279
281
ith : usize ,
282
+ /// Whether or not the item is the first in the list.
280
283
first : bool ,
284
+ /// Whether or not the item is the last in the list.
281
285
last : bool ,
286
+ /// Whether or not the item is the first to be written on the current line.
282
287
first_item_on_line : bool ,
288
+ /// The placement of the separator
289
+ sep_place : SeparatorPlace ,
290
+ /// Whether or not the separator should be written.
291
+ separate : bool ,
292
+ /// Whether or not the separator can be trailing.
293
+ trailing_separator : bool ,
283
294
}
284
295
285
- impl ItemState {
286
- fn new ( ) -> ItemState {
287
- ItemState {
296
+ impl WriteListItemState {
297
+ fn new ( formatting : & ListFormatting < ' _ > ) -> WriteListItemState {
298
+ WriteListItemState {
288
299
ith : 0 ,
289
300
first : false ,
290
301
last : false ,
291
302
first_item_on_line : true ,
292
- }
293
- }
294
- }
295
-
296
- struct WriteListState {
297
- sep_place : SeparatorPlace ,
298
- separate : bool ,
299
- trailing_separator : bool ,
300
- }
301
-
302
- impl WriteListState {
303
- fn new ( formatting : & ListFormatting < ' _ > ) -> WriteListState {
304
- WriteListState {
305
303
sep_place : SeparatorPlace :: from_tactic (
306
304
formatting. separator_place ,
307
305
formatting. tactic ,
@@ -314,27 +312,36 @@ impl WriteListState {
314
312
}
315
313
}
316
314
317
- fn should_separate ( & mut self , item_state : & ItemState ) {
315
+ /// Set if a separator should be written for the current item.
316
+ fn should_separate ( & mut self ) {
318
317
self . separate = match self . sep_place {
319
- SeparatorPlace :: Front => !item_state . first ,
320
- SeparatorPlace :: Back => !item_state . last || self . trailing_separator ,
318
+ SeparatorPlace :: Front => !self . first ,
319
+ SeparatorPlace :: Back => !self . last || self . trailing_separator ,
321
320
} ;
322
321
}
323
322
323
+ /// Returns true if a separtor should be placed in front of the current item.
324
324
fn should_separate_if_front ( & self ) -> bool {
325
325
self . separate && self . sep_place . is_front ( )
326
326
}
327
327
328
+ /// Returns true if a separtor should be placed at the back of the current item.
328
329
fn should_separate_if_back ( & self ) -> bool {
329
330
self . separate && self . sep_place . is_back ( )
330
331
}
331
332
}
332
333
334
+ /// Align comments written after items.
333
335
struct PostCommentAlignment {
336
+ /// The largest item width in a group of items.
334
337
item_max_width : Option < usize > ,
338
+ /// The length of the first item.
335
339
first_item_len : usize ,
340
+ /// Whether or not an item that is not the first has the same length as the first item.
336
341
middle_item_same_len_first : bool ,
342
+ /// The length of the last item.
337
343
last_item_len : usize ,
344
+ /// Whether or not an item that is not the last has the same length as the last item.
338
345
middle_item_same_len_last : bool ,
339
346
}
340
347
@@ -373,11 +380,12 @@ impl PostCommentAlignment {
373
380
}
374
381
}
375
382
383
+ /// Get the length of the largest item starting at the ith-item.
376
384
fn update_item_max_width < T : AsRef < ListItem > > (
377
385
& mut self ,
378
386
items : & [ T ] ,
379
387
formatting : & ListFormatting < ' _ > ,
380
- item_state : & ItemState ,
388
+ item_state : & WriteListItemState ,
381
389
inner_item : & str ,
382
390
overhead : usize ,
383
391
) {
@@ -423,30 +431,35 @@ impl PostCommentAlignment {
423
431
max_width
424
432
}
425
433
426
- fn compute (
434
+ /// Computes the number whitespaces to insert after an item so that its post comment is aligned
435
+ /// with others.
436
+ ///
437
+ /// The alignment logic is complicated by the fact that the alignment is based on the longest
438
+ /// item, without the additional space taken by the separator. For the first and last items
439
+ /// where the separator may be missing, some compensation needs to be computed.
440
+ fn alignment (
427
441
& self ,
428
- item_state : & ItemState ,
442
+ item_state : & WriteListItemState ,
429
443
inner_item_len : usize ,
430
444
fmt : & ListFormatting < ' _ > ,
431
- write_list_state : & WriteListState ,
432
445
) -> usize {
433
446
// 1 = whitespace before the post_comment
434
- let alignment = self
435
- . item_max_width
436
- . unwrap_or ( 0 )
437
- . saturating_sub ( inner_item_len)
438
- + 1 ;
439
447
if self . item_max_width . is_none ( ) {
440
- return alignment ;
448
+ return 1 ;
441
449
}
442
- let first_item_longest = self . item_max_width . unwrap ( ) == self . first_item_len ;
443
- let last_item_longest = self . item_max_width . unwrap ( ) == self . last_item_len ;
444
- let alignment = match write_list_state. sep_place {
450
+ let item_max_width = self . item_max_width . unwrap ( ) ;
451
+ let alignment = item_max_width. saturating_sub ( inner_item_len) + 1 ;
452
+ let first_item_longest = item_max_width == self . first_item_len ;
453
+ let last_item_longest = item_max_width == self . last_item_len ;
454
+
455
+ let alignment = match item_state. sep_place {
445
456
/*
446
457
* Front separator: first item is missing the separator and needs to be compensated
447
458
*/
448
459
SeparatorPlace :: Front => {
449
460
match ( item_state. first , first_item_longest) {
461
+ // in case a middle item has the same length as the first, then all items have
462
+ // the correct alignment: only the first item needs to account for the separator
450
463
_ if self . middle_item_same_len_first => {
451
464
alignment
452
465
+ if item_state. first {
@@ -457,11 +470,13 @@ impl PostCommentAlignment {
457
470
}
458
471
// first item is the longest: others need to minus the (separator + padding)
459
472
// to the alignment
460
- ( false , true ) => {
461
- // FIXME: document the trim the leading whitespace
462
- alignment. saturating_sub ( fmt. separator . trim_start ( ) . len ( ) )
463
- - if fmt. padding { 1 } else { 0 }
464
- }
473
+ ( false , true ) => alignment
474
+ . saturating_sub ( if item_state. first_item_on_line {
475
+ fmt. separator . trim_start ( ) . len ( )
476
+ } else {
477
+ fmt. separator . len ( )
478
+ } )
479
+ . saturating_sub ( if fmt. padding { 1 } else { 0 } ) ,
465
480
// first item is not the longest: first needs to add (searator + padding)
466
481
// to the alignment
467
482
( true , false ) => {
@@ -480,7 +495,7 @@ impl PostCommentAlignment {
480
495
match ( item_state. last , last_item_longest) {
481
496
_ if self . middle_item_same_len_last => {
482
497
alignment
483
- + if item_state. last && !write_list_state . separate {
498
+ + if item_state. last && !item_state . separate {
484
499
fmt. separator . len ( )
485
500
} else {
486
501
0
@@ -491,7 +506,7 @@ impl PostCommentAlignment {
491
506
alignment. saturating_sub ( fmt. separator . len ( ) )
492
507
}
493
508
// last item is not the longest: last needs to add sep to the alignment
494
- ( true , false ) if !write_list_state . separate => alignment + fmt. separator . len ( ) ,
509
+ ( true , false ) if !item_state . separate => alignment + fmt. separator . len ( ) ,
495
510
_ => alignment,
496
511
}
497
512
}
@@ -507,47 +522,36 @@ where
507
522
T : AsRef < ListItem > ,
508
523
{
509
524
let tactic = formatting. tactic ;
525
+ let indent_str = & formatting. shape . indent . to_string ( formatting. config ) ;
510
526
511
527
let mut result = String :: with_capacity ( 128 ) ;
512
528
let mut iter = items. iter ( ) . enumerate ( ) . peekable ( ) ;
529
+
530
+ // Mixed tactic state
513
531
let mut prev_item_had_post_comment = false ;
514
532
let mut prev_item_is_nested_import = false ;
533
+ let mut line_len = 0 ;
515
534
516
- let mut item_state = ItemState :: new ( ) ;
517
- let mut write_list_state = WriteListState :: new ( formatting) ;
535
+ let mut item_state = WriteListItemState :: new ( formatting) ;
518
536
let mut pca = PostCommentAlignment :: new ( items) ;
519
537
520
- let mut line_len = 0 ;
521
- let indent_str = & formatting. shape . indent . to_string ( formatting. config ) ;
522
538
while let Some ( ( i, item) ) = iter. next ( ) {
523
539
let item = item. as_ref ( ) ;
524
540
let inner_item = item. item . as_ref ( ) ?;
541
+
542
+ if !item. is_substantial ( ) {
543
+ continue ;
544
+ }
545
+
525
546
item_state. ith = i;
526
547
item_state. first = i == 0 ;
527
548
item_state. last = iter. peek ( ) . is_none ( ) ;
528
- write_list_state . should_separate ( & item_state ) ;
529
- let item_sep_len = if write_list_state . separate {
549
+ item_state . should_separate ( ) ;
550
+ let item_sep_len = if item_state . separate {
530
551
formatting. separator . len ( )
531
552
} else {
532
553
0
533
554
} ;
534
-
535
- // Item string may be multi-line. Its length (used for block comment alignment)
536
- // should be only the length of the last line.
537
- let item_last_line = if item. is_multiline ( ) {
538
- inner_item. lines ( ) . last ( ) . unwrap_or ( "" )
539
- } else {
540
- inner_item. as_ref ( )
541
- } ;
542
- let mut item_last_line_width = item_last_line. len ( ) + item_sep_len;
543
- if item_last_line. starts_with ( & * * indent_str) {
544
- item_last_line_width -= indent_str. len ( ) ;
545
- }
546
-
547
- if !item. is_substantial ( ) {
548
- continue ;
549
- }
550
-
551
555
match tactic {
552
556
_ if !formatting. custom_list_tactic . is_empty ( ) => {
553
557
if * formatting
@@ -599,19 +603,20 @@ where
599
603
line_len = 0 ;
600
604
item_state. first_item_on_line = true ;
601
605
if formatting. ends_with_newline {
602
- write_list_state . trailing_separator = true ;
606
+ item_state . trailing_separator = true ;
603
607
}
604
608
} else if formatting. padding && line_len > 0 {
605
609
result. push ( ' ' ) ;
606
610
line_len += 1 ;
607
611
}
608
612
609
613
if item_state. last && formatting. ends_with_newline {
610
- write_list_state. separate =
611
- formatting. trailing_separator != SeparatorTactic :: Never ;
614
+ item_state. separate = formatting. trailing_separator != SeparatorTactic :: Never ;
612
615
}
613
616
614
617
line_len += total_width;
618
+ prev_item_had_post_comment = item. post_comment . is_some ( ) ;
619
+ prev_item_is_nested_import = inner_item. contains ( "::" ) ;
615
620
}
616
621
_ => { }
617
622
}
@@ -654,7 +659,7 @@ where
654
659
pca. item_max_width = None ;
655
660
}
656
661
657
- if write_list_state . should_separate_if_front ( ) && !item_state. first {
662
+ if item_state . should_separate_if_front ( ) && !item_state. first {
658
663
if formatting. padding {
659
664
result. push_str ( formatting. separator . trim ( ) ) ;
660
665
result. push ( ' ' ) ;
@@ -685,7 +690,7 @@ where
685
690
result. push_str ( & formatted_comment) ;
686
691
}
687
692
688
- if write_list_state . should_separate_if_back ( ) {
693
+ if item_state . should_separate_if_back ( ) {
689
694
result. push_str ( formatting. separator ) ;
690
695
}
691
696
@@ -699,6 +704,18 @@ where
699
704
} else if let Some ( max_width) = item_max_width {
700
705
max_width + 2
701
706
} else {
707
+ // Item string may be multi-line. Its length (used for block comment alignment)
708
+ // should be only the length of the last line.
709
+ let item_last_line = if item. is_multiline ( ) {
710
+ inner_item. lines ( ) . last ( ) . unwrap_or ( "" )
711
+ } else {
712
+ inner_item. as_ref ( )
713
+ } ;
714
+ let mut item_last_line_width = item_last_line. len ( ) + item_sep_len;
715
+ if item_last_line. starts_with ( & * * indent_str) {
716
+ item_last_line_width -= indent_str. len ( ) ;
717
+ }
718
+
702
719
// 1 = space between item and comment.
703
720
item_last_line_width + 1
704
721
} ;
@@ -725,11 +742,10 @@ where
725
742
if !starts_with_newline ( comment) {
726
743
if formatting. align_comments {
727
744
let mut comment_alignment =
728
- pca. compute ( & item_state, inner_item. len ( ) , formatting, & write_list_state ) ;
745
+ pca. alignment ( & item_state, inner_item. len ( ) , formatting) ;
729
746
if first_line_width ( & formatted_comment)
730
747
+ last_line_width ( & result)
731
748
+ comment_alignment
732
- + 1
733
749
> formatting. config . max_width ( )
734
750
{
735
751
pca. item_max_width = None ;
@@ -741,12 +757,8 @@ where
741
757
overhead,
742
758
) ;
743
759
formatted_comment = rewrite_post_comment ( pca. item_max_width ) ?;
744
- comment_alignment = pca. compute (
745
- & item_state,
746
- inner_item. len ( ) ,
747
- formatting,
748
- & write_list_state,
749
- ) ;
760
+ comment_alignment =
761
+ pca. alignment ( & item_state, inner_item. len ( ) , formatting) ;
750
762
}
751
763
result. push_str ( & " " . repeat ( comment_alignment) ) ;
752
764
} else {
@@ -774,8 +786,6 @@ where
774
786
}
775
787
776
788
item_state. first_item_on_line = false ;
777
- prev_item_had_post_comment = item. post_comment . is_some ( ) ;
778
- prev_item_is_nested_import = inner_item. contains ( "::" ) ;
779
789
}
780
790
781
791
Some ( result)
0 commit comments