diff --git a/src/renderer/html_handlebars/hbs_renderer.rs b/src/renderer/html_handlebars/hbs_renderer.rs index f13f39a1..91f75676 100644 --- a/src/renderer/html_handlebars/hbs_renderer.rs +++ b/src/renderer/html_handlebars/hbs_renderer.rs @@ -6,6 +6,7 @@ use crate::renderer::{RenderContext, Renderer}; use crate::theme::{self, playpen_editor, Theme}; use crate::utils; +use std::borrow::Cow; use std::collections::BTreeMap; use std::collections::HashMap; use std::fs; @@ -584,6 +585,7 @@ fn fix_code_blocks(html: &str) -> String { } fn add_playpen_pre(html: &str, playpen_config: &Playpen) -> String { + let boring_line_regex = Regex::new(r"^(\s*)#(#|.)(.*)$").unwrap(); let regex = Regex::new(r##"((?s)]?class="([^"]+)".*?>(.*?))"##).unwrap(); regex .replace_all(html, |caps: &Captures<'_>| { @@ -597,21 +599,45 @@ fn add_playpen_pre(html: &str, playpen_config: &Playpen) -> String { || classes.contains("mdbook-runnable") { // wrap the contents in an external pre block - if playpen_config.editable && classes.contains("editable") - || text.contains("fn main") - || text.contains("quick_main!") - { - format!("
{}
", text) - } else { - // we need to inject our own main - let (attrs, code) = partition_source(code); + format!("
{}
", + classes, + { + let content: Cow = if playpen_config.editable && classes.contains("editable") + || text.contains("fn main") + || text.contains("quick_main!") + { + code.into() + } else { + // we need to inject our own main + let (attrs, code) = partition_source(code); - format!( - "
\n# \
-                         #![allow(unused_variables)]\n{}#fn main() {{\n{}#}}
", - classes, attrs, code - ) - } + format!("\n# #![allow(unused_variables)]\n{}#fn main() {{\n{}#}}", + attrs, code + ).into() + }; + let mut prev_line_hidden = false; + let mut result = String::with_capacity(content.len()); + for line in content.lines() { + if let Some(caps) = boring_line_regex.captures(line) { + if !prev_line_hidden && &caps[2] != "#" { + result += ""; + prev_line_hidden = true; + } + result += &caps[1]; + if &caps[2] != " " { result += &caps[2]; } + result += &caps[3]; + } else { + if prev_line_hidden { + result += ""; + prev_line_hidden = false; + } + result += line; + } + result += "\n"; + } + result + } + ) } else { // not language-rust, so no-op text.to_owned() @@ -687,4 +713,22 @@ mod tests { assert_eq!(got, should_be); } } + + #[test] + fn add_playpen() { + let inputs = [ + ("x()", + "
\n#![allow(unused_variables)]\nfn main() {\nx()\n}\n
"), + ("fn main() {}", + "
fn main() {}\n
"), + ("let s = \"foo\n # bar\n\";", + "
let s = \"foo\n bar\n\";\n
"), + ("let s = \"foo\n ## bar\n\";", + "
let s = \"foo\n # bar\n\";\n
"), + ]; + for (src, should_be) in &inputs { + let got = add_playpen_pre(src, &Playpen { editable: true, ..Playpen::default() }); + assert_eq!(&*got, *should_be); + } + } } diff --git a/src/theme/book.js b/src/theme/book.js index 263e8ad0..2c2ade22 100644 --- a/src/theme/book.js +++ b/src/theme/book.js @@ -161,72 +161,34 @@ function playpen_text(playpen) { Array.from(document.querySelectorAll("code.language-rust")).forEach(function (block) { - var code_block = block; - var pre_block = block.parentNode; - // hide lines - var lines = code_block.innerHTML.split("\n"); - var first_non_hidden_line = false; - var lines_hidden = false; - var trimmed_line = ""; - - for (var n = 0; n < lines.length; n++) { - trimmed_line = lines[n].trim(); - if (trimmed_line[0] == hiding_character && trimmed_line[1] != hiding_character) { - if (first_non_hidden_line) { - lines[n] = "" + "\n" + lines[n].replace(/(\s*)# ?/, "$1") + ""; - } - else { - lines[n] = "" + lines[n].replace(/(\s*)# ?/, "$1") + "\n" + ""; - } - lines_hidden = true; - } - else if (first_non_hidden_line) { - lines[n] = "\n" + lines[n]; - } - else { - first_non_hidden_line = true; - } - if (trimmed_line[0] == hiding_character && trimmed_line[1] == hiding_character) { - lines[n] = lines[n].replace("##", "#") - } - } - code_block.innerHTML = lines.join(""); - + var lines = Array.from(block.querySelectorAll('.boring')); // If no lines were hidden, return - if (!lines_hidden) { return; } + if (!lines.length) { return; } + block.classList.add("hide-boring"); var buttons = document.createElement('div'); buttons.className = 'buttons'; buttons.innerHTML = ""; // add expand button + var pre_block = block.parentNode; pre_block.insertBefore(buttons, pre_block.firstChild); pre_block.querySelector('.buttons').addEventListener('click', function (e) { if (e.target.classList.contains('fa-expand')) { - var lines = pre_block.querySelectorAll('span.hidden'); - e.target.classList.remove('fa-expand'); e.target.classList.add('fa-compress'); e.target.title = 'Hide lines'; e.target.setAttribute('aria-label', e.target.title); - Array.from(lines).forEach(function (line) { - line.classList.remove('hidden'); - line.classList.add('unhidden'); - }); + block.classList.remove('hide-boring'); } else if (e.target.classList.contains('fa-compress')) { - var lines = pre_block.querySelectorAll('span.unhidden'); - e.target.classList.remove('fa-compress'); e.target.classList.add('fa-expand'); e.target.title = 'Show hidden lines'; e.target.setAttribute('aria-label', e.target.title); - Array.from(lines).forEach(function (line) { - line.classList.remove('unhidden'); - line.classList.add('hidden'); - }); + block.classList.add('hide-boring'); } }); }); diff --git a/src/theme/css/general.css b/src/theme/css/general.css index b45f90fb..2f1a0083 100644 --- a/src/theme/css/general.css +++ b/src/theme/css/general.css @@ -22,8 +22,9 @@ code { .left { float: left; } .right { float: right; } +.boring { opacity: 0.6; } +.hide-boring .boring { display: none; } .hidden { display: none; } -.play-button.hidden { display: none; } h2, h3 { margin-top: 2.5em; } h4, h5 { margin-top: 2em; }