@@ -433,6 +433,58 @@ impl CodeBlockAttribute {
433
433
}
434
434
}
435
435
436
+ /// Block that is formatted as an item.
437
+ ///
438
+ /// An item starts with either a star `*` or a dash `-`. Different level of indentation are
439
+ /// handled.
440
+ struct ItemizedBlock {
441
+ /// the number of whitespaces up to the item sigil
442
+ indent : usize ,
443
+ /// the string that marks the start of an item
444
+ opener : String ,
445
+ /// sequence of whitespaces to prefix new lines that are part of the item
446
+ line_start : String ,
447
+ }
448
+
449
+ impl ItemizedBlock {
450
+ /// Returns true if the line is formatted as an item
451
+ fn is_itemized_line ( line : & str ) -> bool {
452
+ let trimmed = line. trim_left ( ) ;
453
+ trimmed. starts_with ( "* " ) || trimmed. starts_with ( "- " )
454
+ }
455
+
456
+ /// Creates a new ItemizedBlock described with the given line.
457
+ /// The `is_itemized_line` needs to be called first.
458
+ fn new ( line : & str ) -> ItemizedBlock {
459
+ let space_to_sigil = line. chars ( ) . take_while ( |c| c. is_whitespace ( ) ) . count ( ) ;
460
+ let indent = space_to_sigil + 2 ;
461
+ ItemizedBlock {
462
+ indent,
463
+ opener : line[ ..indent] . to_string ( ) ,
464
+ line_start : " " . repeat ( indent) ,
465
+ }
466
+ }
467
+
468
+ /// Returns a `StringFormat` used for formatting the content of an item
469
+ fn create_string_format < ' a > ( & ' a self , fmt : & ' a StringFormat ) -> StringFormat < ' a > {
470
+ StringFormat {
471
+ opener : "" ,
472
+ closer : "" ,
473
+ line_start : "" ,
474
+ line_end : "" ,
475
+ shape : Shape :: legacy ( fmt. shape . width . saturating_sub ( self . indent ) , Indent :: empty ( ) ) ,
476
+ trim_end : true ,
477
+ config : fmt. config ,
478
+ }
479
+ }
480
+
481
+ /// Returns true if the line is part of the current itemized block
482
+ fn in_block ( & self , line : & str ) -> bool {
483
+ !ItemizedBlock :: is_itemized_line ( line)
484
+ && self . indent <= line. chars ( ) . take_while ( |c| c. is_whitespace ( ) ) . count ( )
485
+ }
486
+ }
487
+
436
488
fn rewrite_comment_inner (
437
489
orig : & str ,
438
490
block_style : bool ,
@@ -493,15 +545,17 @@ fn rewrite_comment_inner(
493
545
let mut code_block_buffer = String :: with_capacity ( 128 ) ;
494
546
let mut is_prev_line_multi_line = false ;
495
547
let mut code_block_attr = None ;
548
+ let mut item_block_buffer = String :: with_capacity ( 128 ) ;
549
+ let mut item_block: Option < ItemizedBlock > = None ;
496
550
let comment_line_separator = format ! ( "{}{}" , indent_str, line_start) ;
497
- let join_code_block_with_comment_line_separator = |s : & str | {
551
+ let join_block = |s : & str , sep : & str | {
498
552
let mut result = String :: with_capacity ( s. len ( ) + 128 ) ;
499
553
let mut iter = s. lines ( ) . peekable ( ) ;
500
554
while let Some ( line) = iter. next ( ) {
501
555
result. push_str ( line) ;
502
556
result. push_str ( match iter. peek ( ) {
503
- Some ( next_line) if next_line. is_empty ( ) => comment_line_separator . trim_right ( ) ,
504
- Some ( ..) => & comment_line_separator ,
557
+ Some ( next_line) if next_line. is_empty ( ) => sep . trim_right ( ) ,
558
+ Some ( ..) => & sep ,
505
559
None => "" ,
506
560
} ) ;
507
561
}
@@ -511,7 +565,26 @@ fn rewrite_comment_inner(
511
565
for ( i, ( line, has_leading_whitespace) ) in lines. enumerate ( ) {
512
566
let is_last = i == count_newlines ( orig) ;
513
567
514
- if let Some ( ref attr) = code_block_attr {
568
+ if let Some ( ref ib) = item_block {
569
+ if ib. in_block ( & line) {
570
+ item_block_buffer. push_str ( & line) ;
571
+ item_block_buffer. push ( '\n' ) ;
572
+ continue ;
573
+ }
574
+ is_prev_line_multi_line = false ;
575
+ fmt. shape = Shape :: legacy ( max_chars, fmt_indent) ;
576
+ let item_fmt = ib. create_string_format ( & fmt) ;
577
+ result. push_str ( & comment_line_separator) ;
578
+ result. push_str ( & ib. opener ) ;
579
+ match rewrite_string ( & item_block_buffer. replace ( "\n " , " " ) , & item_fmt) {
580
+ Some ( s) => result. push_str ( & join_block (
581
+ & s,
582
+ & format ! ( "{}{}" , & comment_line_separator, ib. line_start) ,
583
+ ) ) ,
584
+ None => result. push_str ( & join_block ( & item_block_buffer, & comment_line_separator) ) ,
585
+ } ;
586
+ item_block_buffer. clear ( ) ;
587
+ } else if let Some ( ref attr) = code_block_attr {
515
588
if line. starts_with ( "```" ) {
516
589
let code_block = match attr {
517
590
CodeBlockAttribute :: Ignore | CodeBlockAttribute :: Text => {
@@ -529,7 +602,7 @@ fn rewrite_comment_inner(
529
602
} ;
530
603
if !code_block. is_empty ( ) {
531
604
result. push_str ( & comment_line_separator) ;
532
- result. push_str ( & join_code_block_with_comment_line_separator ( & code_block) ) ;
605
+ result. push_str ( & join_block ( & code_block, & comment_line_separator ) ) ;
533
606
}
534
607
code_block_buffer. clear ( ) ;
535
608
result. push_str ( & comment_line_separator) ;
@@ -538,46 +611,42 @@ fn rewrite_comment_inner(
538
611
} else {
539
612
code_block_buffer. push_str ( & hide_sharp_behind_comment ( line) ) ;
540
613
code_block_buffer. push ( '\n' ) ;
541
-
542
- if is_last {
543
- // There is a code block that is not properly enclosed by backticks.
544
- // We will leave them untouched.
545
- result. push_str ( & comment_line_separator) ;
546
- result. push_str ( & join_code_block_with_comment_line_separator (
547
- & trim_custom_comment_prefix ( & code_block_buffer) ,
548
- ) ) ;
549
- }
550
614
}
615
+ continue ;
616
+ }
551
617
618
+ code_block_attr = None ;
619
+ item_block = None ;
620
+ if line. starts_with ( "```" ) {
621
+ code_block_attr = Some ( CodeBlockAttribute :: new ( & line[ 3 ..] ) )
622
+ } else if config. wrap_comments ( ) && ItemizedBlock :: is_itemized_line ( & line) {
623
+ let ib = ItemizedBlock :: new ( & line) ;
624
+ item_block_buffer. push_str ( & line[ ib. indent ..] ) ;
625
+ item_block_buffer. push ( '\n' ) ;
626
+ item_block = Some ( ib) ;
552
627
continue ;
553
- } else {
554
- code_block_attr = if line. starts_with ( "```" ) {
555
- Some ( CodeBlockAttribute :: new ( & line[ 3 ..] ) )
556
- } else {
557
- None
558
- } ;
628
+ }
559
629
560
- if result == opener {
561
- let force_leading_whitespace = opener == "/* " && count_newlines ( orig) == 0 ;
562
- if !has_leading_whitespace && !force_leading_whitespace && result. ends_with ( ' ' ) {
563
- result. pop ( ) ;
564
- }
565
- if line. is_empty ( ) {
566
- continue ;
567
- }
568
- } else if is_prev_line_multi_line && !line. is_empty ( ) {
569
- result. push ( ' ' )
570
- } else if is_last && line. is_empty ( ) {
571
- // trailing blank lines are unwanted
572
- if !closer. is_empty ( ) {
573
- result. push_str ( & indent_str) ;
574
- }
575
- break ;
576
- } else {
577
- result. push_str ( & comment_line_separator) ;
578
- if !has_leading_whitespace && result. ends_with ( ' ' ) {
579
- result. pop ( ) ;
580
- }
630
+ if result == opener {
631
+ let force_leading_whitespace = opener == "/* " && count_newlines ( orig) == 0 ;
632
+ if !has_leading_whitespace && !force_leading_whitespace && result. ends_with ( ' ' ) {
633
+ result. pop ( ) ;
634
+ }
635
+ if line. is_empty ( ) {
636
+ continue ;
637
+ }
638
+ } else if is_prev_line_multi_line && !line. is_empty ( ) {
639
+ result. push ( ' ' )
640
+ } else if is_last && line. is_empty ( ) {
641
+ // trailing blank lines are unwanted
642
+ if !closer. is_empty ( ) {
643
+ result. push_str ( & indent_str) ;
644
+ }
645
+ break ;
646
+ } else {
647
+ result. push_str ( & comment_line_separator) ;
648
+ if !has_leading_whitespace && result. ends_with ( ' ' ) {
649
+ result. pop ( ) ;
581
650
}
582
651
}
583
652
@@ -631,6 +700,30 @@ fn rewrite_comment_inner(
631
700
is_prev_line_multi_line = false ;
632
701
}
633
702
}
703
+ if !code_block_buffer. is_empty ( ) {
704
+ // There is a code block that is not properly enclosed by backticks.
705
+ // We will leave them untouched.
706
+ result. push_str ( & comment_line_separator) ;
707
+ result. push_str ( & join_block (
708
+ & trim_custom_comment_prefix ( & code_block_buffer) ,
709
+ & comment_line_separator,
710
+ ) ) ;
711
+ }
712
+ if !item_block_buffer. is_empty ( ) {
713
+ // the last few lines are part of an itemized block
714
+ let ib = item_block. unwrap ( ) ;
715
+ fmt. shape = Shape :: legacy ( max_chars, fmt_indent) ;
716
+ let item_fmt = ib. create_string_format ( & fmt) ;
717
+ result. push_str ( & comment_line_separator) ;
718
+ result. push_str ( & ib. opener ) ;
719
+ match rewrite_string ( & item_block_buffer. replace ( "\n " , " " ) , & item_fmt) {
720
+ Some ( s) => result. push_str ( & join_block (
721
+ & s,
722
+ & format ! ( "{}{}" , & comment_line_separator, ib. line_start) ,
723
+ ) ) ,
724
+ None => result. push_str ( & join_block ( & item_block_buffer, & comment_line_separator) ) ,
725
+ } ;
726
+ }
634
727
635
728
result. push_str ( closer) ;
636
729
if result. ends_with ( opener) && opener. ends_with ( ' ' ) {
0 commit comments