1
1
use crate :: book:: { Book , BookItem } ;
2
2
use crate :: config:: { Config , HtmlConfig , Playpen } ;
3
3
use crate :: errors:: * ;
4
+ use crate :: renderer:: html_handlebars:: hbs_wrapper:: { HbsWrapper , HbsConfig } ;
4
5
use crate :: renderer:: html_handlebars:: helpers;
5
6
use crate :: renderer:: { RenderContext , Renderer } ;
6
7
use crate :: theme:: { self , playpen_editor, Theme } ;
@@ -11,9 +12,9 @@ use std::collections::HashMap;
11
12
use std:: fs;
12
13
use std:: path:: { Path , PathBuf } ;
13
14
14
- use handlebars:: Handlebars ;
15
15
use regex:: { Captures , Regex } ;
16
16
17
+
17
18
#[ derive( Default ) ]
18
19
pub struct HtmlHandlebars ;
19
20
@@ -76,9 +77,7 @@ impl HtmlHandlebars {
76
77
77
78
// Render the handlebars template with the data
78
79
debug ! ( "Render template" ) ;
79
- let rendered = ctx. handlebars . render ( "index" , & ctx. data ) ?;
80
-
81
- let rendered = self . post_process ( rendered, & ctx. html_config . playpen ) ;
80
+ let rendered = ctx. handlebars . render ( "index" , & ctx. data , & ctx. html_config . playpen ) ?;
82
81
83
82
// Write to file
84
83
debug ! ( "Creating {}" , filepath. display( ) ) ;
@@ -87,8 +86,7 @@ impl HtmlHandlebars {
87
86
if ctx. is_index {
88
87
ctx. data . insert ( "path" . to_owned ( ) , json ! ( "index.md" ) ) ;
89
88
ctx. data . insert ( "path_to_root" . to_owned ( ) , json ! ( "" ) ) ;
90
- let rendered_index = ctx. handlebars . render ( "index" , & ctx. data ) ?;
91
- let rendered_index = self . post_process ( rendered_index, & ctx. html_config . playpen ) ;
89
+ let rendered_index = ctx. handlebars . render ( "index" , & ctx. data , & ctx. html_config . playpen ) ?;
92
90
debug ! ( "Creating index.html from {}" , path) ;
93
91
utils:: fs:: write_file ( & ctx. destination , "index.html" , rendered_index. as_bytes ( ) ) ?;
94
92
}
@@ -97,15 +95,6 @@ impl HtmlHandlebars {
97
95
Ok ( ( ) )
98
96
}
99
97
100
- #[ cfg_attr( feature = "cargo-clippy" , allow( clippy:: let_and_return) ) ]
101
- fn post_process ( & self , rendered : String , playpen_config : & Playpen ) -> String {
102
- let rendered = build_header_links ( & rendered) ;
103
- let rendered = fix_code_blocks ( & rendered) ;
104
- let rendered = add_playpen_pre ( & rendered, playpen_config) ;
105
-
106
- rendered
107
- }
108
-
109
98
fn copy_static_files (
110
99
& self ,
111
100
destination : & Path ,
@@ -204,18 +193,6 @@ impl HtmlHandlebars {
204
193
) ;
205
194
}
206
195
207
- fn register_hbs_helpers ( & self , handlebars : & mut Handlebars , html_config : & HtmlConfig ) {
208
- handlebars. register_helper (
209
- "toc" ,
210
- Box :: new ( helpers:: toc:: RenderToc {
211
- no_section_label : html_config. no_section_label ,
212
- } ) ,
213
- ) ;
214
- handlebars. register_helper ( "previous" , Box :: new ( helpers:: navigation:: previous) ) ;
215
- handlebars. register_helper ( "next" , Box :: new ( helpers:: navigation:: next) ) ;
216
- handlebars. register_helper ( "theme_option" , Box :: new ( helpers:: theme:: theme_option) ) ;
217
- }
218
-
219
196
/// Copy across any additional CSS and JavaScript files which the book
220
197
/// has been configured to use.
221
198
fn copy_additional_css_and_js (
@@ -285,8 +262,6 @@ impl Renderer for HtmlHandlebars {
285
262
let book = & ctx. book ;
286
263
287
264
trace ! ( "render" ) ;
288
- let mut handlebars = Handlebars :: new ( ) ;
289
-
290
265
let theme_dir = match html_config. theme {
291
266
Some ( ref theme) => theme. to_path_buf ( ) ,
292
267
None => ctx. root . join ( "theme" ) ,
@@ -303,15 +278,12 @@ impl Renderer for HtmlHandlebars {
303
278
}
304
279
305
280
let theme = theme:: Theme :: new ( theme_dir) ;
306
-
307
- debug ! ( "Register the index handlebars template" ) ;
308
- handlebars. register_template_string ( "index" , String :: from_utf8 ( theme. index . clone ( ) ) ?) ?;
309
-
310
- debug ! ( "Register the header handlebars template" ) ;
311
- handlebars. register_partial ( "header" , String :: from_utf8 ( theme. header . clone ( ) ) ?) ?;
312
-
313
- debug ! ( "Register handlebars helpers" ) ;
314
- self . register_hbs_helpers ( & mut handlebars, & html_config) ;
281
+ let config = HbsConfig {
282
+ index_template : String :: from_utf8 ( theme. index . clone ( ) ) ?,
283
+ header_template : String :: from_utf8 ( theme. header . clone ( ) ) ?,
284
+ no_section_label : html_config. no_section_label ,
285
+ } ;
286
+ let handlebars = HbsWrapper :: with_config ( config) ?;
315
287
316
288
let mut data = make_data ( & ctx. root , & book, & ctx. config , & html_config) ?;
317
289
@@ -342,9 +314,7 @@ impl Renderer for HtmlHandlebars {
342
314
343
315
// Render the handlebars template with the data
344
316
debug ! ( "Render template" ) ;
345
- let rendered = handlebars. render ( "index" , & data) ?;
346
-
347
- let rendered = self . post_process ( rendered, & html_config. playpen ) ;
317
+ let rendered = handlebars. render ( "index" , & data, & html_config. playpen ) ?;
348
318
349
319
utils:: fs:: write_file ( & destination, "print.html" , rendered. as_bytes ( ) ) ?;
350
320
debug ! ( "Creating print.html ✓" ) ;
@@ -499,177 +469,10 @@ fn make_data(
499
469
Ok ( data)
500
470
}
501
471
502
- /// Goes through the rendered HTML, making sure all header tags have
503
- /// an anchor respectively so people can link to sections directly.
504
- fn build_header_links ( html : & str ) -> String {
505
- let regex = Regex :: new ( r"<h(\d)>(.*?)</h\d>" ) . unwrap ( ) ;
506
- let mut id_counter = HashMap :: new ( ) ;
507
-
508
- regex
509
- . replace_all ( html, |caps : & Captures < ' _ > | {
510
- let level = caps[ 1 ]
511
- . parse ( )
512
- . expect ( "Regex should ensure we only ever get numbers here" ) ;
513
-
514
- insert_link_into_header ( level, & caps[ 2 ] , & mut id_counter)
515
- } )
516
- . into_owned ( )
517
- }
518
-
519
- /// Insert a sinle link into a header, making sure each link gets its own
520
- /// unique ID by appending an auto-incremented number (if necessary).
521
- fn insert_link_into_header (
522
- level : usize ,
523
- content : & str ,
524
- id_counter : & mut HashMap < String , usize > ,
525
- ) -> String {
526
- let raw_id = utils:: id_from_content ( content) ;
527
-
528
- let id_count = id_counter. entry ( raw_id. clone ( ) ) . or_insert ( 0 ) ;
529
-
530
- let id = match * id_count {
531
- 0 => raw_id,
532
- other => format ! ( "{}-{}" , raw_id, other) ,
533
- } ;
534
-
535
- * id_count += 1 ;
536
-
537
- format ! (
538
- r##"<h{level}><a class="header" href="#{id}" id="{id}">{text}</a></h{level}>"## ,
539
- level = level,
540
- id = id,
541
- text = content
542
- )
543
- }
544
-
545
- // The rust book uses annotations for rustdoc to test code snippets,
546
- // like the following:
547
- // ```rust,should_panic
548
- // fn main() {
549
- // // Code here
550
- // }
551
- // ```
552
- // This function replaces all commas by spaces in the code block classes
553
- fn fix_code_blocks ( html : & str ) -> String {
554
- let regex = Regex :: new ( r##"<code([^>]+)class="([^"]+)"([^>]*)>"## ) . unwrap ( ) ;
555
- regex
556
- . replace_all ( html, |caps : & Captures < ' _ > | {
557
- let before = & caps[ 1 ] ;
558
- let classes = & caps[ 2 ] . replace ( "," , " " ) ;
559
- let after = & caps[ 3 ] ;
560
-
561
- format ! (
562
- r#"<code{before}class="{classes}"{after}>"# ,
563
- before = before,
564
- classes = classes,
565
- after = after
566
- )
567
- } )
568
- . into_owned ( )
569
- }
570
-
571
- fn add_playpen_pre ( html : & str , playpen_config : & Playpen ) -> String {
572
- let regex = Regex :: new ( r##"((?s)<code[^>]?class="([^"]+)".*?>(.*?)</code>)"## ) . unwrap ( ) ;
573
- regex
574
- . replace_all ( html, |caps : & Captures < ' _ > | {
575
- let text = & caps[ 1 ] ;
576
- let classes = & caps[ 2 ] ;
577
- let code = & caps[ 3 ] ;
578
-
579
- if ( classes. contains ( "language-rust" )
580
- && !classes. contains ( "ignore" )
581
- && !classes. contains ( "noplaypen" ) )
582
- || classes. contains ( "mdbook-runnable" )
583
- {
584
- // wrap the contents in an external pre block
585
- if playpen_config. editable && classes. contains ( "editable" )
586
- || text. contains ( "fn main" )
587
- || text. contains ( "quick_main!" )
588
- {
589
- format ! ( "<pre class=\" playpen\" >{}</pre>" , text)
590
- } else {
591
- // we need to inject our own main
592
- let ( attrs, code) = partition_source ( code) ;
593
-
594
- format ! (
595
- "<pre class=\" playpen\" ><code class=\" {}\" >\n # \
596
- #![allow(unused_variables)]\n {}#fn main() {{\n {}#}}</code></pre>",
597
- classes, attrs, code
598
- )
599
- }
600
- } else {
601
- // not language-rust, so no-op
602
- text. to_owned ( )
603
- }
604
- } )
605
- . into_owned ( )
606
- }
607
-
608
- fn partition_source ( s : & str ) -> ( String , String ) {
609
- let mut after_header = false ;
610
- let mut before = String :: new ( ) ;
611
- let mut after = String :: new ( ) ;
612
-
613
- for line in s. lines ( ) {
614
- let trimline = line. trim ( ) ;
615
- let header = trimline. chars ( ) . all ( char:: is_whitespace) || trimline. starts_with ( "#![" ) ;
616
- if !header || after_header {
617
- after_header = true ;
618
- after. push_str ( line) ;
619
- after. push_str ( "\n " ) ;
620
- } else {
621
- before. push_str ( line) ;
622
- before. push_str ( "\n " ) ;
623
- }
624
- }
625
-
626
- ( before, after)
627
- }
628
-
629
472
struct RenderItemContext < ' a > {
630
- handlebars : & ' a Handlebars ,
473
+ handlebars : & ' a HbsWrapper ,
631
474
destination : PathBuf ,
632
475
data : serde_json:: Map < String , serde_json:: Value > ,
633
476
is_index : bool ,
634
477
html_config : HtmlConfig ,
635
478
}
636
-
637
- #[ cfg( test) ]
638
- mod tests {
639
- use super :: * ;
640
-
641
- #[ test]
642
- fn original_build_header_links ( ) {
643
- let inputs = vec ! [
644
- (
645
- "blah blah <h1>Foo</h1>" ,
646
- r##"blah blah <h1><a class="header" href="#foo" id="foo">Foo</a></h1>"## ,
647
- ) ,
648
- (
649
- "<h1>Foo</h1>" ,
650
- r##"<h1><a class="header" href="#foo" id="foo">Foo</a></h1>"## ,
651
- ) ,
652
- (
653
- "<h3>Foo^bar</h3>" ,
654
- r##"<h3><a class="header" href="#foobar" id="foobar">Foo^bar</a></h3>"## ,
655
- ) ,
656
- (
657
- "<h4></h4>" ,
658
- r##"<h4><a class="header" href="#" id=""></a></h4>"## ,
659
- ) ,
660
- (
661
- "<h4><em>Hï</em></h4>" ,
662
- r##"<h4><a class="header" href="#hï" id="hï"><em>Hï</em></a></h4>"## ,
663
- ) ,
664
- (
665
- "<h1>Foo</h1><h3>Foo</h3>" ,
666
- r##"<h1><a class="header" href="#foo" id="foo">Foo</a></h1><h3><a class="header" href="#foo-1" id="foo-1">Foo</a></h3>"## ,
667
- ) ,
668
- ] ;
669
-
670
- for ( src, should_be) in inputs {
671
- let got = build_header_links ( & src) ;
672
- assert_eq ! ( got, should_be) ;
673
- }
674
- }
675
- }
0 commit comments