Compare commits

..

43 Commits

Author SHA1 Message Date
Eric Huss
40d91fff29 Merge pull request #2540 from ehuss/bump-version
Update to 0.4.44
2025-01-28 17:58:16 +00:00
Eric Huss
59eab7cfc2 Update to 0.4.44 2025-01-28 09:50:04 -08:00
Eric Huss
1b524ff356 Merge pull request #2539 from ehuss/update-notify
Update notify to 8.0.0
2025-01-28 17:41:05 +00:00
Eric Huss
9b873e9d97 Bump rust-version to 1.77 2025-01-28 09:35:11 -08:00
Eric Huss
b6d6cb2711 Update notify to 8.0.0 2025-01-28 09:32:17 -08:00
Eric Huss
c8095160d0 Merge pull request #2538 from ehuss/update-dependencies
Update dependencies
2025-01-28 17:19:41 +00:00
Eric Huss
ae6db3a87e Update dependencies
Updating anstyle-wincon v3.0.6 -> v3.0.7
Updating anyhow v1.0.93 -> v1.0.95
Updating bitflags v2.6.0 -> v2.8.0
Updating bstr v1.10.0 -> v1.11.3
Updating bytes v1.8.0 -> v1.9.0
Updating cc v1.1.36 -> v1.2.10
Updating chrono v0.4.38 -> v0.4.39
Updating clap v4.5.20 -> v4.5.27
Updating clap_builder v4.5.20 -> v4.5.27
Updating clap_complete v4.5.37 -> v4.5.43
Updating clap_lex v0.7.2 -> v0.7.4
Updating cpufeatures v0.2.14 -> v0.2.17
Updating crossbeam-channel v0.5.13 -> v0.5.14
Updating crossbeam-deque v0.8.5 -> v0.8.6
Updating crossbeam-utils v0.8.20 -> v0.8.21
  Adding darling v0.20.10
  Adding darling_core v0.20.10
  Adding darling_macro v0.20.10
Updating data-encoding v2.6.0 -> v2.7.0
  Adding derive_builder v0.20.2
  Adding derive_builder_core v0.20.2
  Adding derive_builder_macro v0.20.2
Updating env_filter v0.1.2 -> v0.1.3
Updating env_logger v0.11.5 -> v0.11.6
Updating errno v0.3.9 -> v0.3.10
Updating fastrand v2.1.1 -> v2.3.0
Updating float-cmp v0.9.0 -> v0.10.0
Updating handlebars v6.2.0 -> v6.3.0
Updating hashbrown v0.15.1 -> v0.15.2
Removing hermit-abi v0.3.9
Updating http v1.1.0 -> v1.2.0
Updating httparse v1.9.5 -> v1.10.0
Updating hyper v0.14.31 -> v0.14.32
  Adding ident_case v1.0.1
Updating indexmap v2.6.0 -> v2.7.1
Updating itoa v1.0.11 -> v1.0.14
Updating js-sys v0.3.72 -> v0.3.77
Updating libc v0.2.161 -> v0.2.169
Updating linux-raw-sys v0.4.14 -> v0.4.15
Updating litemap v0.7.3 -> v0.7.4
Updating log v0.4.22 -> v0.4.25
Updating miniz_oxide v0.8.0 -> v0.8.3
Updating mio v1.0.2 -> v1.0.3
Updating object v0.36.5 -> v0.36.7
Updating pathdiff v0.2.2 -> v0.2.3
Updating pest v2.7.14 -> v2.7.15
Updating pest_derive v2.7.14 -> v2.7.15
Updating pest_generator v2.7.14 -> v2.7.15
Updating pest_meta v2.7.14 -> v2.7.15
Updating phf v0.11.2 -> v0.11.3
Updating phf_codegen v0.11.2 -> v0.11.3
Updating phf_generator v0.11.2 -> v0.11.3
Updating phf_shared v0.11.2 -> v0.11.3
Updating pin-project v1.1.7 -> v1.1.8
Updating pin-project-internal v1.1.7 -> v1.1.8
Updating pin-project-lite v0.2.15 -> v0.2.16
Updating predicates v3.1.2 -> v3.1.3
Updating predicates-core v1.0.8 -> v1.0.9
Updating predicates-tree v1.0.11 -> v1.0.12
Updating proc-macro2 v1.0.89 -> v1.0.93
Updating quote v1.0.37 -> v1.0.38
Updating redox_syscall v0.5.7 -> v0.5.8
Updating regex-automata v0.4.8 -> v0.4.9
Updating rustix v0.38.39 -> v0.38.44
  Adding rustversion v1.0.19
Updating ryu v1.0.18 -> v1.0.19
Updating semver v1.0.23 -> v1.0.25
Updating serde v1.0.214 -> v1.0.217
Updating serde_derive v1.0.214 -> v1.0.217
Updating serde_json v1.0.132 -> v1.0.137
  Adding siphasher v1.0.1
Updating socket2 v0.5.7 -> v0.5.8
Updating syn v2.0.87 -> v2.0.96
Updating tempfile v3.13.0 -> v3.15.0
Updating terminal_size v0.4.0 -> v0.4.1
Updating termtree v0.4.1 -> v0.5.1
Removing thiserror v1.0.68
  Adding thiserror v1.0.69
  Adding thiserror v2.0.11
Removing thiserror-impl v1.0.68
  Adding thiserror-impl v1.0.69
  Adding thiserror-impl v2.0.11
Updating tokio v1.41.0 -> v1.43.0
Updating tokio-macros v2.4.0 -> v2.5.0
Updating tokio-util v0.7.12 -> v0.7.13
Updating tracing v0.1.40 -> v0.1.41
Updating tracing-core v0.1.32 -> v0.1.33
Updating unicase v2.8.0 -> v2.8.1
Updating unicode-ident v1.0.13 -> v1.0.16
Updating url v2.5.3 -> v2.5.4
Updating wasm-bindgen v0.2.95 -> v0.2.100
Updating wasm-bindgen-backend v0.2.95 -> v0.2.100
Updating wasm-bindgen-macro v0.2.95 -> v0.2.100
Updating wasm-bindgen-macro-support v0.2.95 -> v0.2.100
Updating wasm-bindgen-shared v0.2.95 -> v0.2.100
Updating yoke v0.7.4 -> v0.7.5
Updating yoke-derive v0.7.4 -> v0.7.5
Updating zerofrom v0.1.4 -> v0.1.5
Updating zerofrom-derive v0.1.4 -> v0.1.5
2025-01-28 09:11:17 -08:00
Eric Huss
18f57f5bd9 Merge pull request #2533 from ehuss/search-chapter-settings
Add output.html.search.chapter
2025-01-28 14:43:02 +00:00
Eric Huss
09a37284b0 Add output.html.search.chapter
This config setting provides the ability to disable search indexing on a
per-chapter (or sub-path) basis.

