18
18
//! ```
19
19
//! #![feature(rustc_private)]
20
20
//!
21
- //! use rustdoc::html::markdown::{Markdown, ErrorCodes};
21
+ //! use rustdoc::html::markdown::{IdMap, Markdown, ErrorCodes};
22
+ //! use std::cell::RefCell;
22
23
//!
23
24
//! let s = "My *markdown* _text_";
24
- //! let html = format!("{}", Markdown(s, &[], ErrorCodes::Yes));
25
+ //! let mut id_map = IdMap::new();
26
+ //! let html = format!("{}", Markdown(s, &[], RefCell::new(&mut id_map), ErrorCodes::Yes));
25
27
//! // ... something using html
26
28
//! ```
27
29
@@ -35,7 +37,6 @@ use std::borrow::Cow;
35
37
use std:: ops:: Range ;
36
38
use std:: str;
37
39
38
- use html:: render:: derive_id;
39
40
use html:: toc:: TocBuilder ;
40
41
use html:: highlight;
41
42
use test;
@@ -47,12 +48,13 @@ use pulldown_cmark::{Options, OPTION_ENABLE_FOOTNOTES, OPTION_ENABLE_TABLES};
47
48
/// formatted, this struct will emit the HTML corresponding to the rendered
48
49
/// version of the contained markdown string.
49
50
/// The second parameter is a list of link replacements
50
- pub struct Markdown < ' a > ( pub & ' a str , pub & ' a [ ( String , String ) ] , pub ErrorCodes ) ;
51
+ pub struct Markdown < ' a > (
52
+ pub & ' a str , pub & ' a [ ( String , String ) ] , pub RefCell < & ' a mut IdMap > , pub ErrorCodes ) ;
51
53
/// A unit struct like `Markdown`, that renders the markdown with a
52
54
/// table of contents.
53
- pub struct MarkdownWithToc < ' a > ( pub & ' a str , pub ErrorCodes ) ;
55
+ pub struct MarkdownWithToc < ' a > ( pub & ' a str , pub RefCell < & ' a mut IdMap > , pub ErrorCodes ) ;
54
56
/// A unit struct like `Markdown`, that renders the markdown escaping HTML tags.
55
- pub struct MarkdownHtml < ' a > ( pub & ' a str , pub ErrorCodes ) ;
57
+ pub struct MarkdownHtml < ' a > ( pub & ' a str , pub RefCell < & ' a mut IdMap > , pub ErrorCodes ) ;
56
58
/// A unit struct like `Markdown`, that renders only the first paragraph.
57
59
pub struct MarkdownSummaryLine < ' a > ( pub & ' a str , pub & ' a [ ( String , String ) ] ) ;
58
60
@@ -287,23 +289,25 @@ impl<'a, 'b, I: Iterator<Item = Event<'a>>> Iterator for LinkReplacer<'a, 'b, I>
287
289
}
288
290
289
291
/// Make headings links with anchor ids and build up TOC.
290
- struct HeadingLinks < ' a , ' b , I : Iterator < Item = Event < ' a > > > {
292
+ struct HeadingLinks < ' a , ' b , ' ids , I : Iterator < Item = Event < ' a > > > {
291
293
inner : I ,
292
294
toc : Option < & ' b mut TocBuilder > ,
293
295
buf : VecDeque < Event < ' a > > ,
296
+ id_map : & ' ids mut IdMap ,
294
297
}
295
298
296
- impl < ' a , ' b , I : Iterator < Item = Event < ' a > > > HeadingLinks < ' a , ' b , I > {
297
- fn new ( iter : I , toc : Option < & ' b mut TocBuilder > ) -> Self {
299
+ impl < ' a , ' b , ' ids , I : Iterator < Item = Event < ' a > > > HeadingLinks < ' a , ' b , ' ids , I > {
300
+ fn new ( iter : I , toc : Option < & ' b mut TocBuilder > , ids : & ' ids mut IdMap ) -> Self {
298
301
HeadingLinks {
299
302
inner : iter,
300
303
toc,
301
304
buf : VecDeque :: new ( ) ,
305
+ id_map : ids,
302
306
}
303
307
}
304
308
}
305
309
306
- impl < ' a , ' b , I : Iterator < Item = Event < ' a > > > Iterator for HeadingLinks < ' a , ' b , I > {
310
+ impl < ' a , ' b , ' ids , I : Iterator < Item = Event < ' a > > > Iterator for HeadingLinks < ' a , ' b , ' ids , I > {
307
311
type Item = Event < ' a > ;
308
312
309
313
fn next ( & mut self ) -> Option < Self :: Item > {
@@ -322,7 +326,7 @@ impl<'a, 'b, I: Iterator<Item = Event<'a>>> Iterator for HeadingLinks<'a, 'b, I>
322
326
}
323
327
self . buf . push_back ( event) ;
324
328
}
325
- let id = derive_id ( id) ;
329
+ let id = self . id_map . derive ( id) ;
326
330
327
331
if let Some ( ref mut builder) = self . toc {
328
332
let mut html_header = String :: new ( ) ;
@@ -641,7 +645,8 @@ impl LangString {
641
645
642
646
impl < ' a > fmt:: Display for Markdown < ' a > {
643
647
fn fmt ( & self , fmt : & mut fmt:: Formatter ) -> fmt:: Result {
644
- let Markdown ( md, links, codes) = * self ;
648
+ let Markdown ( md, links, ref ids, codes) = * self ;
649
+ let mut ids = ids. borrow_mut ( ) ;
645
650
646
651
// This is actually common enough to special-case
647
652
if md. is_empty ( ) { return Ok ( ( ) ) }
@@ -661,7 +666,7 @@ impl<'a> fmt::Display for Markdown<'a> {
661
666
662
667
let mut s = String :: with_capacity ( md. len ( ) * 3 / 2 ) ;
663
668
664
- let p = HeadingLinks :: new ( p, None ) ;
669
+ let p = HeadingLinks :: new ( p, None , & mut ids ) ;
665
670
let p = LinkReplacer :: new ( p, links) ;
666
671
let p = CodeBlocks :: new ( p, codes) ;
667
672
let p = Footnotes :: new ( p) ;
@@ -673,7 +678,8 @@ impl<'a> fmt::Display for Markdown<'a> {
673
678
674
679
impl < ' a > fmt:: Display for MarkdownWithToc < ' a > {
675
680
fn fmt ( & self , fmt : & mut fmt:: Formatter ) -> fmt:: Result {
676
- let MarkdownWithToc ( md, codes) = * self ;
681
+ let MarkdownWithToc ( md, ref ids, codes) = * self ;
682
+ let mut ids = ids. borrow_mut ( ) ;
677
683
678
684
let mut opts = Options :: empty ( ) ;
679
685
opts. insert ( OPTION_ENABLE_TABLES ) ;
@@ -686,7 +692,7 @@ impl<'a> fmt::Display for MarkdownWithToc<'a> {
686
692
let mut toc = TocBuilder :: new ( ) ;
687
693
688
694
{
689
- let p = HeadingLinks :: new ( p, Some ( & mut toc) ) ;
695
+ let p = HeadingLinks :: new ( p, Some ( & mut toc) , & mut ids ) ;
690
696
let p = CodeBlocks :: new ( p, codes) ;
691
697
let p = Footnotes :: new ( p) ;
692
698
html:: push_html ( & mut s, p) ;
@@ -700,7 +706,8 @@ impl<'a> fmt::Display for MarkdownWithToc<'a> {
700
706
701
707
impl < ' a > fmt:: Display for MarkdownHtml < ' a > {
702
708
fn fmt ( & self , fmt : & mut fmt:: Formatter ) -> fmt:: Result {
703
- let MarkdownHtml ( md, codes) = * self ;
709
+ let MarkdownHtml ( md, ref ids, codes) = * self ;
710
+ let mut ids = ids. borrow_mut ( ) ;
704
711
705
712
// This is actually common enough to special-case
706
713
if md. is_empty ( ) { return Ok ( ( ) ) }
@@ -718,7 +725,7 @@ impl<'a> fmt::Display for MarkdownHtml<'a> {
718
725
719
726
let mut s = String :: with_capacity ( md. len ( ) * 3 / 2 ) ;
720
727
721
- let p = HeadingLinks :: new ( p, None ) ;
728
+ let p = HeadingLinks :: new ( p, None , & mut ids ) ;
722
729
let p = CodeBlocks :: new ( p, codes) ;
723
730
let p = Footnotes :: new ( p) ;
724
731
html:: push_html ( & mut s, p) ;
@@ -835,7 +842,10 @@ pub fn markdown_links(md: &str) -> Vec<(String, Option<Range<usize>>)> {
835
842
let p = Parser :: new_with_broken_link_callback ( md, opts,
836
843
Some ( & push) ) ;
837
844
838
- let iter = Footnotes :: new ( HeadingLinks :: new ( p, None ) ) ;
845
+ // There's no need to thread an IdMap through to here because
846
+ // the IDs generated aren't going to be emitted anywhere.
847
+ let mut ids = IdMap :: new ( ) ;
848
+ let iter = Footnotes :: new ( HeadingLinks :: new ( p, None , & mut ids) ) ;
839
849
840
850
for ev in iter {
841
851
if let Event :: Start ( Tag :: Link ( dest, _) ) = ev {
@@ -854,11 +864,67 @@ pub fn markdown_links(md: &str) -> Vec<(String, Option<Range<usize>>)> {
854
864
links
855
865
}
856
866
867
+ #[ derive( Default ) ]
868
+ pub struct IdMap {
869
+ map : HashMap < String , usize > ,
870
+ }
871
+
872
+ impl IdMap {
873
+ pub fn new ( ) -> Self {
874
+ IdMap :: default ( )
875
+ }
876
+
877
+ pub fn populate < I : IntoIterator < Item =String > > ( & mut self , ids : I ) {
878
+ for id in ids {
879
+ let _ = self . derive ( id) ;
880
+ }
881
+ }
882
+
883
+ pub fn reset ( & mut self ) {
884
+ self . map = HashMap :: new ( ) ;
885
+ }
886
+
887
+ pub fn derive ( & mut self , candidate : String ) -> String {
888
+ let id = match self . map . get_mut ( & candidate) {
889
+ None => candidate,
890
+ Some ( a) => {
891
+ let id = format ! ( "{}-{}" , candidate, * a) ;
892
+ * a += 1 ;
893
+ id
894
+ }
895
+ } ;
896
+
897
+ self . map . insert ( id. clone ( ) , 1 ) ;
898
+ id
899
+ }
900
+ }
901
+
902
+ #[ cfg( test) ]
903
+ #[ test]
904
+ fn test_unique_id ( ) {
905
+ let input = [ "foo" , "examples" , "examples" , "method.into_iter" , "examples" ,
906
+ "method.into_iter" , "foo" , "main" , "search" , "methods" ,
907
+ "examples" , "method.into_iter" , "assoc_type.Item" , "assoc_type.Item" ] ;
908
+ let expected = [ "foo" , "examples" , "examples-1" , "method.into_iter" , "examples-2" ,
909
+ "method.into_iter-1" , "foo-1" , "main" , "search" , "methods" ,
910
+ "examples-3" , "method.into_iter-2" , "assoc_type.Item" , "assoc_type.Item-1" ] ;
911
+
912
+ let map = RefCell :: new ( IdMap :: new ( ) ) ;
913
+ let test = || {
914
+ let mut map = map. borrow_mut ( ) ;
915
+ let actual: Vec < String > = input. iter ( ) . map ( |s| map. derive ( s. to_string ( ) ) ) . collect ( ) ;
916
+ assert_eq ! ( & actual[ ..] , expected) ;
917
+ } ;
918
+ test ( ) ;
919
+ map. borrow_mut ( ) . reset ( ) ;
920
+ test ( ) ;
921
+ }
922
+
857
923
#[ cfg( test) ]
858
924
mod tests {
859
- use super :: { ErrorCodes , LangString , Markdown , MarkdownHtml } ;
925
+ use super :: { ErrorCodes , LangString , Markdown , MarkdownHtml , IdMap } ;
860
926
use super :: plain_summary_line;
861
- use html :: render :: reset_ids ;
927
+ use std :: cell :: RefCell ;
862
928
863
929
#[ test]
864
930
fn test_lang_string_parse ( ) {
@@ -901,19 +967,12 @@ mod tests {
901
967
t ( "text,no_run" , false , true , false , false , false , false , false , v ( ) ) ;
902
968
}
903
969
904
- #[ test]
905
- fn issue_17736 ( ) {
906
- let markdown = "# title" ;
907
- Markdown ( markdown, & [ ] , ErrorCodes :: Yes ) . to_string ( ) ;
908
- reset_ids ( true ) ;
909
- }
910
-
911
970
#[ test]
912
971
fn test_header ( ) {
913
972
fn t ( input : & str , expect : & str ) {
914
- let output = Markdown ( input, & [ ] , ErrorCodes :: Yes ) . to_string ( ) ;
973
+ let mut map = IdMap :: new ( ) ;
974
+ let output = Markdown ( input, & [ ] , RefCell :: new ( & mut map) , ErrorCodes :: Yes ) . to_string ( ) ;
915
975
assert_eq ! ( output, expect, "original: {}" , input) ;
916
- reset_ids ( true ) ;
917
976
}
918
977
919
978
t ( "# Foo bar" , "<h1 id=\" foo-bar\" class=\" section-header\" >\
@@ -932,28 +991,24 @@ mod tests {
932
991
933
992
#[ test]
934
993
fn test_header_ids_multiple_blocks ( ) {
935
- fn t ( input : & str , expect : & str ) {
936
- let output = Markdown ( input, & [ ] , ErrorCodes :: Yes ) . to_string ( ) ;
994
+ let mut map = IdMap :: new ( ) ;
995
+ fn t ( map : & mut IdMap , input : & str , expect : & str ) {
996
+ let output = Markdown ( input, & [ ] , RefCell :: new ( map) , ErrorCodes :: Yes ) . to_string ( ) ;
937
997
assert_eq ! ( output, expect, "original: {}" , input) ;
938
998
}
939
999
940
- let test = || {
941
- t ( "# Example" , "<h1 id=\" example\" class=\" section-header\" >\
942
- <a href=\" #example\" >Example</a></h1>") ;
943
- t ( "# Panics" , "<h1 id=\" panics\" class=\" section-header\" >\
944
- <a href=\" #panics\" >Panics</a></h1>") ;
945
- t ( "# Example" , "<h1 id=\" example-1\" class=\" section-header\" >\
946
- <a href=\" #example-1\" >Example</a></h1>") ;
947
- t ( "# Main" , "<h1 id=\" main-1\" class=\" section-header\" >\
948
- <a href=\" #main-1\" >Main</a></h1>") ;
949
- t ( "# Example" , "<h1 id=\" example-2\" class=\" section-header\" >\
950
- <a href=\" #example-2\" >Example</a></h1>") ;
951
- t ( "# Panics" , "<h1 id=\" panics-1\" class=\" section-header\" >\
952
- <a href=\" #panics-1\" >Panics</a></h1>") ;
953
- } ;
954
- test ( ) ;
955
- reset_ids ( true ) ;
956
- test ( ) ;
1000
+ t ( & mut map, "# Example" , "<h1 id=\" example\" class=\" section-header\" >\
1001
+ <a href=\" #example\" >Example</a></h1>") ;
1002
+ t ( & mut map, "# Panics" , "<h1 id=\" panics\" class=\" section-header\" >\
1003
+ <a href=\" #panics\" >Panics</a></h1>") ;
1004
+ t ( & mut map, "# Example" , "<h1 id=\" example-1\" class=\" section-header\" >\
1005
+ <a href=\" #example-1\" >Example</a></h1>") ;
1006
+ t ( & mut map, "# Main" , "<h1 id=\" main\" class=\" section-header\" >\
1007
+ <a href=\" #main\" >Main</a></h1>") ;
1008
+ t ( & mut map, "# Example" , "<h1 id=\" example-2\" class=\" section-header\" >\
1009
+ <a href=\" #example-2\" >Example</a></h1>") ;
1010
+ t ( & mut map, "# Panics" , "<h1 id=\" panics-1\" class=\" section-header\" >\
1011
+ <a href=\" #panics-1\" >Panics</a></h1>") ;
957
1012
}
958
1013
959
1014
#[ test]
@@ -974,7 +1029,8 @@ mod tests {
974
1029
#[ test]
975
1030
fn test_markdown_html_escape ( ) {
976
1031
fn t ( input : & str , expect : & str ) {
977
- let output = MarkdownHtml ( input, ErrorCodes :: Yes ) . to_string ( ) ;
1032
+ let mut idmap = IdMap :: new ( ) ;
1033
+ let output = MarkdownHtml ( input, RefCell :: new ( & mut idmap) , ErrorCodes :: Yes ) . to_string ( ) ;
978
1034
assert_eq ! ( output, expect, "original: {}" , input) ;
979
1035
}
980
1036
0 commit comments