@@ -23,6 +23,7 @@ const MAX_LINK_NESTED_DEPTH: usize = 10;
23
23
/// This hides the lines from initial display but shows them when the reader expands the code
24
24
/// block and provides them to Rustdoc for testing.
25
25
/// - `{{# playground}}` - Insert runnable Rust files
26
+ /// - `{{# title}}` - Override \<title\> of a webpage.
26
27
#[ derive( Default ) ]
27
28
pub struct LinkPreprocessor ;
28
29
@@ -51,8 +52,15 @@ impl Preprocessor for LinkPreprocessor {
51
52
. map ( |dir| src_dir. join ( dir) )
52
53
. expect ( "All book items have a parent" ) ;
53
54
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) ;
55
58
ch. content = content;
59
+ if chapter_title != ch. name {
60
+ ctx. chapter_titles
61
+ . borrow_mut ( )
62
+ . insert ( chapter_path. clone ( ) , chapter_title) ;
63
+ }
56
64
}
57
65
}
58
66
} ) ;
@@ -61,7 +69,13 @@ impl Preprocessor for LinkPreprocessor {
61
69
}
62
70
}
63
71
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
65
79
where
66
80
P1 : AsRef < Path > ,
67
81
P2 : AsRef < Path > ,
@@ -77,11 +91,17 @@ where
77
91
for link in find_links ( s) {
78
92
replaced. push_str ( & s[ previous_end_index..link. start_index ] ) ;
79
93
80
- match link. render_with_path ( & path) {
94
+ match link. render_with_path ( & path, chapter_title ) {
81
95
Ok ( new_content) => {
82
96
if depth < MAX_LINK_NESTED_DEPTH {
83
97
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
+ ) ) ;
85
105
} else {
86
106
replaced. push_str ( & new_content) ;
87
107
}
@@ -116,6 +136,7 @@ enum LinkType<'a> {
116
136
Include ( PathBuf , RangeOrAnchor ) ,
117
137
Playground ( PathBuf , Vec < & ' a str > ) ,
118
138
RustdocInclude ( PathBuf , RangeOrAnchor ) ,
139
+ Title ( & ' a str ) ,
119
140
}
120
141
121
142
#[ derive( PartialEq , Debug , Clone ) ]
@@ -185,6 +206,7 @@ impl<'a> LinkType<'a> {
185
206
LinkType :: Include ( p, _) => Some ( return_relative_path ( base, & p) ) ,
186
207
LinkType :: Playground ( p, _) => Some ( return_relative_path ( base, & p) ) ,
187
208
LinkType :: RustdocInclude ( p, _) => Some ( return_relative_path ( base, & p) ) ,
209
+ LinkType :: Title ( _) => None ,
188
210
}
189
211
}
190
212
}
@@ -255,6 +277,9 @@ struct Link<'a> {
255
277
impl < ' a > Link < ' a > {
256
278
fn from_capture ( cap : Captures < ' a > ) -> Option < Link < ' a > > {
257
279
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
+ }
258
283
( _, Some ( typ) , Some ( rest) ) => {
259
284
let mut path_props = rest. as_str ( ) . split_whitespace ( ) ;
260
285
let file_arg = path_props. next ( ) ;
@@ -291,7 +316,11 @@ impl<'a> Link<'a> {
291
316
} )
292
317
}
293
318
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 > {
295
324
let base = base. as_ref ( ) ;
296
325
match self . link_type {
297
326
// omit the escape char
@@ -353,6 +382,10 @@ impl<'a> Link<'a> {
353
382
contents
354
383
) )
355
384
}
385
+ LinkType :: Title ( title) => {
386
+ * chapter_title = title. to_owned ( ) ;
387
+ Ok ( String :: new ( ) )
388
+ }
356
389
}
357
390
}
358
391
}
@@ -373,17 +406,17 @@ impl<'a> Iterator for LinkIter<'a> {
373
406
374
407
fn find_links ( contents : & str ) -> LinkIter < ' _ > {
375
408
// lazily compute following regex
376
- // r"\\\{\{#.*\}\}|\{\{#([a-zA-Z0-9]+)\s*([a-zA-Z0-9_.\-:/\\\s ]+)\}\}")?;
409
+ // r"\\\{\{#.*\}\}|\{\{#([a-zA-Z0-9]+)\s*([^} ]+)\}\}")?;
377
410
lazy_static ! {
378
411
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"
387
420
)
388
421
. unwrap( ) ;
389
422
}
@@ -406,7 +439,21 @@ mod tests {
406
439
```hbs
407
440
{{#include file.rs}} << an escaped link!
408
441
```" ;
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" ) ;
410
457
}
411
458
412
459
#[ test]
0 commit comments