@@ -51,12 +51,12 @@ use tracing::{debug, trace};
5151use crate :: clean:: RenderedLink ;
5252use crate :: doctest;
5353use crate :: doctest:: GlobalTestOptions ;
54- use crate :: html:: escape:: Escape ;
54+ use crate :: html:: escape:: { Escape , EscapeBodyText } ;
5555use crate :: html:: format:: Buffer ;
5656use crate :: html:: highlight;
5757use crate :: html:: length_limit:: HtmlWithLimit ;
5858use crate :: html:: render:: small_url_encode;
59- use crate :: html:: toc:: TocBuilder ;
59+ use crate :: html:: toc:: { Toc , TocBuilder } ;
6060
6161#[ cfg( test) ]
6262mod tests;
@@ -102,6 +102,7 @@ pub struct Markdown<'a> {
102102/// A struct like `Markdown` that renders the markdown with a table of contents.
103103pub ( crate ) struct MarkdownWithToc < ' a > {
104104 pub ( crate ) content : & ' a str ,
105+ pub ( crate ) links : & ' a [ RenderedLink ] ,
105106 pub ( crate ) ids : & ' a mut IdMap ,
106107 pub ( crate ) error_codes : ErrorCodes ,
107108 pub ( crate ) edition : Edition ,
@@ -533,9 +534,11 @@ impl<'a, 'b, 'ids, I: Iterator<Item = SpannedEvent<'a>>> Iterator
533534 let id = self . id_map . derive ( id) ;
534535
535536 if let Some ( ref mut builder) = self . toc {
537+ let mut text_header = String :: new ( ) ;
538+ plain_text_from_events ( self . buf . iter ( ) . map ( |( ev, _) | ev. clone ( ) ) , & mut text_header) ;
536539 let mut html_header = String :: new ( ) ;
537- html :: push_html ( & mut html_header , self . buf . iter ( ) . map ( |( ev, _) | ev. clone ( ) ) ) ;
538- let sec = builder. push ( level as u32 , html_header, id. clone ( ) ) ;
540+ html_text_from_events ( self . buf . iter ( ) . map ( |( ev, _) | ev. clone ( ) ) , & mut html_header ) ;
541+ let sec = builder. push ( level as u32 , text_header , html_header, id. clone ( ) ) ;
539542 self . buf . push_front ( ( Event :: Html ( format ! ( "{sec} " ) . into ( ) ) , 0 ..0 ) ) ;
540543 }
541544
@@ -1412,10 +1415,23 @@ impl Markdown<'_> {
14121415}
14131416
14141417impl MarkdownWithToc < ' _ > {
1415- pub ( crate ) fn into_string ( self ) -> String {
1416- let MarkdownWithToc { content : md, ids, error_codes : codes, edition, playground } = self ;
1418+ pub ( crate ) fn into_parts ( self ) -> ( Toc , String ) {
1419+ let MarkdownWithToc { content : md, links, ids, error_codes : codes, edition, playground } =
1420+ self ;
14171421
1418- let p = Parser :: new_ext ( md, main_body_opts ( ) ) . into_offset_iter ( ) ;
1422+ // This is actually common enough to special-case
1423+ if md. is_empty ( ) {
1424+ return ( Toc { entries : Vec :: new ( ) } , String :: new ( ) ) ;
1425+ }
1426+ let mut replacer = |broken_link : BrokenLink < ' _ > | {
1427+ links
1428+ . iter ( )
1429+ . find ( |link| & * link. original_text == & * broken_link. reference )
1430+ . map ( |link| ( link. href . as_str ( ) . into ( ) , link. tooltip . as_str ( ) . into ( ) ) )
1431+ } ;
1432+
1433+ let p = Parser :: new_with_broken_link_callback ( md, main_body_opts ( ) , Some ( & mut replacer) ) ;
1434+ let p = p. into_offset_iter ( ) ;
14191435
14201436 let mut s = String :: with_capacity ( md. len ( ) * 3 / 2 ) ;
14211437
@@ -1429,7 +1445,11 @@ impl MarkdownWithToc<'_> {
14291445 html:: push_html ( & mut s, p) ;
14301446 }
14311447
1432- format ! ( "<nav id=\" TOC\" >{toc}</nav>{s}" , toc = toc. into_toc( ) . print( ) )
1448+ ( toc. into_toc ( ) , s)
1449+ }
1450+ pub ( crate ) fn into_string ( self ) -> String {
1451+ let ( toc, s) = self . into_parts ( ) ;
1452+ format ! ( "<nav id=\" rustdoc\" >{toc}</nav>{s}" , toc = toc. print( ) )
14331453 }
14341454}
14351455
@@ -1608,7 +1628,16 @@ pub(crate) fn plain_text_summary(md: &str, link_names: &[RenderedLink]) -> Strin
16081628
16091629 let p = Parser :: new_with_broken_link_callback ( md, summary_opts ( ) , Some ( & mut replacer) ) ;
16101630
1611- for event in p {
1631+ plain_text_from_events ( p, & mut s) ;
1632+
1633+ s
1634+ }
1635+
1636+ pub ( crate ) fn plain_text_from_events < ' a > (
1637+ events : impl Iterator < Item = pulldown_cmark:: Event < ' a > > ,
1638+ s : & mut String ,
1639+ ) {
1640+ for event in events {
16121641 match & event {
16131642 Event :: Text ( text) => s. push_str ( text) ,
16141643 Event :: Code ( code) => {
@@ -1623,8 +1652,29 @@ pub(crate) fn plain_text_summary(md: &str, link_names: &[RenderedLink]) -> Strin
16231652 _ => ( ) ,
16241653 }
16251654 }
1655+ }
16261656
1627- s
1657+ pub ( crate ) fn html_text_from_events < ' a > (
1658+ events : impl Iterator < Item = pulldown_cmark:: Event < ' a > > ,
1659+ s : & mut String ,
1660+ ) {
1661+ for event in events {
1662+ match & event {
1663+ Event :: Text ( text) => {
1664+ write ! ( s, "{}" , EscapeBodyText ( text) ) . expect ( "string alloc infallible" )
1665+ }
1666+ Event :: Code ( code) => {
1667+ s. push_str ( "<code>" ) ;
1668+ write ! ( s, "{}" , EscapeBodyText ( code) ) . expect ( "string alloc infallible" ) ;
1669+ s. push_str ( "</code>" ) ;
1670+ }
1671+ Event :: HardBreak | Event :: SoftBreak => s. push ( ' ' ) ,
1672+ Event :: Start ( Tag :: CodeBlock ( ..) ) => break ,
1673+ Event :: End ( TagEnd :: Paragraph ) => break ,
1674+ Event :: End ( TagEnd :: Heading ( ..) ) => break ,
1675+ _ => ( ) ,
1676+ }
1677+ }
16281678}
16291679
16301680#[ derive( Debug ) ]
@@ -1975,7 +2025,8 @@ fn init_id_map() -> FxHashMap<Cow<'static, str>, usize> {
19752025 map. insert ( "default-settings" . into ( ) , 1 ) ;
19762026 map. insert ( "sidebar-vars" . into ( ) , 1 ) ;
19772027 map. insert ( "copy-path" . into ( ) , 1 ) ;
1978- map. insert ( "TOC" . into ( ) , 1 ) ;
2028+ map. insert ( "rustdoc-toc" . into ( ) , 1 ) ;
2029+ map. insert ( "rustdoc-modnav" . into ( ) , 1 ) ;
19792030 // This is the list of IDs used by rustdoc sections (but still generated by
19802031 // rustdoc).
19812032 map. insert ( "fields" . into ( ) , 1 ) ;
0 commit comments