Skip to content

Commit

Permalink
Rebase onto current main branch
Browse files Browse the repository at this point in the history
  • Loading branch information
notriddle committed Aug 15, 2022
1 parent 9235cbf commit abc29e2
Show file tree
Hide file tree
Showing 5 changed files with 168 additions and 293 deletions.
19 changes: 11 additions & 8 deletions src/cmd/gen_syntax_cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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" <dir>
"Output directory for the syntax cache{n}\
Relative paths are interpreted relative to the current working directory.{n}\
.arg(
arg!(-d --"dest-dir" <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.'"
))
Expand Down
275 changes: 2 additions & 273 deletions src/renderer/html_handlebars/hbs_renderer.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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! {
static ref BORING_LINES_REGEX: Regex = Regex::new(r"^(\s*)#(.?)(.*)$").unwrap();
}
Expand Down Expand Up @@ -1049,107 +881,4 @@ mod tests {
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);
}
}
}
5 changes: 4 additions & 1 deletion src/utils/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
2 changes: 1 addition & 1 deletion tests/rendered_output.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Loading

0 comments on commit abc29e2

Please sign in to comment.