8
8
//! extern crate rustc_span;
9
9
//!
10
10
//! use rustc_span::edition::Edition;
11
- //! use rustdoc::html::markdown::{HeadingOffset, IdMap, Markdown, ErrorCodes};
11
+ //! use rustdoc::html::markdown::{HeadingOffset, IdMap, IdPrefix, Markdown, ErrorCodes};
12
12
//!
13
13
//! let s = "My *markdown* _text_";
14
14
//! let mut id_map = IdMap::new();
20
20
//! edition: Edition::Edition2015,
21
21
//! playground: &None,
22
22
//! heading_offset: HeadingOffset::H2,
23
+ //! id_prefix: IdPrefix::none(),
23
24
//! };
24
25
//! let html = md.into_string();
25
26
//! // ... something using html
@@ -40,7 +41,7 @@ use std::fmt::Write;
40
41
use std:: ops:: { ControlFlow , Range } ;
41
42
use std:: str;
42
43
43
- use crate :: clean:: RenderedLink ;
44
+ use crate :: clean:: { self , RenderedLink } ;
44
45
use crate :: doctest;
45
46
use crate :: html:: escape:: Escape ;
46
47
use crate :: html:: format:: Buffer ;
@@ -101,6 +102,7 @@ pub struct Markdown<'a> {
101
102
/// Offset at which we render headings.
102
103
/// E.g. if `heading_offset: HeadingOffset::H2`, then `# something` renders an `<h2>`.
103
104
pub heading_offset : HeadingOffset ,
105
+ pub id_prefix : IdPrefix < ' a > ,
104
106
}
105
107
/// A tuple struct like `Markdown` that renders the markdown with a table of contents.
106
108
crate struct MarkdownWithToc < ' a > (
@@ -109,6 +111,7 @@ crate struct MarkdownWithToc<'a>(
109
111
crate ErrorCodes ,
110
112
crate Edition ,
111
113
crate & ' a Option < Playground > ,
114
+ crate IdPrefix < ' a > ,
112
115
) ;
113
116
/// A tuple struct like `Markdown` that renders the markdown escaping HTML tags.
114
117
crate struct MarkdownHtml < ' a > (
@@ -117,6 +120,7 @@ crate struct MarkdownHtml<'a>(
117
120
crate ErrorCodes ,
118
121
crate Edition ,
119
122
crate & ' a Option < Playground > ,
123
+ crate IdPrefix < ' a > ,
120
124
) ;
121
125
/// A tuple struct like `Markdown` that renders only the first paragraph.
122
126
crate 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> {
508
512
type SpannedEvent < ' a > = ( Event < ' a > , Range < usize > ) ;
509
513
510
514
/// 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 > {
512
516
inner : I ,
513
517
toc : Option < & ' b mut TocBuilder > ,
514
518
buf : VecDeque < SpannedEvent < ' a > > ,
515
519
id_map : & ' ids mut IdMap ,
516
520
heading_offset : HeadingOffset ,
521
+ id_prefix : IdPrefix < ' c > ,
517
522
}
518
523
519
- impl < ' a , ' b , ' ids , I > HeadingLinks < ' a , ' b , ' ids , I > {
524
+ impl < ' a , ' b , ' ids , ' c , I > HeadingLinks < ' a , ' b , ' ids , ' c , I > {
520
525
fn new (
521
526
iter : I ,
522
527
toc : Option < & ' b mut TocBuilder > ,
523
528
ids : & ' ids mut IdMap ,
524
529
heading_offset : HeadingOffset ,
530
+ id_prefix : IdPrefix < ' c > ,
525
531
) -> 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
+ }
527
540
}
528
541
}
529
542
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 >
532
545
{
533
546
type Item = SpannedEvent < ' a > ;
534
547
@@ -551,7 +564,7 @@ impl<'a, 'b, 'ids, I: Iterator<Item = SpannedEvent<'a>>> Iterator
551
564
_ => self . buf . push_back ( event) ,
552
565
}
553
566
}
554
- let id = self . id_map . derive ( id) ;
567
+ let id = self . id_map . derive ( id, self . id_prefix ) ;
555
568
556
569
if let Some ( ref mut builder) = self . toc {
557
570
let mut html_header = String :: new ( ) ;
@@ -1044,6 +1057,7 @@ impl Markdown<'_> {
1044
1057
edition,
1045
1058
playground,
1046
1059
heading_offset,
1060
+ id_prefix,
1047
1061
} = self ;
1048
1062
1049
1063
// This is actually common enough to special-case
@@ -1062,7 +1076,7 @@ impl Markdown<'_> {
1062
1076
1063
1077
let mut s = String :: with_capacity ( md. len ( ) * 3 / 2 ) ;
1064
1078
1065
- let p = HeadingLinks :: new ( p, None , & mut ids, heading_offset) ;
1079
+ let p = HeadingLinks :: new ( p, None , & mut ids, heading_offset, id_prefix ) ;
1066
1080
let p = Footnotes :: new ( p) ;
1067
1081
let p = LinkReplacer :: new ( p. map ( |( ev, _) | ev) , links) ;
1068
1082
let p = TableWrapper :: new ( p) ;
@@ -1075,7 +1089,7 @@ impl Markdown<'_> {
1075
1089
1076
1090
impl MarkdownWithToc < ' _ > {
1077
1091
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 ;
1079
1093
1080
1094
let p = Parser :: new_ext ( md, main_body_opts ( ) ) . into_offset_iter ( ) ;
1081
1095
@@ -1084,7 +1098,7 @@ impl MarkdownWithToc<'_> {
1084
1098
let mut toc = TocBuilder :: new ( ) ;
1085
1099
1086
1100
{
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 ) ;
1088
1102
let p = Footnotes :: new ( p) ;
1089
1103
let p = TableWrapper :: new ( p. map ( |( ev, _) | ev) ) ;
1090
1104
let p = CodeBlocks :: new ( p, codes, edition, playground) ;
@@ -1097,7 +1111,7 @@ impl MarkdownWithToc<'_> {
1097
1111
1098
1112
impl MarkdownHtml < ' _ > {
1099
1113
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 ;
1101
1115
1102
1116
// This is actually common enough to special-case
1103
1117
if md. is_empty ( ) {
@@ -1113,7 +1127,7 @@ impl MarkdownHtml<'_> {
1113
1127
1114
1128
let mut s = String :: with_capacity ( md. len ( ) * 3 / 2 ) ;
1115
1129
1116
- let p = HeadingLinks :: new ( p, None , & mut ids, HeadingOffset :: H1 ) ;
1130
+ let p = HeadingLinks :: new ( p, None , & mut ids, HeadingOffset :: H1 , id_prefix ) ;
1117
1131
let p = Footnotes :: new ( p) ;
1118
1132
let p = TableWrapper :: new ( p. map ( |( ev, _) | ev) ) ;
1119
1133
let p = CodeBlocks :: new ( p, codes, edition, playground) ;
@@ -1325,7 +1339,8 @@ crate fn markdown_links(md: &str) -> Vec<MarkdownLink> {
1325
1339
// There's no need to thread an IdMap through to here because
1326
1340
// the IDs generated aren't going to be emitted anywhere.
1327
1341
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 ( ) ) ) ;
1329
1344
1330
1345
for ev in iter {
1331
1346
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
1430
1445
code_blocks
1431
1446
}
1432
1447
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
+
1433
1481
#[ derive( Clone , Default , Debug ) ]
1434
1482
pub struct IdMap {
1435
1483
map : FxHashMap < String , usize > ,
1436
1484
}
1437
1485
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
-
1485
1486
impl IdMap {
1486
1487
pub fn new ( ) -> Self {
1487
- IdMap { map : init_id_map ( ) }
1488
+ IdMap { map : FxHashMap :: default ( ) }
1488
1489
}
1489
1490
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,
1493
1510
Some ( a) => {
1494
- let id = format ! ( "{}-{}" , candidate. as_ref ( ) , * a) ;
1511
+ let id = format ! ( "{}-{}" , candidate, * a) ;
1495
1512
* a += 1 ;
1496
1513
id
1497
1514
}
0 commit comments