@@ -122,7 +122,7 @@ impl Item {
122
122
123
123
/// Finds the `doc` attribute as a NameValue and returns the corresponding
124
124
/// value found.
125
- crate fn doc_value ( & self ) -> Option < & str > {
125
+ crate fn doc_value ( & self ) -> Option < String > {
126
126
self . attrs . doc_value ( )
127
127
}
128
128
@@ -469,31 +469,66 @@ crate struct DocFragment {
469
469
/// This allows distinguishing between the original documentation and a pub re-export.
470
470
/// If it is `None`, the item was not re-exported.
471
471
crate parent_module : Option < DefId > ,
472
- crate doc : String ,
472
+ crate doc : Symbol ,
473
473
crate kind : DocFragmentKind ,
474
+ crate need_backline : bool ,
475
+ crate indent : usize ,
474
476
}
475
477
476
- #[ derive( Clone , PartialEq , Eq , Debug , Hash ) ]
478
+ #[ derive( Clone , Copy , PartialEq , Eq , Debug , Hash ) ]
477
479
crate enum DocFragmentKind {
478
480
/// A doc fragment created from a `///` or `//!` doc comment.
479
481
SugaredDoc ,
480
482
/// A doc fragment created from a "raw" `#[doc=""]` attribute.
481
483
RawDoc ,
482
484
/// A doc fragment created from a `#[doc(include="filename")]` attribute. Contains both the
483
485
/// 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
+ }
485
513
}
486
514
487
515
impl < ' a > FromIterator < & ' a DocFragment > for String {
488
516
fn from_iter < T > ( iter : T ) -> Self
489
517
where
490
518
T : IntoIterator < Item = & ' a DocFragment > ,
491
519
{
520
+ let mut prev_kind: Option < DocFragmentKind > = None ;
492
521
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
+ {
494
528
acc. push ( '\n' ) ;
495
529
}
496
- acc. push_str ( & frag. doc ) ;
530
+ add_doc_fragment ( & mut acc, & frag) ;
531
+ prev_kind = Some ( frag. kind ) ;
497
532
acc
498
533
} )
499
534
}
@@ -565,25 +600,25 @@ impl Attributes {
565
600
/// Reads a `MetaItem` from within an attribute, looks for whether it is a
566
601
/// `#[doc(include="file")]`, and returns the filename and contents of the file as loaded from
567
602
/// its expansion.
568
- crate fn extract_include ( mi : & ast:: MetaItem ) -> Option < ( String , String ) > {
603
+ crate fn extract_include ( mi : & ast:: MetaItem ) -> Option < ( Symbol , Symbol ) > {
569
604
mi. meta_item_list ( ) . and_then ( |list| {
570
605
for meta in list {
571
606
if meta. has_name ( sym:: include) {
572
607
// the actual compiled `#[doc(include="filename")]` gets expanded to
573
608
// `#[doc(include(file="filename", contents="file contents")]` so we need to
574
609
// look for that instead
575
610
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 ;
578
613
579
614
for it in list {
580
615
if it. has_name ( sym:: file) {
581
616
if let Some ( name) = it. value_str ( ) {
582
- filename = Some ( name. to_string ( ) ) ;
617
+ filename = Some ( name) ;
583
618
}
584
619
} else if it. has_name ( sym:: contents) {
585
620
if let Some ( docs) = it. value_str ( ) {
586
- contents = Some ( docs. to_string ( ) ) ;
621
+ contents = Some ( docs) ;
587
622
}
588
623
}
589
624
}
@@ -622,30 +657,51 @@ impl Attributes {
622
657
attrs : & [ ast:: Attribute ] ,
623
658
additional_attrs : Option < ( & [ ast:: Attribute ] , DefId ) > ,
624
659
) -> Attributes {
625
- let mut doc_strings = vec ! [ ] ;
660
+ let mut doc_strings: Vec < DocFragment > = vec ! [ ] ;
626
661
let mut sp = None ;
627
662
let mut cfg = Cfg :: True ;
628
663
let mut doc_line = 0 ;
629
664
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
+
630
680
let clean_attr = |( attr, parent_module) : ( & ast:: Attribute , _ ) | {
631
681
if let Some ( value) = attr. doc_str ( ) {
632
682
trace ! ( "got doc_str={:?}" , value) ;
633
- let value = beautify_doc_string ( value) . to_string ( ) ;
683
+ let value = beautify_doc_string ( value) ;
634
684
let kind = if attr. is_doc_comment ( ) {
635
685
DocFragmentKind :: SugaredDoc
636
686
} else {
637
687
DocFragmentKind :: RawDoc
638
688
} ;
639
689
640
690
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 {
643
693
line,
644
694
span : attr. span ,
645
695
doc : value,
646
696
kind,
647
697
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) ;
649
705
650
706
if sp. is_none ( ) {
651
707
sp = Some ( attr. span ) ;
@@ -663,14 +719,18 @@ impl Attributes {
663
719
} else if let Some ( ( filename, contents) ) = Attributes :: extract_include ( & mi)
664
720
{
665
721
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 {
668
724
line,
669
725
span : attr. span ,
670
726
doc : contents,
671
727
kind : DocFragmentKind :: Include { filename } ,
672
728
parent_module,
673
- } ) ;
729
+ need_backline : false ,
730
+ indent : 0 ,
731
+ } ;
732
+ update_need_backline ( & mut doc_strings, & frag) ;
733
+ doc_strings. push ( frag) ;
674
734
}
675
735
}
676
736
}
@@ -721,14 +781,41 @@ impl Attributes {
721
781
722
782
/// Finds the `doc` attribute as a NameValue and returns the corresponding
723
783
/// 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
726
813
}
727
814
728
815
/// Finds all `doc` attributes as NameValues and returns their corresponding values, joined
729
816
/// with newlines.
730
817
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 ( ) ) }
732
819
}
733
820
734
821
/// Gets links as a vector
0 commit comments