88//! extern crate rustc_span;
99//!
1010//! use rustc_span::edition::Edition;
11- //! use rustdoc::html::markdown::{IdMap, Markdown, ErrorCodes};
11+ //! use rustdoc::html::markdown::{HeadingOffset, IdMap, Markdown, ErrorCodes};
1212//!
1313//! let s = "My *markdown* _text_";
1414//! let mut id_map = IdMap::new();
15- //! let md = Markdown(s, &[], &mut id_map, ErrorCodes::Yes, Edition::Edition2015, &None);
15+ //! let md = Markdown {
16+ //! content: s,
17+ //! links: &[],
18+ //! ids: &mut id_map,
19+ //! error_codes: ErrorCodes::Yes,
20+ //! edition: Edition::Edition2015,
21+ //! playground: &None,
22+ //! heading_offset: HeadingOffset::H2,
23+ //! };
1624//! let html = md.into_string();
1725//! // ... something using html
1826//! ```
@@ -47,6 +55,8 @@ use pulldown_cmark::{
4755#[ cfg( test) ]
4856mod tests;
4957
58+ const MAX_HEADER_LEVEL : u32 = 6 ;
59+
5060/// Options for rendering Markdown in the main body of documentation.
5161pub ( crate ) fn main_body_opts ( ) -> Options {
5262 Options :: ENABLE_TABLES
@@ -65,20 +75,33 @@ pub(crate) fn summary_opts() -> Options {
6575 | Options :: ENABLE_SMART_PUNCTUATION
6676}
6777
78+ #[ derive( Debug , Clone , Copy ) ]
79+ pub enum HeadingOffset {
80+ H1 = 0 ,
81+ H2 ,
82+ H3 ,
83+ H4 ,
84+ H5 ,
85+ H6 ,
86+ }
87+
6888/// When `to_string` is called, this struct will emit the HTML corresponding to
6989/// the rendered version of the contained markdown string.
70- pub struct Markdown < ' a > (
71- pub & ' a str ,
90+ pub struct Markdown < ' a > {
91+ pub content : & ' a str ,
7292 /// A list of link replacements.
73- pub & ' a [ RenderedLink ] ,
93+ pub links : & ' a [ RenderedLink ] ,
7494 /// The current list of used header IDs.
75- pub & ' a mut IdMap ,
95+ pub ids : & ' a mut IdMap ,
7696 /// Whether to allow the use of explicit error codes in doctest lang strings.
77- pub ErrorCodes ,
97+ pub error_codes : ErrorCodes ,
7898 /// Default edition to use when parsing doctests (to add a `fn main`).
79- pub Edition ,
80- pub & ' a Option < Playground > ,
81- ) ;
99+ pub edition : Edition ,
100+ pub playground : & ' a Option < Playground > ,
101+ /// Offset at which we render headings.
102+ /// E.g. if `heading_offset: HeadingOffset::H2`, then `# something` renders an `<h2>`.
103+ pub heading_offset : HeadingOffset ,
104+ }
82105/// A tuple struct like `Markdown` that renders the markdown with a table of contents.
83106crate struct MarkdownWithToc < ' a > (
84107 crate & ' a str ,
@@ -489,11 +512,17 @@ struct HeadingLinks<'a, 'b, 'ids, I> {
489512 toc : Option < & ' b mut TocBuilder > ,
490513 buf : VecDeque < SpannedEvent < ' a > > ,
491514 id_map : & ' ids mut IdMap ,
515+ heading_offset : HeadingOffset ,
492516}
493517
494518impl < ' a , ' b , ' ids , I > HeadingLinks < ' a , ' b , ' ids , I > {
495- fn new ( iter : I , toc : Option < & ' b mut TocBuilder > , ids : & ' ids mut IdMap ) -> Self {
496- HeadingLinks { inner : iter, toc, buf : VecDeque :: new ( ) , id_map : ids }
519+ fn new (
520+ iter : I ,
521+ toc : Option < & ' b mut TocBuilder > ,
522+ ids : & ' ids mut IdMap ,
523+ heading_offset : HeadingOffset ,
524+ ) -> Self {
525+ HeadingLinks { inner : iter, toc, buf : VecDeque :: new ( ) , id_map : ids, heading_offset }
497526 }
498527}
499528
@@ -530,6 +559,7 @@ impl<'a, 'b, 'ids, I: Iterator<Item = SpannedEvent<'a>>> Iterator
530559 self . buf . push_front ( ( Event :: Html ( format ! ( "{} " , sec) . into ( ) ) , 0 ..0 ) ) ;
531560 }
532561
562+ let level = std:: cmp:: min ( level + ( self . heading_offset as u32 ) , MAX_HEADER_LEVEL ) ;
533563 self . buf . push_back ( ( Event :: Html ( format ! ( "</a></h{}>" , level) . into ( ) ) , 0 ..0 ) ) ;
534564
535565 let start_tags = format ! (
@@ -1005,7 +1035,15 @@ impl LangString {
10051035
10061036impl Markdown < ' _ > {
10071037 pub fn into_string ( self ) -> String {
1008- let Markdown ( md, links, mut ids, codes, edition, playground) = self ;
1038+ let Markdown {
1039+ content : md,
1040+ links,
1041+ mut ids,
1042+ error_codes : codes,
1043+ edition,
1044+ playground,
1045+ heading_offset,
1046+ } = self ;
10091047
10101048 // This is actually common enough to special-case
10111049 if md. is_empty ( ) {
@@ -1026,7 +1064,7 @@ impl Markdown<'_> {
10261064
10271065 let mut s = String :: with_capacity ( md. len ( ) * 3 / 2 ) ;
10281066
1029- let p = HeadingLinks :: new ( p, None , & mut ids) ;
1067+ let p = HeadingLinks :: new ( p, None , & mut ids, heading_offset ) ;
10301068 let p = Footnotes :: new ( p) ;
10311069 let p = LinkReplacer :: new ( p. map ( |( ev, _) | ev) , links) ;
10321070 let p = TableWrapper :: new ( p) ;
@@ -1048,7 +1086,7 @@ impl MarkdownWithToc<'_> {
10481086 let mut toc = TocBuilder :: new ( ) ;
10491087
10501088 {
1051- let p = HeadingLinks :: new ( p, Some ( & mut toc) , & mut ids) ;
1089+ let p = HeadingLinks :: new ( p, Some ( & mut toc) , & mut ids, HeadingOffset :: H1 ) ;
10521090 let p = Footnotes :: new ( p) ;
10531091 let p = TableWrapper :: new ( p. map ( |( ev, _) | ev) ) ;
10541092 let p = CodeBlocks :: new ( p, codes, edition, playground) ;
@@ -1077,7 +1115,7 @@ impl MarkdownHtml<'_> {
10771115
10781116 let mut s = String :: with_capacity ( md. len ( ) * 3 / 2 ) ;
10791117
1080- let p = HeadingLinks :: new ( p, None , & mut ids) ;
1118+ let p = HeadingLinks :: new ( p, None , & mut ids, HeadingOffset :: H1 ) ;
10811119 let p = Footnotes :: new ( p) ;
10821120 let p = TableWrapper :: new ( p. map ( |( ev, _) | ev) ) ;
10831121 let p = CodeBlocks :: new ( p, codes, edition, playground) ;
@@ -1295,7 +1333,7 @@ crate fn markdown_links(md: &str) -> Vec<MarkdownLink> {
12951333 // There's no need to thread an IdMap through to here because
12961334 // the IDs generated aren't going to be emitted anywhere.
12971335 let mut ids = IdMap :: new ( ) ;
1298- let iter = Footnotes :: new ( HeadingLinks :: new ( p, None , & mut ids) ) ;
1336+ let iter = Footnotes :: new ( HeadingLinks :: new ( p, None , & mut ids, HeadingOffset :: H1 ) ) ;
12991337
13001338 for ev in iter {
13011339 if let Event :: Start ( Tag :: Link ( kind, dest, _) ) = ev. 0 {
0 commit comments