@@ -4,7 +4,7 @@ use crate::theme::Theme;
44use anyhow:: { Context , Result , bail} ;
55use handlebars:: Handlebars ;
66use log:: { debug, info, trace, warn} ;
7- use mdbook_core:: book:: { Book , BookItem } ;
7+ use mdbook_core:: book:: { Book , BookItem , Chapter } ;
88use mdbook_core:: config:: { BookConfig , Code , Config , HtmlConfig , Playground , RustEdition } ;
99use mdbook_core:: utils;
1010use mdbook_core:: utils:: fs:: get_404_output_file;
@@ -30,18 +30,17 @@ impl HtmlHandlebars {
3030 HtmlHandlebars
3131 }
3232
33- fn render_item (
33+ fn render_chapter (
3434 & self ,
35- item : & BookItem ,
36- mut ctx : RenderItemContext < ' _ > ,
35+ ch : & Chapter ,
36+ prev_ch : Option < & Chapter > ,
37+ next_ch : Option < & Chapter > ,
38+ mut ctx : RenderChapterContext < ' _ > ,
3739 print_content : & mut String ,
3840 ) -> Result < ( ) > {
3941 // FIXME: This should be made DRY-er and rely less on mutable state
4042
41- let ( ch, path) = match item {
42- BookItem :: Chapter ( ch) if !ch. is_draft_chapter ( ) => ( ch, ch. path . as_ref ( ) . unwrap ( ) ) ,
43- _ => return Ok ( ( ) ) ,
44- } ;
43+ let path = ch. path . as_ref ( ) . unwrap ( ) ;
4544
4645 if let Some ( ref edit_url_template) = ctx. html_config . edit_url_template {
4746 let full_path = ctx. book_config . src . to_str ( ) . unwrap_or_default ( ) . to_owned ( )
@@ -61,7 +60,7 @@ impl HtmlHandlebars {
6160
6261 let fixed_content =
6362 render_markdown_with_path ( & ch. content , ctx. html_config . smart_punctuation , Some ( path) ) ;
64- if !ctx . is_index && ctx. html_config . print . page_break {
63+ if prev_ch . is_some ( ) && ctx. html_config . print . page_break {
6564 // Add page break between chapters
6665 // 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
6766 // Add both two CSS properties because of the compatibility issue
@@ -116,6 +115,25 @@ impl HtmlHandlebars {
116115 ) ;
117116 }
118117
118+ let mut nav = |name : & str , ch : Option < & Chapter > | {
119+ let Some ( ch) = ch else { return } ;
120+ let path = ch
121+ . path
122+ . as_ref ( )
123+ . unwrap ( )
124+ . with_extension ( "html" )
125+ . to_str ( )
126+ . unwrap ( )
127+ . replace ( '\\' , "//" ) ;
128+ let obj = json ! ( {
129+ "title" : ch. name,
130+ "link" : path,
131+ } ) ;
132+ ctx. data . insert ( name. to_string ( ) , obj) ;
133+ } ;
134+ nav ( "previous" , prev_ch) ;
135+ nav ( "next" , next_ch) ;
136+
119137 // Render the handlebars template with the data
120138 debug ! ( "Render template" ) ;
121139 let rendered = ctx. handlebars . render ( "index" , & ctx. data ) ?;
@@ -131,7 +149,7 @@ impl HtmlHandlebars {
131149 debug ! ( "Creating {}" , filepath. display( ) ) ;
132150 utils:: fs:: write_file ( & ctx. destination , & filepath, rendered. as_bytes ( ) ) ?;
133151
134- if ctx . is_index {
152+ if prev_ch . is_none ( ) {
135153 ctx. data . insert ( "path" . to_owned ( ) , json ! ( "index.md" ) ) ;
136154 ctx. data . insert ( "path_to_root" . to_owned ( ) , json ! ( "" ) ) ;
137155 ctx. data . insert ( "is_index" . to_owned ( ) , json ! ( true ) ) ;
@@ -253,8 +271,6 @@ impl HtmlHandlebars {
253271 no_section_label : html_config. no_section_label ,
254272 } ) ,
255273 ) ;
256- handlebars. register_helper ( "previous" , Box :: new ( helpers:: navigation:: previous) ) ;
257- handlebars. register_helper ( "next" , Box :: new ( helpers:: navigation:: next) ) ;
258274 // TODO: remove theme_option in 0.5, it is not needed.
259275 handlebars. register_helper ( "theme_option" , Box :: new ( helpers:: theme:: theme_option) ) ;
260276 }
@@ -442,21 +458,26 @@ impl Renderer for HtmlHandlebars {
442458 utils:: fs:: write_file ( destination, "CNAME" , format ! ( "{cname}\n " ) . as_bytes ( ) ) ?;
443459 }
444460
445- let mut is_index = true ;
446- for item in book. iter ( ) {
447- let ctx = RenderItemContext {
461+ let chapters: Vec < _ > = book
462+ . iter ( )
463+ . filter_map ( |item| match item {
464+ BookItem :: Chapter ( ch) if !ch. is_draft_chapter ( ) => Some ( ch) ,
465+ _ => None ,
466+ } )
467+ . collect ( ) ;
468+ for ( i, ch) in chapters. iter ( ) . enumerate ( ) {
469+ let previous = ( i != 0 ) . then ( || chapters[ i - 1 ] ) ;
470+ let next = ( i != chapters. len ( ) - 1 ) . then ( || chapters[ i + 1 ] ) ;
471+ let ctx = RenderChapterContext {
448472 handlebars : & handlebars,
449473 destination : destination. to_path_buf ( ) ,
450474 data : data. clone ( ) ,
451- is_index,
452475 book_config : book_config. clone ( ) ,
453476 html_config : html_config. clone ( ) ,
454477 edition : ctx. config . rust . edition ,
455478 chapter_titles : & ctx. chapter_titles ,
456479 } ;
457- self . render_item ( item, ctx, & mut print_content) ?;
458- // Only the first non-draft chapter item should be treated as the "index"
459- is_index &= !matches ! ( item, BookItem :: Chapter ( ch) if !ch. is_draft_chapter( ) ) ;
480+ self . render_chapter ( ch, previous, next, ctx, & mut print_content) ?;
460481 }
461482
462483 // Render 404 page
@@ -927,11 +948,10 @@ fn partition_source(s: &str) -> (String, String) {
927948 ( before, after)
928949}
929950
930- struct RenderItemContext < ' a > {
951+ struct RenderChapterContext < ' a > {
931952 handlebars : & ' a Handlebars < ' a > ,
932953 destination : PathBuf ,
933954 data : serde_json:: Map < String , serde_json:: Value > ,
934- is_index : bool ,
935955 book_config : BookConfig ,
936956 html_config : HtmlConfig ,
937957 edition : Option < RustEdition > ,
0 commit comments