@@ -122,7 +122,7 @@ impl Item {
122122
123123 /// Finds the `doc` attribute as a NameValue and returns the corresponding
124124 /// value found.
125- crate fn doc_value ( & self ) -> Option < & str > {
125+ crate fn doc_value ( & self ) -> Option < String > {
126126 self . attrs . doc_value ( )
127127 }
128128
@@ -469,11 +469,13 @@ crate struct DocFragment {
469469 /// This allows distinguishing between the original documentation and a pub re-export.
470470 /// If it is `None`, the item was not re-exported.
471471 crate parent_module : Option < DefId > ,
472- crate doc : String ,
472+ crate doc : Symbol ,
473473 crate kind : DocFragmentKind ,
474+ crate need_backline : bool ,
475+ crate indent : usize ,
474476}
475477
476- #[ derive( Clone , PartialEq , Eq , Debug , Hash ) ]
478+ #[ derive( Clone , Copy , PartialEq , Eq , Debug , Hash ) ]
477479crate enum DocFragmentKind {
478480 /// A doc fragment created from a `///` or `//!` doc comment.
479481 SugaredDoc ,
@@ -484,16 +486,42 @@ crate enum DocFragmentKind {
484486 Include { filename : Symbol } ,
485487}
486488
489+ fn add_doc_fragment ( out : & mut String , frag : & DocFragment ) {
490+ let s = frag. doc . as_str ( ) ;
491+ let mut iter = s. lines ( ) . peekable ( ) ;
492+ while let Some ( line) = iter. next ( ) {
493+ if line. chars ( ) . any ( |c| !c. is_whitespace ( ) ) {
494+ assert ! ( line. len( ) >= frag. indent) ;
495+ out. push_str ( & line[ frag. indent ..] ) ;
496+ } else {
497+ out. push_str ( line) ;
498+ }
499+ if iter. peek ( ) . is_some ( ) {
500+ out. push ( '\n' ) ;
501+ }
502+ }
503+ if frag. need_backline {
504+ out. push ( '\n' ) ;
505+ }
506+ }
507+
487508impl < ' a > FromIterator < & ' a DocFragment > for String {
488509 fn from_iter < T > ( iter : T ) -> Self
489510 where
490511 T : IntoIterator < Item = & ' a DocFragment > ,
491512 {
513+ let mut prev_kind: Option < DocFragmentKind > = None ;
492514 iter. into_iter ( ) . fold ( String :: new ( ) , |mut acc, frag| {
493- if !acc. is_empty ( ) {
515+ if !acc. is_empty ( )
516+ && prev_kind
517+ . take ( )
518+ . map ( |p| matches ! ( p, DocFragmentKind :: Include { .. } ) && p != frag. kind )
519+ . unwrap_or ( false )
520+ {
494521 acc. push ( '\n' ) ;
495522 }
496- acc. push_str ( & frag. doc ) ;
523+ add_doc_fragment ( & mut acc, & frag) ;
524+ prev_kind = Some ( frag. kind ) ;
497525 acc
498526 } )
499527 }
@@ -565,7 +593,7 @@ impl Attributes {
565593 /// Reads a `MetaItem` from within an attribute, looks for whether it is a
566594 /// `#[doc(include="file")]`, and returns the filename and contents of the file as loaded from
567595 /// its expansion.
568- crate fn extract_include ( mi : & ast:: MetaItem ) -> Option < ( Symbol , String ) > {
596+ crate fn extract_include ( mi : & ast:: MetaItem ) -> Option < ( Symbol , Symbol ) > {
569597 mi. meta_item_list ( ) . and_then ( |list| {
570598 for meta in list {
571599 if meta. has_name ( sym:: include) {
@@ -574,7 +602,7 @@ impl Attributes {
574602 // look for that instead
575603 return meta. meta_item_list ( ) . and_then ( |list| {
576604 let mut filename: Option < Symbol > = None ;
577- let mut contents: Option < String > = None ;
605+ let mut contents: Option < Symbol > = None ;
578606
579607 for it in list {
580608 if it. has_name ( sym:: file) {
@@ -583,7 +611,7 @@ impl Attributes {
583611 }
584612 } else if it. has_name ( sym:: contents) {
585613 if let Some ( docs) = it. value_str ( ) {
586- contents = Some ( docs. to_string ( ) ) ;
614+ contents = Some ( docs) ;
587615 }
588616 }
589617 }
@@ -622,30 +650,51 @@ impl Attributes {
622650 attrs : & [ ast:: Attribute ] ,
623651 additional_attrs : Option < ( & [ ast:: Attribute ] , DefId ) > ,
624652 ) -> Attributes {
625- let mut doc_strings = vec ! [ ] ;
653+ let mut doc_strings: Vec < DocFragment > = vec ! [ ] ;
626654 let mut sp = None ;
627655 let mut cfg = Cfg :: True ;
628656 let mut doc_line = 0 ;
629657
658+ fn update_need_backline ( doc_strings : & mut Vec < DocFragment > , frag : & DocFragment ) {
659+ if let Some ( prev) = doc_strings. last_mut ( ) {
660+ if matches ! ( prev. kind, DocFragmentKind :: Include { .. } )
661+ || prev. kind != frag. kind
662+ || prev. parent_module != frag. parent_module
663+ {
664+ // add a newline for extra padding between segments
665+ prev. need_backline = prev. kind == DocFragmentKind :: SugaredDoc
666+ || prev. kind == DocFragmentKind :: RawDoc
667+ } else {
668+ prev. need_backline = true ;
669+ }
670+ }
671+ }
672+
630673 let clean_attr = |( attr, parent_module) : ( & ast:: Attribute , _ ) | {
631674 if let Some ( value) = attr. doc_str ( ) {
632675 trace ! ( "got doc_str={:?}" , value) ;
633- let value = beautify_doc_string ( value) . to_string ( ) ;
676+ let value = beautify_doc_string ( value) ;
634677 let kind = if attr. is_doc_comment ( ) {
635678 DocFragmentKind :: SugaredDoc
636679 } else {
637680 DocFragmentKind :: RawDoc
638681 } ;
639682
640683 let line = doc_line;
641- doc_line += value. lines ( ) . count ( ) ;
642- doc_strings . push ( DocFragment {
684+ doc_line += value. as_str ( ) . lines ( ) . count ( ) ;
685+ let frag = DocFragment {
643686 line,
644687 span : attr. span ,
645688 doc : value,
646689 kind,
647690 parent_module,
648- } ) ;
691+ need_backline : false ,
692+ indent : 0 ,
693+ } ;
694+
695+ update_need_backline ( & mut doc_strings, & frag) ;
696+
697+ doc_strings. push ( frag) ;
649698
650699 if sp. is_none ( ) {
651700 sp = Some ( attr. span ) ;
@@ -663,14 +712,18 @@ impl Attributes {
663712 } else if let Some ( ( filename, contents) ) = Attributes :: extract_include ( & mi)
664713 {
665714 let line = doc_line;
666- doc_line += contents. lines ( ) . count ( ) ;
667- doc_strings . push ( DocFragment {
715+ doc_line += contents. as_str ( ) . lines ( ) . count ( ) ;
716+ let frag = DocFragment {
668717 line,
669718 span : attr. span ,
670719 doc : contents,
671720 kind : DocFragmentKind :: Include { filename } ,
672721 parent_module,
673- } ) ;
722+ need_backline : false ,
723+ indent : 0 ,
724+ } ;
725+ update_need_backline ( & mut doc_strings, & frag) ;
726+ doc_strings. push ( frag) ;
674727 }
675728 }
676729 }
@@ -721,14 +774,39 @@ impl Attributes {
721774
722775 /// Finds the `doc` attribute as a NameValue and returns the corresponding
723776 /// value found.
724- crate fn doc_value ( & self ) -> Option < & str > {
725- self . doc_strings . first ( ) . map ( |s| s. doc . as_str ( ) )
777+ crate fn doc_value ( & self ) -> Option < String > {
778+ let mut iter = self . doc_strings . iter ( ) ;
779+
780+ let ori = iter. next ( ) ?;
781+ let mut out = String :: new ( ) ;
782+ add_doc_fragment ( & mut out, & ori) ;
783+ while let Some ( new_frag) = iter. next ( ) {
784+ if matches ! ( ori. kind, DocFragmentKind :: Include { .. } )
785+ || new_frag. kind != ori. kind
786+ || new_frag. parent_module != ori. parent_module
787+ {
788+ break ;
789+ }
790+ add_doc_fragment ( & mut out, & new_frag) ;
791+ }
792+ if out. is_empty ( ) { None } else { Some ( out) }
793+ }
794+
795+ crate fn collapsed_doc_value_by_module_level ( & self ) -> FxHashMap < Option < DefId > , String > {
796+ let mut ret = FxHashMap :: default ( ) ;
797+
798+ for new_frag in self . doc_strings . iter ( ) {
799+ let out = ret. entry ( new_frag. parent_module ) . or_insert_with ( || String :: new ( ) ) ;
800+ add_doc_fragment ( out, & new_frag) ;
801+ }
802+ ret
726803 }
727804
728805 /// Finds all `doc` attributes as NameValues and returns their corresponding values, joined
729806 /// with newlines.
730807 crate fn collapsed_doc_value ( & self ) -> Option < String > {
731- if !self . doc_strings . is_empty ( ) { Some ( self . doc_strings . iter ( ) . collect ( ) ) } else { None }
808+ let s: String = self . doc_strings . iter ( ) . collect ( ) ;
809+ if s. is_empty ( ) { None } else { Some ( s) }
732810 }
733811
734812 /// Gets links as a vector
0 commit comments