@@ -54,16 +54,38 @@ impl HtmlHandlebars {
54
54
let content = ch. content . clone ( ) ;
55
55
let content = utils:: render_markdown ( & content, ctx. html_config . curly_quotes ) ;
56
56
57
- let fixed_content =
58
- utils:: render_markdown_with_path ( & ch. content , ctx. html_config . curly_quotes , Some ( path) ) ;
57
+ let fixed_content = utils:: render_markdown_with_path (
58
+ & ch. content ,
59
+ ctx. html_config . curly_quotes ,
60
+ Some ( path) ,
61
+ ctx. html_config . redirect ,
62
+ ) ;
59
63
if !ctx. is_index && ctx. html_config . print . page_break {
60
64
// Add page break between chapters
61
65
// See https://developer.mozilla.org/en-US/docs/Web/CSS/break-before and https://developer.mozilla.org/en-US/docs/Web/CSS/page-break-before
62
66
// Add both two CSS properties because of the compatibility issue
63
67
print_content
64
68
. push_str ( r#"<div style="break-before: page; page-break-before: always;"></div>"# ) ;
65
69
}
66
- print_content. push_str ( & fixed_content) ;
70
+ let path_id = {
71
+ let mut base = path. display ( ) . to_string ( ) ;
72
+ if base. ends_with ( ".md" ) {
73
+ base. replace_range ( base. len ( ) - 3 .., "" ) ;
74
+ }
75
+ & base
76
+ . replace ( "/" , "-" )
77
+ . replace ( "\\ " , "-" )
78
+ . to_ascii_lowercase ( )
79
+ } ;
80
+
81
+ // We have to build header links in advance so that we can know the ranges
82
+ // for the headers in one page.
83
+ // Insert a dummy div to make sure that we can locate the specific page.
84
+ print_content. push_str ( & ( format ! ( r#"<div id="{}"></div>"# , & path_id) ) ) ;
85
+ print_content. push_str ( & build_header_links (
86
+ & build_print_element_id ( & fixed_content, & path_id) ,
87
+ Some ( path_id) ,
88
+ ) ) ;
67
89
68
90
// Update the context with data for this file
69
91
let ctx_path = path
@@ -188,19 +210,31 @@ impl HtmlHandlebars {
188
210
}
189
211
190
212
#[ cfg_attr( feature = "cargo-clippy" , allow( clippy:: let_and_return) ) ]
191
- fn post_process (
213
+ fn post_process_print (
192
214
& self ,
193
215
rendered : String ,
194
216
playground_config : & Playground ,
195
217
edition : Option < RustEdition > ,
196
218
) -> String {
197
- let rendered = build_header_links ( & rendered) ;
198
219
let rendered = fix_code_blocks ( & rendered) ;
199
220
let rendered = add_playground_pre ( & rendered, playground_config, edition) ;
200
221
201
222
rendered
202
223
}
203
224
225
+ #[ cfg_attr( feature = "cargo-clippy" , allow( clippy:: let_and_return) ) ]
226
+ fn post_process (
227
+ & self ,
228
+ rendered : String ,
229
+ playground_config : & Playground ,
230
+ edition : Option < RustEdition > ,
231
+ ) -> String {
232
+ let rendered = build_header_links ( & rendered, None ) ;
233
+ let rendered = self . post_process_print ( rendered, & playground_config, edition) ;
234
+
235
+ rendered
236
+ }
237
+
204
238
fn copy_static_files (
205
239
& self ,
206
240
destination : & Path ,
@@ -561,7 +595,7 @@ impl Renderer for HtmlHandlebars {
561
595
let rendered = handlebars. render ( "index" , & data) ?;
562
596
563
597
let rendered =
564
- self . post_process ( rendered, & html_config. playground , ctx. config . rust . edition ) ;
598
+ self . post_process_print ( rendered, & html_config. playground , ctx. config . rust . edition ) ;
565
599
566
600
utils:: fs:: write_file ( destination, "print.html" , rendered. as_bytes ( ) ) ?;
567
601
debug ! ( "Creating print.html ✓" ) ;
@@ -761,9 +795,43 @@ fn make_data(
761
795
Ok ( data)
762
796
}
763
797
798
+ /// Goes through part of the rendered print page HTML,
799
+ /// add path id prefix to all the elements id as well as footnote links.
800
+ fn build_print_element_id ( html : & str , path_id : & str ) -> String {
801
+ let all_id = Regex :: new ( r#"(<[^>]*?id=")([^"]+?)""# ) . unwrap ( ) ;
802
+ let footnote_id = Regex :: new (
803
+ r##"(<sup [^>]*?class="footnote-reference"[^>]*?>[^<]*?<a [^>]*?href="#)([^"]+?)""## ,
804
+ )
805
+ . unwrap ( ) ;
806
+
807
+ if path_id. is_empty ( ) {
808
+ return html. to_string ( ) ;
809
+ }
810
+
811
+ let temp_html = all_id
812
+ . replace_all ( html, |caps : & Captures < ' _ > | {
813
+ let mut fixed = String :: new ( ) ;
814
+ fixed. push_str ( & path_id) ;
815
+ fixed. push_str ( "-" ) ;
816
+ fixed. push_str ( & caps[ 2 ] ) ;
817
+ format ! ( "{}{}\" " , & caps[ 1 ] , fixed)
818
+ } )
819
+ . into_owned ( ) ;
820
+
821
+ footnote_id
822
+ . replace_all ( & temp_html, |caps : & Captures < ' _ > | {
823
+ let mut fixed = String :: new ( ) ;
824
+ fixed. push_str ( & path_id) ;
825
+ fixed. push_str ( "-" ) ;
826
+ fixed. push_str ( & caps[ 2 ] ) ;
827
+ format ! ( "{}{}\" " , & caps[ 1 ] , fixed)
828
+ } )
829
+ . into_owned ( )
830
+ }
831
+
764
832
/// Goes through the rendered HTML, making sure all header tags have
765
833
/// an anchor respectively so people can link to sections directly.
766
- fn build_header_links ( html : & str ) -> String {
834
+ fn build_header_links ( html : & str , path_id : Option < & str > ) -> String {
767
835
lazy_static ! {
768
836
static ref BUILD_HEADER_LINKS : Regex = Regex :: new( r"<h(\d)>(.*?)</h\d>" ) . unwrap( ) ;
769
837
}
@@ -776,19 +844,26 @@ fn build_header_links(html: &str) -> String {
776
844
. parse ( )
777
845
. expect ( "Regex should ensure we only ever get numbers here" ) ;
778
846
779
- insert_link_into_header ( level, & caps[ 2 ] , & mut id_counter)
847
+ insert_link_into_header ( level, & caps[ 2 ] , & mut id_counter, path_id )
780
848
} )
781
849
. into_owned ( )
782
850
}
783
851
784
852
/// Insert a sinle link into a header, making sure each link gets its own
785
853
/// unique ID by appending an auto-incremented number (if necessary).
854
+ ///
855
+ /// For `print.html`, we will add a path id prefix.
786
856
fn insert_link_into_header (
787
857
level : usize ,
788
858
content : & str ,
789
859
id_counter : & mut HashMap < String , usize > ,
860
+ path_id : Option < & str > ,
790
861
) -> String {
791
- let id = utils:: unique_id_from_content ( content, id_counter) ;
862
+ let id = if let Some ( path_id) = path_id {
863
+ utils:: unique_id_from_content_with_path ( content, id_counter, path_id)
864
+ } else {
865
+ utils:: unique_id_from_content ( content, id_counter)
866
+ } ;
792
867
793
868
format ! (
794
869
r##"<h{level} id="{id}"><a class="header" href="#{id}">{text}</a></h{level}>"## ,
@@ -996,7 +1071,7 @@ mod tests {
996
1071
] ;
997
1072
998
1073
for ( src, should_be) in inputs {
999
- let got = build_header_links ( src) ;
1074
+ let got = build_header_links ( src, None ) ;
1000
1075
assert_eq ! ( got, should_be) ;
1001
1076
}
1002
1077
}
0 commit comments