88//! extern crate rustc_span;
99//!
1010//! use rustc_span::edition::Edition;
11- //! use rustdoc::html::markdown::{HeadingOffset, IdMap, Markdown, ErrorCodes};
11+ //! use rustdoc::html::markdown::{HeadingOffset, IdMap, IdPrefix, Markdown, ErrorCodes};
1212//!
1313//! let s = "My *markdown* _text_";
1414//! let mut id_map = IdMap::new();
2020//! edition: Edition::Edition2015,
2121//! playground: &None,
2222//! heading_offset: HeadingOffset::H2,
23+ //! id_prefix: IdPrefix::none(),
2324//! };
2425//! let html = md.into_string();
2526//! // ... something using html
@@ -40,7 +41,7 @@ use std::fmt::Write;
4041use std:: ops:: { ControlFlow , Range } ;
4142use std:: str;
4243
43- use crate :: clean:: RenderedLink ;
44+ use crate :: clean:: { self , RenderedLink } ;
4445use crate :: doctest;
4546use crate :: html:: escape:: Escape ;
4647use crate :: html:: format:: Buffer ;
@@ -101,6 +102,7 @@ pub struct Markdown<'a> {
101102 /// Offset at which we render headings.
102103 /// E.g. if `heading_offset: HeadingOffset::H2`, then `# something` renders an `<h2>`.
103104 pub heading_offset : HeadingOffset ,
105+ pub id_prefix : IdPrefix < ' a > ,
104106}
105107/// A tuple struct like `Markdown` that renders the markdown with a table of contents.
106108crate struct MarkdownWithToc < ' a > (
@@ -109,6 +111,7 @@ crate struct MarkdownWithToc<'a>(
109111 crate ErrorCodes ,
110112 crate Edition ,
111113 crate & ' a Option < Playground > ,
114+ crate IdPrefix < ' a > ,
112115) ;
113116/// A tuple struct like `Markdown` that renders the markdown escaping HTML tags.
114117crate struct MarkdownHtml < ' a > (
@@ -117,6 +120,7 @@ crate struct MarkdownHtml<'a>(
117120 crate ErrorCodes ,
118121 crate Edition ,
119122 crate & ' a Option < Playground > ,
123+ crate IdPrefix < ' a > ,
120124) ;
121125/// A tuple struct like `Markdown` that renders only the first paragraph.
122126crate struct MarkdownSummaryLine < ' a > ( pub & ' a str , pub & ' a [ RenderedLink ] ) ;
@@ -508,27 +512,36 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for TableWrapper<'a, I> {
508512type SpannedEvent < ' a > = ( Event < ' a > , Range < usize > ) ;
509513
510514/// Make headings links with anchor IDs and build up TOC.
511- struct HeadingLinks < ' a , ' b , ' ids , I > {
515+ struct HeadingLinks < ' a , ' b , ' ids , ' c , I > {
512516 inner : I ,
513517 toc : Option < & ' b mut TocBuilder > ,
514518 buf : VecDeque < SpannedEvent < ' a > > ,
515519 id_map : & ' ids mut IdMap ,
516520 heading_offset : HeadingOffset ,
521+ id_prefix : IdPrefix < ' c > ,
517522}
518523
519- impl < ' a , ' b , ' ids , I > HeadingLinks < ' a , ' b , ' ids , I > {
524+ impl < ' a , ' b , ' ids , ' c , I > HeadingLinks < ' a , ' b , ' ids , ' c , I > {
520525 fn new (
521526 iter : I ,
522527 toc : Option < & ' b mut TocBuilder > ,
523528 ids : & ' ids mut IdMap ,
524529 heading_offset : HeadingOffset ,
530+ id_prefix : IdPrefix < ' c > ,
525531 ) -> Self {
526- HeadingLinks { inner : iter, toc, buf : VecDeque :: new ( ) , id_map : ids, heading_offset }
532+ HeadingLinks {
533+ inner : iter,
534+ toc,
535+ buf : VecDeque :: new ( ) ,
536+ id_map : ids,
537+ heading_offset,
538+ id_prefix,
539+ }
527540 }
528541}
529542
530- impl < ' a , ' b , ' ids , I : Iterator < Item = SpannedEvent < ' a > > > Iterator
531- for HeadingLinks < ' a , ' b , ' ids , I >
543+ impl < ' a , ' b , ' ids , ' c , I : Iterator < Item = SpannedEvent < ' a > > > Iterator
544+ for HeadingLinks < ' a , ' b , ' ids , ' c , I >
532545{
533546 type Item = SpannedEvent < ' a > ;
534547
@@ -551,7 +564,7 @@ impl<'a, 'b, 'ids, I: Iterator<Item = SpannedEvent<'a>>> Iterator
551564 _ => self . buf . push_back ( event) ,
552565 }
553566 }
554- let id = self . id_map . derive ( id) ;
567+ let id = self . id_map . derive ( id, self . id_prefix ) ;
555568
556569 if let Some ( ref mut builder) = self . toc {
557570 let mut html_header = String :: new ( ) ;
@@ -1044,6 +1057,7 @@ impl Markdown<'_> {
10441057 edition,
10451058 playground,
10461059 heading_offset,
1060+ id_prefix,
10471061 } = self ;
10481062
10491063 // This is actually common enough to special-case
@@ -1062,7 +1076,7 @@ impl Markdown<'_> {
10621076
10631077 let mut s = String :: with_capacity ( md. len ( ) * 3 / 2 ) ;
10641078
1065- let p = HeadingLinks :: new ( p, None , & mut ids, heading_offset) ;
1079+ let p = HeadingLinks :: new ( p, None , & mut ids, heading_offset, id_prefix ) ;
10661080 let p = Footnotes :: new ( p) ;
10671081 let p = LinkReplacer :: new ( p. map ( |( ev, _) | ev) , links) ;
10681082 let p = TableWrapper :: new ( p) ;
@@ -1075,7 +1089,7 @@ impl Markdown<'_> {
10751089
10761090impl MarkdownWithToc < ' _ > {
10771091 crate fn into_string ( self ) -> String {
1078- let MarkdownWithToc ( md, mut ids, codes, edition, playground) = self ;
1092+ let MarkdownWithToc ( md, mut ids, codes, edition, playground, id_prefix ) = self ;
10791093
10801094 let p = Parser :: new_ext ( md, main_body_opts ( ) ) . into_offset_iter ( ) ;
10811095
@@ -1084,7 +1098,7 @@ impl MarkdownWithToc<'_> {
10841098 let mut toc = TocBuilder :: new ( ) ;
10851099
10861100 {
1087- let p = HeadingLinks :: new ( p, Some ( & mut toc) , & mut ids, HeadingOffset :: H1 ) ;
1101+ let p = HeadingLinks :: new ( p, Some ( & mut toc) , & mut ids, HeadingOffset :: H1 , id_prefix ) ;
10881102 let p = Footnotes :: new ( p) ;
10891103 let p = TableWrapper :: new ( p. map ( |( ev, _) | ev) ) ;
10901104 let p = CodeBlocks :: new ( p, codes, edition, playground) ;
@@ -1097,7 +1111,7 @@ impl MarkdownWithToc<'_> {
10971111
10981112impl MarkdownHtml < ' _ > {
10991113 crate fn into_string ( self ) -> String {
1100- let MarkdownHtml ( md, mut ids, codes, edition, playground) = self ;
1114+ let MarkdownHtml ( md, mut ids, codes, edition, playground, id_prefix ) = self ;
11011115
11021116 // This is actually common enough to special-case
11031117 if md. is_empty ( ) {
@@ -1113,7 +1127,7 @@ impl MarkdownHtml<'_> {
11131127
11141128 let mut s = String :: with_capacity ( md. len ( ) * 3 / 2 ) ;
11151129
1116- let p = HeadingLinks :: new ( p, None , & mut ids, HeadingOffset :: H1 ) ;
1130+ let p = HeadingLinks :: new ( p, None , & mut ids, HeadingOffset :: H1 , id_prefix ) ;
11171131 let p = Footnotes :: new ( p) ;
11181132 let p = TableWrapper :: new ( p. map ( |( ev, _) | ev) ) ;
11191133 let p = CodeBlocks :: new ( p, codes, edition, playground) ;
@@ -1325,7 +1339,8 @@ crate fn markdown_links(md: &str) -> Vec<MarkdownLink> {
13251339 // There's no need to thread an IdMap through to here because
13261340 // the IDs generated aren't going to be emitted anywhere.
13271341 let mut ids = IdMap :: new ( ) ;
1328- let iter = Footnotes :: new ( HeadingLinks :: new ( p, None , & mut ids, HeadingOffset :: H1 ) ) ;
1342+ let iter =
1343+ Footnotes :: new ( HeadingLinks :: new ( p, None , & mut ids, HeadingOffset :: H1 , IdPrefix :: none ( ) ) ) ;
13291344
13301345 for ev in iter {
13311346 if let Event :: Start ( Tag :: Link ( kind, dest, _) ) = ev. 0 {
@@ -1430,68 +1445,70 @@ crate fn rust_code_blocks(md: &str, extra_info: &ExtraInfo<'_>) -> Vec<RustCodeB
14301445 code_blocks
14311446}
14321447
1448+ /// This wrapper exists only to allow the error_index_generator tool to use it.
1449+ #[ derive( Debug , Clone , Copy ) ]
1450+ pub struct IdPrefix < ' a > ( IdPrefixInner < ' a > ) ;
1451+
1452+ impl < ' a > IdPrefix < ' a > {
1453+ crate fn without_parent ( ) -> Self {
1454+ Self ( IdPrefixInner :: WithoutParent )
1455+ }
1456+
1457+ pub fn none ( ) -> Self {
1458+ Self ( IdPrefixInner :: None )
1459+ }
1460+
1461+ crate fn some ( item : & ' a clean:: Item ) -> Self {
1462+ Self ( IdPrefixInner :: Some ( item) )
1463+ }
1464+ }
1465+
1466+ /// Depending if we're generating a link for a markdown file or on a Rust type, we don't want the
1467+ /// same behaviour:
1468+ ///
1469+ /// In Rust, to reduce the number of "conflict" between HTML IDs, we add a prefix to them based on
1470+ /// their parent. However, it is useless if rustdoc is used to render a markdown file.
1471+ #[ derive( Debug , Clone , Copy ) ]
1472+ crate enum IdPrefixInner < ' a > {
1473+ /// Contains the "parent" of the ID. It's used to generate the prefix.
1474+ Some ( & ' a clean:: Item ) ,
1475+ /// This is used in case markdown is inserted using `--markdown-before-content` or using
1476+ /// `--markdown-after-content`.
1477+ WithoutParent ,
1478+ None ,
1479+ }
1480+
14331481#[ derive( Clone , Default , Debug ) ]
14341482pub struct IdMap {
14351483 map : FxHashMap < String , usize > ,
14361484}
14371485
1438- fn init_id_map ( ) -> FxHashMap < String , usize > {
1439- let mut map = FxHashMap :: default ( ) ;
1440- // This is the list of IDs used in Javascript.
1441- map. insert ( "help" . to_owned ( ) , 1 ) ;
1442- // This is the list of IDs used in HTML generated in Rust (including the ones
1443- // used in tera template files).
1444- map. insert ( "mainThemeStyle" . to_owned ( ) , 1 ) ;
1445- map. insert ( "themeStyle" . to_owned ( ) , 1 ) ;
1446- map. insert ( "theme-picker" . to_owned ( ) , 1 ) ;
1447- map. insert ( "theme-choices" . to_owned ( ) , 1 ) ;
1448- map. insert ( "settings-menu" . to_owned ( ) , 1 ) ;
1449- map. insert ( "help-button" . to_owned ( ) , 1 ) ;
1450- map. insert ( "main-content" . to_owned ( ) , 1 ) ;
1451- map. insert ( "search" . to_owned ( ) , 1 ) ;
1452- map. insert ( "crate-search" . to_owned ( ) , 1 ) ;
1453- map. insert ( "render-detail" . to_owned ( ) , 1 ) ;
1454- map. insert ( "toggle-all-docs" . to_owned ( ) , 1 ) ;
1455- map. insert ( "all-types" . to_owned ( ) , 1 ) ;
1456- map. insert ( "default-settings" . to_owned ( ) , 1 ) ;
1457- map. insert ( "rustdoc-vars" . to_owned ( ) , 1 ) ;
1458- map. insert ( "sidebar-vars" . to_owned ( ) , 1 ) ;
1459- map. insert ( "copy-path" . to_owned ( ) , 1 ) ;
1460- map. insert ( "TOC" . to_owned ( ) , 1 ) ;
1461- // This is the list of IDs used by rustdoc sections (but still generated by
1462- // rustdoc).
1463- map. insert ( "fields" . to_owned ( ) , 1 ) ;
1464- map. insert ( "variants" . to_owned ( ) , 1 ) ;
1465- map. insert ( "implementors-list" . to_owned ( ) , 1 ) ;
1466- map. insert ( "synthetic-implementors-list" . to_owned ( ) , 1 ) ;
1467- map. insert ( "foreign-impls" . to_owned ( ) , 1 ) ;
1468- map. insert ( "implementations" . to_owned ( ) , 1 ) ;
1469- map. insert ( "trait-implementations" . to_owned ( ) , 1 ) ;
1470- map. insert ( "synthetic-implementations" . to_owned ( ) , 1 ) ;
1471- map. insert ( "blanket-implementations" . to_owned ( ) , 1 ) ;
1472- map. insert ( "associated-types" . to_owned ( ) , 1 ) ;
1473- map. insert ( "associated-const" . to_owned ( ) , 1 ) ;
1474- map. insert ( "required-methods" . to_owned ( ) , 1 ) ;
1475- map. insert ( "provided-methods" . to_owned ( ) , 1 ) ;
1476- map. insert ( "implementors" . to_owned ( ) , 1 ) ;
1477- map. insert ( "synthetic-implementors" . to_owned ( ) , 1 ) ;
1478- map. insert ( "trait-implementations-list" . to_owned ( ) , 1 ) ;
1479- map. insert ( "synthetic-implementations-list" . to_owned ( ) , 1 ) ;
1480- map. insert ( "blanket-implementations-list" . to_owned ( ) , 1 ) ;
1481- map. insert ( "deref-methods" . to_owned ( ) , 1 ) ;
1482- map
1483- }
1484-
14851486impl IdMap {
14861487 pub fn new ( ) -> Self {
1487- IdMap { map : init_id_map ( ) }
1488+ IdMap { map : FxHashMap :: default ( ) }
14881489 }
14891490
1490- crate fn derive < S : AsRef < str > + ToString > ( & mut self , candidate : S ) -> String {
1491- let id = match self . map . get_mut ( candidate. as_ref ( ) ) {
1492- None => candidate. to_string ( ) ,
1491+ crate fn derive < S : AsRef < str > + ToString > (
1492+ & mut self ,
1493+ candidate : S ,
1494+ id_prefix : IdPrefix < ' _ > ,
1495+ ) -> String {
1496+ let candidate = match id_prefix. 0 {
1497+ IdPrefixInner :: None => candidate. to_string ( ) ,
1498+ IdPrefixInner :: WithoutParent => format ! ( "top.{}" , candidate. as_ref( ) ) ,
1499+ IdPrefixInner :: Some ( item) => {
1500+ let prefix = if let Some ( name) = item. name {
1501+ format ! ( "{}.{}" , item. type_( ) , name)
1502+ } else {
1503+ format ! ( "{}.unknown" , item. type_( ) )
1504+ } ;
1505+ format ! ( "{}.{}" , prefix, candidate. as_ref( ) )
1506+ }
1507+ } ;
1508+ let id = match self . map . get_mut ( & candidate) {
1509+ None => candidate,
14931510 Some ( a) => {
1494- let id = format ! ( "{}-{}" , candidate. as_ref ( ) , * a) ;
1511+ let id = format ! ( "{}-{}" , candidate, * a) ;
14951512 * a += 1 ;
14961513 id
14971514 }
0 commit comments