Add a highlight subcommand

This commit is contained in:
Max Brunsfeld 2019-02-19 11:24:50 -08:00
parent 0dd15e2b02
commit e89b6b2402
14 changed files with 1870 additions and 22 deletions

View file

@ -0,0 +1,191 @@
use super::helpers::fixtures::{get_language, get_property_sheet};
use lazy_static::lazy_static;
use tree_sitter::{Language, PropertySheet};
use tree_sitter_highlight::{
highlight, highlight_html, HighlightEvent, LanguageRegistry, Properties, Scope,
};
lazy_static! {
static ref JS_SHEET: PropertySheet<Properties> =
get_property_sheet("javascript", "highlights.json");
static ref HTML_SHEET: PropertySheet<Properties> =
get_property_sheet("html", "highlights.json");
static ref SCOPE_CLASS_STRINGS: Vec<String> = {
let mut result = Vec::new();
let mut i = 0;
while let Some(scope) = Scope::from_usize(i) {
result.push(format!("class={:?}", scope));
i += 1;
}
result
};
}
#[test]
fn test_highlighting_injected_html_in_javascript() {
let source = vec!["const s = html `<div>${a < b}</div>`;"].join("\n");
assert_eq!(
&to_token_vector(&source, get_language("javascript"), &JS_SHEET).unwrap(),
&[vec![
("const", vec![Scope::Keyword]),
(" ", vec![]),
("s", vec![Scope::Variable]),
(" ", vec![]),
("=", vec![Scope::Operator]),
(" ", vec![]),
("html", vec![Scope::Function]),
(" ", vec![]),
("`<", vec![Scope::String]),
("div", vec![Scope::String, Scope::Tag]),
(">", vec![Scope::String]),
(
"${",
vec![Scope::String, Scope::Embedded, Scope::PunctuationSpecial]
),
("a", vec![Scope::String, Scope::Embedded, Scope::Variable]),
(" ", vec![Scope::String, Scope::Embedded]),
("<", vec![Scope::String, Scope::Embedded, Scope::Operator]),
(" ", vec![Scope::String, Scope::Embedded]),
("b", vec![Scope::String, Scope::Embedded, Scope::Variable]),
(
"}",
vec![Scope::String, Scope::Embedded, Scope::PunctuationSpecial]
),
("</", vec![Scope::String]),
("div", vec![Scope::String, Scope::Tag]),
(">`", vec![Scope::String]),
(";", vec![Scope::PunctuationDelimiter]),
]]
);
}
#[test]
fn test_highlighting_injected_javascript_in_html() {
let source = vec![
"<body>",
" <script>",
" const x = new Thing();",
" </script>",
"</body>",
]
.join("\n");
assert_eq!(
&to_token_vector(&source, get_language("html"), &HTML_SHEET).unwrap(),
&[
vec![("<", vec![]), ("body", vec![Scope::Tag]), (">", vec![]),],
vec![(" <", vec![]), ("script", vec![Scope::Tag]), (">", vec![]),],
vec![
(" ", vec![]),
("const", vec![Scope::Keyword]),
(" ", vec![]),
("x", vec![Scope::Variable]),
(" ", vec![]),
("=", vec![Scope::Operator]),
(" ", vec![]),
("new", vec![Scope::Keyword]),
(" ", vec![]),
("Thing", vec![Scope::Constructor]),
("(", vec![Scope::PunctuationBracket]),
(")", vec![Scope::PunctuationBracket]),
(";", vec![Scope::PunctuationDelimiter]),
],
vec![
(" </", vec![]),
("script", vec![Scope::Tag]),
(">", vec![]),
],
vec![("</", vec![]), ("body", vec![Scope::Tag]), (">", vec![]),],
]
);
}
#[test]
fn test_highlighting_multiline_scopes_to_html() {
let source = vec![
"const SOMETHING = `",
" one ${",
" two()",
" } three",
"`",
]
.join("\n");
assert_eq!(
&to_html(&source, get_language("javascript"), &JS_SHEET,).unwrap(),
&[
"<span class=Keyword>const</span> <span class=Constant>SOMETHING</span> <span class=Operator>=</span> <span class=String>`</span>\n".to_string(),
"<span class=String> one <span class=Embedded><span class=PunctuationSpecial>${</span></span></span>\n".to_string(),
"<span class=String><span class=Embedded> <span class=Function>two</span><span class=PunctuationBracket>(</span><span class=PunctuationBracket>)</span></span></span>\n".to_string(),
"<span class=String><span class=Embedded> <span class=PunctuationSpecial>}</span></span> three</span>\n".to_string(),
"<span class=String>`</span>\n".to_string(),
]
);
}
struct TestLanguageRegistry;
impl LanguageRegistry for TestLanguageRegistry {
fn language_for_injection_string(
&self,
string: &str,
) -> Option<(Language, &PropertySheet<Properties>)> {
match string {
"javascript" => Some((get_language("javascript"), &JS_SHEET)),
"html" => Some((get_language("html"), &HTML_SHEET)),
_ => None,
}
}
}
fn to_html<'a>(
src: &'a str,
language: Language,
property_sheet: &'a PropertySheet<Properties>,
) -> Result<Vec<String>, String> {
highlight_html(
&TestLanguageRegistry,
src.as_bytes(),
language,
property_sheet,
|scope| SCOPE_CLASS_STRINGS[scope as usize].as_str(),
)
}
fn to_token_vector<'a>(
src: &'a str,
language: Language,
property_sheet: &'a PropertySheet<Properties>,
) -> Result<Vec<Vec<(&'a str, Vec<Scope>)>>, String> {
let mut lines = Vec::new();
let mut scopes = Vec::new();
let mut line = Vec::new();
for event in highlight(
&TestLanguageRegistry,
src.as_bytes(),
language,
property_sheet,
)? {
match event {
HighlightEvent::ScopeStart(s) => scopes.push(s),
HighlightEvent::ScopeEnd(s) => {
assert_eq!(*scopes.last().unwrap(), s);
scopes.pop();
}
HighlightEvent::Source(s) => {
for (i, l) in s.lines().enumerate() {
if i > 0 {
lines.push(line);
line = Vec::new();
}
if l.len() > 0 {
line.push((l, scopes.clone()));
}
}
}
}
}
lines.push(line);
Ok(lines)
}