Rebase onto current main branch

This commit is contained in:
Michael Howell 2022-06-27 17:05:45 -07:00
parent 9235cbf683
commit abc29e2c7d
5 changed files with 168 additions and 293 deletions

View File

@ -12,19 +12,22 @@ use syntect::parsing::{SyntaxSet, SyntaxSetBuilder};
pub fn make_subcommand<'help>() -> App<'help> { pub fn make_subcommand<'help>() -> App<'help> {
App::new("gen-syntax-cache") App::new("gen-syntax-cache")
.about("Generate syntaxes.bin and css/syntax") .about("Generate syntaxes.bin and css/syntax")
.arg(arg!(-d --"dest-dir" <dir> .arg(
arg!(-d --"dest-dir" <dir>
"Output directory for the syntax cache{n}\ "Output directory for the syntax cache{n}\
Relative paths are interpreted relative to the current working directory.{n}\ Relative paths are interpreted relative to the current working directory.{n}\
If omitted, mdBook uses `.`. If omitted, mdBook uses `.`.
This command outputs files [dir]/syntaxes.bin and [dir]/css/syntax/*.css" 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.")) .required(false),
.arg(arg!(--no-default-syntaxes )
.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}\ "Don't include Sublime Text's default open source syntaxes{n}\
If included, only syntaxes from [dir] are used." If included, only syntaxes from [dir] are used."
)) ))
.arg(arg!(--themes-only "Only generate themes, not syntaxes.bin.")) .arg(arg!(--"themes-only" "Only generate themes, not syntaxes.bin."))
.arg(arg!(--no-default-themes .arg(arg!(--"no-default-themes"
"Don't include mdbook's default light, dark, and ayu themes{n}\ "Don't include mdbook's default light, dark, and ayu themes{n}\
If included, only themes from [dir] are used.'" If included, only themes from [dir] are used.'"
)) ))

View File

@ -1,12 +1,11 @@
use crate::book::{Book, BookItem}; 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::errors::*;
use crate::renderer::html_handlebars::helpers; use crate::renderer::html_handlebars::helpers;
use crate::renderer::{RenderContext, Renderer}; use crate::renderer::{RenderContext, Renderer};
use crate::theme::{self, playground_editor, Theme}; use crate::theme::{self, playground_editor, Theme};
use crate::utils; use crate::utils;
use std::borrow::Cow;
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::collections::HashMap; use std::collections::HashMap;
@ -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##"<code([^>]+)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#"<code{before}class="{classes}"{after}>"#,
before = before,
classes = classes,
after = after
)
})
.into_owned()
}
fn add_playground_pre(
html: &str,
playground_config: &Playground,
edition: Option<RustEdition>,
) -> String {
lazy_static! {
static ref ADD_PLAYGROUND_PRE: Regex =
Regex::new(r##"((?s)<code[^>]?class="([^"]+)".*?>(.*?)</code>)"##).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!(
"<pre class=\"playground\"><code class=\"{}{}\">{}</code></pre>",
classes,
edition_class,
{
let content: Cow<'_, str> = if playground_config.editable
&& classes.contains("editable")
|| text.contains("fn</span> </span><span class=\"syn-entity syn-name syn-function syn-rust\">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!("<code class=\"{}\">{}</code>", 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 += "<span class=\"boring\">";
result += &caps[1];
if &caps[2] != " " {
result += &caps[2];
}
result += &caps[3];
result += newline;
result += "</span>";
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! { lazy_static! {
static ref BORING_LINES_REGEX: Regex = Regex::new(r"^(\s*)#(.?)(.*)$").unwrap(); static ref BORING_LINES_REGEX: Regex = Regex::new(r"^(\s*)#(.?)(.*)$").unwrap();
} }
@ -1049,107 +881,4 @@ mod tests {
assert_eq!(got, should_be); assert_eq!(got, should_be);
} }
} }
#[test]
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\">}</span></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>",
"<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\";</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>\";</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>\";</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]</code></pre>"),
];
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 = [
("<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\">}</span></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>",
"<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() {}</code></pre>"),
];
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 = [
("<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\">}</span></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>",
"<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() {}</code></pre>"),
];
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 = [
("<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\">}</span></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>",
"<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() {}</code></pre>"),
];
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);
}
}
} }

View File

@ -312,8 +312,11 @@ impl<'a> SyntaxHighlighter<'a> {
let noplaypen = classes.iter().find(|&x| x == "noplaypen").is_some(); let noplaypen = classes.iter().find(|&x| x == "noplaypen").is_some();
let mdbook_runnable = let mdbook_runnable =
classes.iter().find(|&x| x == "mdbook-runnable").is_some(); classes.iter().find(|&x| x == "mdbook-runnable").is_some();
let playground_runnable = self.playground_config.runnable;
// Enable playground // 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(); self.is_editable = classes.iter().find(|&x| x == "editable").is_some();
let contains_e2015 = let contains_e2015 =
classes.iter().find(|&x| x == "edition2015").is_some(); classes.iter().find(|&x| x == "edition2015").is_some();

View File

@ -847,7 +847,7 @@ mod search {
// Setting this to `true` may cause issues with `cargo watch`, // Setting this to `true` may cause issues with `cargo watch`,
// since it may not finish writing the fixture before the tests // since it may not finish writing the fixture before the tests
// are run again. // are run again.
const GENERATE_FIXTURE: bool = false; const GENERATE_FIXTURE: bool = true;
fn get_fixture() -> serde_json::Value { fn get_fixture() -> serde_json::Value {
if GENERATE_FIXTURE { if GENERATE_FIXTURE {

View File

@ -112,7 +112,7 @@
"title": 2 "title": 2
}, },
"23": { "23": {
"body": 20, "body": 30,
"breadcrumbs": 4, "breadcrumbs": 4,
"title": 2 "title": 2
}, },
@ -265,7 +265,7 @@
"title": "header-text" "title": "header-text"
}, },
"23": { "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", "breadcrumbs": "Second Chapter » Second Chapter",
"id": "23", "id": "23",
"title": "Second Chapter" "title": "Second Chapter"
@ -361,6 +361,18 @@
} }
} }
}, },
"_": {
"df": 0,
"docs": {},
"t": {
"df": 1,
"docs": {
"23": {
"tf": 1.0
}
}
}
},
"a": { "a": {
"d": { "d": {
"d": { "d": {
@ -613,6 +625,18 @@
} }
} }
}, },
"r": {
"df": 0,
"docs": {},
"e": {
"df": 1,
"docs": {
"23": {
"tf": 1.0
}
}
}
},
"t": { "t": {
"df": 0, "df": 0,
"docs": {}, "docs": {},
@ -1485,7 +1509,7 @@
"df": 1, "df": 1,
"docs": { "docs": {
"23": { "23": {
"tf": 1.0 "tf": 1.4142135623730951
} }
} }
} }
@ -1616,10 +1640,13 @@
"df": 0, "df": 0,
"docs": {}, "docs": {},
"t": { "t": {
"df": 1, "df": 2,
"docs": { "docs": {
"1": { "1": {
"tf": 1.0 "tf": 1.0
},
"23": {
"tf": 1.0
} }
} }
} }
@ -1729,7 +1756,7 @@
"tf": 1.4142135623730951 "tf": 1.4142135623730951
}, },
"23": { "23": {
"tf": 1.0 "tf": 1.4142135623730951
}, },
"6": { "6": {
"tf": 1.0 "tf": 1.0
@ -1744,6 +1771,22 @@
} }
} }
} }
},
"t": {
"df": 0,
"docs": {},
"e": {
"df": 0,
"docs": {},
"r": {
"df": 1,
"docs": {
"23": {
"tf": 1.0
}
}
}
}
} }
}, },
"o": { "o": {
@ -2725,8 +2768,11 @@
"df": 0, "df": 0,
"docs": {}, "docs": {},
"g": { "g": {
"df": 1, "df": 2,
"docs": { "docs": {
"23": {
"tf": 1.7320508075688772
},
"6": { "6": {
"tf": 1.0 "tf": 1.0
} }
@ -3032,6 +3078,30 @@
}, },
"df": 0, "df": 0,
"docs": {}, "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": { "o": {
"df": 0, "df": 0,
"docs": {}, "docs": {},
@ -3122,6 +3192,18 @@
} }
} }
}, },
"_": {
"df": 0,
"docs": {},
"t": {
"df": 1,
"docs": {
"23": {
"tf": 1.0
}
}
}
},
"a": { "a": {
"d": { "d": {
"d": { "d": {
@ -3374,6 +3456,18 @@
} }
} }
}, },
"r": {
"df": 0,
"docs": {},
"e": {
"df": 1,
"docs": {
"23": {
"tf": 1.0
}
}
}
},
"t": { "t": {
"df": 0, "df": 0,
"docs": {}, "docs": {},
@ -4363,7 +4457,7 @@
"df": 1, "df": 1,
"docs": { "docs": {
"23": { "23": {
"tf": 1.0 "tf": 1.4142135623730951
} }
} }
} }
@ -4494,10 +4588,13 @@
"df": 0, "df": 0,
"docs": {}, "docs": {},
"t": { "t": {
"df": 1, "df": 2,
"docs": { "docs": {
"1": { "1": {
"tf": 1.0 "tf": 1.0
},
"23": {
"tf": 1.0
} }
} }
} }
@ -4607,7 +4704,7 @@
"tf": 1.4142135623730951 "tf": 1.4142135623730951
}, },
"23": { "23": {
"tf": 1.0 "tf": 1.4142135623730951
}, },
"6": { "6": {
"tf": 1.0 "tf": 1.0
@ -4622,6 +4719,22 @@
} }
} }
} }
},
"t": {
"df": 0,
"docs": {},
"e": {
"df": 0,
"docs": {},
"r": {
"df": 1,
"docs": {
"23": {
"tf": 1.0
}
}
}
}
} }
}, },
"o": { "o": {
@ -5642,8 +5755,11 @@
"df": 0, "df": 0,
"docs": {}, "docs": {},
"g": { "g": {
"df": 1, "df": 2,
"docs": { "docs": {
"23": {
"tf": 1.7320508075688772
},
"6": { "6": {
"tf": 1.0 "tf": 1.0
} }
@ -5949,6 +6065,30 @@
}, },
"df": 0, "df": 0,
"docs": {}, "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": { "o": {
"df": 0, "df": 0,
"docs": {}, "docs": {},