Reapply: Move hiding of boring lines into static content (#846) (#1065)

* Move hiding of boring lines into static content (#846)

* Fix test for hidden code
This commit is contained in:
Benedikt Werner 2019-10-16 11:27:14 +02:00 committed by Dylan DPC
parent d63ef8330d
commit 84a2ab0dba
4 changed files with 82 additions and 60 deletions

View file

@ -6,6 +6,7 @@ use crate::renderer::{RenderContext, Renderer};
use crate::theme::{self, playpen_editor, Theme}; use crate::theme::{self, playpen_editor, Theme};
use crate::utils; use crate::utils;
use std::borrow::Cow;
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::collections::HashMap; use std::collections::HashMap;
use std::fs; use std::fs;
@ -584,6 +585,7 @@ fn fix_code_blocks(html: &str) -> String {
} }
fn add_playpen_pre(html: &str, playpen_config: &Playpen) -> 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)<code[^>]?class="([^"]+)".*?>(.*?)</code>)"##).unwrap(); let regex = Regex::new(r##"((?s)<code[^>]?class="([^"]+)".*?>(.*?)</code>)"##).unwrap();
regex regex
.replace_all(html, |caps: &Captures<'_>| { .replace_all(html, |caps: &Captures<'_>| {
@ -597,21 +599,51 @@ fn add_playpen_pre(html: &str, playpen_config: &Playpen) -> String {
|| classes.contains("mdbook-runnable") || classes.contains("mdbook-runnable")
{ {
// wrap the contents in an external pre block // wrap the contents in an external pre block
if playpen_config.editable && classes.contains("editable") format!(
|| text.contains("fn main") "<pre class=\"playpen\"><code class=\"{}\">{}</code></pre>",
|| text.contains("quick_main!") classes,
{ {
format!("<pre class=\"playpen\">{}</pre>", text) let content: Cow<'_, str> = if playpen_config.editable
} else { && classes.contains("editable")
// we need to inject our own main || text.contains("fn main")
let (attrs, code) = partition_source(code); || text.contains("quick_main!")
{
code.into()
} else {
// we need to inject our own main
let (attrs, code) = partition_source(code);
format!( format!(
"<pre class=\"playpen\"><code class=\"{}\">\n# \ "\n# #![allow(unused_variables)]\n{}#fn main() {{\n{}#}}",
#![allow(unused_variables)]\n{}#fn main() {{\n{}#}}</code></pre>", attrs, code
classes, 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 += "<span class=\"boring\">";
prev_line_hidden = true;
}
result += &caps[1];
if &caps[2] != " " {
result += &caps[2];
}
result += &caps[3];
} else {
if prev_line_hidden {
result += "</span>";
prev_line_hidden = false;
}
result += line;
}
result += "\n";
}
result
}
)
} else { } else {
// not language-rust, so no-op // not language-rust, so no-op
text.to_owned() text.to_owned()
@ -687,4 +719,28 @@ mod tests {
assert_eq!(got, should_be); assert_eq!(got, should_be);
} }
} }
#[test]
fn add_playpen() {
let inputs = [
("<code class=\"language-rust\">x()</code>",
"<pre class=\"playpen\"><code class=\"language-rust\">\n<span class=\"boring\">#![allow(unused_variables)]\nfn main() {\n</span>x()\n<span class=\"boring\">}\n</code></pre>"),
("<code class=\"language-rust\">fn main() {}</code>",
"<pre class=\"playpen\"><code class=\"language-rust\">fn main() {}\n</code></pre>"),
("<code class=\"language-rust editable\">let s = \"foo\n # bar\n\";</code>",
"<pre class=\"playpen\"><code class=\"language-rust editable\">let s = \"foo\n<span class=\"boring\"> bar\n</span>\";\n</code></pre>"),
("<code class=\"language-rust editable\">let s = \"foo\n ## bar\n\";</code>",
"<pre class=\"playpen\"><code class=\"language-rust editable\">let s = \"foo\n # bar\n\";\n</code></pre>"),
];
for (src, should_be) in &inputs {
let got = add_playpen_pre(
src,
&Playpen {
editable: true,
..Playpen::default()
},
);
assert_eq!(&*got, *should_be);
}
}
} }

View file

@ -170,72 +170,34 @@ function playpen_text(playpen) {
Array.from(document.querySelectorAll("code.language-rust")).forEach(function (block) { Array.from(document.querySelectorAll("code.language-rust")).forEach(function (block) {
var code_block = block; var lines = Array.from(block.querySelectorAll('.boring'));
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] = "<span class=\"hidden\">" + "\n" + lines[n].replace(/(\s*)# ?/, "$1") + "</span>";
}
else {
lines[n] = "<span class=\"hidden\">" + lines[n].replace(/(\s*)# ?/, "$1") + "\n" + "</span>";
}
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("");
// If no lines were hidden, return // If no lines were hidden, return
if (!lines_hidden) { return; } if (!lines.length) { return; }
block.classList.add("hide-boring");
var buttons = document.createElement('div'); var buttons = document.createElement('div');
buttons.className = 'buttons'; buttons.className = 'buttons';
buttons.innerHTML = "<button class=\"fa fa-expand\" title=\"Show hidden lines\" aria-label=\"Show hidden lines\"></button>"; buttons.innerHTML = "<button class=\"fa fa-expand\" title=\"Show hidden lines\" aria-label=\"Show hidden lines\"></button>";
// add expand button // add expand button
var pre_block = block.parentNode;
pre_block.insertBefore(buttons, pre_block.firstChild); pre_block.insertBefore(buttons, pre_block.firstChild);
pre_block.querySelector('.buttons').addEventListener('click', function (e) { pre_block.querySelector('.buttons').addEventListener('click', function (e) {
if (e.target.classList.contains('fa-expand')) { if (e.target.classList.contains('fa-expand')) {
var lines = pre_block.querySelectorAll('span.hidden');
e.target.classList.remove('fa-expand'); e.target.classList.remove('fa-expand');
e.target.classList.add('fa-compress'); e.target.classList.add('fa-compress');
e.target.title = 'Hide lines'; e.target.title = 'Hide lines';
e.target.setAttribute('aria-label', e.target.title); e.target.setAttribute('aria-label', e.target.title);
Array.from(lines).forEach(function (line) { block.classList.remove('hide-boring');
line.classList.remove('hidden');
line.classList.add('unhidden');
});
} else if (e.target.classList.contains('fa-compress')) { } else if (e.target.classList.contains('fa-compress')) {
var lines = pre_block.querySelectorAll('span.unhidden');
e.target.classList.remove('fa-compress'); e.target.classList.remove('fa-compress');
e.target.classList.add('fa-expand'); e.target.classList.add('fa-expand');
e.target.title = 'Show hidden lines'; e.target.title = 'Show hidden lines';
e.target.setAttribute('aria-label', e.target.title); e.target.setAttribute('aria-label', e.target.title);
Array.from(lines).forEach(function (line) { block.classList.add('hide-boring');
line.classList.remove('unhidden');
line.classList.add('hidden');
});
} }
}); });
}); });

View file

@ -22,8 +22,9 @@ code {
.left { float: left; } .left { float: left; }
.right { float: right; } .right { float: right; }
.boring { opacity: 0.6; }
.hide-boring .boring { display: none; }
.hidden { display: none; } .hidden { display: none; }
.play-button.hidden { display: none; }
h2, h3 { margin-top: 2.5em; } h2, h3 { margin-top: 2.5em; }
h4, h5 { margin-top: 2em; } h4, h5 { margin-top: 2em; }

View file

@ -168,7 +168,10 @@ fn rustdoc_include_hides_the_unspecified_part_of_the_file() {
md.build().unwrap(); md.build().unwrap();
let nested = temp.path().join("book/first/nested.html"); let nested = temp.path().join("book/first/nested.html");
let text = vec!["# fn some_function() {", "# fn some_other_function() {"]; let text = vec![
"<span class=\"boring\">fn some_function() {",
"<span class=\"boring\">fn some_other_function() {",
];
assert_contains_strings(nested, &text); assert_contains_strings(nested, &text);
} }