@@ -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,31 +469,66 @@ 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 ,
480482 /// A doc fragment created from a "raw" `#[doc=""]` attribute.
481483 RawDoc ,
482484 /// A doc fragment created from a `#[doc(include="filename")]` attribute. Contains both the
483485 /// given filename and the file contents.
484- Include { filename : String } ,
486+ Include { filename : Symbol } ,
487+ }
488+
489+ // The goal of this function is to apply the `DocFragment` transformations that are required when
490+ // transforming into the final markdown. So the transformations in here are:
491+ //
492+ // * Applying the computed indent to each lines in each doc fragment (a `DocFragment` can contain
493+ // multiple lines in case of `#[doc = ""]`).
494+ // * Adding backlines between `DocFragment`s and adding an extra one if required (stored in the
495+ // `need_backline` field).
496+ fn add_doc_fragment ( out : & mut String , frag : & DocFragment ) {
497+ let s = frag. doc . as_str ( ) ;
498+ let mut iter = s. lines ( ) . peekable ( ) ;
499+ while let Some ( line) = iter. next ( ) {
500+ if line. chars ( ) . any ( |c| !c. is_whitespace ( ) ) {
501+ assert ! ( line. len( ) >= frag. indent) ;
502+ out. push_str ( & line[ frag. indent ..] ) ;
503+ } else {
504+ out. push_str ( line) ;
505+ }
506+ if iter. peek ( ) . is_some ( ) {
507+ out. push ( '\n' ) ;
508+ }
509+ }
510+ if frag. need_backline {
511+ out. push ( '\n' ) ;
512+ }
485513}
486514
487515impl < ' a > FromIterator < & ' a DocFragment > for String {
488516 fn from_iter < T > ( iter : T ) -> Self
489517 where
490518 T : IntoIterator < Item = & ' a DocFragment > ,
491519 {
520+ let mut prev_kind: Option < DocFragmentKind > = None ;
492521 iter. into_iter ( ) . fold ( String :: new ( ) , |mut acc, frag| {
493- if !acc. is_empty ( ) {
522+ if !acc. is_empty ( )
523+ && prev_kind
524+ . take ( )
525+ . map ( |p| matches ! ( p, DocFragmentKind :: Include { .. } ) && p != frag. kind )
526+ . unwrap_or ( false )
527+ {
494528 acc. push ( '\n' ) ;
495529 }
496- acc. push_str ( & frag. doc ) ;
530+ add_doc_fragment ( & mut acc, & frag) ;
531+ prev_kind = Some ( frag. kind ) ;
497532 acc
498533 } )
499534 }
@@ -565,25 +600,25 @@ impl Attributes {
565600 /// Reads a `MetaItem` from within an attribute, looks for whether it is a
566601 /// `#[doc(include="file")]`, and returns the filename and contents of the file as loaded from
567602 /// its expansion.
568- crate fn extract_include ( mi : & ast:: MetaItem ) -> Option < ( String , String ) > {
603+ crate fn extract_include ( mi : & ast:: MetaItem ) -> Option < ( Symbol , Symbol ) > {
569604 mi. meta_item_list ( ) . and_then ( |list| {
570605 for meta in list {
571606 if meta. has_name ( sym:: include) {
572607 // the actual compiled `#[doc(include="filename")]` gets expanded to
573608 // `#[doc(include(file="filename", contents="file contents")]` so we need to
574609 // look for that instead
575610 return meta. meta_item_list ( ) . and_then ( |list| {
576- let mut filename: Option < String > = None ;
577- let mut contents: Option < String > = None ;
611+ let mut filename: Option < Symbol > = None ;
612+ let mut contents: Option < Symbol > = None ;
578613
579614 for it in list {
580615 if it. has_name ( sym:: file) {
581616 if let Some ( name) = it. value_str ( ) {
582- filename = Some ( name. to_string ( ) ) ;
617+ filename = Some ( name) ;
583618 }
584619 } else if it. has_name ( sym:: contents) {
585620 if let Some ( docs) = it. value_str ( ) {
586- contents = Some ( docs. to_string ( ) ) ;
621+ contents = Some ( docs) ;
587622 }
588623 }
589624 }
@@ -622,30 +657,51 @@ impl Attributes {
622657 attrs : & [ ast:: Attribute ] ,
623658 additional_attrs : Option < ( & [ ast:: Attribute ] , DefId ) > ,
624659 ) -> Attributes {
625- let mut doc_strings = vec ! [ ] ;
660+ let mut doc_strings: Vec < DocFragment > = vec ! [ ] ;
626661 let mut sp = None ;
627662 let mut cfg = Cfg :: True ;
628663 let mut doc_line = 0 ;
629664
665+ fn update_need_backline ( doc_strings : & mut Vec < DocFragment > , frag : & DocFragment ) {
666+ if let Some ( prev) = doc_strings. last_mut ( ) {
667+ if matches ! ( prev. kind, DocFragmentKind :: Include { .. } )
668+ || prev. kind != frag. kind
669+ || prev. parent_module != frag. parent_module
670+ {
671+ // add a newline for extra padding between segments
672+ prev. need_backline = prev. kind == DocFragmentKind :: SugaredDoc
673+ || prev. kind == DocFragmentKind :: RawDoc
674+ } else {
675+ prev. need_backline = true ;
676+ }
677+ }
678+ }
679+
630680 let clean_attr = |( attr, parent_module) : ( & ast:: Attribute , _ ) | {
631681 if let Some ( value) = attr. doc_str ( ) {
632682 trace ! ( "got doc_str={:?}" , value) ;
633- let value = beautify_doc_string ( value) . to_string ( ) ;
683+ let value = beautify_doc_string ( value) ;
634684 let kind = if attr. is_doc_comment ( ) {
635685 DocFragmentKind :: SugaredDoc
636686 } else {
637687 DocFragmentKind :: RawDoc
638688 } ;
639689
640690 let line = doc_line;
641- doc_line += value. lines ( ) . count ( ) ;
642- doc_strings . push ( DocFragment {
691+ doc_line += value. as_str ( ) . lines ( ) . count ( ) ;
692+ let frag = DocFragment {
643693 line,
644694 span : attr. span ,
645695 doc : value,
646696 kind,
647697 parent_module,
648- } ) ;
698+ need_backline : false ,
699+ indent : 0 ,
700+ } ;
701+
702+ update_need_backline ( & mut doc_strings, & frag) ;
703+
704+ doc_strings. push ( frag) ;
649705
650706 if sp. is_none ( ) {
651707 sp = Some ( attr. span ) ;
@@ -663,14 +719,18 @@ impl Attributes {
663719 } else if let Some ( ( filename, contents) ) = Attributes :: extract_include ( & mi)
664720 {
665721 let line = doc_line;
666- doc_line += contents. lines ( ) . count ( ) ;
667- doc_strings . push ( DocFragment {
722+ doc_line += contents. as_str ( ) . lines ( ) . count ( ) ;
723+ let frag = DocFragment {
668724 line,
669725 span : attr. span ,
670726 doc : contents,
671727 kind : DocFragmentKind :: Include { filename } ,
672728 parent_module,
673- } ) ;
729+ need_backline : false ,
730+ indent : 0 ,
731+ } ;
732+ update_need_backline ( & mut doc_strings, & frag) ;
733+ doc_strings. push ( frag) ;
674734 }
675735 }
676736 }
@@ -721,14 +781,41 @@ impl Attributes {
721781
722782 /// Finds the `doc` attribute as a NameValue and returns the corresponding
723783 /// value found.
724- crate fn doc_value ( & self ) -> Option < & str > {
725- self . doc_strings . first ( ) . map ( |s| s. doc . as_str ( ) )
784+ crate fn doc_value ( & self ) -> Option < String > {
785+ let mut iter = self . doc_strings . iter ( ) ;
786+
787+ let ori = iter. next ( ) ?;
788+ let mut out = String :: new ( ) ;
789+ add_doc_fragment ( & mut out, & ori) ;
790+ while let Some ( new_frag) = iter. next ( ) {
791+ if matches ! ( ori. kind, DocFragmentKind :: Include { .. } )
792+ || new_frag. kind != ori. kind
793+ || new_frag. parent_module != ori. parent_module
794+ {
795+ break ;
796+ }
797+ add_doc_fragment ( & mut out, & new_frag) ;
798+ }
799+ if out. is_empty ( ) { None } else { Some ( out) }
800+ }
801+
802+ /// Return the doc-comments on this item, grouped by the module they came from.
803+ ///
804+ /// The module can be different if this is a re-export with added documentation.
805+ crate fn collapsed_doc_value_by_module_level ( & self ) -> FxHashMap < Option < DefId > , String > {
806+ let mut ret = FxHashMap :: default ( ) ;
807+
808+ for new_frag in self . doc_strings . iter ( ) {
809+ let out = ret. entry ( new_frag. parent_module ) . or_default ( ) ;
810+ add_doc_fragment ( out, & new_frag) ;
811+ }
812+ ret
726813 }
727814
728815 /// Finds all `doc` attributes as NameValues and returns their corresponding values, joined
729816 /// with newlines.
730817 crate fn collapsed_doc_value ( & self ) -> Option < String > {
731- if ! self . doc_strings . is_empty ( ) { Some ( self . doc_strings . iter ( ) . collect ( ) ) } else { None }
818+ if self . doc_strings . is_empty ( ) { None } else { Some ( self . doc_strings . iter ( ) . collect ( ) ) }
732819 }
733820
734821 /// Gets links as a vector
0 commit comments