Use syntect instead of highlight.js

This commit is contained in:
ThePuzzlemaker 2021-08-02 21:34:56 -05:00 committed by Michael Howell
parent fff067b2a8
commit 8d8c103eca
24 changed files with 1877 additions and 376 deletions

153
Cargo.lock generated
View File

@ -2,6 +2,12 @@
# It is not intended for manual editing. # It is not intended for manual editing.
version = 3 version = 3
[[package]]
name = "adler"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]] [[package]]
name = "aho-corasick" name = "aho-corasick"
version = "0.7.18" version = "0.7.18"
@ -78,6 +84,15 @@ version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
[[package]]
name = "bincode"
version = "1.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"
dependencies = [
"serde",
]
[[package]] [[package]]
name = "bit-set" name = "bit-set"
version = "0.5.2" version = "0.5.2"
@ -158,6 +173,12 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b700ce4376041dcd0a327fd0097c41095743c4c8af8887265942faf1100bd040" checksum = "b700ce4376041dcd0a327fd0097c41095743c4c8af8887265942faf1100bd040"
[[package]]
name = "cc"
version = "1.0.71"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79c2681d6594606957bbb8631c4b90a7fcaaa72cdb714743a437b156d6a7eedd"
[[package]] [[package]]
name = "cfg-if" name = "cfg-if"
version = "0.1.10" version = "0.1.10"
@ -217,6 +238,15 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "crc32fast"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a"
dependencies = [
"cfg-if 1.0.0",
]
[[package]] [[package]]
name = "ctor" name = "ctor"
version = "0.1.20" version = "0.1.20"
@ -312,6 +342,18 @@ dependencies = [
"winapi 0.3.9", "winapi 0.3.9",
] ]
[[package]]
name = "flate2"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e6988e897c1c9c485f43b47a529cef42fde0547f9d8d41a7062518f1d8fc53f"
dependencies = [
"cfg-if 1.0.0",
"crc32fast",
"libc",
"miniz_oxide",
]
[[package]] [[package]]
name = "float-cmp" name = "float-cmp"
version = "0.9.0" version = "0.9.0"
@ -741,6 +783,21 @@ version = "0.2.100"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1fa8cddc8fbbee11227ef194b5317ed014b8acbf15139bd716a18ad3fe99ec5" checksum = "a1fa8cddc8fbbee11227ef194b5317ed014b8acbf15139bd716a18ad3fe99ec5"
[[package]]
name = "line-wrap"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f30344350a2a51da54c1d53be93fade8a237e545dbcc4bdbe635413f2117cab9"
dependencies = [
"safemem",
]
[[package]]
name = "linked-hash-map"
version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3"
[[package]] [[package]]
name = "log" name = "log"
version = "0.4.14" version = "0.4.14"
@ -823,6 +880,7 @@ dependencies = [
"serde", "serde",
"serde_json", "serde_json",
"shlex", "shlex",
"syntect",
"tempfile", "tempfile",
"tokio", "tokio",
"toml", "toml",
@ -853,6 +911,16 @@ dependencies = [
"unicase", "unicase",
] ]
[[package]]
name = "miniz_oxide"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b"
dependencies = [
"adler",
"autocfg",
]
[[package]] [[package]]
name = "mio" name = "mio"
version = "0.6.23" version = "0.6.23"
@ -997,6 +1065,28 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "onig"
version = "6.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b17403cf40f61e3ee059e3e90b7fc0a2953297168d4379b160f80d18fed848a4"
dependencies = [
"bitflags",
"lazy_static",
"libc",
"onig_sys",
]
[[package]]
name = "onig_sys"
version = "69.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5dd3eee045c84695b53b20255bb7317063df090b68e18bfac0abb6c39cf7f33e"
dependencies = [
"cc",
"pkg-config",
]
[[package]] [[package]]
name = "opaque-debug" name = "opaque-debug"
version = "0.2.3" version = "0.2.3"
@ -1156,6 +1246,26 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "pkg-config"
version = "0.3.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c9b1041b4387893b91ee6746cddfc28516aff326a3519fb2adf820932c5e6cb"
[[package]]
name = "plist"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a38d026d73eeaf2ade76309d0c65db5a35ecf649e3cec428db316243ea9d6711"
dependencies = [
"base64",
"chrono",
"indexmap",
"line-wrap",
"serde",
"xml-rs",
]
[[package]] [[package]]
name = "ppv-lite86" name = "ppv-lite86"
version = "0.2.10" version = "0.2.10"
@ -1395,6 +1505,12 @@ version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
[[package]]
name = "safemem"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072"
[[package]] [[package]]
name = "same-file" name = "same-file"
version = "1.0.6" version = "1.0.6"
@ -1565,6 +1681,28 @@ dependencies = [
"unicode-xid", "unicode-xid",
] ]
[[package]]
name = "syntect"
version = "4.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b20815bbe80ee0be06e6957450a841185fcf690fe0178f14d77a05ce2caa031"
dependencies = [
"bincode",
"bitflags",
"flate2",
"fnv",
"lazy_static",
"lazycell",
"onig",
"plist",
"regex-syntax",
"serde",
"serde_derive",
"serde_json",
"walkdir",
"yaml-rust",
]
[[package]] [[package]]
name = "tempfile" name = "tempfile"
version = "3.2.0" version = "3.2.0"
@ -1978,6 +2116,12 @@ dependencies = [
"winapi-build", "winapi-build",
] ]
[[package]]
name = "xml-rs"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2d7d3948613f75c98fd9328cfdcc45acc4d360655289d0a7d4ec931392200a3"
[[package]] [[package]]
name = "xml5ever" name = "xml5ever"
version = "0.16.1" version = "0.16.1"
@ -1989,3 +2133,12 @@ dependencies = [
"markup5ever", "markup5ever",
"time", "time",
] ]
[[package]]
name = "yaml-rust"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85"
dependencies = [
"linked-hash-map",
]

View File

@ -34,6 +34,7 @@ shlex = "1"
tempfile = "3.0" tempfile = "3.0"
toml = "0.5.1" toml = "0.5.1"
topological-sort = "0.1.0" topological-sort = "0.1.0"
syntect = { version = "4.6.0", default-features = false, features = ["regex-onig", "parsing", "html", "dump-load", "yaml-load"] }
# Watch feature # Watch feature
notify = { version = "4.0", optional = true } notify = { version = "4.0", optional = true }
@ -65,3 +66,8 @@ search = ["elasticlunr-rs", "ammonia"]
[[bin]] [[bin]]
doc = false doc = false
name = "mdbook" name = "mdbook"
# For profiling only.
# If this ends up uncommented in a commit, let me know.
[profile.release]
debug = true

View File

