@@ -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 ,
@@ -554,7 +588,7 @@ impl Renderer for HtmlHandlebars {
554
588
let rendered = handlebars. render ( "index" , & data) ?;
555
589
556
590
let rendered =
557
- self . post_process ( rendered, & html_config. playground , ctx. config . rust . edition ) ;
591
+ self . post_process_print ( rendered, & html_config. playground , ctx. config . rust . edition ) ;
558
592
559
593
utils:: fs:: write_file ( destination, "print.html" , rendered. as_bytes ( ) ) ?;
560
594
debug ! ( "Creating print.html ✓" ) ;
@@ -754,9 +788,43 @@ fn make_data(
754
788
Ok ( data)
755
789
}
756
790
791
+ /// Goes through part of the rendered print page HTML,
792
+ /// add path id prefix to all the elements id as well as footnote links.
793
+ fn build_print_element_id ( html : & str , path_id : & str ) -> String {
794
+ let all_id = Regex :: new ( r#"(<[^>]*?id=")([^"]+?)""# ) . unwrap ( ) ;
795
+ let footnote_id = Regex :: new (
796
+ r##"(<sup [^>]*?class="footnote-reference"[^>]*?>[^<]*?<a [^>]*?href="#)([^"]+?)""## ,
797
+ )
798
+ . unwrap ( ) ;
799
+
800
+ if path_id. is_empty ( ) {
801
+ return html. to_string ( ) ;
802
+ }
803
+
804
+ let temp_html = all_id
805
+ . replace_all ( html, |caps : & Captures < ' _ > | {
806
+ let mut fixed = String :: new ( ) ;
807
+ fixed. push_str ( & path_id) ;
808
+ fixed. push_str ( "-" ) ;
809
+ fixed. push_str ( & caps[ 2 ] ) ;
810
+ format ! ( "{}{}\" " , & caps[ 1 ] , fixed)
811
+ } )
812
+ . into_owned ( ) ;
813
+
814
+ footnote_id
815
+ . replace_all ( & temp_html, |caps : & Captures < ' _ > | {
816
+ let mut fixed = String :: new ( ) ;
817
+ fixed. push_str ( & path_id) ;
818
+ fixed. push_str ( "-" ) ;
819
+ fixed. push_str ( & caps[ 2 ] ) ;
820
+ format ! ( "{}{}\" " , & caps[ 1 ] , fixed)
821
+ } )
822
+ . into_owned ( )
823
+ }
824
+
757
825
/// Goes through the rendered HTML, making sure all header tags have
758
826
/// an anchor respectively so people can link to sections directly.
759
- fn build_header_links ( html : & str ) -> String {
827
+ fn build_header_links ( html : & str , path_id : Option < & str > ) -> String {
760
828
let regex = Regex :: new ( r"<h(\d)>(.*?)</h\d>" ) . unwrap ( ) ;
761
829
let mut id_counter = HashMap :: new ( ) ;
762
830
@@ -766,19 +834,26 @@ fn build_header_links(html: &str) -> String {
766
834
. parse ( )
767
835
. expect ( "Regex should ensure we only ever get numbers here" ) ;
768
836
769
- insert_link_into_header ( level, & caps[ 2 ] , & mut id_counter)
837
+ insert_link_into_header ( level, & caps[ 2 ] , & mut id_counter, path_id )
770
838
} )
771
839
. into_owned ( )
772
840
}
773
841
774
842
/// Insert a sinle link into a header, making sure each link gets its own
775
843
/// unique ID by appending an auto-incremented number (if necessary).
844
+ ///
845
+ /// For `print.html`, we will add a path id prefix.
776
846
fn insert_link_into_header (
777
847
level : usize ,
778
848
content : & str ,
779
849
id_counter : & mut HashMap < String , usize > ,
850
+ path_id : Option < & str > ,
780
851
) -> String {
781
- let id = utils:: unique_id_from_content ( content, id_counter) ;
852
+ let id = if let Some ( path_id) = path_id {
853
+ utils:: unique_id_from_content_with_path ( content, id_counter, path_id)
854
+ } else {
855
+ utils:: unique_id_from_content ( content, id_counter)
856
+ } ;
782
857
783
858
format ! (
784
859
r##"<h{level} id="{id}"><a class="header" href="#{id}">{text}</a></h{level}>"## ,
@@ -982,7 +1057,7 @@ mod tests {
982
1057
] ;
983
1058
984
1059
for ( src, should_be) in inputs {
985
- let got = build_header_links ( src) ;
1060
+ let got = build_header_links ( src, None ) ;
986
1061
assert_eq ! ( got, should_be) ;
987
1062
}
988
1063
}
0 commit comments