From 64d98f8ee037282c35007b64c2649055c56af1db Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 17 Apr 2024 14:19:03 +0200 Subject: Merging upstream version 1.68.2+dfsg1. Signed-off-by: Daniel Baumann --- vendor/mdbook/src/book/book.rs | 2 +- vendor/mdbook/src/book/init.rs | 1 + vendor/mdbook/src/book/mod.rs | 28 +++++++- vendor/mdbook/src/book/summary.rs | 3 +- vendor/mdbook/src/cmd/build.rs | 30 +++----- vendor/mdbook/src/cmd/clean.rs | 26 ++----- vendor/mdbook/src/cmd/command_prelude.rs | 45 ++++++++++++ vendor/mdbook/src/cmd/init.rs | 44 +++++------- vendor/mdbook/src/cmd/mod.rs | 1 + vendor/mdbook/src/cmd/serve.rs | 47 +++++-------- vendor/mdbook/src/cmd/test.rs | 66 +++++++++--------- vendor/mdbook/src/cmd/watch.rs | 81 ++++++++++++---------- vendor/mdbook/src/config.rs | 16 +++-- vendor/mdbook/src/lib.rs | 11 --- vendor/mdbook/src/main.rs | 42 +++++------ vendor/mdbook/src/preprocess/cmd.rs | 1 + vendor/mdbook/src/preprocess/index.rs | 10 +-- vendor/mdbook/src/preprocess/links.rs | 25 ++++--- vendor/mdbook/src/preprocess/mod.rs | 3 +- .../src/renderer/html_handlebars/hbs_renderer.rs | 76 ++++++++++---------- .../renderer/html_handlebars/helpers/navigation.rs | 17 +++-- .../src/renderer/html_handlebars/helpers/theme.rs | 1 + .../src/renderer/html_handlebars/helpers/toc.rs | 56 +++++++-------- .../mdbook/src/renderer/html_handlebars/search.rs | 33 +++++---- vendor/mdbook/src/renderer/markdown_renderer.rs | 2 +- vendor/mdbook/src/renderer/mod.rs | 1 + vendor/mdbook/src/theme/book.js | 17 +++-- vendor/mdbook/src/theme/css/chrome.css | 14 ++-- vendor/mdbook/src/theme/css/general.css | 16 ++++- vendor/mdbook/src/theme/css/variables.css | 2 + vendor/mdbook/src/theme/index.hbs | 59 ++++++++-------- vendor/mdbook/src/theme/mod.rs | 2 +- vendor/mdbook/src/theme/redirect.hbs | 4 +- vendor/mdbook/src/utils/fs.rs | 1 + vendor/mdbook/src/utils/mod.rs | 26 +++---- vendor/mdbook/src/utils/string.rs | 9 +-- 36 files changed, 436 insertions(+), 382 deletions(-) create mode 100644 vendor/mdbook/src/cmd/command_prelude.rs (limited to 'vendor/mdbook/src') diff --git a/vendor/mdbook/src/book/book.rs b/vendor/mdbook/src/book/book.rs index d28c22dad..b46843df5 100644 --- a/vendor/mdbook/src/book/book.rs +++ b/vendor/mdbook/src/book/book.rs @@ -8,7 +8,7 @@ use super::summary::{parse_summary, Link, SectionNumber, Summary, SummaryItem}; use crate::config::BuildConfig; use crate::errors::*; use crate::utils::bracket_escape; - +use log::debug; use serde::{Deserialize, Serialize}; /// Load a book into memory from its `src/` directory. diff --git a/vendor/mdbook/src/book/init.rs b/vendor/mdbook/src/book/init.rs index 264c113d3..dd3fa8b0d 100644 --- a/vendor/mdbook/src/book/init.rs +++ b/vendor/mdbook/src/book/init.rs @@ -6,6 +6,7 @@ use super::MDBook; use crate::config::Config; use crate::errors::*; use crate::theme; +use log::{debug, error, info, trace}; /// A helper for setting up a new book and its directory structure. #[derive(Debug, Clone, PartialEq)] diff --git a/vendor/mdbook/src/book/mod.rs b/vendor/mdbook/src/book/mod.rs index 9745d2b7e..75bbcc714 100644 --- a/vendor/mdbook/src/book/mod.rs +++ b/vendor/mdbook/src/book/mod.rs @@ -14,6 +14,7 @@ pub use self::book::{load_book, Book, BookItem, BookItems, Chapter}; pub use self::init::BookBuilder; pub use self::summary::{parse_summary, Link, SectionNumber, Summary, SummaryItem}; +use log::{debug, error, info, log_enabled, trace, warn}; use std::io::Write; use std::path::PathBuf; use std::process::Command; @@ -246,6 +247,13 @@ impl MDBook { /// Run `rustdoc` tests on the book, linking against the provided libraries. pub fn test(&mut self, library_paths: Vec<&str>) -> Result<()> { + // test_chapter with chapter:None will run all tests. + self.test_chapter(library_paths, None) + } + + /// Run `rustdoc` tests on a specific chapter of the book, linking against the provided libraries. + /// If `chapter` is `None`, all tests will be run. + pub fn test_chapter(&mut self, library_paths: Vec<&str>, chapter: Option<&str>) -> Result<()> { let library_args: Vec<&str> = (0..library_paths.len()) .map(|_| "-L") .zip(library_paths.into_iter()) @@ -254,6 +262,8 @@ impl MDBook { let temp_dir = TempFileBuilder::new().prefix("mdbook-").tempdir()?; + let mut chapter_found = false; + // FIXME: Is "test" the proper renderer name to use here? let preprocess_context = PreprocessorContext::new(self.root.clone(), self.config.clone(), "test".to_string()); @@ -270,8 +280,16 @@ impl MDBook { _ => continue, }; - let path = self.source_dir().join(&chapter_path); - info!("Testing file: {:?}", path); + if let Some(chapter) = chapter { + if ch.name != chapter && chapter_path.to_str() != Some(chapter) { + if chapter == "?" { + info!("Skipping chapter '{}'...", ch.name); + } + continue; + } + } + chapter_found = true; + info!("Testing chapter '{}': {:?}", ch.name, chapter_path); // write preprocessed file to tempdir let path = temp_dir.path().join(&chapter_path); @@ -295,6 +313,7 @@ impl MDBook { } } + debug!("running {:?}", cmd); let output = cmd.output()?; if !output.status.success() { @@ -311,6 +330,11 @@ impl MDBook { if failed { bail!("One or more tests failed"); } + if let Some(chapter) = chapter { + if !chapter_found { + bail!("Chapter not found: {}", chapter); + } + } Ok(()) } diff --git a/vendor/mdbook/src/book/summary.rs b/vendor/mdbook/src/book/summary.rs index 2bd81580f..b2784ce5f 100644 --- a/vendor/mdbook/src/book/summary.rs +++ b/vendor/mdbook/src/book/summary.rs @@ -1,4 +1,5 @@ use crate::errors::*; +use log::{debug, trace, warn}; use memchr::{self, Memchr}; use pulldown_cmark::{self, Event, HeadingLevel, Tag}; use serde::{Deserialize, Serialize}; @@ -453,7 +454,7 @@ impl<'a> SummaryParser<'a> { items.push(item); } Some(Event::Start(Tag::List(..))) => { - // Skip this tag after comment bacause it is not nested. + // Skip this tag after comment because it is not nested. if items.is_empty() { continue; } diff --git a/vendor/mdbook/src/cmd/build.rs b/vendor/mdbook/src/cmd/build.rs index 5fe73236c..14a9fec6e 100644 --- a/vendor/mdbook/src/cmd/build.rs +++ b/vendor/mdbook/src/cmd/build.rs @@ -1,28 +1,16 @@ +use super::command_prelude::*; use crate::{get_book_dir, open}; -use clap::{arg, App, Arg, ArgMatches}; use mdbook::errors::Result; use mdbook::MDBook; +use std::path::PathBuf; // Create clap subcommand arguments -pub fn make_subcommand<'help>() -> App<'help> { - App::new("build") +pub fn make_subcommand() -> Command { + Command::new("build") .about("Builds a book from its markdown files") - .arg( - Arg::new("dest-dir") - .short('d') - .long("dest-dir") - .value_name("dest-dir") - .help( - "Output directory for the book{n}\ - Relative paths are interpreted relative to the book's root directory.{n}\ - If omitted, mdBook uses build.build-dir from book.toml or defaults to `./book`.", - ), - ) - .arg(arg!([dir] - "Root directory for the book{n}\ - (Defaults to the Current Directory when omitted)" - )) - .arg(arg!(-o --open "Opens the compiled book in a web browser")) + .arg_dest_dir() + .arg_root_dir() + .arg_open() } // Build command implementation @@ -30,13 +18,13 @@ pub fn execute(args: &ArgMatches) -> Result<()> { let book_dir = get_book_dir(args); let mut book = MDBook::load(&book_dir)?; - if let Some(dest_dir) = args.value_of("dest-dir") { + if let Some(dest_dir) = args.get_one::("dest-dir") { book.config.build.build_dir = dest_dir.into(); } book.build()?; - if args.is_present("open") { + if args.get_flag("open") { // FIXME: What's the right behaviour if we don't use the HTML renderer? let path = book.build_dir_for("html").join("index.html"); if !path.exists() { diff --git a/vendor/mdbook/src/cmd/clean.rs b/vendor/mdbook/src/cmd/clean.rs index 0569726e1..3ec605fea 100644 --- a/vendor/mdbook/src/cmd/clean.rs +++ b/vendor/mdbook/src/cmd/clean.rs @@ -1,28 +1,16 @@ +use super::command_prelude::*; use crate::get_book_dir; use anyhow::Context; -use clap::{arg, App, Arg, ArgMatches}; use mdbook::MDBook; use std::fs; +use std::path::PathBuf; // Create clap subcommand arguments -pub fn make_subcommand<'help>() -> App<'help> { - App::new("clean") +pub fn make_subcommand() -> Command { + Command::new("clean") .about("Deletes a built book") - .arg( - Arg::new("dest-dir") - .short('d') - .long("dest-dir") - .value_name("dest-dir") - .help( - "Output directory for the book{n}\ - Relative paths are interpreted relative to the book's root directory.{n}\ - If omitted, mdBook uses build.build-dir from book.toml or defaults to `./book`.", - ), - ) - .arg(arg!([dir] - "Root directory for the book{n}\ - (Defaults to the Current Directory when omitted)" - )) + .arg_dest_dir() + .arg_root_dir() } // Clean command implementation @@ -30,7 +18,7 @@ pub fn execute(args: &ArgMatches) -> mdbook::errors::Result<()> { let book_dir = get_book_dir(args); let book = MDBook::load(&book_dir)?; - let dir_to_remove = match args.value_of("dest-dir") { + let dir_to_remove = match args.get_one::("dest-dir") { Some(dest_dir) => dest_dir.into(), None => book.root.join(&book.config.build.build_dir), }; diff --git a/vendor/mdbook/src/cmd/command_prelude.rs b/vendor/mdbook/src/cmd/command_prelude.rs new file mode 100644 index 000000000..b6362e603 --- /dev/null +++ b/vendor/mdbook/src/cmd/command_prelude.rs @@ -0,0 +1,45 @@ +//! Helpers for building the command-line arguments for commands. + +pub use clap::{arg, Arg, ArgMatches, Command}; +use std::path::PathBuf; + +pub trait CommandExt: Sized { + fn _arg(self, arg: Arg) -> Self; + + fn arg_dest_dir(self) -> Self { + self._arg( + Arg::new("dest-dir") + .short('d') + .long("dest-dir") + .value_name("dest-dir") + .value_parser(clap::value_parser!(PathBuf)) + .help( + "Output directory for the book\n\ + Relative paths are interpreted relative to the book's root directory.\n\ + If omitted, mdBook uses build.build-dir from book.toml \ + or defaults to `./book`.", + ), + ) + } + + fn arg_root_dir(self) -> Self { + self._arg( + Arg::new("dir") + .help( + "Root directory for the book\n\ + (Defaults to the current directory when omitted)", + ) + .value_parser(clap::value_parser!(PathBuf)), + ) + } + + fn arg_open(self) -> Self { + self._arg(arg!(-o --open "Opens the compiled book in a web browser")) + } +} + +impl CommandExt for Command { + fn _arg(self, arg: Arg) -> Self { + self.arg(arg) + } +} diff --git a/vendor/mdbook/src/cmd/init.rs b/vendor/mdbook/src/cmd/init.rs index c964dcc13..d8ce93d16 100644 --- a/vendor/mdbook/src/cmd/init.rs +++ b/vendor/mdbook/src/cmd/init.rs @@ -1,5 +1,5 @@ use crate::get_book_dir; -use clap::{arg, App, Arg, ArgMatches}; +use clap::{arg, ArgMatches, Command as ClapCommand}; use mdbook::config; use mdbook::errors::Result; use mdbook::MDBook; @@ -8,30 +8,22 @@ use std::io::Write; use std::process::Command; // Create clap subcommand arguments -pub fn make_subcommand<'help>() -> App<'help> { - App::new("init") +pub fn make_subcommand() -> ClapCommand { + ClapCommand::new("init") .about("Creates the boilerplate structure and files for a new book") - // the {n} denotes a newline which will properly aligned in all help messages - .arg(arg!([dir] - "Directory to create the book in{n}\ - (Defaults to the Current Directory when omitted)" - )) - .arg(arg!(--theme "Copies the default theme into your source folder")) - .arg(arg!(--force "Skips confirmation prompts")) .arg( - Arg::new("title") - .long("title") - .takes_value(true) - .help("Sets the book title") - .required(false), + arg!([dir] + "Directory to create the book in\n\ + (Defaults to the current directory when omitted)" + ) + .value_parser(clap::value_parser!(std::path::PathBuf)), ) + .arg(arg!(--theme "Copies the default theme into your source folder")) + .arg(arg!(--force "Skips confirmation prompts")) + .arg(arg!(--title "Sets the book title")) .arg( - Arg::new("ignore") - .long("ignore") - .takes_value(true) - .possible_values(&["none", "git"]) - .help("Creates a VCS ignore file (i.e. .gitignore)") - .required(false), + arg!(--ignore <ignore> "Creates a VCS ignore file (i.e. .gitignore)") + .value_parser(["none", "git"]), ) } @@ -41,12 +33,12 @@ pub fn execute(args: &ArgMatches) -> Result<()> { let mut builder = MDBook::init(&book_dir); let mut config = config::Config::default(); // If flag `--theme` is present, copy theme to src - if args.is_present("theme") { + if args.get_flag("theme") { let theme_dir = book_dir.join("theme"); println!(); println!("Copying the default theme to {}", theme_dir.display()); // Skip this if `--force` is present - if !args.is_present("force") && theme_dir.exists() { + if !args.get_flag("force") && theme_dir.exists() { println!("This could potentially overwrite files already present in that directory."); print!("\nAre you sure you want to continue? (y/n) "); @@ -59,7 +51,7 @@ pub fn execute(args: &ArgMatches) -> Result<()> { } } - if let Some(ignore) = args.value_of("ignore") { + if let Some(ignore) = args.get_one::<String>("ignore").map(|s| s.as_str()) { match ignore { "git" => builder.create_gitignore(true), _ => builder.create_gitignore(false), @@ -71,8 +63,8 @@ pub fn execute(args: &ArgMatches) -> Result<()> { } } - config.book.title = if args.is_present("title") { - args.value_of("title").map(String::from) + config.book.title = if args.contains_id("title") { + args.get_one::<String>("title").map(String::from) } else { request_book_title() }; diff --git a/vendor/mdbook/src/cmd/mod.rs b/vendor/mdbook/src/cmd/mod.rs index c5b6730f1..b21979b27 100644 --- a/vendor/mdbook/src/cmd/mod.rs +++ b/vendor/mdbook/src/cmd/mod.rs @@ -2,6 +2,7 @@ pub mod build; pub mod clean; +pub mod command_prelude; pub mod init; #[cfg(feature = "serve")] pub mod serve; diff --git a/vendor/mdbook/src/cmd/serve.rs b/vendor/mdbook/src/cmd/serve.rs index bafbfd52e..88898567e 100644 --- a/vendor/mdbook/src/cmd/serve.rs +++ b/vendor/mdbook/src/cmd/serve.rs @@ -1,7 +1,8 @@ +use super::command_prelude::*; #[cfg(feature = "watch")] use super::watch; use crate::{get_book_dir, open}; -use clap::{arg, App, Arg, ArgMatches}; +use clap::builder::NonEmptyStringValueParser; use futures_util::sink::SinkExt; use futures_util::StreamExt; use mdbook::errors::*; @@ -18,43 +19,30 @@ use warp::Filter; const LIVE_RELOAD_ENDPOINT: &str = "__livereload"; // Create clap subcommand arguments -pub fn make_subcommand<'help>() -> App<'help> { - App::new("serve") +pub fn make_subcommand() -> Command { + Command::new("serve") .about("Serves a book at http://localhost:3000, and rebuilds it on changes") - .arg( - Arg::new("dest-dir") - .short('d') - .long("dest-dir") - .value_name("dest-dir") - .help( - "Output directory for the book{n}\ - Relative paths are interpreted relative to the book's root directory.{n}\ - If omitted, mdBook uses build.build-dir from book.toml or defaults to `./book`.", - ), - ) - .arg(arg!([dir] - "Root directory for the book{n}\ - (Defaults to the Current Directory when omitted)" - )) + .arg_dest_dir() + .arg_root_dir() .arg( Arg::new("hostname") .short('n') .long("hostname") - .takes_value(true) + .num_args(1) .default_value("localhost") - .forbid_empty_values(true) + .value_parser(NonEmptyStringValueParser::new()) .help("Hostname to listen on for HTTP connections"), ) .arg( Arg::new("port") .short('p') .long("port") - .takes_value(true) + .num_args(1) .default_value("3000") - .forbid_empty_values(true) + .value_parser(NonEmptyStringValueParser::new()) .help("Port to use for HTTP connections"), ) - .arg(arg!(-o --open "Opens the compiled book in a web browser")) + .arg_open() } // Serve command implementation @@ -62,17 +50,17 @@ pub fn execute(args: &ArgMatches) -> Result<()> { let book_dir = get_book_dir(args); let mut book = MDBook::load(&book_dir)?; - let port = args.value_of("port").unwrap(); - let hostname = args.value_of("hostname").unwrap(); - let open_browser = args.is_present("open"); + let port = args.get_one::<String>("port").unwrap(); + let hostname = args.get_one::<String>("hostname").unwrap(); + let open_browser = args.get_flag("open"); let address = format!("{}:{}", hostname, port); let update_config = |book: &mut MDBook| { book.config - .set("output.html.live-reload-endpoint", &LIVE_RELOAD_ENDPOINT) + .set("output.html.live-reload-endpoint", LIVE_RELOAD_ENDPOINT) .expect("live-reload-endpoint update failed"); - if let Some(dest_dir) = args.value_of("dest-dir") { + if let Some(dest_dir) = args.get_one::<PathBuf>("dest-dir") { book.config.build.build_dir = dest_dir.into(); } // Override site-url for local serving of the 404 file @@ -89,8 +77,7 @@ pub fn execute(args: &ArgMatches) -> Result<()> { let input_404 = book .config .get("output.html.input-404") - .map(toml::Value::as_str) - .and_then(std::convert::identity) // flatten + .and_then(toml::Value::as_str) .map(ToString::to_string); let file_404 = get_404_output_file(&input_404); diff --git a/vendor/mdbook/src/cmd/test.rs b/vendor/mdbook/src/cmd/test.rs index 02f982a49..3efe130b1 100644 --- a/vendor/mdbook/src/cmd/test.rs +++ b/vendor/mdbook/src/cmd/test.rs @@ -1,54 +1,58 @@ +use super::command_prelude::*; use crate::get_book_dir; -use clap::{arg, App, Arg, ArgMatches}; +use clap::builder::NonEmptyStringValueParser; +use clap::{Arg, ArgAction, ArgMatches, Command}; use mdbook::errors::Result; use mdbook::MDBook; +use std::path::PathBuf; // Create clap subcommand arguments -pub fn make_subcommand<'help>() -> App<'help> { - App::new("test") +pub fn make_subcommand() -> Command { + Command::new("test") .about("Tests that a book's Rust code samples compile") + // FIXME: --dest-dir is unused by the test command, it should be removed + .arg_dest_dir() + .arg_root_dir() .arg( - Arg::new("dest-dir") - .short('d') - .long("dest-dir") - .value_name("dest-dir") + Arg::new("chapter") + .short('c') + .long("chapter") + .value_name("chapter"), + ) + .arg( + Arg::new("library-path") + .short('L') + .long("library-path") + .value_name("dir") + .value_delimiter(',') + .value_parser(NonEmptyStringValueParser::new()) + .action(ArgAction::Append) .help( - "Output directory for the book{n}\ - Relative paths are interpreted relative to the book's root directory.{n}\ - If omitted, mdBook uses build.build-dir from book.toml or defaults to `./book`.", + "A comma-separated list of directories to add to the crate \ + search path when building tests", ), ) - .arg(arg!([dir] - "Root directory for the book{n}\ - (Defaults to the Current Directory when omitted)" - )) - .arg(Arg::new("library-path") - .short('L') - .long("library-path") - .value_name("dir") - .takes_value(true) - .use_delimiter(true) - .require_delimiter(true) - .multiple_values(true) - .multiple_occurrences(true) - .forbid_empty_values(true) - .help("A comma-separated list of directories to add to {n}the crate search path when building tests")) } // test command implementation pub fn execute(args: &ArgMatches) -> Result<()> { let library_paths: Vec<&str> = args - .values_of("library-path") - .map(std::iter::Iterator::collect) + .get_many("library-path") + .map(|it| it.map(String::as_str).collect()) .unwrap_or_default(); + + let chapter: Option<&str> = args.get_one::<String>("chapter").map(|s| s.as_str()); + let book_dir = get_book_dir(args); let mut book = MDBook::load(&book_dir)?; - if let Some(dest_dir) = args.value_of("dest-dir") { - book.config.build.build_dir = dest_dir.into(); + if let Some(dest_dir) = args.get_one::<PathBuf>("dest-dir") { + book.config.build.build_dir = dest_dir.to_path_buf(); } - - book.test(library_paths)?; + match chapter { + Some(_) => book.test_chapter(library_paths, chapter), + None => book.test(library_paths), + }?; Ok(()) } diff --git a/vendor/mdbook/src/cmd/watch.rs b/vendor/mdbook/src/cmd/watch.rs index 9336af779..bbc6bde71 100644 --- a/vendor/mdbook/src/cmd/watch.rs +++ b/vendor/mdbook/src/cmd/watch.rs @@ -1,34 +1,20 @@ +use super::command_prelude::*; use crate::{get_book_dir, open}; -use clap::{arg, App, Arg, ArgMatches}; use mdbook::errors::Result; use mdbook::utils; use mdbook::MDBook; -use notify::Watcher; use std::path::{Path, PathBuf}; use std::sync::mpsc::channel; use std::thread::sleep; use std::time::Duration; // Create clap subcommand arguments -pub fn make_subcommand<'help>() -> App<'help> { - App::new("watch") +pub fn make_subcommand() -> Command { + Command::new("watch") .about("Watches a book's files and rebuilds it on changes") - .arg( - Arg::new("dest-dir") - .short('d') - .long("dest-dir") - .value_name("dest-dir") - .help( - "Output directory for the book{n}\ - Relative paths are interpreted relative to the book's root directory.{n}\ - If omitted, mdBook uses build.build-dir from book.toml or defaults to `./book`.", - ), - ) - .arg(arg!([dir] - "Root directory for the book{n}\ - (Defaults to the Current Directory when omitted)" - )) - .arg(arg!(-o --open "Opens the compiled book in a web browser")) + .arg_dest_dir() + .arg_root_dir() + .arg_open() } // Watch command implementation @@ -37,13 +23,13 @@ pub fn execute(args: &ArgMatches) -> Result<()> { let mut book = MDBook::load(&book_dir)?; let update_config = |book: &mut MDBook| { - if let Some(dest_dir) = args.value_of("dest-dir") { + if let Some(dest_dir) = args.get_one::<PathBuf>("dest-dir") { book.config.build.build_dir = dest_dir.into(); } }; update_config(&mut book); - if args.is_present("open") { + if args.get_flag("open") { book.build()?; let path = book.build_dir_for("html").join("index.html"); if !path.exists() { @@ -121,30 +107,42 @@ pub fn trigger_on_change<F>(book: &MDBook, closure: F) where F: Fn(Vec<PathBuf>, &Path), { - use notify::DebouncedEvent::*; use notify::RecursiveMode::*; // Create a channel to receive the events. let (tx, rx) = channel(); - let mut watcher = match notify::watcher(tx, Duration::from_secs(1)) { - Ok(w) => w, + let mut debouncer = match notify_debouncer_mini::new_debouncer(Duration::from_secs(1), None, tx) + { + Ok(d) => d, Err(e) => { error!("Error while trying to watch the files:\n\n\t{:?}", e); std::process::exit(1) } }; + let watcher = debouncer.watcher(); // Add the source directory to the watcher - if let Err(e) = watcher.watch(book.source_dir(), Recursive) { + if let Err(e) = watcher.watch(&book.source_dir(), Recursive) { error!("Error while watching {:?}:\n {:?}", book.source_dir(), e); std::process::exit(1); }; - let _ = watcher.watch(book.theme_dir(), Recursive); + let _ = watcher.watch(&book.theme_dir(), Recursive); // Add the book.toml file to the watcher if it exists - let _ = watcher.watch(book.root.join("book.toml"), NonRecursive); + let _ = watcher.watch(&book.root.join("book.toml"), NonRecursive); + + for dir in &book.config.build.extra_watch_dirs { + let path = dir.canonicalize().unwrap(); + if let Err(e) = watcher.watch(&path, Recursive) { + error!( + "Error while watching extra directory {:?}:\n {:?}", + path, e + ); + std::process::exit(1); + } + } info!("Listening for changes..."); @@ -155,18 +153,25 @@ where let all_events = std::iter::once(first_event).chain(other_events); - let paths = all_events - .filter_map(|event| { - debug!("Received filesystem event: {:?}", event); - - match event { - Create(path) | Write(path) | Remove(path) | Rename(_, path) => Some(path), - _ => None, + let paths: Vec<_> = all_events + .filter_map(|event| match event { + Ok(events) => Some(events), + Err(errors) => { + for error in errors { + log::warn!("error while watching for changes: {error}"); + } + None } }) - .collect::<Vec<_>>(); - - let paths = remove_ignored_files(&book.root, &paths[..]); + .flatten() + .map(|event| event.path) + .collect(); + + // If we are watching files outside the current repository (via extra-watch-dirs), then they are definitionally + // ignored by gitignore. So we handle this case by including such files into the watched paths list. + let any_external_paths = paths.iter().filter(|p| !p.starts_with(&book.root)).cloned(); + let mut paths = remove_ignored_files(&book.root, &paths[..]); + paths.extend(any_external_paths); if !paths.is_empty() { closure(paths, &book.root); diff --git a/vendor/mdbook/src/config.rs b/vendor/mdbook/src/config.rs index b7d03d1a2..0c367d848 100644 --- a/vendor/mdbook/src/config.rs +++ b/vendor/mdbook/src/config.rs @@ -49,6 +49,7 @@ #![deny(missing_docs)] +use log::{debug, trace, warn}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use std::collections::HashMap; use std::env; @@ -295,7 +296,7 @@ impl Default for Config { } } -impl<'de> Deserialize<'de> for Config { +impl<'de> serde::Deserialize<'de> for Config { fn deserialize<D: Deserializer<'de>>(de: D) -> std::result::Result<Self, D::Error> { let raw = Value::deserialize(de)?; @@ -437,6 +438,8 @@ pub struct BuildConfig { /// Should the default preprocessors always be used when they are /// compatible with the renderer? pub use_default_preprocessors: bool, + /// Extra directories to trigger rebuild when watching/serving + pub extra_watch_dirs: Vec<PathBuf>, } impl Default for BuildConfig { @@ -445,6 +448,7 @@ impl Default for BuildConfig { build_dir: PathBuf::from("book"), create_missing: true, use_default_preprocessors: true, + extra_watch_dirs: Vec::new(), } } } @@ -526,10 +530,9 @@ pub struct HtmlConfig { /// directly jumping to editing the currently viewed page. /// Contains {path} that is replaced with chapter source file path pub edit_url_template: Option<String>, - /// Endpoint of websocket, for livereload usage. Value loaded from .toml file - /// is ignored, because our code overrides this field with the value [`LIVE_RELOAD_ENDPOINT`] - /// - /// [`LIVE_RELOAD_ENDPOINT`]: cmd::serve::LIVE_RELOAD_ENDPOINT + /// Endpoint of websocket, for livereload usage. Value loaded from .toml + /// file is ignored, because our code overrides this field with an + /// internal value (`LIVE_RELOAD_ENDPOINT) /// /// This config item *should not be edited* by the end user. #[doc(hidden)] @@ -717,6 +720,7 @@ impl<'de, T> Updateable<'de> for T where T: Serialize + Deserialize<'de> {} mod tests { use super::*; use crate::utils::fs::get_404_output_file; + use serde_json::json; const COMPLEX_CONFIG: &str = r#" [book] @@ -770,6 +774,7 @@ mod tests { build_dir: PathBuf::from("outputs"), create_missing: false, use_default_preprocessors: true, + extra_watch_dirs: Vec::new(), }; let rust_should_be = RustConfig { edition: None }; let playground_should_be = Playground { @@ -980,6 +985,7 @@ mod tests { build_dir: PathBuf::from("my-book"), create_missing: true, use_default_preprocessors: true, + extra_watch_dirs: Vec::new(), }; let html_should_be = HtmlConfig { diff --git a/vendor/mdbook/src/lib.rs b/vendor/mdbook/src/lib.rs index cc62b0abd..14cd94d9d 100644 --- a/vendor/mdbook/src/lib.rs +++ b/vendor/mdbook/src/lib.rs @@ -83,17 +83,6 @@ #![deny(missing_docs)] #![deny(rust_2018_idioms)] -#[macro_use] -extern crate lazy_static; -#[macro_use] -extern crate log; -#[macro_use] -extern crate serde_json; - -#[cfg(test)] -#[macro_use] -extern crate pretty_assertions; - pub mod book; pub mod config; pub mod preprocess; diff --git a/vendor/mdbook/src/main.rs b/vendor/mdbook/src/main.rs index 35562e64b..3e576c5b5 100644 --- a/vendor/mdbook/src/main.rs +++ b/vendor/mdbook/src/main.rs @@ -5,7 +5,7 @@ extern crate log; use anyhow::anyhow; use chrono::Local; -use clap::{App, AppSettings, Arg, ArgMatches}; +use clap::{Arg, ArgMatches, Command}; use clap_complete::Shell; use env_logger::Builder; use log::LevelFilter; @@ -13,7 +13,7 @@ use mdbook::utils; use std::env; use std::ffi::OsStr; use std::io::Write; -use std::path::{Path, PathBuf}; +use std::path::PathBuf; mod cmd; @@ -22,10 +22,10 @@ const VERSION: &str = concat!("v", crate_version!()); fn main() { init_logger(); - let app = create_clap_app(); + let command = create_clap_command(); - // Check which subcomamnd the user ran... - let res = match app.get_matches().subcommand() { + // Check which subcommand the user ran... + let res = match command.get_matches().subcommand() { Some(("init", sub_matches)) => cmd::init::execute(sub_matches), Some(("build", sub_matches)) => cmd::build::execute(sub_matches), Some(("clean", sub_matches)) => cmd::clean::execute(sub_matches), @@ -35,15 +35,13 @@ fn main() { Some(("serve", sub_matches)) => cmd::serve::execute(sub_matches), Some(("test", sub_matches)) => cmd::test::execute(sub_matches), Some(("completions", sub_matches)) => (|| { - let shell: Shell = sub_matches - .value_of("shell") - .ok_or_else(|| anyhow!("Shell name missing."))? - .parse() - .map_err(|s| anyhow!("Invalid shell: {}", s))?; + let shell = sub_matches + .get_one::<Shell>("shell") + .ok_or_else(|| anyhow!("Shell name missing."))?; - let mut complete_app = create_clap_app(); + let mut complete_app = create_clap_command(); clap_complete::generate( - shell, + *shell, &mut complete_app, "mdbook", &mut std::io::stdout().lock(), @@ -61,13 +59,13 @@ fn main() { } /// Create a list of valid arguments and sub-commands -fn create_clap_app() -> App<'static> { - let app = App::new(crate_name!()) +fn create_clap_command() -> Command { + let app = Command::new(crate_name!()) .about(crate_description!()) .author("Mathieu David <mathieudavid@mathieudavid.org>") .version(VERSION) - .setting(AppSettings::PropagateVersion) - .setting(AppSettings::ArgRequiredElseHelp) + .propagate_version(true) + .arg_required_else_help(true) .after_help( "For more information about a specific command, try `mdbook <command> --help`\n\ The source code for mdBook is available at: https://github.com/rust-lang/mdBook", @@ -77,12 +75,11 @@ fn create_clap_app() -> App<'static> { .subcommand(cmd::test::make_subcommand()) .subcommand(cmd::clean::make_subcommand()) .subcommand( - App::new("completions") + Command::new("completions") .about("Generate shell completions for your shell to stdout") .arg( Arg::new("shell") - .takes_value(true) - .possible_values(Shell::possible_values()) + .value_parser(clap::value_parser!(Shell)) .help("the shell to generate completions for") .value_name("SHELL") .required(true), @@ -124,11 +121,10 @@ fn init_logger() { } fn get_book_dir(args: &ArgMatches) -> PathBuf { - if let Some(dir) = args.value_of("dir") { + if let Some(p) = args.get_one::<PathBuf>("dir") { // Check if path is relative from current dir, or absolute... - let p = Path::new(dir); if p.is_relative() { - env::current_dir().unwrap().join(dir) + env::current_dir().unwrap().join(p) } else { p.to_path_buf() } @@ -146,5 +142,5 @@ fn open<P: AsRef<OsStr>>(path: P) { #[test] fn verify_app() { - create_clap_app().debug_assert(); + create_clap_command().debug_assert(); } diff --git a/vendor/mdbook/src/preprocess/cmd.rs b/vendor/mdbook/src/preprocess/cmd.rs index c47fd5d22..149dabda5 100644 --- a/vendor/mdbook/src/preprocess/cmd.rs +++ b/vendor/mdbook/src/preprocess/cmd.rs @@ -1,6 +1,7 @@ use super::{Preprocessor, PreprocessorContext}; use crate::book::Book; use crate::errors::*; +use log::{debug, trace, warn}; use shlex::Shlex; use std::io::{self, Read, Write}; use std::process::{Child, Command, Stdio}; diff --git a/vendor/mdbook/src/preprocess/index.rs b/vendor/mdbook/src/preprocess/index.rs index fd60ad4da..004b7eda6 100644 --- a/vendor/mdbook/src/preprocess/index.rs +++ b/vendor/mdbook/src/preprocess/index.rs @@ -1,10 +1,11 @@ use regex::Regex; use std::path::Path; -use crate::errors::*; - use super::{Preprocessor, PreprocessorContext}; use crate::book::{Book, BookItem}; +use crate::errors::*; +use log::warn; +use once_cell::sync::Lazy; /// A preprocessor for converting file name `README.md` to `index.md` since /// `README.md` is the de facto index file in markdown-based documentation. @@ -67,9 +68,8 @@ fn warn_readme_name_conflict<P: AsRef<Path>>(readme_path: P, index_path: P) { } fn is_readme_file<P: AsRef<Path>>(path: P) -> bool { - lazy_static! { - static ref RE: Regex = Regex::new(r"(?i)^readme$").unwrap(); - } + static RE: Lazy<Regex> = Lazy::new(|| Regex::new(r"(?i)^readme$").unwrap()); + RE.is_match( path.as_ref() .file_stem() diff --git a/vendor/mdbook/src/preprocess/links.rs b/vendor/mdbook/src/preprocess/links.rs index 7ca6fd345..c2c81f522 100644 --- a/vendor/mdbook/src/preprocess/links.rs +++ b/vendor/mdbook/src/preprocess/links.rs @@ -10,6 +10,8 @@ use std::path::{Path, PathBuf}; use super::{Preprocessor, PreprocessorContext}; use crate::book::{Book, BookItem}; +use log::{error, warn}; +use once_cell::sync::Lazy; const ESCAPE_CHAR: char = '\\'; const MAX_LINK_NESTED_DEPTH: usize = 10; @@ -408,19 +410,20 @@ impl<'a> Iterator for LinkIter<'a> { fn find_links(contents: &str) -> LinkIter<'_> { // lazily compute following regex // r"\\\{\{#.*\}\}|\{\{#([a-zA-Z0-9]+)\s*([^}]+)\}\}")?; - lazy_static! { - static ref RE: Regex = Regex::new( + static RE: Lazy<Regex> = Lazy::new(|| { + Regex::new( r"(?x) # insignificant whitespace mode - \\\{\{\#.*\}\} # match escaped link - | # or - \{\{\s* # link opening parens and whitespace - \#([a-zA-Z0-9_]+) # link type - \s+ # separating whitespace - ([^}]+) # link target path and space separated properties - \}\} # link closing parens" + \\\{\{\#.*\}\} # match escaped link + | # or + \{\{\s* # link opening parens and whitespace + \#([a-zA-Z0-9_]+) # link type + \s+ # separating whitespace + ([^}]+) # link target path and space separated properties + \}\} # link closing parens", ) - .unwrap(); - } + .unwrap() + }); + LinkIter(RE.captures_iter(contents)) } diff --git a/vendor/mdbook/src/preprocess/mod.rs b/vendor/mdbook/src/preprocess/mod.rs index 894e20035..df01a3dbf 100644 --- a/vendor/mdbook/src/preprocess/mod.rs +++ b/vendor/mdbook/src/preprocess/mod.rs @@ -12,12 +12,11 @@ use crate::book::Book; use crate::config::Config; use crate::errors::*; +use serde::{Deserialize, Serialize}; use std::cell::RefCell; use std::collections::HashMap; use std::path::PathBuf; -use serde::{Deserialize, Serialize}; - /// Extra information for a `Preprocessor` to give them more context when /// processing a book. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] diff --git a/vendor/mdbook/src/renderer/html_handlebars/hbs_renderer.rs b/vendor/mdbook/src/renderer/html_handlebars/hbs_renderer.rs index b933a359a..1b648dac1 100644 --- a/vendor/mdbook/src/renderer/html_handlebars/hbs_renderer.rs +++ b/vendor/mdbook/src/renderer/html_handlebars/hbs_renderer.rs @@ -14,7 +14,10 @@ use std::path::{Path, PathBuf}; use crate::utils::fs::get_404_output_file; use handlebars::Handlebars; +use log::{debug, trace, warn}; +use once_cell::sync::Lazy; use regex::{Captures, Regex}; +use serde_json::json; #[derive(Default)] pub struct HtmlHandlebars; @@ -337,6 +340,7 @@ impl HtmlHandlebars { ); handlebars.register_helper("previous", Box::new(helpers::navigation::previous)); handlebars.register_helper("next", Box::new(helpers::navigation::next)); + // TODO: remove theme_option in 0.5, it is not needed. handlebars.register_helper("theme_option", Box::new(helpers::theme::theme_option)); } @@ -627,6 +631,7 @@ fn make_data( ); } + // TODO: remove default_theme in 0.5, it is not needed. let default_theme = match html_config.default_theme { Some(ref theme) => theme.to_lowercase(), None => "light".to_string(), @@ -764,9 +769,8 @@ fn make_data( /// Goes through the rendered HTML, making sure all header tags have /// an anchor respectively so people can link to sections directly. fn build_header_links(html: &str) -> String { - lazy_static! { - static ref BUILD_HEADER_LINKS: Regex = Regex::new(r"<h(\d)>(.*?)</h\d>").unwrap(); - } + static BUILD_HEADER_LINKS: Lazy<Regex> = + Lazy::new(|| Regex::new(r"<h(\d)>(.*?)</h\d>").unwrap()); let mut id_counter = HashMap::new(); @@ -807,10 +811,8 @@ fn insert_link_into_header( // ``` // This function replaces all commas by spaces in the code block classes fn fix_code_blocks(html: &str) -> String { - lazy_static! { - static ref FIX_CODE_BLOCKS: Regex = - Regex::new(r##"<code([^>]+)class="([^"]+)"([^>]*)>"##).unwrap(); - } + static FIX_CODE_BLOCKS: Lazy<Regex> = + Lazy::new(|| Regex::new(r##"<code([^>]+)class="([^"]+)"([^>]*)>"##).unwrap()); FIX_CODE_BLOCKS .replace_all(html, |caps: &Captures<'_>| { @@ -833,10 +835,9 @@ fn add_playground_pre( playground_config: &Playground, edition: Option<RustEdition>, ) -> String { - lazy_static! { - static ref ADD_PLAYGROUND_PRE: Regex = - Regex::new(r##"((?s)<code[^>]?class="([^"]+)".*?>(.*?)</code>)"##).unwrap(); - } + static ADD_PLAYGROUND_PRE: Lazy<Regex> = + Lazy::new(|| Regex::new(r##"((?s)<code[^>]?class="([^"]+)".*?>(.*?)</code>)"##).unwrap()); + ADD_PLAYGROUND_PRE .replace_all(html, |caps: &Captures<'_>| { let text = &caps[1]; @@ -899,18 +900,19 @@ fn add_playground_pre( } fn hide_lines(content: &str) -> String { - lazy_static! { - static ref BORING_LINES_REGEX: Regex = Regex::new(r"^(\s*)#(.?)(.*)$").unwrap(); - } + static BORING_LINES_REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r"^(\s*)#(.?)(.*)$").unwrap()); let mut result = String::with_capacity(content.len()); - for line in content.lines() { + let mut lines = content.lines().peekable(); + while let Some(line) = lines.next() { + // Don't include newline on the last line. + let newline = if lines.peek().is_none() { "" } else { "\n" }; if let Some(caps) = BORING_LINES_REGEX.captures(line) { if &caps[2] == "#" { result += &caps[1]; result += &caps[2]; result += &caps[3]; - result += "\n"; + result += newline; continue; } else if &caps[2] != "!" && &caps[2] != "[" { result += "<span class=\"boring\">"; @@ -919,13 +921,13 @@ fn hide_lines(content: &str) -> String { result += &caps[2]; } result += &caps[3]; - result += "\n"; + result += newline; result += "</span>"; continue; } } result += line; - result += "\n"; + result += newline; } result } @@ -1005,19 +1007,19 @@ mod tests { fn add_playground() { let inputs = [ ("<code class=\"language-rust\">x()</code>", - "<pre class=\"playground\"><code class=\"language-rust\"><span class=\"boring\">#![allow(unused)]\n</span><span class=\"boring\">fn main() {\n</span>x()\n<span class=\"boring\">}\n</span></code></pre>"), + "<pre class=\"playground\"><code class=\"language-rust\"><span class=\"boring\">#![allow(unused)]\n</span><span class=\"boring\">fn main() {\n</span>x()\n<span class=\"boring\">}</span></code></pre>"), ("<code class=\"language-rust\">fn main() {}</code>", - "<pre class=\"playground\"><code class=\"language-rust\">fn main() {}\n</code></pre>"), + "<pre class=\"playground\"><code class=\"language-rust\">fn main() {}</code></pre>"), ("<code class=\"language-rust editable\">let s = \"foo\n # bar\n\";</code>", - "<pre class=\"playground\"><code class=\"language-rust editable\">let s = \"foo\n<span class=\"boring\"> bar\n</span>\";\n</code></pre>"), + "<pre class=\"playground\"><code class=\"language-rust editable\">let s = \"foo\n<span class=\"boring\"> bar\n</span>\";</code></pre>"), ("<code class=\"language-rust editable\">let s = \"foo\n ## bar\n\";</code>", - "<pre class=\"playground\"><code class=\"language-rust editable\">let s = \"foo\n # bar\n\";\n</code></pre>"), + "<pre class=\"playground\"><code class=\"language-rust editable\">let s = \"foo\n # bar\n\";</code></pre>"), ("<code class=\"language-rust editable\">let s = \"foo\n # bar\n#\n\";</code>", - "<pre class=\"playground\"><code class=\"language-rust editable\">let s = \"foo\n<span class=\"boring\"> bar\n</span><span class=\"boring\">\n</span>\";\n</code></pre>"), + "<pre class=\"playground\"><code class=\"language-rust editable\">let s = \"foo\n<span class=\"boring\"> bar\n</span><span class=\"boring\">\n</span>\";</code></pre>"), ("<code class=\"language-rust ignore\">let s = \"foo\n # bar\n\";</code>", - "<code class=\"language-rust ignore\">let s = \"foo\n<span class=\"boring\"> bar\n</span>\";\n</code>"), + "<code class=\"language-rust ignore\">let s = \"foo\n<span class=\"boring\"> bar\n</span>\";</code>"), ("<code class=\"language-rust editable\">#![no_std]\nlet s = \"foo\";\n #[some_attr]</code>", - "<pre class=\"playground\"><code class=\"language-rust editable\">#![no_std]\nlet s = \"foo\";\n #[some_attr]\n</code></pre>"), + "<pre class=\"playground\"><code class=\"language-rust editable\">#![no_std]\nlet s = \"foo\";\n #[some_attr]</code></pre>"), ]; for (src, should_be) in &inputs { let got = add_playground_pre( @@ -1035,13 +1037,13 @@ mod tests { fn add_playground_edition2015() { let inputs = [ ("<code class=\"language-rust\">x()</code>", - "<pre class=\"playground\"><code class=\"language-rust edition2015\"><span class=\"boring\">#![allow(unused)]\n</span><span class=\"boring\">fn main() {\n</span>x()\n<span class=\"boring\">}\n</span></code></pre>"), + "<pre class=\"playground\"><code class=\"language-rust edition2015\"><span class=\"boring\">#![allow(unused)]\n</span><span class=\"boring\">fn main() {\n</span>x()\n<span class=\"boring\">}</span></code></pre>"), ("<code class=\"language-rust\">fn main() {}</code>", - "<pre class=\"playground\"><code class=\"language-rust edition2015\">fn main() {}\n</code></pre>"), + "<pre class=\"playground\"><code class=\"language-rust edition2015\">fn main() {}</code></pre>"), ("<code class=\"language-rust edition2015\">fn main() {}</code>", - "<pre class=\"playground\"><code class=\"language-rust edition2015\">fn main() {}\n</code></pre>"), + "<pre class=\"playground\"><code class=\"language-rust edition2015\">fn main() {}</code></pre>"), ("<code class=\"language-rust edition2018\">fn main() {}</code>", - "<pre class=\"playground\"><code class=\"language-rust edition2018\">fn main() {}\n</code></pre>"), + "<pre class=\"playground\"><code class=\"language-rust edition2018\">fn main() {}</code></pre>"), ]; for (src, should_be) in &inputs { let got = add_playground_pre( @@ -1059,13 +1061,13 @@ mod tests { fn add_playground_edition2018() { let inputs = [ ("<code class=\"language-rust\">x()</code>", - "<pre class=\"playground\"><code class=\"language-rust edition2018\"><span class=\"boring\">#![allow(unused)]\n</span><span class=\"boring\">fn main() {\n</span>x()\n<span class=\"boring\">}\n</span></code></pre>"), + "<pre class=\"playground\"><code class=\"language-rust edition2018\"><span class=\"boring\">#![allow(unused)]\n</span><span class=\"boring\">fn main() {\n</span>x()\n<span class=\"boring\">}</span></code></pre>"), ("<code class=\"language-rust\">fn main() {}</code>", - "<pre class=\"playground\"><code class=\"language-rust edition2018\">fn main() {}\n</code></pre>"), + "<pre class=\"playground\"><code class=\"language-rust edition2018\">fn main() {}</code></pre>"), ("<code class=\"language-rust edition2015\">fn main() {}</code>", - "<pre class=\"playground\"><code class=\"language-rust edition2015\">fn main() {}\n</code></pre>"), + "<pre class=\"playground\"><code class=\"language-rust edition2015\">fn main() {}</code></pre>"), ("<code class=\"language-rust edition2018\">fn main() {}</code>", - "<pre class=\"playground\"><code class=\"language-rust edition2018\">fn main() {}\n</code></pre>"), + "<pre class=\"playground\"><code class=\"language-rust edition2018\">fn main() {}</code></pre>"), ]; for (src, should_be) in &inputs { let got = add_playground_pre( @@ -1083,13 +1085,13 @@ mod tests { fn add_playground_edition2021() { let inputs = [ ("<code class=\"language-rust\">x()</code>", - "<pre class=\"playground\"><code class=\"language-rust edition2021\"><span class=\"boring\">#![allow(unused)]\n</span><span class=\"boring\">fn main() {\n</span>x()\n<span class=\"boring\">}\n</span></code></pre>"), + "<pre class=\"playground\"><code class=\"language-rust edition2021\"><span class=\"boring\">#![allow(unused)]\n</span><span class=\"boring\">fn main() {\n</span>x()\n<span class=\"boring\">}</span></code></pre>"), ("<code class=\"language-rust\">fn main() {}</code>", - "<pre class=\"playground\"><code class=\"language-rust edition2021\">fn main() {}\n</code></pre>"), + "<pre class=\"playground\"><code class=\"language-rust edition2021\">fn main() {}</code></pre>"), ("<code class=\"language-rust edition2015\">fn main() {}</code>", - "<pre class=\"playground\"><code class=\"language-rust edition2015\">fn main() {}\n</code></pre>"), + "<pre class=\"playground\"><code class=\"language-rust edition2015\">fn main() {}</code></pre>"), ("<code class=\"language-rust edition2018\">fn main() {}</code>", - "<pre class=\"playground\"><code class=\"language-rust edition2018\">fn main() {}\n</code></pre>"), + "<pre class=\"playground\"><code class=\"language-rust edition2018\">fn main() {}</code></pre>"), ]; for (src, should_be) in &inputs { let got = add_playground_pre( diff --git a/vendor/mdbook/src/renderer/html_handlebars/helpers/navigation.rs b/vendor/mdbook/src/renderer/html_handlebars/helpers/navigation.rs index 65929bbfc..b184c4410 100644 --- a/vendor/mdbook/src/renderer/html_handlebars/helpers/navigation.rs +++ b/vendor/mdbook/src/renderer/html_handlebars/helpers/navigation.rs @@ -4,6 +4,8 @@ use std::path::Path; use handlebars::{Context, Handlebars, Helper, Output, RenderContext, RenderError, Renderable}; use crate::utils; +use log::{debug, trace}; +use serde_json::json; type StringMap = BTreeMap<String, String>; @@ -146,15 +148,12 @@ fn render( trace!("Render template"); - _h.template() - .ok_or_else(|| RenderError::new("Error with the handlebars template")) - .and_then(|t| { - let local_ctx = Context::wraps(&context)?; - let mut local_rc = rc.clone(); - t.render(r, &local_ctx, &mut local_rc, out) - })?; - - Ok(()) + let t = _h + .template() + .ok_or_else(|| RenderError::new("Error with the handlebars template"))?; + let local_ctx = Context::wraps(&context)?; + let mut local_rc = rc.clone(); + t.render(r, &local_ctx, &mut local_rc, out) } pub fn previous( diff --git a/vendor/mdbook/src/renderer/html_handlebars/helpers/theme.rs b/vendor/mdbook/src/renderer/html_handlebars/helpers/theme.rs index 809ee1176..83aba6774 100644 --- a/vendor/mdbook/src/renderer/html_handlebars/helpers/theme.rs +++ b/vendor/mdbook/src/renderer/html_handlebars/helpers/theme.rs @@ -1,4 +1,5 @@ use handlebars::{Context, Handlebars, Helper, Output, RenderContext, RenderError}; +use log::trace; pub fn theme_option( h: &Helper<'_, '_>, diff --git a/vendor/mdbook/src/renderer/html_handlebars/helpers/toc.rs b/vendor/mdbook/src/renderer/html_handlebars/helpers/toc.rs index 0884d30ad..e96e6ef64 100644 --- a/vendor/mdbook/src/renderer/html_handlebars/helpers/toc.rs +++ b/vendor/mdbook/src/renderer/html_handlebars/helpers/toc.rs @@ -117,35 +117,35 @@ impl HelperDef for RenderToc { } // Link - let path_exists = if let Some(path) = - item.get("path") - .and_then(|p| if p.is_empty() { None } else { Some(p) }) - { - out.write("<a href=\"")?; - - let tmp = Path::new(item.get("path").expect("Error: path should be Some(_)")) - .with_extension("html") - .to_str() - .unwrap() - // Hack for windows who tends to use `\` as separator instead of `/` - .replace('\\', "/"); - - // Add link - out.write(&utils::fs::path_to_root(¤t_path))?; - out.write(&tmp)?; - out.write("\"")?; - - if path == ¤t_path || is_first_chapter { - is_first_chapter = false; - out.write(" class=\"active\"")?; - } + let path_exists: bool; + match item.get("path") { + Some(path) if !path.is_empty() => { + out.write("<a href=\"")?; + let tmp = Path::new(path) + .with_extension("html") + .to_str() + .unwrap() + // Hack for windows who tends to use `\` as separator instead of `/` + .replace('\\', "/"); + + // Add link + out.write(&utils::fs::path_to_root(¤t_path))?; + out.write(&tmp)?; + out.write("\"")?; + + if path == ¤t_path || is_first_chapter { + is_first_chapter = false; + out.write(" class=\"active\"")?; + } - out.write(">")?; - true - } else { - out.write("<div>")?; - false - }; + out.write(">")?; + path_exists = true; + } + _ => { + out.write("<div>")?; + path_exists = false; + } + } if !self.no_section_label { // Section does not necessarily exist diff --git a/vendor/mdbook/src/renderer/html_handlebars/search.rs b/vendor/mdbook/src/renderer/html_handlebars/search.rs index c3b944c9d..a9e2f5ca6 100644 --- a/vendor/mdbook/src/renderer/html_handlebars/search.rs +++ b/vendor/mdbook/src/renderer/html_handlebars/search.rs @@ -3,6 +3,7 @@ use std::collections::{HashMap, HashSet}; use std::path::Path; use elasticlunr::{Index, IndexBuilder}; +use once_cell::sync::Lazy; use pulldown_cmark::*; use crate::book::{Book, BookItem}; @@ -10,7 +11,7 @@ use crate::config::Search; use crate::errors::*; use crate::theme::searcher; use crate::utils; - +use log::{debug, warn}; use serde::Serialize; const MAX_WORD_LENGTH_TO_INDEX: usize = 80; @@ -266,21 +267,19 @@ fn write_to_json(index: Index, search_config: &Search, doc_urls: Vec<String>) -> } fn clean_html(html: &str) -> String { - lazy_static! { - static ref AMMONIA: ammonia::Builder<'static> = { - let mut clean_content = HashSet::new(); - clean_content.insert("script"); - clean_content.insert("style"); - let mut builder = ammonia::Builder::new(); - builder - .tags(HashSet::new()) - .tag_attributes(HashMap::new()) - .generic_attributes(HashSet::new()) - .link_rel(None) - .allowed_classes(HashMap::new()) - .clean_content_tags(clean_content); - builder - }; - } + static AMMONIA: Lazy<ammonia::Builder<'static>> = Lazy::new(|| { + let mut clean_content = HashSet::new(); + clean_content.insert("script"); + clean_content.insert("style"); + let mut builder = ammonia::Builder::new(); + builder + .tags(HashSet::new()) + .tag_attributes(HashMap::new()) + .generic_attributes(HashSet::new()) + .link_rel(None) + .allowed_classes(HashMap::new()) + .clean_content_tags(clean_content); + builder + }); AMMONIA.clean(html).to_string() } diff --git a/vendor/mdbook/src/renderer/markdown_renderer.rs b/vendor/mdbook/src/renderer/markdown_renderer.rs index bd5def1f4..13bd05cc3 100644 --- a/vendor/mdbook/src/renderer/markdown_renderer.rs +++ b/vendor/mdbook/src/renderer/markdown_renderer.rs @@ -2,7 +2,7 @@ use crate::book::BookItem; use crate::errors::*; use crate::renderer::{RenderContext, Renderer}; use crate::utils; - +use log::trace; use std::fs; #[derive(Default)] diff --git a/vendor/mdbook/src/renderer/mod.rs b/vendor/mdbook/src/renderer/mod.rs index 15465fbce..1c97f8f22 100644 --- a/vendor/mdbook/src/renderer/mod.rs +++ b/vendor/mdbook/src/renderer/mod.rs @@ -27,6 +27,7 @@ use std::process::{Command, Stdio}; use crate::book::Book; use crate::config::Config; use crate::errors::*; +use log::{error, info, trace, warn}; use toml::Value; use serde::{Deserialize, Serialize}; diff --git a/vendor/mdbook/src/theme/book.js b/vendor/mdbook/src/theme/book.js index d40440c72..e303ebb45 100644 --- a/vendor/mdbook/src/theme/book.js +++ b/vendor/mdbook/src/theme/book.js @@ -4,14 +4,16 @@ window.onunload = function () { }; // Global variable, shared between modules -function playground_text(playground) { +function playground_text(playground, hidden = true) { let code_block = playground.querySelector("code"); if (window.ace && code_block.classList.contains("editable")) { let editor = window.ace.edit(code_block); return editor.getValue(); - } else { + } else if (hidden) { return code_block.textContent; + } else { + return code_block.innerText; } } @@ -166,7 +168,6 @@ function playground_text(playground) { .filter(function (node) {return node.classList.contains("editable"); }) .forEach(function (block) { block.classList.remove('language-rust'); }); - Array code_nodes .filter(function (node) {return !node.classList.contains("editable"); }) .forEach(function (block) { hljs.highlightBlock(block); }); @@ -300,6 +301,13 @@ function playground_text(playground) { themePopup.querySelector("button#" + get_theme()).focus(); } + function updateThemeSelected() { + themePopup.querySelectorAll('.theme-selected').forEach(function (el) { + el.classList.remove('theme-selected'); + }); + themePopup.querySelector("button#" + get_theme()).classList.add('theme-selected'); + } + function hideThemes() { themePopup.style.display = 'none'; themeToggleButton.setAttribute('aria-expanded', false); @@ -355,6 +363,7 @@ function playground_text(playground) { html.classList.remove(previousTheme); html.classList.add(theme); + updateThemeSelected(); } // Set theme @@ -592,7 +601,7 @@ function playground_text(playground) { text: function (trigger) { hideTooltip(trigger); let playground = trigger.closest("pre"); - return playground_text(playground); + return playground_text(playground, false); } }); diff --git a/vendor/mdbook/src/theme/css/chrome.css b/vendor/mdbook/src/theme/css/chrome.css index 10fa4b365..59eae11fd 100644 --- a/vendor/mdbook/src/theme/css/chrome.css +++ b/vendor/mdbook/src/theme/css/chrome.css @@ -507,6 +507,8 @@ ul#searchresults span.teaser em { padding: 0; list-style: none; display: none; + /* Don't let the children's background extend past the rounded corners. */ + overflow: hidden; } .theme-popup .default { color: var(--icons); @@ -515,7 +517,7 @@ ul#searchresults span.teaser em { width: 100%; border: 0; margin: 0; - padding: 2px 10px; + padding: 2px 20px; line-height: 25px; white-space: nowrap; text-align: left; @@ -527,8 +529,10 @@ ul#searchresults span.teaser em { .theme-popup .theme:hover { background-color: var(--theme-hover); } -.theme-popup .theme:hover:first-child, -.theme-popup .theme:hover:last-child { - border-top-left-radius: inherit; - border-top-right-radius: inherit; + +.theme-selected::before { + display: inline-block; + content: "✓"; + margin-left: -14px; + width: 14px; } diff --git a/vendor/mdbook/src/theme/css/general.css b/vendor/mdbook/src/theme/css/general.css index 0e4f07a50..344b53eb7 100644 --- a/vendor/mdbook/src/theme/css/general.css +++ b/vendor/mdbook/src/theme/css/general.css @@ -22,8 +22,8 @@ body { } code { - font-family: "Source Code Pro", Consolas, "Ubuntu Mono", Menlo, "DejaVu Sans Mono", monospace, monospace !important; - font-size: 0.875em; /* please adjust the ace font size accordingly in editor.js */ + font-family: var(--mono-font) !important; + font-size: var(--code-font-size); } /* make long words/inline code not x overflow */ @@ -148,6 +148,18 @@ blockquote { border-bottom: .1em solid var(--quote-border); } +kbd { + background-color: var(--table-border-color); + border-radius: 4px; + border: solid 1px var(--theme-popup-border); + box-shadow: inset 0 -1px 0 var(--theme-hover); + display: inline-block; + font-size: var(--code-font-size); + font-family: var(--mono-font); + line-height: 10px; + padding: 4px 5px; + vertical-align: middle; +} :not(.footnote-definition) + .footnote-definition, .footnote-definition + :not(.footnote-definition) { diff --git a/vendor/mdbook/src/theme/css/variables.css b/vendor/mdbook/src/theme/css/variables.css index 56b634bc3..21bf8e55e 100644 --- a/vendor/mdbook/src/theme/css/variables.css +++ b/vendor/mdbook/src/theme/css/variables.css @@ -6,6 +6,8 @@ --page-padding: 15px; --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 */ } /* Themes */ diff --git a/vendor/mdbook/src/theme/index.hbs b/vendor/mdbook/src/theme/index.hbs index 18d984a2b..147eb9af2 100644 --- a/vendor/mdbook/src/theme/index.hbs +++ b/vendor/mdbook/src/theme/index.hbs @@ -15,7 +15,6 @@ <!-- Custom HTML head --> {{> head}} - <meta content="text/html; charset=utf-8" http-equiv="Content-Type"> <meta name="description" content="{{ description }}"> <meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="theme-color" content="#ffffff" /> @@ -51,18 +50,18 @@ {{#if mathjax_support}} <!-- MathJax --> - <script async type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script> + <script async src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script> {{/if}} </head> <body> <!-- Provide site root to javascript --> - <script type="text/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> <!-- Work around some values being stored in localStorage wrapped in quotes --> - <script type="text/javascript"> + <script> try { var theme = localStorage.getItem('mdbook-theme'); var sidebar = localStorage.getItem('mdbook-sidebar'); @@ -78,7 +77,7 @@ </script> <!-- Set the theme before any content is loaded, prevents flash --> - <script type="text/javascript"> + <script> var theme; try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { } if (theme === null || theme === undefined) { theme = default_theme; } @@ -90,7 +89,7 @@ </script> <!-- Hide / unhide sidebar before it is displayed --> - <script type="text/javascript"> + <script> var html = document.querySelector('html'); var sidebar = 'hidden'; if (document.body.clientWidth >= 1080) { @@ -122,11 +121,11 @@ <i class="fa fa-paint-brush"></i> </button> <ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu"> - <li role="none"><button role="menuitem" class="theme" id="light">{{ theme_option "Light" }}</button></li> - <li role="none"><button role="menuitem" class="theme" id="rust">{{ theme_option "Rust" }}</button></li> - <li role="none"><button role="menuitem" class="theme" id="coal">{{ theme_option "Coal" }}</button></li> - <li role="none"><button role="menuitem" class="theme" id="navy">{{ theme_option "Navy" }}</button></li> - <li role="none"><button role="menuitem" class="theme" id="ayu">{{ theme_option "Ayu" }}</button></li> + <li role="none"><button role="menuitem" class="theme" id="light">Light</button></li> + <li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li> + <li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li> + <li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li> + <li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li> </ul> {{#if search_enabled}} <button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar"> @@ -171,7 +170,7 @@ {{/if}} <!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM --> - <script type="text/javascript"> + <script> document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible'); document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible'); Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) { @@ -221,7 +220,7 @@ {{#if live_reload_endpoint}} <!-- Livereload script (if served using the cli tool) --> - <script type="text/javascript"> + <script> const wsProtocol = location.protocol === 'https:' ? 'wss:' : 'ws:'; const wsAddress = wsProtocol + "//" + location.host + "/" + "{{{live_reload_endpoint}}}"; const socket = new WebSocket(wsAddress); @@ -240,7 +239,7 @@ {{#if google_analytics}} <!-- Google Analytics Tag --> - <script type="text/javascript"> + <script> var localAddrs = ["localhost", "127.0.0.1", ""]; // make sure we don't activate google analytics if the developer is @@ -258,43 +257,43 @@ {{/if}} {{#if playground_line_numbers}} - <script type="text/javascript"> + <script> window.playground_line_numbers = true; </script> {{/if}} {{#if playground_copyable}} - <script type="text/javascript"> + <script> window.playground_copyable = true; </script> {{/if}} {{#if playground_js}} - <script src="{{ path_to_root }}ace.js" type="text/javascript" charset="utf-8"></script> - <script src="{{ path_to_root }}editor.js" type="text/javascript" charset="utf-8"></script> - <script src="{{ path_to_root }}mode-rust.js" type="text/javascript" charset="utf-8"></script> - <script src="{{ path_to_root }}theme-dawn.js" type="text/javascript" charset="utf-8"></script> - <script src="{{ path_to_root }}theme-tomorrow_night.js" type="text/javascript" charset="utf-8"></script> + <script src="{{ path_to_root }}ace.js"></script> + <script src="{{ path_to_root }}editor.js"></script> + <script src="{{ path_to_root }}mode-rust.js"></script> + <script src="{{ path_to_root }}theme-dawn.js"></script> + <script src="{{ path_to_root }}theme-tomorrow_night.js"></script> {{/if}} {{#if search_js}} - <script src="{{ path_to_root }}elasticlunr.min.js" type="text/javascript" charset="utf-8"></script> - <script src="{{ path_to_root }}mark.min.js" type="text/javascript" charset="utf-8"></script> - <script src="{{ path_to_root }}searcher.js" type="text/javascript" charset="utf-8"></script> + <script src="{{ path_to_root }}elasticlunr.min.js"></script> + <script src="{{ path_to_root }}mark.min.js"></script> + <script src="{{ path_to_root }}searcher.js"></script> {{/if}} - <script src="{{ path_to_root }}clipboard.min.js" type="text/javascript" charset="utf-8"></script> - <script src="{{ path_to_root }}highlight.js" type="text/javascript" charset="utf-8"></script> - <script src="{{ path_to_root }}book.js" type="text/javascript" charset="utf-8"></script> + <script src="{{ path_to_root }}clipboard.min.js"></script> + <script src="{{ path_to_root }}highlight.js"></script> + <script src="{{ path_to_root }}book.js"></script> <!-- Custom JS scripts --> {{#each additional_js}} - <script type="text/javascript" src="{{ ../path_to_root }}{{this}}"></script> + <script src="{{ ../path_to_root }}{{this}}"></script> {{/each}} {{#if is_print}} {{#if mathjax_support}} - <script type="text/javascript"> + <script> window.addEventListener('load', function() { MathJax.Hub.Register.StartupHook('End', function() { window.setTimeout(window.print, 100); @@ -302,7 +301,7 @@ }); </script> {{else}} - <script type="text/javascript"> + <script> window.addEventListener('load', function() { window.setTimeout(window.print, 100); }); diff --git a/vendor/mdbook/src/theme/mod.rs b/vendor/mdbook/src/theme/mod.rs index a1ee18aff..7af5e2b70 100644 --- a/vendor/mdbook/src/theme/mod.rs +++ b/vendor/mdbook/src/theme/mod.rs @@ -12,7 +12,7 @@ use std::io::Read; use std::path::Path; use crate::errors::*; - +use log::warn; pub static INDEX: &[u8] = include_bytes!("index.hbs"); pub static HEAD: &[u8] = include_bytes!("head.hbs"); pub static REDIRECT: &[u8] = include_bytes!("redirect.hbs"); diff --git a/vendor/mdbook/src/theme/redirect.hbs b/vendor/mdbook/src/theme/redirect.hbs index 9f49e6d09..d0532a504 100644 --- a/vendor/mdbook/src/theme/redirect.hbs +++ b/vendor/mdbook/src/theme/redirect.hbs @@ -3,8 +3,8 @@ <head> <meta charset="utf-8"> <title>Redirecting... - - + +

