diff --git a/book-example/book.toml b/book-example/book.toml index 100247fa..ec4f41bb 100644 --- a/book-example/book.toml +++ b/book-example/book.toml @@ -10,6 +10,7 @@ mathjax-support = true [output.html.playpen] editable = true line-numbers = true +edition = "2018" [output.html.search] limit-results = 20 diff --git a/book-example/src/format/config.md b/book-example/src/format/config.md index e1145aa8..896012ad 100644 --- a/book-example/src/format/config.md +++ b/book-example/src/format/config.md @@ -178,7 +178,7 @@ The following configuration options are available: an icon link will be output in the menu bar of the book. - **git-repository-icon:** The FontAwesome icon class to use for the git repository link. Defaults to `fa-github`. - + Available configuration options for the `[output.html.fold]` table: - **enable:** Enable section-folding. When off, all folds are open. @@ -193,6 +193,7 @@ Available configuration options for the `[output.html.playpen]` table: - **copy-js:** Copy JavaScript files for the editor to the output directory. Defaults to `true`. - **line-numbers** Display line numbers on editable sections of code. Requires both `editable` and `copy-js` to be `true`. Defaults to `false`. +- **edition**: Rust edition to use by default for the code snippets. Defaults to `2015`. [Ace]: https://ace.c9.io/ diff --git a/src/book/mod.rs b/src/book/mod.rs index 8d69eea2..29d2dc3d 100644 --- a/src/book/mod.rs +++ b/src/book/mod.rs @@ -27,7 +27,7 @@ use crate::preprocess::{ use crate::renderer::{CmdRenderer, HtmlHandlebars, MarkdownRenderer, RenderContext, Renderer}; use crate::utils; -use crate::config::Config; +use crate::config::{Config, RustEdition}; /// The object used to manage and build a book. pub struct MDBook { @@ -262,11 +262,17 @@ impl MDBook { let mut tmpf = utils::fs::create_file(&path)?; tmpf.write_all(ch.content.as_bytes())?; - let output = Command::new("rustdoc") - .arg(&path) - .arg("--test") - .args(&library_args) - .output()?; + let mut cmd = Command::new("rustdoc"); + + cmd.arg(&path).arg("--test").args(&library_args); + + if let Some(html_cfg) = self.config.html_config() { + if html_cfg.playpen.edition == RustEdition::E2018 { + cmd.args(&["--edition", "2018"]); + } + } + + let output = cmd.output()?; if !output.status.success() { bail!(ErrorKind::Subprocess( diff --git a/src/config.rs b/src/config.rs index fae3da84..4c8624a6 100644 --- a/src/config.rs +++ b/src/config.rs @@ -513,6 +513,8 @@ pub struct Playpen { pub copy_js: bool, /// Display line numbers on playpen snippets. Default: `false`. pub line_numbers: bool, + /// Rust edition to use for the code. Default: `2015`. + pub edition: RustEdition, } impl Default for Playpen { @@ -522,10 +524,66 @@ impl Default for Playpen { copyable: true, copy_js: true, line_numbers: false, + edition: RustEdition::E2015, } } } +#[derive(Debug, Clone, PartialEq)] +/// The edition of Rust used in a playpen code snippet +pub enum RustEdition { + /// The 2018 edition of Rust + E2018, + /// The 2015 edition of Rust + E2015, +} + +impl Default for RustEdition { + fn default() -> Self { + RustEdition::E2015 + } +} + +impl Serialize for RustEdition { + fn serialize(&self, serializer: S) -> std::result::Result + where + S: Serializer, + { + match self { + RustEdition::E2015 => serializer.serialize_str("2015"), + RustEdition::E2018 => serializer.serialize_str("2018"), + } + } +} + +impl<'de> Deserialize<'de> for RustEdition { + fn deserialize(de: D) -> std::result::Result + where + D: Deserializer<'de>, + { + use serde::de::Error; + + let raw = Value::deserialize(de)?; + + let edition = match raw { + Value::String(s) => s, + _ => { + return Err(D::Error::custom("Rust edition should be a string")); + } + }; + + let edition = match edition.as_str() { + "2018" => RustEdition::E2018, + "2015" => RustEdition::E2015, + _ => { + return Err(D::Error::custom("Unknown Rust edition")); + } + }; + + Ok(edition) + } +} + /// Configuration of the search functionality of the HTML renderer. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[serde(default, rename_all = "kebab-case")] @@ -630,6 +688,7 @@ mod tests { [output.html.playpen] editable = true editor = "ace" + edition = "2018" [preprocessor.first] @@ -658,6 +717,7 @@ mod tests { copyable: true, copy_js: true, line_numbers: false, + edition: RustEdition::E2018, }; let html_should_be = HtmlConfig { curly_quotes: true, diff --git a/src/renderer/html_handlebars/hbs_renderer.rs b/src/renderer/html_handlebars/hbs_renderer.rs index 0be92562..aa2bdcc9 100644 --- a/src/renderer/html_handlebars/hbs_renderer.rs +++ b/src/renderer/html_handlebars/hbs_renderer.rs @@ -1,5 +1,5 @@ use crate::book::{Book, BookItem}; -use crate::config::{Config, HtmlConfig, Playpen}; +use crate::config::{Config, HtmlConfig, Playpen, RustEdition}; use crate::errors::*; use crate::renderer::html_handlebars::helpers; use crate::renderer::{RenderContext, Renderer}; @@ -608,14 +608,27 @@ fn add_playpen_pre(html: &str, playpen_config: &Playpen) -> String { let classes = &caps[2]; let code = &caps[3]; - if classes.contains("language-rust") { - if (!classes.contains("ignore") && !classes.contains("noplaypen")) - || classes.contains("mdbook-runnable") - { - // wrap the contents in an external pre block - format!( - "
{}
", - classes, + + if (classes.contains("language-rust") + && !classes.contains("ignore") + && !classes.contains("noplaypen")) + || classes.contains("mdbook-runnable") + { + let mut classes = classes.to_string(); + match playpen_config.edition { + RustEdition::E2018 => classes += " edition2018", + _ => (), + } + + // wrap the contents in an external pre block + format!( + "
{}
", + classes, + { + let content: Cow<'_, str> = if playpen_config.editable + && classes.contains("editable") + || text.contains("fn main") + || text.contains("quick_main!") { let content: Cow<'_, str> = if playpen_config.editable && classes.contains("editable") diff --git a/tests/rendered_output.rs b/tests/rendered_output.rs index 95ff8594..b041d3bc 100644 --- a/tests/rendered_output.rs +++ b/tests/rendered_output.rs @@ -490,12 +490,15 @@ fn markdown_options() { "bim", ], ); - assert_contains_strings(&path, &[ - r##"1"##, - r##"2"##, - r##"
1"##, - r##"
2"##, - ]); + assert_contains_strings( + &path, + &[ + r##"1"##, + r##"2"##, + r##"
1"##, + r##"
2"##, + ], + ); assert_contains_strings(&path, &["strikethrough example"]); assert_contains_strings( &path,