@@ -584,52 +584,83 @@ impl<'sess> State<'sess, '_> {
584584 return ;
585585 }
586586
587- let post_break_prefix = |prefix : & ' static str , line_len : usize | -> & ' static str {
587+ fn post_break_prefix ( prefix : & ' static str , has_content : bool ) -> & ' static str {
588+ if !has_content {
589+ return prefix;
590+ }
588591 match prefix {
589- "///" if line_len > 3 => "/// " ,
590- "//" if line_len > 2 => "// " ,
591- "/*" if line_len > 2 => "/* " ,
592- " *" if line_len > 2 => " * " ,
592+ "///" => "/// " ,
593+ "//" => "// " ,
594+ "/*" => "/* " ,
595+ " *" => " * " ,
593596 _ => prefix,
594597 }
595- } ;
598+ }
596599
597600 self . ibox ( 0 ) ;
598- let ( prefix, content) = if is_doc {
599- // Doc comments preserve leading whitespaces (right after the prefix).
600- self . word ( prefix) ;
601- let content = & line[ prefix. len ( ) ..] ;
602- let ( leading_ws, rest) =
603- content. split_at ( content. chars ( ) . take_while ( |& c| c. is_whitespace ( ) ) . count ( ) ) ;
601+ self . word ( prefix) ;
602+
603+ let content = & line[ prefix. len ( ) ..] ;
604+ let content = if is_doc {
605+ // Doc comments preserve leading whitespaces (right after the prefix) as nbps.
606+ let ws_len = content
607+ . char_indices ( )
608+ . take_while ( |( _, c) | c. is_whitespace ( ) )
609+ . last ( )
610+ . map_or ( 0 , |( idx, c) | idx + c. len_utf8 ( ) ) ;
611+ let ( leading_ws, rest) = content. split_at ( ws_len) ;
604612 if !leading_ws. is_empty ( ) {
605613 self . word ( leading_ws. to_owned ( ) ) ;
606614 }
607- let prefix = post_break_prefix ( prefix, rest. len ( ) ) ;
608- ( prefix, rest)
615+ rest
609616 } else {
610- let content = line[ prefix. len ( ) ..] . trim ( ) ;
611- let prefix = post_break_prefix ( prefix, content. len ( ) ) ;
612- self . word ( prefix) ;
613- ( prefix, content)
614- } ;
615-
616- // Split the rest of the content into words.
617- let mut words = content. split_whitespace ( ) . peekable ( ) ;
618- while let Some ( word) = words. next ( ) {
619- self . word ( word. to_owned ( ) ) ;
620- if let Some ( next_word) = words. peek ( ) {
621- if * next_word == "*/" {
617+ // Non-doc comments: replace first whitespace with nbsp, rest of content continues
618+ if let Some ( first_char) = content. chars ( ) . next ( ) {
619+ if first_char. is_whitespace ( ) {
622620 self . nbsp ( ) ;
621+ & content[ first_char. len_utf8 ( ) ..]
623622 } else {
624- self . s . scan_break ( BreakToken {
625- offset : break_offset,
626- blank_space : 1 ,
627- post_break : if matches ! ( prefix, "/* " ) { None } else { Some ( prefix) } ,
628- ..Default :: default ( )
629- } ) ;
623+ content
624+ }
625+ } else {
626+ ""
627+ }
628+ } ;
629+
630+ let post_break = post_break_prefix ( prefix, !content. is_empty ( ) ) ;
631+
632+ // Process content character by character to preserve consecutive whitespaces
633+ let ( mut chars, mut current_word) = ( content. chars ( ) . peekable ( ) , String :: new ( ) ) ;
634+ while let Some ( ch) = chars. next ( ) {
635+ if ch. is_whitespace ( ) {
636+ // Print current word
637+ if !current_word. is_empty ( ) {
638+ self . word ( std:: mem:: take ( & mut current_word) ) ;
630639 }
640+
641+ // Preserve multiple spaces while adding a single break
642+ let mut ws_count = 1 ;
643+ while chars. peek ( ) . is_some_and ( |c| c. is_whitespace ( ) ) {
644+ ws_count += 1 ;
645+ chars. next ( ) ;
646+ }
647+ self . s . scan_break ( BreakToken {
648+ offset : break_offset,
649+ blank_space : ws_count,
650+ post_break : if post_break. starts_with ( "/*" ) { None } else { Some ( post_break) } ,
651+ ..Default :: default ( )
652+ } ) ;
653+ continue ;
631654 }
655+
656+ current_word. push ( ch) ;
657+ }
658+
659+ // Print final word
660+ if !current_word. is_empty ( ) {
661+ self . word ( current_word) ;
632662 }
663+
633664 self . end ( ) ;
634665 }
635666
0 commit comments