@@ -23,6 +23,7 @@ const MAX_LINK_NESTED_DEPTH: usize = 10;
2323/// This hides the lines from initial display but shows them when the reader expands the code
2424/// block and provides them to Rustdoc for testing.
2525/// - `{{# playground}}` - Insert runnable Rust files
26+ /// - `{{# title}}` - Override \<title\> of a webpage.
2627#[ derive( Default ) ]
2728pub struct LinkPreprocessor ;
2829
@@ -51,8 +52,15 @@ impl Preprocessor for LinkPreprocessor {
5152 . map ( |dir| src_dir. join ( dir) )
5253 . expect ( "All book items have a parent" ) ;
5354
54- let content = replace_all ( & ch. content , base, chapter_path, 0 ) ;
55+ let mut chapter_title = ch. name . clone ( ) ;
56+ let content =
57+ replace_all ( & ch. content , base, chapter_path, 0 , & mut chapter_title) ;
5558 ch. content = content;
59+ if chapter_title != ch. name {
60+ ctx. chapter_titles
61+ . borrow_mut ( )
62+ . insert ( chapter_path. clone ( ) , chapter_title) ;
63+ }
5664 }
5765 }
5866 } ) ;
@@ -61,7 +69,13 @@ impl Preprocessor for LinkPreprocessor {
6169 }
6270}
6371
64- fn replace_all < P1 , P2 > ( s : & str , path : P1 , source : P2 , depth : usize ) -> String
72+ fn replace_all < P1 , P2 > (
73+ s : & str ,
74+ path : P1 ,
75+ source : P2 ,
76+ depth : usize ,
77+ chapter_title : & mut String ,
78+ ) -> String
6579where
6680 P1 : AsRef < Path > ,
6781 P2 : AsRef < Path > ,
@@ -77,11 +91,17 @@ where
7791 for link in find_links ( s) {
7892 replaced. push_str ( & s[ previous_end_index..link. start_index ] ) ;
7993
80- match link. render_with_path ( & path) {
94+ match link. render_with_path ( & path, chapter_title ) {
8195 Ok ( new_content) => {
8296 if depth < MAX_LINK_NESTED_DEPTH {
8397 if let Some ( rel_path) = link. link_type . relative_path ( path) {
84- replaced. push_str ( & replace_all ( & new_content, rel_path, source, depth + 1 ) ) ;
98+ replaced. push_str ( & replace_all (
99+ & new_content,
100+ rel_path,
101+ source,
102+ depth + 1 ,
103+ chapter_title,
104+ ) ) ;
85105 } else {
86106 replaced. push_str ( & new_content) ;
87107 }
@@ -116,6 +136,7 @@ enum LinkType<'a> {
116136 Include ( PathBuf , RangeOrAnchor ) ,
117137 Playground ( PathBuf , Vec < & ' a str > ) ,
118138 RustdocInclude ( PathBuf , RangeOrAnchor ) ,
139+ Title ( & ' a str ) ,
119140}
120141
121142#[ derive( PartialEq , Debug , Clone ) ]
@@ -185,6 +206,7 @@ impl<'a> LinkType<'a> {
185206 LinkType :: Include ( p, _) => Some ( return_relative_path ( base, & p) ) ,
186207 LinkType :: Playground ( p, _) => Some ( return_relative_path ( base, & p) ) ,
187208 LinkType :: RustdocInclude ( p, _) => Some ( return_relative_path ( base, & p) ) ,
209+ LinkType :: Title ( _) => None ,
188210 }
189211 }
190212}
@@ -255,6 +277,9 @@ struct Link<'a> {
255277impl < ' a > Link < ' a > {
256278 fn from_capture ( cap : Captures < ' a > ) -> Option < Link < ' a > > {
257279 let link_type = match ( cap. get ( 0 ) , cap. get ( 1 ) , cap. get ( 2 ) ) {
280+ ( _, Some ( typ) , Some ( title) ) if typ. as_str ( ) == "title" => {
281+ Some ( LinkType :: Title ( title. as_str ( ) ) )
282+ }
258283 ( _, Some ( typ) , Some ( rest) ) => {
259284 let mut path_props = rest. as_str ( ) . split_whitespace ( ) ;
260285 let file_arg = path_props. next ( ) ;
@@ -291,7 +316,11 @@ impl<'a> Link<'a> {
291316 } )
292317 }
293318
294- fn render_with_path < P : AsRef < Path > > ( & self , base : P ) -> Result < String > {
319+ fn render_with_path < P : AsRef < Path > > (
320+ & self ,
321+ base : P ,
322+ chapter_title : & mut String ,
323+ ) -> Result < String > {
295324 let base = base. as_ref ( ) ;
296325 match self . link_type {
297326 // omit the escape char
@@ -353,6 +382,10 @@ impl<'a> Link<'a> {
353382 contents
354383 ) )
355384 }
385+ LinkType :: Title ( title) => {
386+ * chapter_title = title. to_owned ( ) ;
387+ Ok ( String :: new ( ) )
388+ }
356389 }
357390 }
358391}
@@ -373,17 +406,17 @@ impl<'a> Iterator for LinkIter<'a> {
373406
374407fn find_links ( contents : & str ) -> LinkIter < ' _ > {
375408 // lazily compute following regex
376- // r"\\\{\{#.*\}\}|\{\{#([a-zA-Z0-9]+)\s*([a-zA-Z0-9_.\-:/\\\s ]+)\}\}")?;
409+ // r"\\\{\{#.*\}\}|\{\{#([a-zA-Z0-9]+)\s*([^} ]+)\}\}")?;
377410 lazy_static ! {
378411 static ref RE : Regex = Regex :: new(
379- r"(?x) # insignificant whitespace mode
380- \\\{\{\#.*\}\} # match escaped link
381- | # or
382- \{\{\s* # link opening parens and whitespace
383- \#([a-zA-Z0-9_]+) # link type
384- \s+ # separating whitespace
385- ([a-zA-Z0-9\s_.\-:/\\\+ ]+) # link target path and space separated properties
386- \s*\ }\} # whitespace and link closing parens"
412+ r"(?x) # insignificant whitespace mode
413+ \\\{\{\#.*\}\} # match escaped link
414+ | # or
415+ \{\{\s* # link opening parens and whitespace
416+ \#([a-zA-Z0-9_]+) # link type
417+ \s+ # separating whitespace
418+ ([^} ]+) # link target path and space separated properties
419+ \}\} # link closing parens"
387420 )
388421 . unwrap( ) ;
389422 }
@@ -406,7 +439,21 @@ mod tests {
406439 ```hbs
407440 {{#include file.rs}} << an escaped link!
408441 ```" ;
409- assert_eq ! ( replace_all( start, "" , "" , 0 ) , end) ;
442+ let mut chapter_title = "test_replace_all_escaped" . to_owned ( ) ;
443+ assert_eq ! ( replace_all( start, "" , "" , 0 , & mut chapter_title) , end) ;
444+ }
445+
446+ #[ test]
447+ fn test_set_chapter_title ( ) {
448+ let start = r"{{#title My Title}}
449+ # My Chapter
450+ " ;
451+ let end = r"
452+ # My Chapter
453+ " ;
454+ let mut chapter_title = "test_set_chapter_title" . to_owned ( ) ;
455+ assert_eq ! ( replace_all( start, "" , "" , 0 , & mut chapter_title) , end) ;
456+ assert_eq ! ( chapter_title, "My Title" ) ;
410457 }
411458
412459 #[ test]
0 commit comments