Redirecting to... {{url}}.

diff --git a/vendor/mdbook/src/utils/fs.rs b/vendor/mdbook/src/utils/fs.rs index a933d548a..0d6f38374 100644 --- a/vendor/mdbook/src/utils/fs.rs +++ b/vendor/mdbook/src/utils/fs.rs @@ -1,4 +1,5 @@ use crate::errors::*; +use log::{debug, trace}; use std::convert::Into; use std::fs::{self, File}; use std::io::Write; diff --git a/vendor/mdbook/src/utils/mod.rs b/vendor/mdbook/src/utils/mod.rs index a205633f9..9f67deda7 100644 --- a/vendor/mdbook/src/utils/mod.rs +++ b/vendor/mdbook/src/utils/mod.rs @@ -4,9 +4,10 @@ pub mod fs; mod string; pub(crate) mod toml_ext; use crate::errors::Error; -use regex::Regex; - +use log::error; +use once_cell::sync::Lazy; use pulldown_cmark::{html, CodeBlockKind, CowStr, Event, Options, Parser, Tag}; +use regex::Regex; use std::borrow::Cow; use std::collections::HashMap; @@ -20,9 +21,7 @@ pub use self::string::{ /// Replaces multiple consecutive whitespace characters with a single space character. pub fn collapse_whitespace(text: &str) -> Cow<'_, str> { - lazy_static! { - static ref RE: Regex = Regex::new(r"\s\s+").unwrap(); - } + static RE: Lazy = Lazy::new(|| Regex::new(r"\s\s+").unwrap()); RE.replace_all(text, " ") } @@ -51,9 +50,7 @@ pub fn id_from_content(content: &str) -> String { let mut content = content.to_string(); // Skip any tags or html-encoded stuff - lazy_static! { - static ref HTML: Regex = Regex::new(r"(<.*?>)").unwrap(); - } + static HTML: Lazy = Lazy::new(|| Regex::new(r"(<.*?>)").unwrap()); content = HTML.replace_all(&content, "").into(); const REPL_SUB: &[&str] = &["<", ">", "&", "'", """]; for sub in REPL_SUB { @@ -96,10 +93,9 @@ pub fn unique_id_from_content(content: &str, id_counter: &mut HashMap(event: Event<'a>, path: Option<&Path>) -> Event<'a> { - lazy_static! { - static ref SCHEME_LINK: Regex = Regex::new(r"^[a-z][a-z0-9+.-]*:").unwrap(); - static ref MD_LINK: Regex = Regex::new(r"(?P.*)\.md(?P#.*)?").unwrap(); - } + static SCHEME_LINK: Lazy = Lazy::new(|| Regex::new(r"^[a-z][a-z0-9+.-]*:").unwrap()); + static MD_LINK: Lazy = + Lazy::new(|| Regex::new(r"(?P.*)\.md(?P#.*)?").unwrap()); fn fix<'a>(dest: CowStr<'a>, path: Option<&Path>) -> CowStr<'a> { if dest.starts_with('#') { @@ -152,10 +148,8 @@ fn adjust_links<'a>(event: Event<'a>, path: Option<&Path>) -> Event<'a> { // There are dozens of HTML tags/attributes that contain paths, so // feel free to add more tags if desired; these are the only ones I // care about right now. - lazy_static! { - static ref HTML_LINK: Regex = - Regex::new(r#"(<(?:a|img) [^>]*?(?:src|href)=")([^"]+?)""#).unwrap(); - } + static HTML_LINK: Lazy = + Lazy::new(|| Regex::new(r#"(<(?:a|img) [^>]*?(?:src|href)=")([^"]+?)""#).unwrap()); HTML_LINK .replace_all(&html, |caps: ®ex::Captures<'_>| { diff --git a/vendor/mdbook/src/utils/string.rs b/vendor/mdbook/src/utils/string.rs index 97485d7b6..6dafe2603 100644 --- a/vendor/mdbook/src/utils/string.rs +++ b/vendor/mdbook/src/utils/string.rs @@ -1,3 +1,4 @@ +use once_cell::sync::Lazy; use regex::Regex; use std::ops::Bound::{Excluded, Included, Unbounded}; use std::ops::RangeBounds; @@ -23,10 +24,10 @@ pub fn take_lines>(s: &str, range: R) -> String { } } -lazy_static! { - static ref ANCHOR_START: Regex = Regex::new(r"ANCHOR:\s*(?P[\w_-]+)").unwrap(); - static ref ANCHOR_END: Regex = Regex::new(r"ANCHOR_END:\s*(?P[\w_-]+)").unwrap(); -} +static ANCHOR_START: Lazy = + Lazy::new(|| Regex::new(r"ANCHOR:\s*(?P[\w_-]+)").unwrap()); +static ANCHOR_END: Lazy = + Lazy::new(|| Regex::new(r"ANCHOR_END:\s*(?P[\w_-]+)").unwrap()); /// Take anchored lines from a string. /// Lines containing anchor are ignored. -- cgit v1.2.3