This is structured to possibly add additional settings, such as perhaps
a score multiplier or other settings.
2025-01-27 19:45:50 -08:00
Eric Huss
dff5ac64e5 Merge pull request #2458 from dcampbell24/display-for-clean
Display what is removed from mdbook clean.
2025-01-25 21:54:30 +00:00
Eric Huss
0ee565a5ff Merge pull request #2530 from max-heller/rust-hidelines
fix: make line hiding in Rust code blocks consistent with `rustdoc`
2025-01-25 21:50:47 +00:00
Eric Huss
9e4854f349 Merge pull request #2532 from notriddle/sync-toggle
Prevent the real sidebar position from becoming unsynced from the JS
2025-01-25 21:17:31 +00:00
Michael Howell
74d48f5ad2 Prevent the real sidebar position from becoming unsynced from the JS
This way, whatever behavior the browser might use for checkboxes
will apply to the CSS class, localStorage, and the visible state.
2025-01-23 10:18:21 -07:00
Eric Huss
0b51a74c16 Merge pull request #2531 from GuillaumeGomez/regression-test-2529
Add GUI regression test for #2529
2025-01-23 14:33:22 +00:00
Guillaume Gomez
ce63cc31f4 Add GUI regression test for #2529 2025-01-23 14:01:38 +01:00
Guillaume Gomez
d6720fc671 Update browser-ui-test version to 0.19.0 2025-01-23 13:58:35 +01:00
Eric Huss
629c2ad2fd Merge pull request #2529 from GuillaumeGomez/fix-sidebar-display
Fix display of sidebar when JS is disabled
2025-01-20 17:42:49 +00:00
Max Heller
d325e821cd fix: make line hiding in Rust code blocks consistent with rustdoc
Requires a space following a `#` for a line to be hidden.
2025-01-20 11:43:39 -05:00
Guillaume Gomez
ac3a7faa54 Fix display of sidebar when JS is disabled 2025-01-20 17:29:07 +01:00
Eric Huss
35ed24cd18 Merge pull request #2523 from marcoieni/ubuntu-22
ci: move ubuntu-20 jobs to ubuntu-22
2025-01-15 14:37:44 +00:00
MarcoIeni
81d42f1c6e ci: move ubuntu-20 jobs to ubuntu-22 2025-01-15 10:21:10 +01:00
Eric Huss
618a2fa78b Merge pull request #2476 from GuillaumeGomez/gui-tests
Add base for GUI tests
2025-01-06 22:46:26 +00:00
Eric Huss
0bf6751eed Merge pull request #2517 from notriddle/master
Ignore fragment when figuring out sidebar items
2025-01-02 20:25:15 +00:00
Michael Howell
f92eac4acd Ignore fragment when figuring out sidebar items 2025-01-02 10:34:03 -07:00
Guillaume Gomez
69ef52fd13 Disable sandbox when running GUI tests 2024-12-19 20:01:25 +01:00
Guillaume Gomez
cc8ce35b4d Run GUI tests as a separate testsuite 2024-12-18 11:25:11 +01:00
Guillaume Gomez
2a13ca2fbf Add base for GUI tests 2024-12-16 17:45:36 +01:00
Eric Huss
59e6afcaad Merge pull request #2500 from rukai/release_for_aarch64_macos
Add aarch64-apple-darwin release target
2024-12-02 14:51:39 +00:00
Lucas Kent
4d9a455a27 Add aarch64-apple-darwin release target 2024-12-02 11:43:57 +11:00
Eric Huss
74b2c79d46 Merge pull request #2497 from ehuss/bump-version
Update to 0.4.43
2024-11-25 17:21:18 +00:00
Eric Huss
ed407b091c Update to 0.4.43 2024-11-25 09:14:43 -08:00
Eric Huss
6c8020a3b9 Merge pull request #2495 from ehuss/stabilize-2024
Stabilize 2024 flag
2024-11-25 16:05:27 +00:00
Eric Huss
42f18d1e51 Stabilize 2024 flag
The 2024 edition is now stable on nightly, so the `-Z` flag is no longer necessary.
2024-11-23 15:25:29 -08:00
David Campbell
abf3e4ab50 Display what is removed from mdbook clean.
This is based off of [cargo's][1] clean command. cargo is licensed
under MIT or Apache-2.0.

[1]: https://github.com/rust-lang/cargo
2024-11-22 15:10:51 -05:00
Eric Huss
d1078434af Merge pull request #2486 from eureka-cpu/eureka-cpu/2485
fix `init --title` option failure when git user is not configured
2024-11-18 19:25:00 +00:00
eureka-cpu
8f024dabc3 fix init --title option failure when git user is not configured 2024-11-18 11:10:11 -08:00
Eric Huss
0c580c32c4 Add regression test for mdbook init title with no git config
Regression test for https://github.com/rust-lang/mdBook/issues/2485
2024-11-18 11:08:26 -08:00
Eric Huss
90960126e8 Merge pull request #2478 from rust-lang/ehuss-patch-1
Add note about updating `index.hbs`
2024-11-09 13:47:04 +00:00
Eric Huss
aa37f24fc1 Add note about updating index.hbs 2024-11-09 05:40:12 -08:00
Eric Huss
3f4f287e6e Merge pull request #2474 from ehuss/bump-version
Update to 0.4.42
2024-11-07 14:49:21 +00:00
Eric Huss
55fe75c716 Update to 0.4.42 2024-11-07 06:42:05 -08:00
Eric Huss
c6236ead67 Merge pull request #2473 from notriddle/notriddle/folding
Fix inadvertently broken folding behavior
2024-11-07 14:39:31 +00:00
Michael Howell
68e3572278 Fix inadvertently broken folding behavior 2024-11-06 15:47:12 -07:00
27 changed files with 998 additions and 443 deletions

View File

@@ -17,13 +17,15 @@ jobs:
matrix:
include:
- target: aarch64-unknown-linux-musl
os: ubuntu-20.04
os: ubuntu-22.04
- target: x86_64-unknown-linux-gnu
os: ubuntu-20.04
os: ubuntu-22.04
- target: x86_64-unknown-linux-musl
os: ubuntu-20.04
os: ubuntu-22.04
- target: x86_64-apple-darwin
os: macos-latest
- target: aarch64-apple-darwin
os: macos-latest
- target: x86_64-pc-windows-msvc
os: windows-latest
name: Deploy ${{ matrix.target }}

View File

@@ -3,6 +3,9 @@ on:
pull_request:
merge_group:
env:
BROWSER_UI_TEST_VERSION: '0.19.0'
jobs:
test:
runs-on: ${{ matrix.os }}
@@ -22,7 +25,7 @@ jobs:
rust: nightly
target: x86_64-unknown-linux-gnu
- name: stable x86_64-unknown-linux-musl
os: ubuntu-20.04
os: ubuntu-22.04
rust: stable
target: x86_64-unknown-linux-musl
- name: stable x86_64 macos
@@ -38,9 +41,9 @@ jobs:
rust: stable
target: x86_64-pc-windows-msvc
- name: msrv
os: ubuntu-20.04
os: ubuntu-22.04
# sync MSRV with docs: guide/src/guide/installation.md and Cargo.toml
rust: 1.74.0
rust: 1.77.0
target: x86_64-unknown-linux-gnu
name: ${{ matrix.name }}
steps:
@@ -53,7 +56,7 @@ jobs:
run: cargo test --no-default-features --target ${{ matrix.target }}
aarch64-cross-builds:
runs-on: ubuntu-20.04
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- name: Install Rust
@@ -70,6 +73,22 @@ jobs:
run: rustup update stable && rustup default stable && rustup component add rustfmt
- run: cargo fmt --check
gui:
name: GUI tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Rust
run: bash ci/install-rust.sh stable x86_64-unknown-linux-gnu
- name: Install npm
uses: actions/setup-node@v3
with:
node-version: 20
- name: Install browser-ui-test
run: npm install browser-ui-test@"${BROWSER_UI_TEST_VERSION}"
- name: Build and run tests (+ GUI)
run: cargo test --locked --target x86_64-unknown-linux-gnu --test gui
# The success job is here to consolidate the total success/failure state of
# all other jobs. This job is then included in the GitHub branch protection
# rule which prevents merges unless all other jobs are passing. This makes

5
.gitignore vendored
View File

@@ -16,3 +16,8 @@ test_book/book/
# Ignore Vim temporary and swap files.
*.sw?
*~
# GUI tests
node_modules
package-lock.json
package.json

View File

@@ -1,8 +1,62 @@
# Changelog
## mdBook 0.4.44
[v0.4.43...v0.4.44](https://github.com/rust-lang/mdBook/compare/v0.4.43...v0.4.44)
### Added
- Added pre-built aarch64-apple-darwin binaries to the releases.
[#2500](https://github.com/rust-lang/mdBook/pull/2500)
- `mdbook clean` now shows a summary of what it did.
[#2458](https://github.com/rust-lang/mdBook/pull/2458)
- Added the `output.html.search.chapter` config setting to disable search indexing of individual chapters.
[#2533](https://github.com/rust-lang/mdBook/pull/2533)
### Fixed
- Fixed auto-scrolling the side-bar when loading a page with a `#` fragment URL.
[#2517](https://github.com/rust-lang/mdBook/pull/2517)
- Fixed display of sidebar when javascript is disabled.
[#2529](https://github.com/rust-lang/mdBook/pull/2529)
- Fixed the sidebar visibility getting out of sync with the button.
[#2532](https://github.com/rust-lang/mdBook/pull/2532)
### Changed
- ❗ Rust code block hidden lines now follow the same logic as rustdoc. This requires a space after the `#` symbol.
[#2530](https://github.com/rust-lang/mdBook/pull/2530)
- ❗ Updated the Linux pre-built binaries which requires a newer version of glibc (2.34).
[#2523](https://github.com/rust-lang/mdBook/pull/2523)
- Updated dependencies
[#2538](https://github.com/rust-lang/mdBook/pull/2538)
[#2539](https://github.com/rust-lang/mdBook/pull/2539)
## mdBook 0.4.43
[v0.4.42...v0.4.43](https://github.com/rust-lang/mdBook/compare/v0.4.42...v0.4.43)
### Fixed
- Fixed setting the title in `mdbook init` when no git user is configured.
[#2486](https://github.com/rust-lang/mdBook/pull/2486)
### Changed
- The Rust 2024 edition no longer needs `-Zunstable-options`.
[#2495](https://github.com/rust-lang/mdBook/pull/2495)
## mdBook 0.4.42
[v0.4.41...v0.4.42](https://github.com/rust-lang/mdBook/compare/v0.4.41...v0.4.42)
### Fixed
- Fixed chapter list folding.
[#2473](https://github.com/rust-lang/mdBook/pull/2473)
## mdBook 0.4.41
[v0.4.40...v0.4.41](https://github.com/rust-lang/mdBook/compare/v0.4.40...v0.4.41)
**Note:** If you have a custom `index.hbs` theme file, you will need to update it to the latest version.
### Added
- Added preliminary support for Rust 2024 edition.

View File

@@ -138,8 +138,23 @@ We generally strive to keep mdBook compatible with a relatively recent browser o
That is, supporting Chrome, Safari, Firefox, Edge on Windows, macOS, Linux, iOS, and Android.
If possible, do your best to avoid breaking older browser releases.
Any change to the HTML or styling is encouraged to manually check on as many browsers and platforms that you can.
Unfortunately at this time we don't have any automated UI or browser testing, so your assistance in testing is appreciated.
GUI tests are checked with the GUI testsuite. To run it, you need to install `npm` first. Then run:
```
cargo test --test gui
```
The first time, it'll fail and ask you to install the `browser-ui-test` package. Install it then re-run the tests.
If you want to disable the headless mode, use the `DISABLE_HEADLESS_TEST=1` environment variable:
```
cargo test --test gui -- --disable-headless-test
```
The GUI tests are in the directory `tests/gui` in text files with the `.goml` extension. These tests are run
using a `node.js` framework called `browser-ui-test`. You can find documentation for this language on its
[repository](https://github.com/GuillaumeGomez/browser-UI-test/blob/master/goml-script.md).
## Updating highlight.js

679
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -3,7 +3,7 @@ members = [".", "examples/remove-emphasis/mdbook-remove-emphasis"]
[package]
name = "mdbook"
version = "0.4.41"
version = "0.4.44"
authors = [
"Mathieu David <mathieudavid@mathieudavid.org>",
"Michael-F-Bryan <michaelfbryan@gmail.com>",
@@ -17,7 +17,7 @@ license = "MPL-2.0"
readme = "README.md"
repository = "https://github.com/rust-lang/mdBook"
description = "Creates a book from markdown files"
rust-version = "1.74"
rust-version = "1.77" # Keep in sync with installation.md and .github/workflows/main.yml
[dependencies]
anyhow = "1.0.71"
@@ -40,8 +40,8 @@ toml = "0.5.11" # Do not update, see https://github.com/rust-lang/mdBook/issues/
topological-sort = "0.2.2"
# Watch feature
notify = { version = "6.1.1", optional = true }
notify-debouncer-mini = { version = "0.4.1", optional = true }
notify = { version = "8.0.0", optional = true }
notify-debouncer-mini = { version = "0.6.0", optional = true }
ignore = { version = "0.4.20", optional = true }
pathdiff = { version = "0.2.1", optional = true }
walkdir = { version = "2.3.3", optional = true }
@@ -82,3 +82,10 @@ name = "remove-emphasis"
path = "examples/remove-emphasis/test.rs"
crate-type = ["lib"]
test = true
[[test]]
harness = false
test = false
name = "gui"
path = "tests/gui/runner.rs"
crate-type = ["bin"]

View File

@@ -21,7 +21,7 @@ A simple approach would be to use the popular `curl` CLI tool to download the ex
```sh
mkdir bin
curl -sSL https://github.com/rust-lang/mdBook/releases/download/v0.4.41/mdbook-v0.4.41-x86_64-unknown-linux-gnu.tar.gz | tar -xz --directory=bin
curl -sSL https://github.com/rust-lang/mdBook/releases/download/v0.4.44/mdbook-v0.4.44-x86_64-unknown-linux-gnu.tar.gz | tar -xz --directory=bin
bin/mdbook build
```

View File

@@ -281,6 +281,20 @@ copy-js = true # include Javascript code for search
- **copy-js:** Copy JavaScript files for the search implementation to the output
directory. Defaults to `true`.
#### `[output.html.search.chapter]`
The [`output.html.search.chapter`] table provides the ability to modify search settings per chapter or directory. Each key is the path to the chapter source file or directory, and the value is a table of settings to apply to that path. This will merge recursively, with more specific paths taking precedence.
```toml
[output.html.search.chapter]
# Disables search indexing for all chapters in the `appendix` directory.
"appendix" = { enable = false }
# Enables search indexing for just this one appendix chapter.
"appendix/glossary.md" = { enable = true }
```
- **enable:** Enables or disables search indexing for the given chapters. Defaults to `true`. This does not override the overall `output.html.search.enable` setting; that must be `true` for any search functionality to be enabled. Be cautious when disabling indexing for chapters because that can potentially lead to user confusion when they search for terms and expect them to be found. This should only be used in exceptional circumstances where keeping the chapter in the index will cause issues with the quality of the search results.
### `[output.html.redirect]`
The `[output.html.redirect]` table provides a way to add redirects.

View File

@@ -4,7 +4,8 @@
There is a feature in mdBook that lets you hide code lines by prepending them with a specific prefix.
For the Rust language, you can use the `#` character as a prefix which will hide lines [like you would with Rustdoc][rustdoc-hide].
For the Rust language, you can prefix lines with `# ` (`#` followed by a space) to hide them [like you would with Rustdoc][rustdoc-hide].
This prefix can be escaped with `##` to prevent the hiding of a line that should begin with the literal string `# ` (see [Rustdoc's docs][rustdoc-hide] for more details)
[rustdoc-hide]: https://doc.rust-lang.org/stable/rustdoc/write-documentation/documentation-tests.html#hiding-portions-of-the-example

View File

@@ -20,7 +20,7 @@ To make it easier to run, put the path to the binary into your `PATH`.
To build the `mdbook` executable from source, you will first need to install Rust and Cargo.
Follow the instructions on the [Rust installation page].
mdBook currently requires at least Rust version 1.74.
mdBook currently requires at least Rust version 1.77.
Once you have installed Rust, the following command can be used to build and install mdBook:

View File

@@ -346,7 +346,7 @@ impl MDBook {
cmd.args(["--edition", "2021"]);
}
RustEdition::E2024 => {
cmd.args(["--edition", "2024", "-Zunstable-options"]);
cmd.args(["--edition", "2024"]);
}
}
}

View File

@@ -2,8 +2,9 @@ use super::command_prelude::*;
use crate::get_book_dir;
use anyhow::Context;
use mdbook::MDBook;
use std::fs;
use std::mem::take;
use std::path::PathBuf;
use std::{fmt, fs};
// Create clap subcommand arguments
pub fn make_subcommand() -> Command {
@@ -23,10 +24,88 @@ pub fn execute(args: &ArgMatches) -> mdbook::errors::Result<()> {
None => book.root.join(&book.config.build.build_dir),
};
if dir_to_remove.exists() {
fs::remove_dir_all(&dir_to_remove)
.with_context(|| "Unable to remove the build directory")?;
}
let removed = Clean::new(&dir_to_remove)?;
println!("{removed}");
Ok(())
}
/// Formats a number of bytes into a human readable SI-prefixed size.
/// Returns a tuple of `(quantity, units)`.
pub fn human_readable_bytes(bytes: u64) -> (f32, &'static str) {
static UNITS: [&str; 7] = ["B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB"];
let bytes = bytes as f32;
let i = ((bytes.log2() / 10.0) as usize).min(UNITS.len() - 1);
(bytes / 1024_f32.powi(i as i32), UNITS[i])
}
#[derive(Debug)]
pub struct Clean {
num_files_removed: u64,
num_dirs_removed: u64,
total_bytes_removed: u64,
}
impl Clean {
fn new(dir: &PathBuf) -> mdbook::errors::Result<Clean> {
let mut files = vec![dir.clone()];
let mut children = Vec::new();
let mut num_files_removed = 0;
let mut num_dirs_removed = 0;
let mut total_bytes_removed = 0;
if dir.exists() {
while !files.is_empty() {
for file in files {
if let Ok(meta) = file.metadata() {
// Note: This can over-count bytes removed for hard-linked
// files. It also under-counts since it only counts the exact
// byte sizes and not the block sizes.
total_bytes_removed += meta.len();
}
if file.is_file() {
num_files_removed += 1;
} else if file.is_dir() {
num_dirs_removed += 1;
for entry in fs::read_dir(file)? {
children.push(entry?.path());
}
}
}
files = take(&mut children);
}
fs::remove_dir_all(&dir).with_context(|| "Unable to remove the build directory")?;
}
Ok(Clean {
num_files_removed,
num_dirs_removed,
total_bytes_removed,
})
}
}
impl fmt::Display for Clean {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Removed ")?;
match (self.num_files_removed, self.num_dirs_removed) {
(0, 0) => write!(f, "0 files")?,
(0, 1) => write!(f, "1 directory")?,
(0, 2..) => write!(f, "{} directories", self.num_dirs_removed)?,
(1, _) => write!(f, "1 file")?,
(2.., _) => write!(f, "{} files", self.num_files_removed)?,
}
if self.total_bytes_removed == 0 {
Ok(())
} else {
// Don't show a fractional number of bytes.
if self.total_bytes_removed < 1024 {
write!(f, ", {}B total", self.total_bytes_removed)
} else {
let (bytes, unit) = human_readable_bytes(self.total_bytes_removed);
write!(f, ", {bytes:.2}{unit} total")
}
}
}
}

View File

@@ -74,9 +74,9 @@ pub fn execute(args: &ArgMatches) -> Result<()> {
if let Some(author) = get_author_name() {
debug!("Obtained user name from gitconfig: {:?}", author);
config.book.authors.push(author);
builder.with_config(config);
}
builder.with_config(config);
builder.build()?;
println!("\nAll done, no errors...");

View File

@@ -735,6 +735,11 @@ pub struct Search {
/// Copy JavaScript files for the search functionality to the output directory?
/// Default: `true`.
pub copy_js: bool,
/// Specifies search settings for the given path.
///
/// The path can be for a specific chapter, or a directory. This will
/// merge recursively, with more specific paths taking precedence.
pub chapter: HashMap<String, SearchChapterSettings>,
}
impl Default for Search {
@@ -751,10 +756,19 @@ impl Default for Search {
expand: true,
heading_split_level: 3,
copy_js: true,
chapter: HashMap::new(),
}
}
}
/// Search options for chapters (or paths).
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
#[serde(default, rename_all = "kebab-case")]
pub struct SearchChapterSettings {
/// Whether or not indexing is enabled, default `true`.
pub enable: Option<bool>,
}
/// Allows you to "update" any arbitrary field in a struct by round-tripping via
/// a `toml::Value`.
///

View File

@@ -931,7 +931,7 @@ fn add_playground_pre(
// we need to inject our own main
let (attrs, code) = partition_source(code);
format!("# #![allow(unused)]\n{attrs}#fn main() {{\n{code}#}}").into()
format!("# #![allow(unused)]\n{attrs}# fn main() {{\n{code}# }}").into()
};
content
}
@@ -1003,12 +1003,9 @@ fn hide_lines_rust(content: &str) -> String {
result += &caps[3];
result += newline;
continue;
} else if &caps[2] != "!" && &caps[2] != "[" {
} else if matches!(&caps[2], "" | " ") {
result += "<span class=\"boring\">";
result += &caps[1];
if &caps[2] != " " {
result += &caps[2];
}
result += &caps[3];
result += newline;
result += "</span>";
@@ -1134,7 +1131,7 @@ mod tests {
fn add_playground() {
let inputs = [
("<code class=\"language-rust\">x()</code>",
"<pre class=\"playground\"><code class=\"language-rust\"># #![allow(unused)]\n#fn main() {\nx()\n#}</code></pre>"),
"<pre class=\"playground\"><code class=\"language-rust\"># #![allow(unused)]\n# fn main() {\nx()\n# }</code></pre>"),
("<code class=\"language-rust\">fn main() {}</code>",
"<pre class=\"playground\"><code class=\"language-rust\">fn main() {}</code></pre>"),
("<code class=\"language-rust editable\">let s = \"foo\n # bar\n\";</code>",
@@ -1164,7 +1161,7 @@ mod tests {
fn add_playground_edition2015() {
let inputs = [
("<code class=\"language-rust\">x()</code>",
"<pre class=\"playground\"><code class=\"language-rust edition2015\"># #![allow(unused)]\n#fn main() {\nx()\n#}</code></pre>"),
"<pre class=\"playground\"><code class=\"language-rust edition2015\"># #![allow(unused)]\n# fn main() {\nx()\n# }</code></pre>"),
("<code class=\"language-rust\">fn main() {}</code>",
"<pre class=\"playground\"><code class=\"language-rust edition2015\">fn main() {}</code></pre>"),
("<code class=\"language-rust edition2015\">fn main() {}</code>",
@@ -1188,7 +1185,7 @@ mod tests {
fn add_playground_edition2018() {
let inputs = [
("<code class=\"language-rust\">x()</code>",
"<pre class=\"playground\"><code class=\"language-rust edition2018\"># #![allow(unused)]\n#fn main() {\nx()\n#}</code></pre>"),
"<pre class=\"playground\"><code class=\"language-rust edition2018\"># #![allow(unused)]\n# fn main() {\nx()\n# }</code></pre>"),
("<code class=\"language-rust\">fn main() {}</code>",
"<pre class=\"playground\"><code class=\"language-rust edition2018\">fn main() {}</code></pre>"),
("<code class=\"language-rust edition2015\">fn main() {}</code>",
@@ -1212,7 +1209,7 @@ mod tests {
fn add_playground_edition2021() {
let inputs = [
("<code class=\"language-rust\">x()</code>",
"<pre class=\"playground\"><code class=\"language-rust edition2021\"># #![allow(unused)]\n#fn main() {\nx()\n#}</code></pre>"),
"<pre class=\"playground\"><code class=\"language-rust edition2021\"># #![allow(unused)]\n# fn main() {\nx()\n# }</code></pre>"),
("<code class=\"language-rust\">fn main() {}</code>",
"<pre class=\"playground\"><code class=\"language-rust edition2021\">fn main() {}</code></pre>"),
("<code class=\"language-rust edition2015\">fn main() {}</code>",
@@ -1237,8 +1234,12 @@ mod tests {
fn hide_lines_language_rust() {
let inputs = [
(
"<pre class=\"playground\"><code class=\"language-rust\">\n# #![allow(unused)]\n#fn main() {\nx()\n#}</code></pre>",
"<pre class=\"playground\"><code class=\"language-rust\">\n# #![allow(unused)]\n# fn main() {\nx()\n# }</code></pre>",
"<pre class=\"playground\"><code class=\"language-rust\">\n<span class=\"boring\">#![allow(unused)]\n</span><span class=\"boring\">fn main() {\n</span>x()\n<span class=\"boring\">}</span></code></pre>",),
// # must be followed by a space for a line to be hidden
(
"<pre class=\"playground\"><code class=\"language-rust\">\n#fn main() {\nx()\n#}</code></pre>",
"<pre class=\"playground\"><code class=\"language-rust\">\n#fn main() {\nx()\n#}</code></pre>",),
(
"<pre class=\"playground\"><code class=\"language-rust\">fn main() {}</code></pre>",
"<pre class=\"playground\"><code class=\"language-rust\">fn main() {}</code></pre>",),

View File

@@ -1,13 +1,13 @@
use std::borrow::Cow;
use std::collections::{HashMap, HashSet};
use std::path::Path;
use std::path::{Path, PathBuf};
use elasticlunr::{Index, IndexBuilder};
use once_cell::sync::Lazy;
use pulldown_cmark::*;
use crate::book::{Book, BookItem};
use crate::config::Search;
use crate::book::{Book, BookItem, Chapter};
use crate::config::{Search, SearchChapterSettings};
use crate::errors::*;
use crate::theme::searcher;
use crate::utils;
@@ -35,8 +35,20 @@ pub fn create_files(search_config: &Search, destination: &Path, book: &Book) ->
let mut doc_urls = Vec::with_capacity(book.sections.len());
let chapter_configs = sort_search_config(&search_config.chapter);
validate_chapter_config(&chapter_configs, book)?;
for item in book.iter() {
render_item(&mut index, search_config, &mut doc_urls, item)?;
let chapter = match item {
BookItem::Chapter(ch) if !ch.is_draft_chapter() => ch,
_ => continue,
};
let chapter_settings =
get_chapter_settings(&chapter_configs, chapter.source_path.as_ref().unwrap());
if !chapter_settings.enable.unwrap_or(true) {
continue;
}
render_item(&mut index, search_config, &mut doc_urls, chapter)?;
}
let index = write_to_json(index, search_config, doc_urls)?;
@@ -100,13 +112,8 @@ fn render_item(
index: &mut Index,
search_config: &Search,
doc_urls: &mut Vec<String>,
item: &BookItem,
chapter: &Chapter,
) -> Result<()> {
let chapter = match *item {
BookItem::Chapter(ref ch) if !ch.is_draft_chapter() => ch,
_ => return Ok(()),
};
let chapter_path = chapter
.path
.as_ref()
@@ -313,3 +320,81 @@ fn clean_html(html: &str) -> String {
});
AMMONIA.clean(html).to_string()
}
fn validate_chapter_config(
chapter_configs: &[(PathBuf, SearchChapterSettings)],
book: &Book,
) -> Result<()> {
for (path, _) in chapter_configs {
let found = book
.iter()
.filter_map(|item| match item {
BookItem::Chapter(ch) if !ch.is_draft_chapter() => Some(ch),
_ => None,
})
.any(|chapter| {
let ch_path = chapter.source_path.as_ref().unwrap();
ch_path.starts_with(path)
});
if !found {
bail!(
"[output.html.search.chapter] key `{}` does not match any chapter paths",
path.display()
);
}
}
Ok(())
}
fn sort_search_config(
map: &HashMap<String, SearchChapterSettings>,
) -> Vec<(PathBuf, SearchChapterSettings)> {
let mut settings: Vec<_> = map
.iter()
.map(|(key, value)| (PathBuf::from(key), value.clone()))
.collect();
// Note: This is case-sensitive, and assumes the author uses the same case
// as the actual filename.
settings.sort_by(|a, b| a.0.cmp(&b.0));
settings
}
fn get_chapter_settings(
chapter_configs: &[(PathBuf, SearchChapterSettings)],
source_path: &Path,
) -> SearchChapterSettings {
let mut result = SearchChapterSettings::default();
for (path, config) in chapter_configs {
if source_path.starts_with(path) {
result.enable = config.enable.or(result.enable);
}
}
result
}
#[test]
fn chapter_settings_priority() {
let cfg = r#"
[output.html.search.chapter]
"cli/watch.md" = { enable = true }
"cli" = { enable = false }
"cli/inner/foo.md" = { enable = false }
"cli/inner" = { enable = true }
"foo" = {} # Just to make sure empty table is allowed.
"#;
let cfg: crate::Config = toml::from_str(cfg).unwrap();
let html = cfg.html_config().unwrap();
let chapter_configs = sort_search_config(&html.search.unwrap().chapter);
for (path, enable) in [
("foo.md", None),
("cli/watch.md", Some(true)),
("cli/index.md", Some(false)),
("cli/inner/index.md", Some(true)),
("cli/inner/foo.md", Some(false)),
] {
assert_eq!(
get_chapter_settings(&chapter_configs, Path::new(path)),
SearchChapterSettings { enable }
);
}
}

View File

@@ -449,6 +449,7 @@ function playground_text(playground, hidden = true) {
var sidebar = document.getElementById("sidebar");
var sidebarLinks = document.querySelectorAll('#sidebar a');
var sidebarToggleButton = document.getElementById("sidebar-toggle");
var sidebarToggleAnchor = document.getElementById("sidebar-toggle-anchor");
var sidebarResizeHandle = document.getElementById("sidebar-resize-handle");
var firstContact = null;
@@ -463,17 +464,6 @@ function playground_text(playground, hidden = true) {
try { localStorage.setItem('mdbook-sidebar', 'visible'); } catch (e) { }
}
var sidebarAnchorToggles = document.querySelectorAll('#sidebar a.toggle');
function toggleSection(ev) {
ev.currentTarget.parentElement.classList.toggle('expanded');
}
Array.from(sidebarAnchorToggles).forEach(function (el) {
el.addEventListener('click', toggleSection);
});
function hideSidebar() {
body.classList.remove('sidebar-visible')
body.classList.add('sidebar-hidden');
@@ -486,22 +476,16 @@ function playground_text(playground, hidden = true) {
}
// Toggle sidebar
sidebarToggleButton.addEventListener('click', function sidebarToggle() {
if (body.classList.contains("sidebar-hidden")) {
sidebarToggleAnchor.addEventListener('change', function sidebarToggle() {
if (sidebarToggleAnchor.checked) {
var current_width = parseInt(
document.documentElement.style.getPropertyValue('--sidebar-width'), 10);
if (current_width < 150) {
document.documentElement.style.setProperty('--sidebar-width', '150px');
}
showSidebar();
} else if (body.classList.contains("sidebar-visible")) {
hideSidebar();
} else {
if (getComputedStyle(sidebar)['transform'] === 'none') {
hideSidebar();
} else {
showSidebar();
}
hideSidebar();
}
});

View File

@@ -421,11 +421,14 @@ ul#searchresults span.teaser em {
color: var(--sidebar-fg);
}
.sidebar-iframe-inner {
--padding: 10px;
background-color: var(--sidebar-bg);
color: var(--sidebar-fg);
padding: 10px 10px;
padding: var(--padding);
margin: 0;
font-size: 1.4rem;
color: var(--sidebar-fg);
min-height: calc(100vh - var(--padding) * 2);
}
.sidebar-iframe-outer {
border: none;

View File

@@ -9,7 +9,7 @@
--content-max-width: 750px;
--menu-bar-height: 50px;
--mono-font: "Source Code Pro", Consolas, "Ubuntu Mono", Menlo, "DejaVu Sans Mono", monospace, monospace;
--code-font-size: 0.875em /* please adjust the ace font size accordingly in editor.js */
--code-font-size: 0.875em; /* please adjust the ace font size accordingly in editor.js */
}
/* Themes */

View File

@@ -52,15 +52,17 @@
<!-- MathJax -->
<script async src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
{{/if}}
</head>
<body>
<div id="body-container">
<!-- Provide site root to javascript -->
<script>
var path_to_root = "{{ path_to_root }}";
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "{{ preferred_dark_theme }}" : "{{ default_theme }}";
</script>
<!-- Start loading toc.js asap -->
<script src="{{ path_to_root }}toc.js"></script>
</head>
<body>
<div id="body-container">
<!-- Work around some values being stored in localStorage wrapped in quotes -->
<script>
try {
@@ -107,7 +109,7 @@
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
<!-- populated by js -->
<div class="sidebar-scrollbox"></div>
<mdbook-sidebar-scrollbox class="sidebar-scrollbox"></mdbook-sidebar-scrollbox>
<noscript>
<iframe class="sidebar-iframe-outer" src="{{ path_to_root }}toc.html"></iframe>
</noscript>
@@ -116,8 +118,6 @@
</div>
</nav>
<script async src="{{ path_to_root }}toc.js"></script>
<div id="page-wrapper" class="page-wrapper">
<div class="page">

View File

@@ -3,52 +3,68 @@
// This is a script, and not included directly in the page, to control the total size of the book.
// The TOC contains an entry for each page, so if each page includes a copy of the TOC,
// the total size of the page becomes O(n**2).
var sidebarScrollbox = document.querySelector("#sidebar .sidebar-scrollbox");
sidebarScrollbox.innerHTML = '{{#toc}}{{/toc}}';
(function() {
let current_page = document.location.href.toString();
if (current_page.endsWith("/")) {
current_page += "index.html";
class MDBookSidebarScrollbox extends HTMLElement {
constructor() {
super();
}
var links = sidebarScrollbox.querySelectorAll("a");
var l = links.length;
for (var i = 0; i < l; ++i) {
var link = links[i];
var href = link.getAttribute("href");
if (href && !href.startsWith("#") && !/^(?:[a-z+]+:)?\/\//.test(href)) {
link.href = path_to_root + href;
connectedCallback() {
this.innerHTML = '{{#toc}}{{/toc}}';
// Set the current, active page, and reveal it if it's hidden
let current_page = document.location.href.toString().split("#")[0];
if (current_page.endsWith("/")) {
current_page += "index.html";
}
// The "index" page is supposed to alias the first chapter in the book.
if (link.href === current_page || (i === 0 && path_to_root === "" && current_page.endsWith("/index.html"))) {
link.classList.add("active");
var parent = link.parentElement;
while (parent) {
if (parent.tagName === "LI" && parent.previousElementSibling) {
if (parent.previousElementSibling.classList.contains("chapter-item")) {
parent.previousElementSibling.classList.add("expanded");
}
var links = Array.prototype.slice.call(this.querySelectorAll("a"));
var l = links.length;
for (var i = 0; i < l; ++i) {
var link = links[i];
var href = link.getAttribute("href");
if (href && !href.startsWith("#") && !/^(?:[a-z+]+:)?\/\//.test(href)) {
link.href = path_to_root + href;
}
// The "index" page is supposed to alias the first chapter in the book.
if (link.href === current_page || (i === 0 && path_to_root === "" && current_page.endsWith("/index.html"))) {
link.classList.add("active");
var parent = link.parentElement;
if (parent && parent.classList.contains("chapter-item")) {
parent.classList.add("expanded");
}
while (parent) {
if (parent.tagName === "LI" && parent.previousElementSibling) {
if (parent.previousElementSibling.classList.contains("chapter-item")) {
parent.previousElementSibling.classList.add("expanded");
}
}
parent = parent.parentElement;
}
parent = parent.parentElement;
}
}
}
})();
// Track and set sidebar scroll position
sidebarScrollbox.addEventListener('click', function(e) {
if (e.target.tagName === 'A') {
sessionStorage.setItem('sidebar-scroll', sidebarScrollbox.scrollTop);
}
}, { passive: true });
var sidebarScrollTop = sessionStorage.getItem('sidebar-scroll');
sessionStorage.removeItem('sidebar-scroll');
if (sidebarScrollTop) {
// preserve sidebar scroll position when navigating via links within sidebar
sidebarScrollbox.scrollTop = sidebarScrollTop;
} else {
// scroll sidebar to current active section when navigating via "next/previous chapter" buttons
var activeSection = document.querySelector('#sidebar .active');
if (activeSection) {
activeSection.scrollIntoView({ block: 'center' });
// Track and set sidebar scroll position
this.addEventListener('click', function(e) {
if (e.target.tagName === 'A') {
sessionStorage.setItem('sidebar-scroll', this.scrollTop);
}
}, { passive: true });
var sidebarScrollTop = sessionStorage.getItem('sidebar-scroll');
sessionStorage.removeItem('sidebar-scroll');
if (sidebarScrollTop) {
// preserve sidebar scroll position when navigating via links within sidebar
this.scrollTop = sidebarScrollTop;
} else {
// scroll sidebar to current active section when navigating via "next/previous chapter" buttons
var activeSection = document.querySelector('#sidebar .active');
if (activeSection) {
activeSection.scrollIntoView({ block: 'center' });
}
}
// Toggle buttons
var sidebarAnchorToggles = document.querySelectorAll('#sidebar a.toggle');
function toggleSection(ev) {
ev.currentTarget.parentElement.classList.toggle('expanded');
}
Array.from(sidebarAnchorToggles).forEach(function (el) {
el.addEventListener('click', toggleSection);
});
}
}
window.customElements.define("mdbook-sidebar-scrollbox", MDBookSidebarScrollbox);

View File

@@ -22,3 +22,26 @@ fn base_mdbook_init_can_skip_confirmation_prompts() {
assert!(!temp.path().join(".gitignore").exists());
}
/// Run `mdbook init` with `--title` without git config.
///
/// Regression test for https://github.com/rust-lang/mdBook/issues/2485
#[test]
fn no_git_config_with_title() {
let temp = DummyBook::new().build().unwrap();
// doesn't exist before
assert!(!temp.path().join("book").exists());
let mut cmd = mdbook_cmd();
cmd.args(["init", "--title", "Example title"])
.current_dir(temp.path())
.env("GIT_CONFIG_GLOBAL", "")
.env("GIT_CONFIG_NOSYSTEM", "1");
cmd.assert()
.success()
.stdout(predicates::str::contains("\nAll done, no errors...\n"));
let config = Config::from_disk(temp.path().join("book.toml")).unwrap();
assert_eq!(config.book.title.as_deref(), Some("Example title"));
}

87
tests/gui/runner.rs Normal file
View File

@@ -0,0 +1,87 @@
use std::env::current_dir;
use std::fs::{read_to_string, remove_dir_all};
use std::process::Command;
fn get_available_browser_ui_test_version_inner(global: bool) -> Option<String> {
let mut command = Command::new("npm");
command
.arg("list")
.arg("--parseable")
.arg("--long")
.arg("--depth=0");
if global {
command.arg("--global");
}
let stdout = command.output().expect("`npm` command not found").stdout;
let lines = String::from_utf8_lossy(&stdout);
lines
.lines()
.find_map(|l| l.split(':').nth(1)?.strip_prefix("browser-ui-test@"))
.map(std::borrow::ToOwned::to_owned)
}
fn get_available_browser_ui_test_version() -> Option<String> {
get_available_browser_ui_test_version_inner(false)
.or_else(|| get_available_browser_ui_test_version_inner(true))
}
fn expected_browser_ui_test_version() -> String {
let content = read_to_string(".github/workflows/main.yml")
.expect("failed to read `.github/workflows/main.yml`");
for line in content.lines() {
let line = line.trim();
if let Some(version) = line.strip_prefix("BROWSER_UI_TEST_VERSION:") {
return version.trim().replace('\'', "");
}
}
panic!("failed to retrieved `browser-ui-test` version");
}
fn main() {
let browser_ui_test_version = expected_browser_ui_test_version();
match get_available_browser_ui_test_version() {
Some(version) => {
if version != browser_ui_test_version {
eprintln!(
"⚠️ Installed version of browser-ui-test (`{version}`) is different than the \
one used in the CI (`{browser_ui_test_version}`) You can install this version \
using `npm update browser-ui-test` or by using `npm install browser-ui-test\
@{browser_ui_test_version}`",
);
}
}
None => {
panic!(
"`browser-ui-test` is not installed. You can install this package using `npm \
update browser-ui-test` or by using `npm install browser-ui-test\
@{browser_ui_test_version}`",
);
}
}
let current_dir = current_dir().expect("failed to retrieve current directory");
let test_book = current_dir.join("test_book");
// Result doesn't matter.
let _ = remove_dir_all(test_book.join("book"));
let mut cmd = Command::new("cargo");
cmd.arg("run").arg("build").arg(&test_book);
// Then we run the GUI tests on it.
assert!(cmd.status().is_ok_and(|status| status.success()));
let book_dir = format!("file://{}", current_dir.join("test_book/book/").display());
let mut command = Command::new("npx");
command
.arg("browser-ui-test")
.args(["--variable", "DOC_PATH", book_dir.as_str()])
.args(["--test-folder", "tests/gui"]);
if std::env::args().any(|arg| arg == "--disable-headless-test") {
command.arg("--no-headless");
}
// Then we run the GUI tests on it.
let status = command.status().expect("failed to get command output");
assert!(status.success(), "{status:?}");
}

View File

@@ -0,0 +1,16 @@
// This GUI test checks that the sidebar takes the whole height when it's inside
// an iframe (because of JS disabled).
// Regression test for <https://github.com/rust-lang/mdBook/issues/2528>.
// We disable the requests checks because `searchindex.json` will always fail
// locally.
fail-on-request-error: false
// We disable javascript
javascript: false
go-to: |DOC_PATH| + "index.html"
store-value: (height, 1000)
set-window-size: (1000, |height|)
within-iframe: (".sidebar-iframe-outer", block {
assert-size: (" body", {"height": |height|})
})

59
tests/gui/sidebar.goml Normal file
View File

@@ -0,0 +1,59 @@
// This GUI test checks sidebar hide/show and also its behaviour on smaller
// width.
// We disable the requests checks because `searchindex.json` will always fail
// locally.
fail-on-request-error: false
go-to: |DOC_PATH| + "index.html"
set-window-size: (1100, 600)
// Need to reload for the new size to be taken account by the JS.
reload:
store-value: (content_indent, 308)
define-function: (
"hide-sidebar",
[],
block {
// The content should be "moved" to the right because of the sidebar.
assert-css: ("#sidebar", {"transform": "none"})
assert-position: ("#page-wrapper", {"x": |content_indent|})
// We now hide the sidebar.
click: "#sidebar-toggle"
wait-for: "body.sidebar-hidden"
// `transform` is 0.3s so we need to wait a bit (0.5s) to ensure the animation is done.
wait-for: 5000
assert-css-false: ("#sidebar", {"transform": "none"})
// The page content should now be on the left.
assert-position: ("#page-wrapper", {"x": 0})
},
)
define-function: (
"show-sidebar",
[],
block {
// The page content should be on the left and the sidebar "moved out".
assert-css: ("#sidebar", {"transform": "matrix(1, 0, 0, 1, -308, 0)"})
assert-position: ("#page-wrapper", {"x": 0})
// We expand the sidebar.
click: "#sidebar-toggle"
wait-for: "body.sidebar-visible"
// `transform` is 0.3s so we need to wait a bit (0.5s) to ensure the animation is done.
wait-for: 5000
assert-css-false: ("#sidebar", {"transform": "matrix(1, 0, 0, 1, -308, 0)"})
// The page content should be moved to the right.
assert-position: ("#page-wrapper", {"x": |content_indent|})
},
)
call-function: ("hide-sidebar", {})
call-function: ("show-sidebar", {})
// We now test on smaller width to ensure that the sidebar is collapsed by default.
set-window-size: (900, 600)
reload:
call-function: ("show-sidebar", {})
call-function: ("hide-sidebar", {})

View File

@@ -243,7 +243,7 @@ fn toc_js_html() -> Result<Document> {
let toc_path = temp.path().join("book").join("toc.js");
let html = fs::read_to_string(toc_path).with_context(|| "Unable to read index.html")?;
for line in html.lines() {
if let Some(left) = line.strip_prefix("sidebarScrollbox.innerHTML = '") {
if let Some(left) = line.strip_prefix(" this.innerHTML = '") {
if let Some(html) = left.strip_suffix("';") {
return Ok(Document::from(html));
}
@@ -736,6 +736,7 @@ fn failure_on_missing_theme_directory() {
#[cfg(feature = "search")]
mod search {
use crate::dummy_book::DummyBook;
use mdbook::utils::fs::write_file;
use mdbook::MDBook;
use std::fs::{self, File};
use std::path::Path;
@@ -810,6 +811,51 @@ mod search {
);
}
#[test]
fn can_disable_individual_chapters() {
let temp = DummyBook::new().build().unwrap();
let book_toml = r#"
[book]
title = "Search Test"
[output.html.search.chapter]
"second" = { enable = false }
"first/unicode.md" = { enable = false }
"#;
write_file(temp.path(), "book.toml", book_toml.as_bytes()).unwrap();
let md = MDBook::load(temp.path()).unwrap();
md.build().unwrap();
let index = read_book_index(temp.path());
let doc_urls = index["doc_urls"].as_array().unwrap();
let contains = |path| {
doc_urls
.iter()
.any(|p| p.as_str().unwrap().starts_with(path))
};
assert!(contains("second.html"));
assert!(!contains("second/"));
assert!(!contains("first/unicode.html"));
assert!(contains("first/markdown.html"));
}
#[test]
fn chapter_settings_validation_error() {
let temp = DummyBook::new().build().unwrap();
let book_toml = r#"
[book]
title = "Search Test"
[output.html.search.chapter]
"does-not-exist" = { enable = false }
"#;
write_file(temp.path(), "book.toml", book_toml.as_bytes()).unwrap();
let md = MDBook::load(temp.path()).unwrap();
let err = md.build().unwrap_err();
assert!(format!("{err:?}").contains(
"[output.html.search.chapter] key `does-not-exist` does not match any chapter paths"
));
}
// Setting this to `true` may cause issues with `cargo watch`,
// since it may not finish writing the fixture before the tests
// are run again.