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