@ -20,5 +20,6 @@ shout-out to them!
- Vivek Akupatni ([apatniv](https://github.com/apatniv)) - Vivek Akupatni ([apatniv](https://github.com/apatniv))
- Eric Huss ([ehuss](https://github.com/ehuss)) - Eric Huss ([ehuss](https://github.com/ehuss))
- Josh Rotenberg ([joshrotenberg](https://github.com/joshrotenberg)) - Josh Rotenberg ([joshrotenberg](https://github.com/joshrotenberg))
- James ([ThePuzzlemaker](https://github.com/ThePuzzlemaker))
If you feel you're missing from this list, feel free to add yourself in a PR. If you feel you're missing from this list, feel free to add yourself in a PR.

View File

@ -151,11 +151,13 @@ impl BookBuilder {
let mut js = File::create(themedir.join("book.js"))?; let mut js = File::create(themedir.join("book.js"))?;
js.write_all(theme::JS)?; js.write_all(theme::JS)?;
let mut highlight_css = File::create(themedir.join("highlight.css"))?; let syntax_dir = cssdir.join("syntax");
highlight_css.write_all(theme::HIGHLIGHT_CSS)?; if !syntax_dir.exists() {
fs::create_dir(&syntax_dir)?;
}
let mut highlight_js = File::create(themedir.join("highlight.js"))?; let mut highlight_css = File::create(syntax_dir.join("light.css"))?;
highlight_js.write_all(theme::HIGHLIGHT_JS)?; highlight_css.write_all(theme::SYNTAX_LIGHT_CSS)?;
Ok(()) Ok(())
} }

View File

@ -7,6 +7,7 @@ use crate::theme::{self, playground_editor, Theme};
use crate::utils; use crate::utils;
use std::borrow::Cow; use std::borrow::Cow;
use std::cell::RefCell;
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::collections::HashMap; use std::collections::HashMap;
use std::fs::{self, File}; use std::fs::{self, File};
@ -15,13 +16,18 @@ use std::path::{Path, PathBuf};
use crate::utils::fs::get_404_output_file; use crate::utils::fs::get_404_output_file;
use handlebars::Handlebars; use handlebars::Handlebars;
use regex::{Captures, Regex}; use regex::{Captures, Regex};
use syntect::parsing::SyntaxSet;
#[derive(Default)] #[derive(Default)]
pub struct HtmlHandlebars; pub struct HtmlHandlebars {
pub(crate) syntaxes: RefCell<Option<SyntaxSet>>,
}
impl HtmlHandlebars { impl HtmlHandlebars {
pub fn new() -> Self { pub fn new() -> Self {
HtmlHandlebars HtmlHandlebars {
syntaxes: RefCell::new(None),
}
} }
fn render_item( fn render_item(
@ -52,11 +58,19 @@ impl HtmlHandlebars {
} }
let content = ch.content.clone(); let content = ch.content.clone();
let content = utils::render_markdown(&content, ctx.html_config.curly_quotes); let content = utils::render_markdown(
&content,
ctx.html_config.curly_quotes,
self.syntaxes.borrow().as_ref().unwrap(),
);
let fixed_content = let fixed_content = utils::render_markdown_with_path(
utils::render_markdown_with_path(&ch.content, ctx.html_config.curly_quotes, Some(path)); &ch.content,
if !ctx.is_index && ctx.html_config.print.page_break { ctx.html_config.curly_quotes,
Some(&path),
self.syntaxes.borrow().as_ref().unwrap(),
);
if !ctx.is_index && ctx.html_config.print.page_break {
// Add page break between chapters // 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 // 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 // Add both two CSS properties because of the compatibility issue
@ -153,7 +167,11 @@ impl HtmlHandlebars {
.to_string() .to_string()
} }
}; };
let html_content_404 = utils::render_markdown(&content_404, html_config.curly_quotes); let html_content_404 = utils::render_markdown(
&content_404,
html_config.curly_quotes,
self.syntaxes.borrow().as_ref().unwrap(),
);
let mut data_404 = data.clone(); let mut data_404 = data.clone();
let base_url = if let Some(site_url) = &html_config.site_url { let base_url = if let Some(site_url) = &html_config.site_url {
@ -232,10 +250,9 @@ impl HtmlHandlebars {
if let Some(contents) = &theme.favicon_svg { if let Some(contents) = &theme.favicon_svg {
write_file(destination, "favicon.svg", contents)?; write_file(destination, "favicon.svg", contents)?;
} }
write_file(destination, "highlight.css", &theme.highlight_css)?; write_file(destination, "css/syntax/ayu.css", &theme.syntax_ayu_css)?;
write_file(destination, "tomorrow-night.css", &theme.tomorrow_night_css)?; write_file(destination, "css/syntax/dark.css", &theme.syntax_dark_css)?;
write_file(destination, "ayu-highlight.css", &theme.ayu_highlight_css)?; write_file(destination, "css/syntax/light.css", &theme.syntax_light_css)?;
write_file(destination, "highlight.js", &theme.highlight_js)?;
write_file(destination, "clipboard.min.js", &theme.clipboard_js)?; write_file(destination, "clipboard.min.js", &theme.clipboard_js)?;
write_file( write_file(
destination, destination,
@ -438,6 +455,21 @@ impl HtmlHandlebars {
Ok(()) Ok(())
} }
fn build_syntaxset(&self, theme: &Theme, theme_dir: &Path) {
let base: SyntaxSet = syntect::dumps::from_binary(&theme.base_syntaxes);
let mut base_builder = base.into_builder();
let syntaxes_path = theme_dir.join("syntaxes");
if syntaxes_path.is_dir() {
if let Err(e) = base_builder.add_from_folder(&theme_dir.join("syntaxes"), true) {
warn!("Couldn't load custom syntax definitions: {}", e);
}
}
let syntaxes = base_builder.build();
self.syntaxes.replace(Some(syntaxes));
}
} }
// TODO(mattico): Remove some time after the 0.1.8 release // TODO(mattico): Remove some time after the 0.1.8 release
@ -501,7 +533,10 @@ impl Renderer for HtmlHandlebars {
warn!("Please move your theme files to `./theme` for them to continue being used"); warn!("Please move your theme files to `./theme` for them to continue being used");
} }
let theme = theme::Theme::new(theme_dir); let theme = theme::Theme::new(&theme_dir);
debug!("Collect syntaxes into a syntax set");
self.build_syntaxset(&theme, &theme_dir);
debug!("Register the index handlebars template"); debug!("Register the index handlebars template");
handlebars.register_template_string("index", String::from_utf8(theme.index.clone())?)?; handlebars.register_template_string("index", String::from_utf8(theme.index.clone())?)?;
@ -873,6 +908,7 @@ fn add_playground_pre(
{ {
let content: Cow<'_, str> = if playground_config.editable let content: Cow<'_, str> = if playground_config.editable
&& classes.contains("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("fn main")
|| text.contains("quick_main!") || text.contains("quick_main!")
{ {
@ -881,8 +917,18 @@ fn add_playground_pre(
// we need to inject our own main // we need to inject our own main
let (attrs, code) = partition_source(code); let (attrs, code) = partition_source(code);
format!("# #![allow(unused)]\n{}#fn main() {{\n{}#}}", attrs, code) // FIXME: This doesn't highlight the added playground preamble, as it's added
.into() // *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) hide_lines(&content)
} }

View File

@ -1,78 +0,0 @@
/*
Based off of the Ayu theme
Original by Dempfi (https://github.com/dempfi/ayu)
*/
.hljs {
display: block;
overflow-x: auto;
background: #191f26;
color: #e6e1cf;
}
.hljs-comment,
.hljs-quote {
color: #5c6773;
font-style: italic;
}
.hljs-variable,
.hljs-template-variable,
.hljs-attribute,
.hljs-attr,
.hljs-regexp,
.hljs-link,
.hljs-selector-id,
.hljs-selector-class {
color: #ff7733;
}
.hljs-number,
.hljs-meta,
.hljs-builtin-name,
.hljs-literal,
.hljs-type,
.hljs-params {
color: #ffee99;
}
.hljs-string,
.hljs-bullet {
color: #b8cc52;
}
.hljs-title,
.hljs-built_in,
.hljs-section {
color: #ffb454;
}
.hljs-keyword,
.hljs-selector-tag,
.hljs-symbol {
color: #ff7733;
}
.hljs-name {
color: #36a3d9;
}
.hljs-tag {
color: #00568d;
}
.hljs-emphasis {
font-style: italic;
}
.hljs-strong {
font-weight: bold;
}
.hljs-addition {
color: #91b362;
}
.hljs-deletion {
color: #d96c75;
}

View File

@ -101,7 +101,7 @@ function playground_text(playground) {
var result_block = code_block.querySelector(".result"); var result_block = code_block.querySelector(".result");
if (!result_block) { if (!result_block) {
result_block = document.createElement('code'); result_block = document.createElement('code');
result_block.className = 'result hljs language-bash'; result_block.className = 'result syn-source syn-bash';
code_block.append(result_block); code_block.append(result_block);
} }
@ -148,35 +148,14 @@ function playground_text(playground) {
.catch(error => result_block.innerText = "Playground Communication: " + error.message); .catch(error => result_block.innerText = "Playground Communication: " + error.message);
} }
// Syntax highlighting Configuration
hljs.configure({
tabReplace: ' ', // 4 spaces
languages: [], // Languages used for auto-detection
});
let code_nodes = Array let code_nodes = Array
.from(document.querySelectorAll('code')) .from(document.querySelectorAll('code'))
// Don't highlight `inline code` blocks in headers. // Don't highlight `inline code` blocks in headers.
.filter(function (node) {return !node.parentElement.classList.contains("header"); }); .filter(function (node) {return !node.parentElement.classList.contains("header"); });
if (window.ace) { // Adding the scode class gives code blocks the color css
// language-rust class needs to be removed for editable
// blocks or highlightjs will capture events
code_nodes
.filter(function (node) {return node.classList.contains("editable"); })
.forEach(function (block) { block.classList.remove('language-rust'); });
Array
code_nodes
.filter(function (node) {return !node.classList.contains("editable"); })
.forEach(function (block) { hljs.highlightBlock(block); });
} else {
code_nodes.forEach(function (block) { hljs.highlightBlock(block); });
}
// Adding the hljs class gives code blocks the color css
// even if highlighting doesn't apply // even if highlighting doesn't apply
code_nodes.forEach(function (block) { block.classList.add('hljs'); }); code_nodes.forEach(function (block) { block.classList.add('scode'); });
Array.from(document.querySelectorAll("code.language-rust")).forEach(function (block) { Array.from(document.querySelectorAll("code.language-rust")).forEach(function (block) {
@ -289,9 +268,9 @@ function playground_text(playground) {
var themePopup = document.getElementById('theme-list'); var themePopup = document.getElementById('theme-list');
var themeColorMetaTag = document.querySelector('meta[name="theme-color"]'); var themeColorMetaTag = document.querySelector('meta[name="theme-color"]');
var stylesheets = { var stylesheets = {
ayuHighlight: document.querySelector("[href$='ayu-highlight.css']"), ayuHighlight: document.querySelector("[href$='css/syntax/ayu.css']"),
tomorrowNight: document.querySelector("[href$='tomorrow-night.css']"), tomorrowNight: document.querySelector("[href$='css/syntax/dark.css']"),
highlight: document.querySelector("[href$='highlight.css']"), highlight: document.querySelector("[href$='css/syntax/light.css']"),
}; };
function showThemes() { function showThemes() {

View File

@ -14,7 +14,7 @@ html {
#searchresults a, #searchresults a,
.content a:link, .content a:link,
a:visited, a:visited,
a > .hljs { a > .scode {
color: var(--links); color: var(--links);
} }
@ -187,18 +187,18 @@ a > .hljs {
/* Inline code */ /* Inline code */
:not(pre) > .hljs { :not(pre) > .scode {
display: inline; display: inline;
padding: 0.1em 0.3em; padding: 0.1em 0.3em;
border-radius: 3px; border-radius: 3px;
} }
:not(pre):not(a) > .hljs { :not(pre):not(a) > .scode {
color: var(--inline-code-color); color: var(--inline-code-color);
overflow-x: initial; overflow-x: initial;
} }
a:hover > .hljs { a:hover > .scode {
text-decoration: underline; text-decoration: underline;
} }

View File

@ -0,0 +1,412 @@
/*
* theme "ayu" generated by syntect
* based off of Ayu by Dempfi
* https://github.com/dempfi/ayu/blob/master/ayu-dark.sublime-color-scheme
*/
.syn-code {
color: #b3b1ad;
background-color: #0a0e14;
}
.syn-comment {
color: #626a73;
font-style: italic;
}
.syn-string {
color: #c2d94c;
}
.syn-constant.syn-other.syn-symbol {
color: #c2d94c;
}
.syn-string.syn-regexp {
color: #95e6cb;
}
.syn-constant.syn-character {
color: #95e6cb;
}
.syn-constant.syn-other {
color: #95e6cb;
}
.syn-constant.syn-numeric {
color: #e6b450;
}
.syn-constant.syn-language {
color: #e6b450;
}
.syn-meta.syn-constant {
color: #ffee99;
}
.syn-entity.syn-name.syn-constant {
color: #ffee99;
}
.syn-variable {
color: #b3b1ad;
}
.syn-variable.syn-member {
color: #f07178;
}
.syn-variable.syn-language {
color: #39bae6;
font-style: italic;
}
.syn-storage {
color: #ff8f40;
}
.syn-storage.syn-type.syn-keyword {
color: #ff8f40;
}
.syn-keyword {
color: #ff8f40;
}
.syn-source.syn-java {
color: #ff8f40;
}
.syn-meta.syn-class.syn-java {
color: #ff8f40;
}
.syn-meta.syn-class.syn-identifier.syn-java {
color: #ff8f40;
}
.syn-storage.syn-type.syn-java {
color: #ff8f40;
}
.syn-keyword.syn-operator {
color: #f29668;
}
.syn-punctuation.syn-separator {
color: #b3b1ad;
}
.syn-punctuation.syn-terminator {
color: #b3b1ad;
}
.syn-punctuation.syn-section {
color: #b3b1ad;
}
.syn-punctuation.syn-accessor {
color: #f29668;
}
.syn-source.syn-java {
color: #59c2ff;
}
.syn-storage.syn-type {
color: #59c2ff;
}
.syn-source.syn-haskell {
color: #59c2ff;
}
.syn-storage.syn-type {
color: #59c2ff;
}
.syn-source.syn-c {
color: #59c2ff;
}
.syn-storage.syn-type {
color: #59c2ff;
}
.syn-entity.syn-other.syn-inherited-class {
color: #39bae6;
}
.syn-storage.syn-type.syn-function {
color: #ff8f40;
}
.syn-source.syn-java {
color: #39bae6;
}
.syn-storage.syn-type.syn-primitive {
color: #39bae6;
}
.syn-entity.syn-name.syn-function {
color: #ffb454;
}
.syn-variable.syn-parameter {
color: #ffee99;
}
.syn-meta.syn-parameter {
color: #ffee99;
}
.syn-variable.syn-function {
color: #ffb454;
}
.syn-variable.syn-annotation {
color: #ffb454;
}
.syn-meta.syn-function-call.syn-generic {
color: #ffb454;
}
.syn-support.syn-function.syn-go {
color: #ffb454;
}
.syn-support.syn-function {
color: #f07178;
}
.syn-support.syn-macro {
color: #f07178;
}
.syn-entity.syn-name.syn-import {
color: #c2d94c;
}
.syn-entity.syn-name.syn-package {
color: #c2d94c;
}
.syn-entity.syn-name {
color: #59c2ff;
}
.syn-source.syn-js {
color: #59c2ff;
}
.syn-meta.syn-function-call.syn-constructor {
color: #59c2ff;
}
.syn-variable.syn-type {
color: #59c2ff;
}
.syn-entity.syn-name.syn-tag {
color: #39bae6;
}
.syn-meta.syn-tag.syn-sgml {
color: #39bae6;
}
.syn-punctuation.syn-definition.syn-tag.syn-end {
color: #39bae6;
}
.syn-punctuation.syn-definition.syn-tag.syn-begin {
color: #39bae6;
}
.syn-punctuation.syn-definition.syn-tag {
color: #39bae6;
}
.syn-entity.syn-other.syn-attribute-name {
color: #ffb454;
}
.syn-support.syn-constant {
color: #f29668;
font-style: italic;
}
.syn-support.syn-type {
color: #39bae6;
}
.syn-support.syn-class {
color: #39bae6;
}
.syn-source.syn-go {
color: #39bae6;
}
.syn-storage.syn-type {
color: #39bae6;
}
.syn-meta.syn-decorator {
color: #e6b673;
}
.syn-variable.syn-other {
color: #e6b673;
}
.syn-meta.syn-decorator {
color: #e6b673;
}
.syn-punctuation.syn-decorator {
color: #e6b673;
}
.syn-storage.syn-type.syn-annotation {
color: #e6b673;
}
.syn-variable.syn-annotation {
color: #e6b673;
}
.syn-punctuation.syn-definition.syn-annotation {
color: #e6b673;
}
.syn-invalid {
color: #ff3333;
}
.syn-meta.syn-diff {
color: #c594c5;
}
.syn-meta.syn-diff.syn-header {
color: #c594c5;
}
.syn-source.syn-ruby {
color: #ffb454;
}
.syn-variable.syn-other.syn-readwrite {
color: #ffb454;
}
.syn-source.syn-css {
color: #59c2ff;
}
.syn-entity.syn-name.syn-tag {
color: #59c2ff;
}
.syn-source.syn-sass {
color: #59c2ff;
}
.syn-entity.syn-name.syn-tag {
color: #59c2ff;
}
.syn-source.syn-scss {
color: #59c2ff;
}
.syn-entity.syn-name.syn-tag {
color: #59c2ff;
}
.syn-source.syn-less {
color: #59c2ff;
}
.syn-entity.syn-name.syn-tag {
color: #59c2ff;
}
.syn-source.syn-stylus {
color: #59c2ff;
}
.syn-entity.syn-name.syn-tag {
color: #59c2ff;
}
.syn-source.syn-css {
color: #626a73;
}
.syn-support.syn-type {
color: #626a73;
}
.syn-source.syn-sass {
color: #626a73;
}
.syn-support.syn-type {
color: #626a73;
}
.syn-source.syn-scss {
color: #626a73;
}
.syn-support.syn-type {
color: #626a73;
}
.syn-source.syn-less {
color: #626a73;
}
.syn-support.syn-type {
color: #626a73;
}
.syn-source.syn-stylus {
color: #626a73;
}
.syn-support.syn-type {
color: #626a73;
}
.syn-support.syn-type.syn-property-name {
color: #39bae6;
}
.syn-constant.syn-numeric.syn-line-number.syn-find-in-files {
color: #626a73;
}
.syn-constant.syn-numeric.syn-line-number.syn-match {
color: #ff8f40;
}
.syn-entity.syn-name.syn-filename.syn-find-in-files {
color: #c2d94c;
}
.syn-message.syn-error {
color: #ff3333;
}
.syn-markup.syn-heading {
color: #c2d94c;
font-weight: bold;
}
.syn-markup.syn-heading {
color: #c2d94c;
font-weight: bold;
}
.syn-entity.syn-name {
color: #c2d94c;
font-weight: bold;
}
.syn-markup.syn-underline.syn-link {
color: #39bae6;
}
.syn-string.syn-other.syn-link {
color: #39bae6;
}
.syn-markup.syn-italic {
color: #f07178;
font-style: italic;
}
.syn-markup.syn-bold {
color: #f07178;
font-weight: bold;
}
.syn-markup.syn-italic {
font-weight: bold;
font-style: italic;
}
.syn-markup.syn-bold {
font-weight: bold;
font-style: italic;
}
.syn-markup.syn-bold {
font-weight: bold;
font-style: italic;
}
.syn-markup.syn-italic {
font-weight: bold;
font-style: italic;
}
.syn-markup.syn-raw {
background: #191f26;
}
.syn-markup.syn-raw.syn-inline {
background: #191f26;
}
.syn-meta.syn-separator {
color: #626a73;
background-color: #b3b1ad;
font-weight: bold;
}
.syn-markup.syn-quote {
color: #95e6cb;
font-style: italic;
}
.syn-markup.syn-list {
color: #ffb454;
}
.syn-punctuation.syn-definition.syn-list.syn-begin {
color: #ffb454;
}
.syn-markup.syn-inserted {
color: #91b362;
}
.syn-markup.syn-changed {
color: #6994bf;
}
.syn-markup.syn-deleted {
color: #d96c75;
}
.syn-markup.syn-strike {
color: #e6b673;
}
.syn-markup.syn-table {
color: #39bae6;
background-color: #b3b1ad;
}
.syn-text.syn-html.syn-markdown {
color: #f29668;
}
.syn-markup.syn-inline.syn-raw {
color: #f29668;
}
.syn-text.syn-html.syn-markdown {
color: #626a73;
background-color: #626a73;
}
.syn-meta.syn-dummy.syn-line-break {
color: #626a73;
background-color: #626a73;
}
.syn-punctuation.syn-definition.syn-markdown {
color: #626a73;
background-color: #b3b1ad;
}
.scode {
display: block;
overflow-x: auto;
background: #191f26;
color: #e6e1cf;
padding: 0.5em;
}

View File

@ -0,0 +1,222 @@
* theme "Base16 Tomorrow Night" generated by syntect
* based off of Base16 Tomorrow Night by Chris Kempson
* https://github.com/chriskempson/base16-textmate/blob/master/Themes/base16-tomorrow-night.tmTheme
*/
.syn-code {
color: #c5c8c6;
background-color: #1d1f21;
}
.syn-variable.syn-parameter.syn-function {
color: #c5c8c6;
}
.syn-comment {
color: #969896;
}
.syn-punctuation.syn-definition.syn-comment {
color: #969896;
}
.syn-punctuation.syn-definition.syn-string {
color: #c5c8c6;
}
.syn-punctuation.syn-definition.syn-variable {
color: #c5c8c6;
}
.syn-punctuation.syn-definition.syn-string {
color: #c5c8c6;
}
.syn-punctuation.syn-definition.syn-parameters {
color: #c5c8c6;
}
.syn-punctuation.syn-definition.syn-string {
color: #c5c8c6;
}
.syn-punctuation.syn-definition.syn-array {
color: #c5c8c6;
}
.syn-none {
color: #c5c8c6;
}
.syn-keyword.syn-operator {
color: #c5c8c6;
}
.syn-keyword {
color: #b294bb;
}
.syn-variable {
color: #cc6666;
}
.syn-entity.syn-name.syn-function {
color: #81a2be;
}
.syn-meta.syn-require {
color: #81a2be;
}
.syn-support.syn-function.syn-any-method {
color: #81a2be;
}
.syn-entity.syn-name.syn-label {
color: #a3685a;
}
.syn-support.syn-class {
color: #f0c674;
}
.syn-entity.syn-name.syn-class {
color: #f0c674;
}
.syn-entity.syn-name.syn-type.syn-class {
color: #f0c674;
}
.syn-meta.syn-class {
color: #ffffff;
}
.syn-keyword.syn-other.syn-special-method {
color: #81a2be;
}
.syn-storage {
color: #b294bb;
}
.syn-support.syn-function {
color: #8abeb7;
}
.syn-string {
color: #b5bd68;
}
.syn-constant.syn-other.syn-symbol {
color: #b5bd68;
}
.syn-entity.syn-other.syn-inherited-class {
color: #b5bd68;
}
.syn-constant.syn-numeric {
color: #de935f;
}
.syn-none {
color: #de935f;
}
.syn-none {
color: #de935f;
}
.syn-constant {
color: #de935f;
}
.syn-entity.syn-name.syn-tag {
color: #cc6666;
}
.syn-entity.syn-other.syn-attribute-name {
color: #de935f;
}
.syn-entity.syn-other.syn-attribute-name.syn-id {
color: #81a2be;
}
.syn-punctuation.syn-definition.syn-entity {
color: #81a2be;
}
.syn-meta.syn-selector {
color: #b294bb;
}
.syn-none {
color: #de935f;
}
.syn-markup.syn-heading {
color: #81a2be;
}
.syn-punctuation.syn-definition.syn-heading {
color: #81a2be;
}
.syn-entity.syn-name.syn-section {
color: #81a2be;
}
.syn-keyword.syn-other.syn-unit {
color: #de935f;
}
.syn-markup.syn-bold {
color: #f0c674;
font-weight: bold;
}
.syn-punctuation.syn-definition.syn-bold {
color: #f0c674;
font-weight: bold;
}
.syn-markup.syn-italic {
color: #b294bb;
font-style: italic;
}
.syn-punctuation.syn-definition.syn-italic {
color: #b294bb;
font-style: italic;
}
.syn-markup.syn-raw.syn-inline {
color: #b5bd68;
}
.syn-string.syn-other.syn-link {
color: #cc6666;
}
.syn-punctuation.syn-definition.syn-string.syn-end.syn-markdown {
color: #cc6666;
}
.syn-punctuation.syn-definition.syn-string.syn-begin.syn-markdown {
color: #cc6666;
}
.syn-meta.syn-link {
color: #de935f;
}
.syn-markup.syn-list {
color: #cc6666;
}
.syn-markup.syn-quote {
color: #de935f;
}
.syn-meta.syn-separator {
color: #c5c8c6;
background-color: #373b41;
}
.syn-markup.syn-inserted {
color: #b5bd68;
}
.syn-markup.syn-deleted {
color: #cc6666;
}
.syn-markup.syn-changed {
color: #b294bb;
}
.syn-constant.syn-other.syn-color {
color: #8abeb7;
}
.syn-string.syn-regexp {
color: #8abeb7;
}
.syn-constant.syn-character.syn-escape {
color: #8abeb7;
}
.syn-punctuation.syn-section.syn-embedded {
color: #b294bb;
}
.syn-variable.syn-interpolation {
color: #b294bb;
}
.syn-invalid.syn-illegal {
color: #ffffff;
background-color: #cc6666;
}
.syn-invalid.syn-broken {
color: #1d1f21;
background-color: #de935f;
}
.syn-invalid.syn-deprecated {
color: #ffffff;
background-color: #a3685a;
}
.syn-invalid.syn-unimplemented {
color: #ffffff;
background-color: #969896;
}
.scode {
display: block;
overflow-x: auto;
background: #1d1f21;
color: #c5c8c6;
padding: 0.5em;
-webkit-text-size-adjust: none;
}

View File

@ -0,0 +1,215 @@
/*
* theme "Base16 Atelier Dune Light" generated by syntect
* Based off of the "Base16 Atelier Dune Light" theme by Bram de Haan
* (http://atelierbram.github.io/syntax-highlighting/atelier-schemes/dune)
* Original Base16 color scheme by Chris Kempson
* (https://github.com/chriskempson/base16)
*/
.syn-code {
color: #6e6b5e;
background-color: #fefbec;
}
.syn-variable.syn-parameter.syn-function {
color: #6e6b5e;
}
.syn-comment {
color: #999580;
}
.syn-punctuation.syn-definition.syn-comment {
color: #999580;
}
.syn-punctuation.syn-definition.syn-string {
color: #6e6b5e;
}
.syn-punctuation.syn-definition.syn-variable {
color: #6e6b5e;
}
.syn-punctuation.syn-definition.syn-string {
color: #6e6b5e;
}
.syn-punctuation.syn-definition.syn-parameters {
color: #6e6b5e;
}
.syn-punctuation.syn-definition.syn-string {
color: #6e6b5e;
}
.syn-punctuation.syn-definition.syn-array {
color: #6e6b5e;
}
.syn-none {
color: #6e6b5e;
}
.syn-keyword.syn-operator {
color: #6e6b5e;
}
.syn-keyword {
color: #b854d4;
}
.syn-variable {
color: #d73737;
}
.syn-entity.syn-name.syn-function {
color: #6684e1;
}
.syn-meta.syn-require {
color: #6684e1;
}
.syn-support.syn-function.syn-any-method {
color: #6684e1;
}
.syn-support.syn-class {
color: #b65611;
}
.syn-entity.syn-name.syn-class {
color: #b65611;
}
.syn-entity.syn-name.syn-type.syn-class {
color: #b65611;
}
.syn-meta.syn-class {
color: #292824;
}
.syn-keyword.syn-other.syn-special-method {
color: #6684e1;
}
.syn-storage {
color: #b854d4;
}
.syn-support.syn-function {
color: #1fad83;
}
.syn-string {
color: #60ac39;
}
.syn-constant.syn-other.syn-symbol {
color: #60ac39;
}
.syn-entity.syn-other.syn-inherited-class {
color: #60ac39;
}
.syn-constant.syn-numeric {
color: #b65611;
}
.syn-none {
color: #b65611;
}
.syn-none {
color: #b65611;
}
.syn-constant {
color: #b65611;
}
.syn-entity.syn-name.syn-tag {
color: #d73737;
}
.syn-entity.syn-other.syn-attribute-name {
color: #b65611;
}
.syn-entity.syn-other.syn-attribute-name.syn-id {
color: #6684e1;
}
.syn-punctuation.syn-definition.syn-entity {
color: #6684e1;
}
.syn-meta.syn-selector {
color: #b854d4;
}
.syn-none {
color: #b65611;
}
.syn-markup.syn-heading {
color: #6684e1;
}
.syn-punctuation.syn-definition.syn-heading {
color: #6684e1;
}
.syn-entity.syn-name.syn-section {
color: #6684e1;
}
.syn-keyword.syn-other.syn-unit {
color: #b65611;
}
.syn-markup.syn-bold {
color: #b65611;
font-weight: bold;
}
.syn-punctuation.syn-definition.syn-bold {
color: #b65611;
font-weight: bold;
}
.syn-markup.syn-italic {
color: #b854d4;
font-style: italic;
}
.syn-punctuation.syn-definition.syn-italic {
color: #b854d4;
font-style: italic;
}
.syn-markup.syn-raw.syn-inline {
color: #60ac39;
}
.syn-string.syn-other.syn-link {
color: #d73737;
}
.syn-meta.syn-link {
color: #b65611;
}
.syn-markup.syn-list {
color: #d73737;
}
.syn-markup.syn-quote {
color: #b65611;
}
.syn-meta.syn-separator {
color: #6e6b5e;
background-color: #e8e4cf;
}
.syn-markup.syn-inserted {
color: #60ac39;
}
.syn-markup.syn-deleted {
color: #d73737;
}
.syn-markup.syn-changed {
color: #b854d4;
}
.syn-constant.syn-other.syn-color {
color: #1fad83;
}
.syn-string.syn-regexp {
color: #1fad83;
}
.syn-constant.syn-character.syn-escape {
color: #1fad83;
}
.syn-punctuation.syn-section.syn-embedded {
color: #d43552;
}
.syn-variable.syn-interpolation {
color: #d43552;
}
.syn-invalid.syn-illegal {
color: #fefbec;
background-color: #d73737;
}
.syn-invalid.syn-broken {
color: #20201d;
background-color: #b65611;
}
.syn-invalid.syn-deprecated {
color: #fefbec;
background-color: #d43552;
}
.syn-invalid.syn-unimplemented {
color: #20201d;
background-color: #a6a28c;
}
.scode {
display: block;
overflow-x: auto;
background: #f6f7f6;
color: #000;
padding: 0.5em;
}

View File

@ -1,82 +0,0 @@
/*
* An increased contrast highlighting scheme loosely based on the
* "Base16 Atelier Dune Light" theme by Bram de Haan
* (http://atelierbram.github.io/syntax-highlighting/atelier-schemes/dune)
* Original Base16 color scheme by Chris Kempson
* (https://github.com/chriskempson/base16)
*/
/* Comment */
.hljs-comment,
.hljs-quote {
color: #575757;
}
/* Red */
.hljs-variable,
.hljs-template-variable,
.hljs-attribute,
.hljs-tag,
.hljs-name,
.hljs-regexp,
.hljs-link,
.hljs-name,
.hljs-selector-id,
.hljs-selector-class {
color: #d70025;
}
/* Orange */
.hljs-number,
.hljs-meta,
.hljs-built_in,
.hljs-builtin-name,
.hljs-literal,
.hljs-type,
.hljs-params {
color: #b21e00;
}
/* Green */
.hljs-string,
.hljs-symbol,
.hljs-bullet {
color: #008200;
}
/* Blue */
.hljs-title,
.hljs-section {
color: #0030f2;
}
/* Purple */
.hljs-keyword,
.hljs-selector-tag {
color: #9d00ec;
}
.hljs {
display: block;
overflow-x: auto;
background: #f6f7f6;
color: #000;
}
.hljs-emphasis {
font-style: italic;
}
.hljs-strong {
font-weight: bold;
}
.hljs-addition {
color: #22863a;
background-color: #f0fff4;
}
.hljs-deletion {
color: #b31d28;
background-color: #ffeef0;
}

File diff suppressed because one or more lines are too long

View File

@ -39,10 +39,10 @@
<link rel="stylesheet" href="{{ path_to_root }}fonts/fonts.css"> <link rel="stylesheet" href="{{ path_to_root }}fonts/fonts.css">
{{/if}} {{/if}}
<!-- Highlight.js Stylesheets --> <!-- Syntax Highlighting Stylesheets -->
<link rel="stylesheet" href="{{ path_to_root }}highlight.css"> <link rel="stylesheet" href="{{ path_to_root }}css/syntax/light.css">
<link rel="stylesheet" href="{{ path_to_root }}tomorrow-night.css"> <link rel="stylesheet" href="{{ path_to_root }}css/syntax/dark.css">
<link rel="stylesheet" href="{{ path_to_root }}ayu-highlight.css"> <link rel="stylesheet" href="{{ path_to_root }}css/syntax/ayu.css">
<!-- Custom theme stylesheets --> <!-- Custom theme stylesheets -->
{{#each additional_css}} {{#each additional_css}}
@ -284,7 +284,6 @@
{{/if}} {{/if}}
<script src="{{ path_to_root }}clipboard.min.js" type="text/javascript" charset="utf-8"></script> <script src="{{ path_to_root }}clipboard.min.js" type="text/javascript" charset="utf-8"></script>
<script src="{{ path_to_root }}highlight.js" type="text/javascript" charset="utf-8"></script>
<script src="{{ path_to_root }}book.js" type="text/javascript" charset="utf-8"></script> <script src="{{ path_to_root }}book.js" type="text/javascript" charset="utf-8"></script>
<!-- Custom JS scripts --> <!-- Custom JS scripts -->

View File

@ -24,10 +24,9 @@ pub static VARIABLES_CSS: &[u8] = include_bytes!("css/variables.css");
pub static FAVICON_PNG: &[u8] = include_bytes!("favicon.png"); pub static FAVICON_PNG: &[u8] = include_bytes!("favicon.png");
pub static FAVICON_SVG: &[u8] = include_bytes!("favicon.svg"); pub static FAVICON_SVG: &[u8] = include_bytes!("favicon.svg");
pub static JS: &[u8] = include_bytes!("book.js"); pub static JS: &[u8] = include_bytes!("book.js");
pub static HIGHLIGHT_JS: &[u8] = include_bytes!("highlight.js"); pub static SYNTAX_DARK_CSS: &[u8] = include_bytes!("css/syntax/dark.css");
pub static TOMORROW_NIGHT_CSS: &[u8] = include_bytes!("tomorrow-night.css"); pub static SYNTAX_LIGHT_CSS: &[u8] = include_bytes!("css/syntax/light.css");
pub static HIGHLIGHT_CSS: &[u8] = include_bytes!("highlight.css"); pub static SYNTAX_AYU_CSS: &[u8] = include_bytes!("css/syntax/ayu.css");
pub static AYU_HIGHLIGHT_CSS: &[u8] = include_bytes!("ayu-highlight.css");
pub static CLIPBOARD_JS: &[u8] = include_bytes!("clipboard.min.js"); pub static CLIPBOARD_JS: &[u8] = include_bytes!("clipboard.min.js");
pub static FONT_AWESOME: &[u8] = include_bytes!("FontAwesome/css/font-awesome.min.css"); pub static FONT_AWESOME: &[u8] = include_bytes!("FontAwesome/css/font-awesome.min.css");
pub static FONT_AWESOME_EOT: &[u8] = include_bytes!("FontAwesome/fonts/fontawesome-webfont.eot"); pub static FONT_AWESOME_EOT: &[u8] = include_bytes!("FontAwesome/fonts/fontawesome-webfont.eot");
@ -37,6 +36,7 @@ pub static FONT_AWESOME_WOFF: &[u8] = include_bytes!("FontAwesome/fonts/fontawes
pub static FONT_AWESOME_WOFF2: &[u8] = pub static FONT_AWESOME_WOFF2: &[u8] =
include_bytes!("FontAwesome/fonts/fontawesome-webfont.woff2"); include_bytes!("FontAwesome/fonts/fontawesome-webfont.woff2");
pub static FONT_AWESOME_OTF: &[u8] = include_bytes!("FontAwesome/fonts/FontAwesome.otf"); pub static FONT_AWESOME_OTF: &[u8] = include_bytes!("FontAwesome/fonts/FontAwesome.otf");
pub static SYNTAXES_BIN: &[u8] = include_bytes!("syntaxes.bin");
/// The `Theme` struct should be used instead of the static variables because /// The `Theme` struct should be used instead of the static variables because
/// the `new()` method will look if the user has a theme directory in their /// the `new()` method will look if the user has a theme directory in their
@ -57,11 +57,11 @@ pub struct Theme {
pub favicon_png: Option<Vec<u8>>, pub favicon_png: Option<Vec<u8>>,
pub favicon_svg: Option<Vec<u8>>, pub favicon_svg: Option<Vec<u8>>,
pub js: Vec<u8>, pub js: Vec<u8>,
pub highlight_css: Vec<u8>, pub syntax_dark_css: Vec<u8>,
pub tomorrow_night_css: Vec<u8>, pub syntax_light_css: Vec<u8>,
pub ayu_highlight_css: Vec<u8>, pub syntax_ayu_css: Vec<u8>,
pub highlight_js: Vec<u8>,
pub clipboard_js: Vec<u8>, pub clipboard_js: Vec<u8>,
pub base_syntaxes: Vec<u8>,
} }
impl Theme { impl Theme {
@ -91,17 +91,20 @@ impl Theme {
theme_dir.join("css/variables.css"), theme_dir.join("css/variables.css"),
&mut theme.variables_css, &mut theme.variables_css,
), ),
(theme_dir.join("highlight.js"), &mut theme.highlight_js),
(theme_dir.join("clipboard.min.js"), &mut theme.clipboard_js), (theme_dir.join("clipboard.min.js"), &mut theme.clipboard_js),
(theme_dir.join("highlight.css"), &mut theme.highlight_css),
( (
theme_dir.join("tomorrow-night.css"), theme_dir.join("css/syntax/light.css"),
&mut theme.tomorrow_night_css, &mut theme.syntax_light_css,
), ),
( (
theme_dir.join("ayu-highlight.css"), theme_dir.join("css/syntax/dark.css"),
&mut theme.ayu_highlight_css, &mut theme.syntax_dark_css,
), ),
(
theme_dir.join("css/syntax/ayu.css"),
&mut theme.syntax_ayu_css,
),
(theme_dir.join("syntaxes.bin"), &mut theme.base_syntaxes),
]; ];
let load_with_warn = |filename: &Path, dest| { let load_with_warn = |filename: &Path, dest| {
@ -156,11 +159,11 @@ impl Default for Theme {
favicon_png: Some(FAVICON_PNG.to_owned()), favicon_png: Some(FAVICON_PNG.to_owned()),
favicon_svg: Some(FAVICON_SVG.to_owned()), favicon_svg: Some(FAVICON_SVG.to_owned()),
js: JS.to_owned(), js: JS.to_owned(),
highlight_css: HIGHLIGHT_CSS.to_owned(), syntax_dark_css: SYNTAX_DARK_CSS.to_owned(),
tomorrow_night_css: TOMORROW_NIGHT_CSS.to_owned(), syntax_ayu_css: SYNTAX_AYU_CSS.to_owned(),
ayu_highlight_css: AYU_HIGHLIGHT_CSS.to_owned(), syntax_light_css: SYNTAX_LIGHT_CSS.to_owned(),
highlight_js: HIGHLIGHT_JS.to_owned(),
clipboard_js: CLIPBOARD_JS.to_owned(), clipboard_js: CLIPBOARD_JS.to_owned(),
base_syntaxes: SYNTAXES_BIN.to_owned(),
} }
} }
} }
@ -212,17 +215,18 @@ mod tests {
"css/fonts.css", "css/fonts.css",
"css/general.css", "css/general.css",
"css/print.css", "css/print.css",
"css/syntax/ayu.css",
"css/syntax/dark.css",
"css/syntax/light.css",
"css/variables.css", "css/variables.css",
"book.js", "book.js",
"highlight.js",
"tomorrow-night.css",
"highlight.css",
"ayu-highlight.css",
"clipboard.min.js", "clipboard.min.js",
"syntaxes.bin",
]; ];
let temp = TempFileBuilder::new().prefix("mdbook-").tempdir().unwrap(); let temp = TempFileBuilder::new().prefix("mdbook-").tempdir().unwrap();
fs::create_dir(temp.path().join("css")).unwrap(); fs::create_dir(temp.path().join("css")).unwrap();
fs::create_dir(temp.path().join("css/syntax")).unwrap();
// "touch" all of the special files so we have empty copies // "touch" all of the special files so we have empty copies
for file in &files { for file in &files {
@ -243,11 +247,11 @@ mod tests {
favicon_png: Some(Vec::new()), favicon_png: Some(Vec::new()),
favicon_svg: Some(Vec::new()), favicon_svg: Some(Vec::new()),
js: Vec::new(), js: Vec::new(),
highlight_css: Vec::new(), syntax_ayu_css: Vec::new(),
tomorrow_night_css: Vec::new(), syntax_dark_css: Vec::new(),
ayu_highlight_css: Vec::new(), syntax_light_css: Vec::new(),
highlight_js: Vec::new(),
clipboard_js: Vec::new(), clipboard_js: Vec::new(),
base_syntaxes: Vec::new(),
}; };
assert_eq!(got, empty); assert_eq!(got, empty);

BIN
src/theme/syntaxes.bin Normal file

Binary file not shown.

View File

@ -0,0 +1,476 @@
%YAML 1.2
---
# http://www.sublimetext.com/docs/3/syntax.html
name: Handlebars
file_extensions:
- handlebars
- handlebars.html
- hbr
- hbrs
- hbs
- hdbs
- hjs
- mu
- mustache
- rac
- stache
- template
- tmpl
scope: text.html.handlebars
contexts:
main:
- include: yfm
- include: extends
- include: block_comments
- include: comments
- include: block_helper
- include: end_block
- include: else_token
- include: partial_and_var
- include: inline_script
- include: html_tags
- include: scope:text.html.basic
block_comments:
- match: '\{\{!--'
push:
- meta_scope: comment.block.handlebars
- match: '--\}\}'
pop: true
- match: '@\w*'
scope: keyword.annotation.handlebars
- include: comments
- match: <!--
captures:
0: punctuation.definition.comment.html
push:
- meta_scope: comment.block.html
- match: '-{2,3}\s*>'
captures:
0: punctuation.definition.comment.html
pop: true
- match: "--"
scope: invalid.illegal.bad-comments-or-CDATA.html
block_helper:
- match: '(\{\{)(~?\#)([-a-zA-Z0-9_\./>]+)\s?(@?[-a-zA-Z0-9_\./]+)*\s?(@?[-a-zA-Z0-9_\./]+)*\s?(@?[-a-zA-Z0-9_\./]+)*'
captures:
1: support.constant.handlebars
2: support.constant.handlebars keyword.control
3: support.constant.handlebars keyword.control
4: variable.parameter.handlebars
5: support.constant.handlebars
6: variable.parameter.handlebars
7: support.constant.handlebars
push:
- meta_scope: meta.function.block.start.handlebars
- match: '(~?\}\})'
captures:
1: support.constant.handlebars
pop: true
- include: string
- include: handlebars_attribute
comments:
- match: '\{\{!'
push:
- meta_scope: comment.block.handlebars
- match: '\}\}'
pop: true
- match: '@\w*'
scope: keyword.annotation.handlebars
- include: comments
- match: <!--
captures:
0: punctuation.definition.comment.html
push:
- meta_scope: comment.block.html
- match: '-{2,3}\s*>'
captures:
0: punctuation.definition.comment.html
pop: true
- match: "--"
scope: invalid.illegal.bad-comments-or-CDATA.html
else_token:
- match: '(\{\{)(~?else)(@?\s(if)\s([-a-zA-Z0-9_\.\(\s\)/]+))?'
captures:
1: support.constant.handlebars
2: support.constant.handlebars keyword.control
3: support.constant.handlebars
4: variable.parameter.handlebars
push:
- meta_scope: meta.function.inline.else.handlebars
- match: '(~?\}\}\}*)'
captures:
1: support.constant.handlebars
pop: true
end_block:
- match: '(\{\{)(~?/)([a-zA-Z0-9/_\.-]+)\s*'
captures:
1: support.constant.handlebars
2: support.constant.handlebars keyword.control
3: support.constant.handlebars keyword.control
push:
- meta_scope: meta.function.block.end.handlebars
- match: '(~?\}\})'
captures:
1: support.constant.handlebars
pop: true
entities:
- match: "(&)([a-zA-Z0-9]+|#[0-9]+|#x[0-9a-fA-F]+)(;)"
scope: constant.character.entity.html
captures:
1: punctuation.definition.entity.html
3: punctuation.definition.entity.html
- match: "&"
scope: invalid.illegal.bad-ampersand.html
escaped-double-quote:
- match: \\"
scope: constant.character.escape.js
escaped-single-quote:
- match: \\'
scope: constant.character.escape.js
extends:
- match: '(\{\{!<)\s([-a-zA-Z0-9_\./]+)'
captures:
1: support.function.handlebars
2: support.class.handlebars
push:
- meta_scope: meta.preprocessor.handlebars
- match: '(\}\})'
captures:
1: support.function.handlebars
pop: true
handlebars_attribute:
- include: handlebars_attribute_name
- include: handlebars_attribute_value
handlebars_attribute_name:
- match: '\b([-a-zA-Z0-9_\.]+)\b='
captures:
1: variable.parameter.handlebars
push:
- meta_scope: entity.other.attribute-name.handlebars
- match: (?='|"|)
captures:
1: variable.parameter.handlebars
pop: true
handlebars_attribute_value:
- match: '([-a-zA-Z0-9_\./]+)\b'
captures:
1: variable.parameter.handlebars
push:
- meta_scope: entity.other.attribute-value.handlebars
- match: ('|"|)
captures:
1: variable.parameter.handlebars
pop: true
- include: string
html_tags:
- match: '(<)([a-zA-Z0-9:-]+)(?=[^>]*></\2>)'
captures:
1: punctuation.definition.tag.html
2: entity.name.tag.html
push:
- meta_scope: meta.tag.any.html
- match: (>(<)/)(\2)(>)
captures:
1: punctuation.definition.tag.html
2: meta.scope.between-tag-pair.html
3: entity.name.tag.html
4: punctuation.definition.tag.html
pop: true
- include: tag-stuff
- match: (<\?)(xml)
captures:
1: punctuation.definition.tag.html
2: entity.name.tag.xml.html
push:
- meta_scope: meta.tag.preprocessor.xml.html
- match: (\?>)
captures:
1: punctuation.definition.tag.html
2: entity.name.tag.xml.html
pop: true
- include: tag_generic_attribute
- include: string
- match: <!--
captures:
0: punctuation.definition.comment.html
push:
- meta_scope: comment.block.html
- match: '--\s*>'
captures:
0: punctuation.definition.comment.html
pop: true
- match: "--"
scope: invalid.illegal.bad-comments-or-CDATA.html
- match: <!
captures:
0: punctuation.definition.tag.html
push:
- meta_scope: meta.tag.sgml.html
- match: ">"
captures:
0: punctuation.definition.tag.html
pop: true
- match: (DOCTYPE|doctype)
captures:
1: entity.name.tag.doctype.html
push:
- meta_scope: meta.tag.sgml.doctype.html
- match: (?=>)
captures:
1: entity.name.tag.doctype.html
pop: true
- match: '"[^">]*"'
scope: string.quoted.double.doctype.identifiers-and-DTDs.html
- match: '\[CDATA\['
push:
- meta_scope: constant.other.inline-data.html
- match: "]](?=>)"
pop: true
- match: (\s*)(?!--|>)\S(\s*)
scope: invalid.illegal.bad-comments-or-CDATA.html
- match: '(?:^\s+)?(<)((?i:style))\b(?![^>]*/>)'
captures:
1: punctuation.definition.tag.html
2: entity.name.tag.style.html
3: punctuation.definition.tag.html
push:
- meta_scope: source.css.embedded.html
- match: (</)((?i:style))(>)(?:\s*\n)?
captures:
1: punctuation.definition.tag.html
2: entity.name.tag.style.html
3: punctuation.definition.tag.html
pop: true
- include: tag-stuff
- match: (>)
captures:
1: punctuation.definition.tag.html
push:
- match: (?=</(?i:style))
pop: true
- include: scope:source.css
- match: '(?:^\s+)?(<)((?i:script))\b(?![^>]*/>)'
captures:
1: punctuation.definition.tag.html
2: entity.name.tag.script.html
push:
- meta_scope: source.js.embedded.html
- match: (?<=</(script|SCRIPT))(>)(?:\s*\n)?
captures:
2: punctuation.definition.tag.html
pop: true
- include: tag-stuff
- match: (?<!</(?:script|SCRIPT))(>)
captures:
1: punctuation.definition.tag.html
2: entity.name.tag.script.html
push:
- match: (</)((?i:script))
captures:
1: punctuation.definition.tag.html
2: entity.name.tag.script.html
pop: true
- match: (//).*?((?=</script)|$\n?)
scope: comment.line.double-slash.js
captures:
1: punctuation.definition.comment.js
- match: /\*
captures:
0: punctuation.definition.comment.js
push:
- meta_scope: comment.block.js
- match: \*/|(?=</script)
captures:
0: punctuation.definition.comment.js
pop: true
- include: scope:source.js
- match: (</?)((?i:body|head|html)\b)
captures:
1: punctuation.definition.tag.html
2: entity.name.tag.structure.any.html
push:
- meta_scope: meta.tag.structure.any.html
- match: (>)
captures:
1: punctuation.definition.tag.html
2: entity.name.tag.structure.any.html
pop: true
- include: tag-stuff
- match: (</?)((?i:address|blockquote|dd|div|header|section|footer|aside|nav|dl|dt|fieldset|form|frame|frameset|h1|h2|h3|h4|h5|h6|iframe|noframes|object|ol|p|ul|applet|center|dir|hr|menu|pre)\b)
captures:
1: punctuation.definition.tag.html
2: entity.name.tag.block.any.html
push:
- meta_scope: meta.tag.block.any.html
- match: (>)
captures:
1: punctuation.definition.tag.html
2: entity.name.tag.block.any.html
pop: true
- include: tag-stuff
- match: (</?)((?i:a|abbr|acronym|area|b|base|basefont|bdo|big|br|button|caption|cite|code|col|colgroup|del|dfn|em|font|head|html|i|img|input|ins|isindex|kbd|label|legend|li|link|map|meta|noscript|optgroup|option|param|q|s|samp|script|select|small|span|strike|strong|style|sub|sup|table|tbody|td|textarea|tfoot|th|thead|title|tr|tt|u|var)\b)
captures:
1: punctuation.definition.tag.html
2: entity.name.tag.inline.any.html
push:
- meta_scope: meta.tag.inline.any.html
- match: "((?: ?/)?>)"
captures:
1: punctuation.definition.tag.html
2: entity.name.tag.inline.any.html
pop: true
- include: tag-stuff
- match: "(</?)([a-zA-Z0-9:-]+)"
captures:
1: punctuation.definition.tag.html
2: entity.name.tag.other.html
push:
- meta_scope: meta.tag.other.html
- match: (>)
captures:
1: punctuation.definition.tag.html
2: entity.name.tag.other.html
pop: true
- include: tag-stuff
- match: "(</?)([a-zA-Z0-9{}:-]+)"
captures:
1: punctuation.definition.tag.html
2: entity.name.tag.tokenised.html
push:
- meta_scope: meta.tag.tokenised.html
- match: (>)
captures:
1: punctuation.definition.tag.html
2: entity.name.tag.tokenised.html
pop: true
- include: tag-stuff
- include: entities
- match: <>
scope: invalid.illegal.incomplete.html
- match: <
scope: invalid.illegal.bad-angle-bracket.html
inline_script:
- match: '(?:^\s+)?(<)((?i:script))\b(?:.*(type)=(["''](?:text/x-handlebars-template|text/x-handlebars|text/template|x-tmpl-handlebars)["'']))(?![^>]*/>)'
captures:
1: punctuation.definition.tag.html
2: entity.name.tag.script.html
3: entity.other.attribute-name.html
4: string.quoted.double.html
push:
- meta_scope: source.handlebars.embedded.html
- match: (?<=</(script|SCRIPT))(>)(?:\s*\n)?
captures:
2: punctuation.definition.tag.html
pop: true
- include: tag-stuff
- match: (?<!</(?:script|SCRIPT))(>)
captures:
1: punctuation.definition.tag.html
2: entity.name.tag.script.html
push:
- match: (</)((?i:script))
captures:
1: punctuation.definition.tag.html
2: entity.name.tag.script.html
pop: true
- include: block_comments
- include: comments
- include: block_helper
- include: end_block
- include: else_token
- include: partial_and_var
- include: html_tags
- include: scope:text.html.basic
partial_and_var:
- match: '(\{\{~?\{*(>|!<)*)\s*(@?[-a-zA-Z0-9$_\./]+)*'
captures:
1: support.constant.handlebars
3: variable.parameter.handlebars
push:
- meta_scope: meta.function.inline.other.handlebars
- match: '(~?\}\}\}*)'
captures:
1: support.constant.handlebars
pop: true
- include: string
- include: handlebars_attribute
string:
- include: string-single-quoted
- include: string-double-quoted
string-double-quoted:
- match: '"'
captures:
0: punctuation.definition.string.begin.html
push:
- meta_scope: string.quoted.double.handlebars
- match: '"'
captures:
0: punctuation.definition.string.end.html
pop: true
- include: escaped-double-quote
- include: block_comments
- include: comments
- include: block_helper
- include: else_token
- include: end_block
- include: partial_and_var
string-single-quoted:
- match: "'"
captures:
0: punctuation.definition.string.begin.html
push:
- meta_scope: string.quoted.single.handlebars
- match: "'"
captures:
0: punctuation.definition.string.end.html
pop: true
- include: escaped-single-quote
- include: block_comments
- include: comments
- include: block_helper
- include: else_token
- include: end_block
- include: partial_and_var
tag-stuff:
- include: tag_id_attribute
- include: tag_generic_attribute
- include: string
- include: block_comments
- include: comments
- include: block_helper
- include: end_block
- include: else_token
- include: partial_and_var
tag_generic_attribute:
- match: '\b([a-zA-Z0-9_-]+)\b\s*(=)'
captures:
1: entity.other.attribute-name.generic.html
2: punctuation.separator.key-value.html
push:
- meta_scope: entity.other.attribute-name.html
- match: (?<='|"|)
captures:
1: entity.other.attribute-name.generic.html
2: punctuation.separator.key-value.html
pop: true
- include: string
tag_id_attribute:
- match: \b(id)\b\s*(=)
captures:
1: entity.other.attribute-name.id.html
2: punctuation.separator.key-value.html
push:
- meta_scope: meta.attribute-with-value.id.html
- match: (?<='|"|)
captures:
1: entity.other.attribute-name.id.html
2: punctuation.separator.key-value.html
pop: true
- include: string
yfm:
- match: (?<!\s)---\n$
push:
- meta_scope: markup.raw.yaml.front-matter
- match: ^---\s
pop: true
- include: scope:source.yaml

View File

@ -0,0 +1,16 @@
# Note
This folder is not copied over to the book directory when using `mdbook init`, nor is it indexed at runtime. To add new syntaxes, first add a `.sublime-syntax` file in this directory, then run (from inside this directory):
```shell
$ ./gen-syntaxcache.sh
```
Make sure you have [`bat`](https://github.com/sharkdp/bat) installed, otherwise this won't work.
Don't worry if bat says:
```
No themes were found in './themes', using the default set
```
Now, you may rebuild the `mdBook` binary and the new syntaxes should be included.
This only needs to be run when the syntaxes in this folder are updated.

View File

@ -0,0 +1,6 @@
#!/bin/env bash
set -Eeuxo pipefail
bat cache --build --source=".." --target=".."
rm -f ../themes.bin
rm -f ../metadata.yaml

View File

@ -1,102 +0,0 @@
/* Tomorrow Night Theme */
/* http://jmblog.github.com/color-themes-for-google-code-highlightjs */
/* Original theme - https://github.com/chriskempson/tomorrow-theme */
/* http://jmblog.github.com/color-themes-for-google-code-highlightjs */
/* Tomorrow Comment */
.hljs-comment {
color: #969896;
}
/* Tomorrow Red */
.hljs-variable,
.hljs-attribute,
.hljs-tag,
.hljs-regexp,
.ruby .hljs-constant,
.xml .hljs-tag .hljs-title,
.xml .hljs-pi,
.xml .hljs-doctype,
.html .hljs-doctype,
.css .hljs-id,
.css .hljs-class,
.css .hljs-pseudo {
color: #cc6666;
}
/* Tomorrow Orange */
.hljs-number,
.hljs-preprocessor,
.hljs-pragma,
.hljs-built_in,
.hljs-literal,
.hljs-params,
.hljs-constant {
color: #de935f;
}
/* Tomorrow Yellow */
.ruby .hljs-class .hljs-title,
.css .hljs-rule .hljs-attribute {
color: #f0c674;
}
/* Tomorrow Green */
.hljs-string,
.hljs-value,
.hljs-inheritance,
.hljs-header,
.hljs-name,
.ruby .hljs-symbol,
.xml .hljs-cdata {
color: #b5bd68;
}
/* Tomorrow Aqua */
.hljs-title,
.css .hljs-hexcolor {
color: #8abeb7;
}
/* Tomorrow Blue */
.hljs-function,
.python .hljs-decorator,
.python .hljs-title,
.ruby .hljs-function .hljs-title,
.ruby .hljs-title .hljs-keyword,
.perl .hljs-sub,
.javascript .hljs-title,
.coffeescript .hljs-title {
color: #81a2be;
}
/* Tomorrow Purple */
.hljs-keyword,
.javascript .hljs-function {
color: #b294bb;
}
.hljs {
display: block;
overflow-x: auto;
background: #1d1f21;
color: #c5c8c6;
}
.coffeescript .javascript,
.javascript .xml,
.tex .hljs-formula,
.xml .javascript,
.xml .vbscript,
.xml .css,
.xml .hljs-cdata {
opacity: 0.5;
}
.hljs-addition {
color: #718c00;
}
.hljs-deletion {
color: #c82829;
}

95
src/utils/highlight.rs Normal file
View File

@ -0,0 +1,95 @@
//! Syntax highlighter with support for hiding lines.
//! This is essentially a version of [`syntect::html::ClassedHTMLGenerator`]
//! which allows you to mark a line as boring (hidden).
use std::borrow::Cow;
use regex::Regex;
use syntect::{
html::{self, ClassStyle},
parsing::{ParseState, ScopeStack, SyntaxReference, SyntaxSet},
};
pub struct HtmlGenerator<'a> {
syntaxes: &'a SyntaxSet,
open_spans: isize,
parse_state: ParseState,
scope_stack: ScopeStack,
html: String,
style: ClassStyle,
}
impl<'a> HtmlGenerator<'a> {
pub fn new(syntax: &'a SyntaxReference, syntaxes: &'a SyntaxSet, style: ClassStyle) -> Self {
let parse_state = ParseState::new(syntax);
let open_spans = 0;
let html = String::new();
let scope_stack = ScopeStack::new();
Self {
syntaxes,
open_spans,
parse_state,
scope_stack,
html,
style,
}
}
pub fn parse_line(&mut self, line: &str, is_rust: bool) {
let (line, did_boringify) = if is_rust {
let (line, did_boringify) = boringify(line);
(Cow::from(line), did_boringify)
} else {
(Cow::from(line), false)
};
let parsed_line = self.parse_state.parse_line(&line, self.syntaxes);
let (formatted_line, delta) = html::line_tokens_to_classed_spans(
&line,
parsed_line.as_slice(),
self.style,
&mut self.scope_stack,
);
self.open_spans += delta;
self.html.push_str(&if did_boringify {
format!("<span class=\"boring\">{}</span>", formatted_line)
} else {
formatted_line
});
}
pub fn finalize(mut self) -> String {
for _ in 0..self.open_spans {
self.html.push_str("</span>");
}
self.html
}
}
lazy_static! {
static ref BORING_LINE_REGEX: Regex = Regex::new(r"^(\s*)#(.?)(.*)\n$").unwrap();
}
fn boringify(line: &str) -> (String, bool) {
let mut result = String::with_capacity(line.len());
if let Some(caps) = BORING_LINE_REGEX.captures(line) {
if &caps[2] == "#" {
result += &caps[1];
result += &caps[2];
result += &caps[3];
result += "\n";
return (result, true);
} else if &caps[2] != "!" && &caps[2] != "[" {
result += &caps[1];
if &caps[2] != " " {
result += &caps[2];
}
result += &caps[3];
result += "\n";
return (result, true);
}
}
result += line;
result += "\n";
(result, false)
}

View File

@ -1,12 +1,17 @@
#![allow(missing_docs)] // FIXME: Document this #![allow(missing_docs)] // FIXME: Document this
pub mod fs; pub mod fs;
pub mod highlight;
mod string; mod string;
pub(crate) mod toml_ext; pub(crate) mod toml_ext;
use crate::errors::Error; use crate::errors::Error;
use regex::Regex; use regex::Regex;
use pulldown_cmark::{html, CodeBlockKind, CowStr, Event, Options, Parser, Tag}; use pulldown_cmark::{html, CodeBlockKind, CowStr, Event, Options, Parser, Tag};
use syntect::html::ClassStyle;
use syntect::parsing::SyntaxReference;
use syntect::parsing::SyntaxSet;
use syntect::util::LinesWithEndings;
use std::borrow::Cow; use std::borrow::Cow;
use std::collections::HashMap; use std::collections::HashMap;
@ -179,8 +184,8 @@ fn adjust_links<'a>(event: Event<'a>, path: Option<&Path>) -> Event<'a> {
} }
/// Wrapper around the pulldown-cmark parser for rendering markdown to HTML. /// Wrapper around the pulldown-cmark parser for rendering markdown to HTML.
pub fn render_markdown(text: &str, curly_quotes: bool) -> String { pub fn render_markdown(text: &str, curly_quotes: bool, syntaxes: &SyntaxSet) -> String {
render_markdown_with_path(text, curly_quotes, None) render_markdown_with_path(text, curly_quotes, None, syntaxes)
} }
pub fn new_cmark_parser(text: &str, curly_quotes: bool) -> Parser<'_, '_> { pub fn new_cmark_parser(text: &str, curly_quotes: bool) -> Parser<'_, '_> {
@ -195,11 +200,18 @@ pub fn new_cmark_parser(text: &str, curly_quotes: bool) -> Parser<'_, '_> {
Parser::new_ext(text, opts) Parser::new_ext(text, opts)
} }
pub fn render_markdown_with_path(text: &str, curly_quotes: bool, path: Option<&Path>) -> String { pub fn render_markdown_with_path(
text: &str,
curly_quotes: bool,
path: Option<&Path>,
syntaxes: &SyntaxSet,
) -> String {
let mut s = String::with_capacity(text.len() * 3 / 2); let mut s = String::with_capacity(text.len() * 3 / 2);
let p = new_cmark_parser(text, curly_quotes); let p = new_cmark_parser(text, curly_quotes);
let mut highlighter = SyntaxHighlighter::default();
let events = p let events = p
.map(clean_codeblock_headers) .map(clean_codeblock_headers)
.map(|event| highlighter.highlight(syntaxes, event))
.map(|event| adjust_links(event, path)) .map(|event| adjust_links(event, path))
.flat_map(|event| { .flat_map(|event| {
let (a, b) = wrap_tables(event); let (a, b) = wrap_tables(event);
@ -222,6 +234,107 @@ fn wrap_tables(event: Event<'_>) -> (Option<Event<'_>>, Option<Event<'_>>) {
} }
} }
#[derive(Default)]
struct SyntaxHighlighter<'a> {
highlight: bool,
is_rust: bool,
syntax: Option<&'a SyntaxReference>,
}
impl<'a> SyntaxHighlighter<'a> {
fn highlight<'b>(&mut self, syntaxes: &'a SyntaxSet, event: Event<'b>) -> Event<'b> {
match event {
Event::Start(Tag::CodeBlock(CodeBlockKind::Fenced(ref info))) => {
self.highlight = true;
let lang_name = info.split(',').next();
if let Some(name) = lang_name {
// If we're given an empty name, or it is marked as
// plaintext, we shouldn't highlight it.
if name.is_empty()
|| name.eq_ignore_ascii_case("plaintext")
|| name.eq_ignore_ascii_case("text")
|| name.eq_ignore_ascii_case("plain")
|| name.eq_ignore_ascii_case("txt")
{
self.highlight = false;
return event;
}
self.syntax = syntaxes.find_syntax_by_token(name);
if self.syntax.is_none() {
self.highlight = false;
return event;
}
if let Some(syntax) = self.syntax {
if syntax.name == "Rust" {
self.is_rust = true;
}
}
event
} else {
// We also don't perform auto-detection of languages, so we
// shouldn't highlight code blocks without lang tags.
self.highlight = false;
event
}
}
Event::End(Tag::CodeBlock(CodeBlockKind::Fenced(_))) => {
self.highlight = false;
self.is_rust = false;
self.syntax = None;
event
}
Event::Text(ref code) if self.highlight => {
let mut gen = highlight::HtmlGenerator::new(
self.syntax.unwrap(),
syntaxes,
ClassStyle::SpacedPrefixed { prefix: "syn-" },
);
for line in LinesWithEndings::from(code) {
gen.parse_line(line, self.is_rust);
}
Event::Html(CowStr::from(gen.finalize()))
}
_ => event,
}
}
}
struct EventQuoteConverter {
enabled: bool,
convert_text: bool,
}
impl EventQuoteConverter {
fn new(enabled: bool) -> Self {
EventQuoteConverter {
enabled,
convert_text: true,
}
}
fn convert<'a>(&mut self, event: Event<'a>) -> Event<'a> {
if !self.enabled {
return event;
}
match event {
Event::Start(Tag::CodeBlock(_)) => {
self.convert_text = false;
event
}
Event::End(Tag::CodeBlock(_)) => {
self.convert_text = true;
event
}
Event::Text(ref text) if self.convert_text => {
Event::Text(CowStr::from(convert_quotes_to_curly(text)))
}
_ => event,
}
}
}
fn clean_codeblock_headers(event: Event<'_>) -> Event<'_> { fn clean_codeblock_headers(event: Event<'_>) -> Event<'_> {
match event { match event {
Event::Start(Tag::CodeBlock(CodeBlockKind::Fenced(ref info))) => { Event::Start(Tag::CodeBlock(CodeBlockKind::Fenced(ref info))) => {
@ -270,12 +383,21 @@ mod tests {
use super::bracket_escape; use super::bracket_escape;
mod render_markdown { mod render_markdown {
use syntect::parsing::SyntaxSet;
fn default_syntaxes() -> SyntaxSet {
syntect::dumps::from_binary(crate::theme::SYNTAXES_BIN)
}
use super::super::render_markdown; use super::super::render_markdown;
#[test] #[test]
fn preserves_external_links() { fn preserves_external_links() {
assert_eq!( assert_eq!(
render_markdown("[example](https://www.rust-lang.org/)", false), render_markdown(
"[example](https://www.rust-lang.org/)",
false,
&default_syntaxes()
),
"<p><a href=\"https://www.rust-lang.org/\">example</a></p>\n" "<p><a href=\"https://www.rust-lang.org/\">example</a></p>\n"
); );
} }
@ -283,17 +405,25 @@ mod tests {
#[test] #[test]
fn it_can_adjust_markdown_links() { fn it_can_adjust_markdown_links() {
assert_eq!( assert_eq!(
render_markdown("[example](example.md)", false), render_markdown("[example](example.md)", false, &default_syntaxes()),
"<p><a href=\"example.html\">example</a></p>\n" "<p><a href=\"example.html\">example</a></p>\n"
); );
assert_eq!( assert_eq!(
render_markdown("[example_anchor](example.md#anchor)", false), render_markdown(
"[example_anchor](example.md#anchor)",
false,
&default_syntaxes()
),
"<p><a href=\"example.html#anchor\">example_anchor</a></p>\n" "<p><a href=\"example.html#anchor\">example_anchor</a></p>\n"
); );
// this anchor contains 'md' inside of it // this anchor contains 'md' inside of it
assert_eq!( assert_eq!(
render_markdown("[phantom data](foo.html#phantomdata)", false), render_markdown(
"[phantom data](foo.html#phantomdata)",
false,
&default_syntaxes()
),
"<p><a href=\"foo.html#phantomdata\">phantom data</a></p>\n" "<p><a href=\"foo.html#phantomdata\">phantom data</a></p>\n"
); );
} }
@ -316,7 +446,10 @@ mod tests {
#[test] #[test]
fn it_can_keep_quotes_straight() { fn it_can_keep_quotes_straight() {
assert_eq!(render_markdown("'one'", false), "<p>'one'</p>\n"); assert_eq!(
render_markdown("'one'", false, &default_syntaxes()),
"<p>'one'</p>\n"
);
} }
#[test] #[test]
@ -332,7 +465,7 @@ mod tests {
</code></pre> </code></pre>
<p><code>'three'</code> four</p> <p><code>'three'</code> four</p>
"#; "#;
assert_eq!(render_markdown(input, true), expected); assert_eq!(render_markdown(input, true, &default_syntaxes()), expected);
} }
#[test] #[test]
@ -348,14 +481,17 @@ more text with spaces
"#; "#;
let expected = r#"<p>some text with spaces</p> let expected = r#"<p>some text with spaces</p>
<pre><code class="language-rust">fn main() { <pre><code class="language-rust"><span class="syn-source syn-rust"><span class="syn-meta syn-function syn-rust"><span class="syn-meta syn-function syn-rust"><span class="syn-storage syn-type syn-function syn-rust">fn</span> </span><span class="syn-entity syn-name syn-function syn-rust">main</span></span><span class="syn-meta syn-function syn-rust"><span class="syn-meta syn-function syn-parameters syn-rust"><span class="syn-punctuation syn-section syn-parameters syn-begin syn-rust">(</span></span><span class="syn-meta syn-function syn-rust"><span class="syn-meta syn-function syn-parameters syn-rust"><span class="syn-punctuation syn-section syn-parameters syn-end syn-rust">)</span></span></span></span><span class="syn-meta syn-function syn-rust"> </span><span class="syn-meta syn-function syn-rust"><span class="syn-meta syn-block syn-rust"><span class="syn-punctuation syn-section syn-block syn-begin syn-rust">{</span>
// code inside is unchanged
} <span class="syn-comment syn-line syn-double-slash syn-rust"><span class="syn-punctuation syn-definition syn-comment syn-rust">//</span> code inside is unchanged
</code></pre> </span>
</span><span class="syn-meta syn-block syn-rust"><span class="syn-punctuation syn-section syn-block syn-end syn-rust">}</span></span></span>
</span></code></pre>
<p>more text with spaces</p> <p>more text with spaces</p>
"#; "#;
assert_eq!(render_markdown(input, false), expected); assert_eq!(render_markdown(input, false, &default_syntaxes()), expected);
assert_eq!(render_markdown(input, true), expected); assert_eq!(render_markdown(input, true, &default_syntaxes()), expected);
} }
#[test] #[test]
@ -367,8 +503,8 @@ more text with spaces
let expected = r#"<pre><code class="language-rust,no_run,should_panic,property_3"></code></pre> let expected = r#"<pre><code class="language-rust,no_run,should_panic,property_3"></code></pre>
"#; "#;
assert_eq!(render_markdown(input, false), expected); assert_eq!(render_markdown(input, false, &default_syntaxes()), expected);
assert_eq!(render_markdown(input, true), expected); assert_eq!(render_markdown(input, true, &default_syntaxes()), expected);
} }
#[test] #[test]
@ -380,8 +516,8 @@ more text with spaces
let expected = r#"<pre><code class="language-rust,,,,,no_run,,,should_panic,,,,property_3"></code></pre> let expected = r#"<pre><code class="language-rust,,,,,no_run,,,should_panic,,,,property_3"></code></pre>
"#; "#;
assert_eq!(render_markdown(input, false), expected); assert_eq!(render_markdown(input, false, &default_syntaxes()), expected);
assert_eq!(render_markdown(input, true), expected); assert_eq!(render_markdown(input, true, &default_syntaxes()), expected);
} }
#[test] #[test]
@ -393,15 +529,17 @@ more text with spaces
let expected = r#"<pre><code class="language-rust"></code></pre> let expected = r#"<pre><code class="language-rust"></code></pre>
"#; "#;
assert_eq!(render_markdown(input, false), expected); assert_eq!(render_markdown(input, false, &default_syntaxes()), expected);
assert_eq!(render_markdown(input, true), expected); assert_eq!(render_markdown(input, true, &default_syntaxes()), expected);
// FIXME: Why are we doing this twice? It seems to be the same
// input and assertions.
let input = r#" let input = r#"
```rust ```rust
``` ```
"#; "#;
assert_eq!(render_markdown(input, false), expected); assert_eq!(render_markdown(input, false, &default_syntaxes()), expected);
assert_eq!(render_markdown(input, true), expected); assert_eq!(render_markdown(input, true, &default_syntaxes()), expected);
} }
} }

View File

@ -118,11 +118,10 @@ fn copy_theme() {
"css/chrome.css", "css/chrome.css",
"css/general.css", "css/general.css",
"css/print.css", "css/print.css",
"css/syntax/light.css",
"css/variables.css", "css/variables.css",
"favicon.png", "favicon.png",
"favicon.svg", "favicon.svg",
"highlight.css",
"highlight.js",
"index.hbs", "index.hbs",
]; ];
let theme_dir = temp.path().join("theme"); let theme_dir = temp.path().join("theme");

View File

@ -193,8 +193,8 @@ fn rustdoc_include_hides_the_unspecified_part_of_the_file() {
let nested = temp.path().join("book/first/nested.html"); let nested = temp.path().join("book/first/nested.html");
let text = vec![ let text = vec![
"<span class=\"boring\">fn some_function() {", r#"<span class="boring"><span class="syn-source syn-rust"><span class="syn-meta syn-function syn-rust"><span class="syn-meta syn-function syn-rust"><span class="syn-storage syn-type syn-function syn-rust">fn</span> </span><span class="syn-entity syn-name syn-function syn-rust">some_function"#,
"<span class=\"boring\">fn some_other_function() {", r#"<span class="boring"><span class="syn-source syn-rust"><span class="syn-meta syn-function syn-rust"><span class="syn-meta syn-function syn-rust"><span class="syn-storage syn-type syn-function syn-rust">fn</span> </span><span class="syn-entity syn-name syn-function syn-rust">some_other_function"#,
]; ];
assert_contains_strings(nested, &text); assert_contains_strings(nested, &text);
@ -378,7 +378,7 @@ fn able_to_include_playground_files_in_chapters() {
let playground_strings = &[ let playground_strings = &[
r#"class="playground""#, r#"class="playground""#,
r#"println!(&quot;Hello World!&quot;);"#, r#"<span class="syn-support syn-macro syn-rust">println!</span><span class="syn-meta syn-group syn-rust"><span class="syn-punctuation syn-section syn-group syn-begin syn-rust">(</span></span><span class="syn-meta syn-group syn-rust"><span class="syn-string syn-quoted syn-double syn-rust"><span class="syn-punctuation syn-definition syn-string syn-begin syn-rust">&quot;</span>Hello World!"#,
]; ];
assert_contains_strings(&second, playground_strings); assert_contains_strings(&second, playground_strings);