@@ -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,11 +469,13 @@ 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 ,
@@ -484,16 +486,42 @@ crate enum DocFragmentKind {
484
486
Include { filename : Symbol } ,
485
487
}
486
488
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
+
487
508
impl < ' a > FromIterator < & ' a DocFragment > for String {
488
509
fn from_iter < T > ( iter : T ) -> Self
489
510
where
490
511
T : IntoIterator < Item = & ' a DocFragment > ,
491
512
{
513
+ let mut prev_kind: Option < DocFragmentKind > = None ;
492
514
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
+ {
494
521
acc. push ( '\n' ) ;
495
522
}
496
- acc. push_str ( & frag. doc ) ;
523
+ add_doc_fragment ( & mut acc, & frag) ;
524
+ prev_kind = Some ( frag. kind ) ;
497
525
acc
498
526
} )
499
527
}
@@ -565,7 +593,7 @@ impl Attributes {
565
593
/// Reads a `MetaItem` from within an attribute, looks for whether it is a
566
594
/// `#[doc(include="file")]`, and returns the filename and contents of the file as loaded from
567
595
/// its expansion.
568
- crate fn extract_include ( mi : & ast:: MetaItem ) -> Option < ( Symbol , String ) > {
596
+ crate fn extract_include ( mi : & ast:: MetaItem ) -> Option < ( Symbol , Symbol ) > {
569
597
mi. meta_item_list ( ) . and_then ( |list| {
570
598
for meta in list {
571
599
if meta. has_name ( sym:: include) {
@@ -574,7 +602,7 @@ impl Attributes {
574
602
// look for that instead
575
603
return meta. meta_item_list ( ) . and_then ( |list| {
576
604
let mut filename: Option < Symbol > = None ;
577
- let mut contents: Option < String > = None ;
605
+ let mut contents: Option < Symbol > = None ;
578
606
579
607
for it in list {
580
608
if it. has_name ( sym:: file) {
@@ -583,7 +611,7 @@ impl Attributes {
583
611
}
584
612
} else if it. has_name ( sym:: contents) {
585
613
if let Some ( docs) = it. value_str ( ) {
586
- contents = Some ( docs. to_string ( ) ) ;
614
+ contents = Some ( docs) ;
587
615
}
588
616
}
589
617
}
@@ -622,30 +650,51 @@ impl Attributes {
622
650
attrs : & [ ast:: Attribute ] ,
623
651
additional_attrs : Option < ( & [ ast:: Attribute ] , DefId ) > ,
624
652
) -> Attributes {
625
- let mut doc_strings = vec ! [ ] ;
653
+ let mut doc_strings: Vec < DocFragment > = vec ! [ ] ;
626
654
let mut sp = None ;
627
655
let mut cfg = Cfg :: True ;
628
656
let mut doc_line = 0 ;
629
657
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
+
630
673
let clean_attr = |( attr, parent_module) : ( & ast:: Attribute , _ ) | {
631
674
if let Some ( value) = attr. doc_str ( ) {
632
675
trace ! ( "got doc_str={:?}" , value) ;
633
- let value = beautify_doc_string ( value) . to_string ( ) ;
676
+ let value = beautify_doc_string ( value) ;
634
677
let kind = if attr. is_doc_comment ( ) {
635
678
DocFragmentKind :: SugaredDoc
636
679
} else {
637
680
DocFragmentKind :: RawDoc
638
681
} ;
639
682
640
683
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 {
643
686
line,
644
687
span : attr. span ,
645
688
doc : value,
646
689
kind,
647
690
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) ;
649
698
650
699
if sp. is_none ( ) {
651
700
sp = Some ( attr. span ) ;
@@ -663,14 +712,18 @@ impl Attributes {
663
712
} else if let Some ( ( filename, contents) ) = Attributes :: extract_include ( & mi)
664
713
{
665
714
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 {
668
717
line,
669
718
span : attr. span ,
670
719
doc : contents,
671
720
kind : DocFragmentKind :: Include { filename } ,
672
721
parent_module,
673
- } ) ;
722
+ need_backline : false ,
723
+ indent : 0 ,
724
+ } ;
725
+ update_need_backline ( & mut doc_strings, & frag) ;
726
+ doc_strings. push ( frag) ;
674
727
}
675
728
}
676
729
}
@@ -721,14 +774,39 @@ impl Attributes {
721
774
722
775
/// Finds the `doc` attribute as a NameValue and returns the corresponding
723
776
/// 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
726
803
}
727
804
728
805
/// Finds all `doc` attributes as NameValues and returns their corresponding values, joined
729
806
/// with newlines.
730
807
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) }
732
810
}
733
811
734
812
/// Gets links as a vector
0 commit comments