From 857188392348cb07f3300d6c3db7302f7adfca88 Mon Sep 17 00:00:00 2001 From: ISSOtm Date: Wed, 22 Jun 2022 20:58:47 +0200 Subject: [PATCH 1/3] Mark the first chapter as "index", even if not the first book item --- src/renderer/html_handlebars/hbs_renderer.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/renderer/html_handlebars/hbs_renderer.rs b/src/renderer/html_handlebars/hbs_renderer.rs index 26f1432c33..8aebbce809 100644 --- a/src/renderer/html_handlebars/hbs_renderer.rs +++ b/src/renderer/html_handlebars/hbs_renderer.rs @@ -540,7 +540,8 @@ impl Renderer for HtmlHandlebars { chapter_titles: &ctx.chapter_titles, }; self.render_item(item, ctx, &mut print_content)?; - is_index = false; + // Only the first non-draft chapter item should be treated as the "index" + is_index &= !matches!(item, BookItem::Chapter(ch) if !ch.is_draft_chapter()); } // Render 404 page From a91e8885755bfc1f39a9b2edd7af0d37cd89b4ee Mon Sep 17 00:00:00 2001 From: ISSOtm Date: Wed, 22 Jun 2022 22:55:52 +0200 Subject: [PATCH 2/3] Add test for index page --- tests/dummy_book/index_html_test/SUMMARY.md | 11 +++++++ tests/dummy_book/index_html_test/chapter_1.md | 1 + tests/rendered_output.rs | 30 ++++++++++++++++++- 3 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 tests/dummy_book/index_html_test/SUMMARY.md create mode 100644 tests/dummy_book/index_html_test/chapter_1.md diff --git a/tests/dummy_book/index_html_test/SUMMARY.md b/tests/dummy_book/index_html_test/SUMMARY.md new file mode 100644 index 0000000000..37bf68cdef --- /dev/null +++ b/tests/dummy_book/index_html_test/SUMMARY.md @@ -0,0 +1,11 @@ +# Summary + +--- + +- [None of these should be treated as the "index chapter"]() + +# Part 1 + +- [Not this either]() +- [Chapter 1](./chapter_1.md) +- [And not this]() diff --git a/tests/dummy_book/index_html_test/chapter_1.md b/tests/dummy_book/index_html_test/chapter_1.md new file mode 100644 index 0000000000..b743fda354 --- /dev/null +++ b/tests/dummy_book/index_html_test/chapter_1.md @@ -0,0 +1 @@ +# Chapter 1 diff --git a/tests/rendered_output.rs b/tests/rendered_output.rs index c6267830e3..24d3427bf9 100644 --- a/tests/rendered_output.rs +++ b/tests/rendered_output.rs @@ -15,7 +15,7 @@ use select::predicate::{Class, Name, Predicate}; use std::collections::HashMap; use std::ffi::OsStr; use std::fs; -use std::io::Write; +use std::io::{Read, Write}; use std::path::{Component, Path, PathBuf}; use std::str::FromStr; use tempfile::Builder as TempFileBuilder; @@ -467,6 +467,34 @@ fn by_default_mdbook_use_index_preprocessor_to_convert_readme_to_index() { assert_doesnt_contain_strings(&second_index, &unexpected_strings); } +#[test] +fn first_chapter_is_copied_as_index_even_if_not_first_elem() { + let temp = DummyBook::new().build().unwrap(); + let mut cfg = Config::default(); + cfg.set("book.src", "index_html_test") + .expect("Couldn't set config.book.src to \"index_html_test\""); + let md = MDBook::load_with_config(temp.path(), cfg).unwrap(); + md.build().unwrap(); + + // In theory, just reading the entire files into memory and comparing is sufficient for *testing*, + // but since the files are temporary and get deleted when the test completes, we'll want to print + // the differences on failure. + // We could invoke `diff` on the files on failure, but that may not be portable (hi, Windows...) + // so we'll do the job ourselves—potentially a bit sloppily, but that can always be piped into + // `diff` manually afterwards. + let book_path = temp.path().join("book"); + let read_file = |path: &str| { + let mut buf = String::new(); + fs::File::open(book_path.join(path)) + .with_context(|| format!("Failed to read {}", path)) + .unwrap() + .read_to_string(&mut buf) + .unwrap(); + buf + }; + pretty_assertions::assert_eq!(read_file("chapter_1.html"), read_file("index.html")); +} + #[test] fn theme_dir_overrides_work_correctly() { let book_dir = dummy_book::new_copy_of_example_book().unwrap(); From fa5f32c7fdc9ff74ac9aee633505083fdabab62c Mon Sep 17 00:00:00 2001 From: ISSOtm Date: Wed, 22 Jun 2022 23:40:36 +0200 Subject: [PATCH 3/3] Make link to first chapter active in index page Makes both pages more consistent, and also the previous test pass Co-authored-by: Eric Huss --- src/renderer/html_handlebars/hbs_renderer.rs | 2 +- src/renderer/html_handlebars/helpers/toc.rs | 8 ++++++- tests/rendered_output.rs | 23 +++++--------------- 3 files changed, 13 insertions(+), 20 deletions(-) diff --git a/src/renderer/html_handlebars/hbs_renderer.rs b/src/renderer/html_handlebars/hbs_renderer.rs index 8aebbce809..b933a359aa 100644 --- a/src/renderer/html_handlebars/hbs_renderer.rs +++ b/src/renderer/html_handlebars/hbs_renderer.rs @@ -116,7 +116,7 @@ impl HtmlHandlebars { if ctx.is_index { ctx.data.insert("path".to_owned(), json!("index.md")); ctx.data.insert("path_to_root".to_owned(), json!("")); - ctx.data.insert("is_index".to_owned(), json!("true")); + ctx.data.insert("is_index".to_owned(), json!(true)); let rendered_index = ctx.handlebars.render("index", &ctx.data)?; let rendered_index = self.post_process(rendered_index, &ctx.html_config.playground, ctx.edition); diff --git a/src/renderer/html_handlebars/helpers/toc.rs b/src/renderer/html_handlebars/helpers/toc.rs index 6ae62aa7ca..0884d30ad1 100644 --- a/src/renderer/html_handlebars/helpers/toc.rs +++ b/src/renderer/html_handlebars/helpers/toc.rs @@ -57,6 +57,11 @@ impl HelperDef for RenderToc { out.write("
    ")?; let mut current_level = 1; + // The "index" page, which has this attribute set, is supposed to alias the first chapter in + // the book, i.e. the first link. There seems to be no easy way to determine which chapter + // the "index" is aliasing from within the renderer, so this is used instead to force the + // first link to be active. See further below. + let mut is_first_chapter = ctx.data().get("is_index").is_some(); for item in chapters { // Spacer @@ -130,7 +135,8 @@ impl HelperDef for RenderToc { out.write(&tmp)?; out.write("\"")?; - if path == ¤t_path { + if path == ¤t_path || is_first_chapter { + is_first_chapter = false; out.write(" class=\"active\"")?; } diff --git a/tests/rendered_output.rs b/tests/rendered_output.rs index 24d3427bf9..9750a35e22 100644 --- a/tests/rendered_output.rs +++ b/tests/rendered_output.rs @@ -15,7 +15,7 @@ use select::predicate::{Class, Name, Predicate}; use std::collections::HashMap; use std::ffi::OsStr; use std::fs; -use std::io::{Read, Write}; +use std::io::Write; use std::path::{Component, Path, PathBuf}; use std::str::FromStr; use tempfile::Builder as TempFileBuilder; @@ -476,23 +476,10 @@ fn first_chapter_is_copied_as_index_even_if_not_first_elem() { let md = MDBook::load_with_config(temp.path(), cfg).unwrap(); md.build().unwrap(); - // In theory, just reading the entire files into memory and comparing is sufficient for *testing*, - // but since the files are temporary and get deleted when the test completes, we'll want to print - // the differences on failure. - // We could invoke `diff` on the files on failure, but that may not be portable (hi, Windows...) - // so we'll do the job ourselves—potentially a bit sloppily, but that can always be piped into - // `diff` manually afterwards. - let book_path = temp.path().join("book"); - let read_file = |path: &str| { - let mut buf = String::new(); - fs::File::open(book_path.join(path)) - .with_context(|| format!("Failed to read {}", path)) - .unwrap() - .read_to_string(&mut buf) - .unwrap(); - buf - }; - pretty_assertions::assert_eq!(read_file("chapter_1.html"), read_file("index.html")); + let root = temp.path().join("book"); + let chapter = fs::read_to_string(root.join("chapter_1.html")).expect("read chapter 1"); + let index = fs::read_to_string(root.join("index.html")).expect("read index"); + pretty_assertions::assert_eq!(chapter, index); } #[test]