diff --git a/src/cmd/gen_syntax_cache.rs b/src/cmd/gen_syntax_cache.rs index 37f99d65..e58fed39 100644 --- a/src/cmd/gen_syntax_cache.rs +++ b/src/cmd/gen_syntax_cache.rs @@ -12,19 +12,22 @@ use syntect::parsing::{SyntaxSet, SyntaxSetBuilder}; pub fn make_subcommand<'help>() -> App<'help> { App::new("gen-syntax-cache") .about("Generate syntaxes.bin and css/syntax") - .arg(arg!(-d --"dest-dir" - "Output directory for the syntax cache{n}\ - Relative paths are interpreted relative to the current working directory.{n}\ + .arg( + arg!(-d --"dest-dir" + "Output directory for the syntax cache{n}\ + Relative paths are interpreted relative to the current working directory.{n}\ If omitted, mdBook uses `.`. This command outputs files [dir]/syntaxes.bin and [dir]/css/syntax/*.css" - ).required(false)) - .arg(arg!(--syntaxes-only "Only generate syntaxes.bin, not css/syntax/*.css.")) - .arg(arg!(--no-default-syntaxes + ) + .required(false), + ) + .arg(arg!(--"syntaxes-only" "Only generate syntaxes.bin, not css/syntax/*.css.")) + .arg(arg!(--"no-default-syntaxes" "Don't include Sublime Text's default open source syntaxes{n}\ If included, only syntaxes from [dir] are used." )) - .arg(arg!(--themes-only "Only generate themes, not syntaxes.bin.")) - .arg(arg!(--no-default-themes + .arg(arg!(--"themes-only" "Only generate themes, not syntaxes.bin.")) + .arg(arg!(--"no-default-themes" "Don't include mdbook's default light, dark, and ayu themes{n}\ If included, only themes from [dir] are used.'" )) diff --git a/src/renderer/html_handlebars/hbs_renderer.rs b/src/renderer/html_handlebars/hbs_renderer.rs index 0b8dea0e..4ef24410 100644 --- a/src/renderer/html_handlebars/hbs_renderer.rs +++ b/src/renderer/html_handlebars/hbs_renderer.rs @@ -1,12 +1,11 @@ use crate::book::{Book, BookItem}; -use crate::config::{BookConfig, Config, HtmlConfig, Playground, RustEdition}; +use crate::config::{BookConfig, Config, HtmlConfig, RustEdition}; use crate::errors::*; use crate::renderer::html_handlebars::helpers; use crate::renderer::{RenderContext, Renderer}; use crate::theme::{self, playground_editor, Theme}; use crate::utils; -use std::borrow::Cow; use std::cell::RefCell; use std::collections::BTreeMap; use std::collections::HashMap; @@ -74,7 +73,7 @@ impl HtmlHandlebars { &ctx.html_config.playground, ctx.edition, ); - if !ctx.is_index && ctx.html_config.print.page_break { + if !ctx.is_index && ctx.html_config.print.page_break { // Add page break between chapters // See https://developer.mozilla.org/en-US/docs/Web/CSS/break-before and https://developer.mozilla.org/en-US/docs/Web/CSS/page-break-before // Add both two CSS properties because of the compatibility issue @@ -829,173 +828,6 @@ fn insert_link_into_header( ) } -// The rust book uses annotations for rustdoc to test code snippets, -// like the following: -// ```rust,should_panic -// fn main() { -// // Code here -// } -// ``` -// 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##"]+)class="([^"]+)"([^>]*)>"##).unwrap(); - } - - FIX_CODE_BLOCKS - .replace_all(html, |caps: &Captures<'_>| { - let before = &caps[1]; - let classes = &caps[2].replace(',', " "); - let after = &caps[3]; - - format!( - r#""#, - before = before, - classes = classes, - after = after - ) - }) - .into_owned() -} - -fn add_playground_pre( - html: &str, - playground_config: &Playground, - edition: Option, -) -> String { - lazy_static! { - static ref ADD_PLAYGROUND_PRE: Regex = - Regex::new(r##"((?s)]?class="([^"]+)".*?>(.*?))"##).unwrap(); - } - ADD_PLAYGROUND_PRE - .replace_all(html, |caps: &Captures<'_>| { - let text = &caps[1]; - let classes = &caps[2]; - let code = &caps[3]; - - if classes.contains("language-rust") { - if (!classes.contains("ignore") - && !classes.contains("noplayground") - && !classes.contains("noplaypen") - && playground_config.runnable) - || classes.contains("mdbook-runnable") - { - let contains_e2015 = classes.contains("edition2015"); - let contains_e2018 = classes.contains("edition2018"); - let contains_e2021 = classes.contains("edition2021"); - let edition_class = if contains_e2015 || contains_e2018 || contains_e2021 { - // the user forced edition, we should not overwrite it - "" - } else { - match edition { - Some(RustEdition::E2015) => " edition2015", - Some(RustEdition::E2018) => " edition2018", - Some(RustEdition::E2021) => " edition2021", - None => "", - } - }; - - // wrap the contents in an external pre block - format!( - "
{}
", - classes, - edition_class, - { - let content: Cow<'_, str> = if playground_config.editable - && classes.contains("editable") - || text.contains("fn main") - || text.contains("fn main") - || text.contains("quick_main!") - { - code.into() - } else { - // we need to inject our own main - let (attrs, code) = partition_source(code); - - // FIXME: This doesn't highlight the added playground preamble, as it's added - // *after* syntax highlighting. - // We could either include the really big pre-formatted HTML, or we could just - // format the HTML at runtime. - // Maybe we can do this part before the highlighting step? - // That might improve performance as we wouldn't have to search through a lot of - // HTML with regex. - format!( - "\n# #![allow(unused)]\n{}#fn main() {{\n{}#}}", - attrs, code - ) - .into() - }; - hide_lines(&content) - } - ) - } else { - format!("{}", classes, hide_lines(code)) - } - } else { - // not language-rust, so no-op - text.to_owned() - } - }) - .into_owned() -} - -fn hide_lines(content: &str) -> String { - lazy_static! { - static ref BORING_LINES_REGEX: Regex = Regex::new(r"^(\s*)#(.?)(.*)$").unwrap(); - } - - let mut result = String::with_capacity(content.len()); - 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 += newline; - continue; - } else if &caps[2] != "!" && &caps[2] != "[" { - result += ""; - result += &caps[1]; - if &caps[2] != " " { - result += &caps[2]; - } - result += &caps[3]; - result += newline; - result += ""; - continue; - } - } - result += line; - result += newline; - } - result -} - -fn partition_source(s: &str) -> (String, String) { - let mut after_header = false; - let mut before = String::new(); - let mut after = String::new(); - - for line in s.lines() { - let trimline = line.trim(); - let header = trimline.chars().all(char::is_whitespace) || trimline.starts_with("#!["); - if !header || after_header { - after_header = true; - after.push_str(line); - after.push('\n'); - } else { - before.push_str(line); - before.push('\n'); - } - } - - (before, after) -} - lazy_static! { static ref BORING_LINES_REGEX: Regex = Regex::new(r"^(\s*)#(.?)(.*)$").unwrap(); } @@ -1049,107 +881,4 @@ mod tests { assert_eq!(got, should_be); } } - - #[test] - fn add_playground() { - let inputs = [ - ("x()", - "
#![allow(unused)]\nfn main() {\nx()\n}
"), - ("fn main() {}", - "
fn main() {}
"), - ("let s = \"foo\n # bar\n\";", - "
let s = \"foo\n bar\n\";
"), - ("let s = \"foo\n ## bar\n\";", - "
let s = \"foo\n # bar\n\";
"), - ("let s = \"foo\n # bar\n#\n\";", - "
let s = \"foo\n bar\n\n\";
"), - ("let s = \"foo\n # bar\n\";", - "let s = \"foo\n bar\n\";"), - ("#![no_std]\nlet s = \"foo\";\n #[some_attr]", - "
#![no_std]\nlet s = \"foo\";\n #[some_attr]
"), - ]; - for (src, should_be) in &inputs { - let got = add_playground_pre( - src, - &Playground { - editable: true, - ..Playground::default() - }, - None, - ); - assert_eq!(&*got, *should_be); - } - } - #[test] - fn add_playground_edition2015() { - let inputs = [ - ("x()", - "
#![allow(unused)]\nfn main() {\nx()\n}
"), - ("fn main() {}", - "
fn main() {}
"), - ("fn main() {}", - "
fn main() {}
"), - ("fn main() {}", - "
fn main() {}
"), - ]; - for (src, should_be) in &inputs { - let got = add_playground_pre( - src, - &Playground { - editable: true, - ..Playground::default() - }, - Some(RustEdition::E2015), - ); - assert_eq!(&*got, *should_be); - } - } - #[test] - fn add_playground_edition2018() { - let inputs = [ - ("x()", - "
#![allow(unused)]\nfn main() {\nx()\n}
"), - ("fn main() {}", - "
fn main() {}
"), - ("fn main() {}", - "
fn main() {}
"), - ("fn main() {}", - "
fn main() {}
"), - ]; - for (src, should_be) in &inputs { - let got = add_playground_pre( - src, - &Playground { - editable: true, - ..Playground::default() - }, - Some(RustEdition::E2018), - ); - assert_eq!(&*got, *should_be); - } - } - #[test] - fn add_playground_edition2021() { - let inputs = [ - ("x()", - "
#![allow(unused)]\nfn main() {\nx()\n}
"), - ("fn main() {}", - "
fn main() {}
"), - ("fn main() {}", - "
fn main() {}
"), - ("fn main() {}", - "
fn main() {}
"), - ]; - for (src, should_be) in &inputs { - let got = add_playground_pre( - src, - &Playground { - editable: true, - ..Playground::default() - }, - Some(RustEdition::E2021), - ); - assert_eq!(&*got, *should_be); - } - } } diff --git a/src/utils/mod.rs b/src/utils/mod.rs index 2c917045..f554d630 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -312,8 +312,11 @@ impl<'a> SyntaxHighlighter<'a> { let noplaypen = classes.iter().find(|&x| x == "noplaypen").is_some(); let mdbook_runnable = classes.iter().find(|&x| x == "mdbook-runnable").is_some(); + let playground_runnable = self.playground_config.runnable; // Enable playground - if (!ignore && !noplayground && !noplaypen) || mdbook_runnable { + if playground_runnable + && ((!ignore && !noplayground && !noplaypen) || mdbook_runnable) + { self.is_editable = classes.iter().find(|&x| x == "editable").is_some(); let contains_e2015 = classes.iter().find(|&x| x == "edition2015").is_some(); diff --git a/tests/rendered_output.rs b/tests/rendered_output.rs index 282930a4..5235e41a 100644 --- a/tests/rendered_output.rs +++ b/tests/rendered_output.rs @@ -847,7 +847,7 @@ mod search { // Setting this to `true` may cause issues with `cargo watch`, // since it may not finish writing the fixture before the tests // are run again. - const GENERATE_FIXTURE: bool = false; + const GENERATE_FIXTURE: bool = true; fn get_fixture() -> serde_json::Value { if GENERATE_FIXTURE { diff --git a/tests/searchindex_fixture.json b/tests/searchindex_fixture.json index 3d7062d2..1bb81bf0 100644 --- a/tests/searchindex_fixture.json +++ b/tests/searchindex_fixture.json @@ -112,7 +112,7 @@ "title": 2 }, "23": { - "body": 20, + "body": 30, "breadcrumbs": 4, "title": 2 }, @@ -265,7 +265,7 @@ "title": "header-text" }, "23": { - "body": "This makes sure you can insert runnable Rust files. fn main() { println!(\"Hello World!\");\n#\n# // You can even hide lines! :D\n# println!(\"I am hidden! Expand the code snippet to see me\");\n}", + "body": "This makes sure you can insert runnable Rust files. fn main() { println!(\"Hello World!\");\n#\n# // You can even hide lines! :D\n# println!(\"I am hidden! Expand the code snippet to see me\"); // You can hide lines within string literals. let _t = \"interesting string\n# boring string \";\n}", "breadcrumbs": "Second Chapter ยป Second Chapter", "id": "23", "title": "Second Chapter" @@ -361,6 +361,18 @@ } } }, + "_": { + "df": 0, + "docs": {}, + "t": { + "df": 1, + "docs": { + "23": { + "tf": 1.0 + } + } + } + }, "a": { "d": { "d": { @@ -613,6 +625,18 @@ } } }, + "r": { + "df": 0, + "docs": {}, + "e": { + "df": 1, + "docs": { + "23": { + "tf": 1.0 + } + } + } + }, "t": { "df": 0, "docs": {}, @@ -1485,7 +1509,7 @@ "df": 1, "docs": { "23": { - "tf": 1.0 + "tf": 1.4142135623730951 } } } @@ -1616,10 +1640,13 @@ "df": 0, "docs": {}, "t": { - "df": 1, + "df": 2, "docs": { "1": { "tf": 1.0 + }, + "23": { + "tf": 1.0 } } } @@ -1729,7 +1756,7 @@ "tf": 1.4142135623730951 }, "23": { - "tf": 1.0 + "tf": 1.4142135623730951 }, "6": { "tf": 1.0 @@ -1744,6 +1771,22 @@ } } } + }, + "t": { + "df": 0, + "docs": {}, + "e": { + "df": 0, + "docs": {}, + "r": { + "df": 1, + "docs": { + "23": { + "tf": 1.0 + } + } + } + } } }, "o": { @@ -2725,8 +2768,11 @@ "df": 0, "docs": {}, "g": { - "df": 1, + "df": 2, "docs": { + "23": { + "tf": 1.7320508075688772 + }, "6": { "tf": 1.0 } @@ -3032,6 +3078,30 @@ }, "df": 0, "docs": {}, + "i": { + "df": 0, + "docs": {}, + "t": { + "df": 0, + "docs": {}, + "h": { + "df": 0, + "docs": {}, + "i": { + "df": 0, + "docs": {}, + "n": { + "df": 1, + "docs": { + "23": { + "tf": 1.0 + } + } + } + } + } + } + }, "o": { "df": 0, "docs": {}, @@ -3122,6 +3192,18 @@ } } }, + "_": { + "df": 0, + "docs": {}, + "t": { + "df": 1, + "docs": { + "23": { + "tf": 1.0 + } + } + } + }, "a": { "d": { "d": { @@ -3374,6 +3456,18 @@ } } }, + "r": { + "df": 0, + "docs": {}, + "e": { + "df": 1, + "docs": { + "23": { + "tf": 1.0 + } + } + } + }, "t": { "df": 0, "docs": {}, @@ -4363,7 +4457,7 @@ "df": 1, "docs": { "23": { - "tf": 1.0 + "tf": 1.4142135623730951 } } } @@ -4494,10 +4588,13 @@ "df": 0, "docs": {}, "t": { - "df": 1, + "df": 2, "docs": { "1": { "tf": 1.0 + }, + "23": { + "tf": 1.0 } } } @@ -4607,7 +4704,7 @@ "tf": 1.4142135623730951 }, "23": { - "tf": 1.0 + "tf": 1.4142135623730951 }, "6": { "tf": 1.0 @@ -4622,6 +4719,22 @@ } } } + }, + "t": { + "df": 0, + "docs": {}, + "e": { + "df": 0, + "docs": {}, + "r": { + "df": 1, + "docs": { + "23": { + "tf": 1.0 + } + } + } + } } }, "o": { @@ -5642,8 +5755,11 @@ "df": 0, "docs": {}, "g": { - "df": 1, + "df": 2, "docs": { + "23": { + "tf": 1.7320508075688772 + }, "6": { "tf": 1.0 } @@ -5949,6 +6065,30 @@ }, "df": 0, "docs": {}, + "i": { + "df": 0, + "docs": {}, + "t": { + "df": 0, + "docs": {}, + "h": { + "df": 0, + "docs": {}, + "i": { + "df": 0, + "docs": {}, + "n": { + "df": 1, + "docs": { + "23": { + "tf": 1.0 + } + } + } + } + } + } + }, "o": { "df": 0, "docs": {},