diff --git a/cli/src/highlight.rs b/cli/src/highlight.rs index 1651b98d..0f88149a 100644 --- a/cli/src/highlight.rs +++ b/cli/src/highlight.rs @@ -4,7 +4,7 @@ use ansi_term::{Color, Style}; use lazy_static::lazy_static; use serde_json::Value; use std::collections::HashMap; -use std::{fmt, fs, io, mem, path}; +use std::{fmt, fs, io, path}; use tree_sitter::{Language, PropertySheet}; use tree_sitter_highlight::{highlight, highlight_html, HighlightEvent, Properties, Scope}; @@ -195,7 +195,9 @@ pub fn ansi( let stdout = io::stdout(); let mut stdout = stdout.lock(); let mut scope_stack = Vec::new(); - for event in highlight(loader, source, language, property_sheet)? { + for event in highlight(source, language, property_sheet, &|s| { + language_for_injection_string(loader, s) + })? { match event { HighlightEvent::Source(s) => { if let Some(style) = scope_stack.last().and_then(|s| theme.ansi_style(*s)) { @@ -252,13 +254,19 @@ pub fn html( let stdout = io::stdout(); let mut stdout = stdout.lock(); write!(&mut stdout, "\n")?; - let lines = highlight_html(loader, source, language, property_sheet, |scope| { - if let Some(css_style) = theme.css_style(scope) { - css_style - } else { - "" - } - })?; + let lines = highlight_html( + source, + language, + property_sheet, + &|s| language_for_injection_string(loader, s), + &|scope| { + if let Some(css_style) = theme.css_style(scope) { + css_style + } else { + "" + } + }, + )?; for (i, line) in lines.into_iter().enumerate() { write!( &mut stdout, @@ -270,3 +278,32 @@ pub fn html( write!(&mut stdout, "
\n")?; Ok(()) } + +fn language_for_injection_string<'a>( + loader: &'a Loader, + string: &str, +) -> Option<(Language, &'a PropertySheet)> { + match loader.language_configuration_for_injection_string(string) { + Err(message) => { + eprintln!( + "Failed to load language for injection string '{}': {}", + string, message.0 + ); + None + } + Ok(None) => None, + Ok(Some((language, configuration))) => { + match configuration.highlight_property_sheet(language) { + Err(message) => { + eprintln!( + "Failed to load property sheet for injection string '{}': {}", + string, message.0 + ); + None + } + Ok(None) => None, + Ok(Some(sheet)) => Some((language, sheet)), + } + } + } +} diff --git a/cli/src/loader.rs b/cli/src/loader.rs index d19acf46..49bab4b4 100644 --- a/cli/src/loader.rs +++ b/cli/src/loader.rs @@ -10,7 +10,7 @@ use std::process::Command; use std::time::SystemTime; use std::{fs, mem}; use tree_sitter::{Language, PropertySheet}; -use tree_sitter_highlight::{load_property_sheet, LanguageRegistry, Properties}; +use tree_sitter_highlight::{load_property_sheet, Properties}; #[cfg(unix)] const DYLIB_EXTENSION: &'static str = "so"; @@ -320,37 +320,6 @@ impl Loader { } } -impl LanguageRegistry for Loader { - fn language_for_injection_string<'a>( - &'a self, - string: &str, - ) -> Option<(Language, &'a PropertySheet)> { - match self.language_configuration_for_injection_string(string) { - Err(message) => { - eprintln!( - "Failed to load language for injection string '{}': {}", - string, message.0 - ); - None - } - Ok(None) => None, - Ok(Some((language, configuration))) => { - match configuration.highlight_property_sheet(language) { - Err(message) => { - eprintln!( - "Failed to load property sheet for injection string '{}': {}", - string, message.0 - ); - None - } - Ok(None) => None, - Ok(Some(sheet)) => Some((language, sheet)), - } - } - } - } -} - impl LanguageConfiguration { pub fn highlight_property_sheet( &self, diff --git a/cli/src/tests/highlight_test.rs b/cli/src/tests/highlight_test.rs index ea14a1c2..6e07ab4a 100644 --- a/cli/src/tests/highlight_test.rs +++ b/cli/src/tests/highlight_test.rs @@ -1,9 +1,7 @@ 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, -}; +use tree_sitter_highlight::{highlight, highlight_html, HighlightEvent, Properties, Scope}; lazy_static! { static ref JS_SHEET: PropertySheet = @@ -124,18 +122,13 @@ fn test_highlighting_multiline_scopes_to_html() { ); } -struct TestLanguageRegistry; - -impl LanguageRegistry for TestLanguageRegistry { - fn language_for_injection_string( - &self, - string: &str, - ) -> Option<(Language, &PropertySheet)> { - match string { - "javascript" => Some((get_language("javascript"), &JS_SHEET)), - "html" => Some((get_language("html"), &HTML_SHEET)), - _ => None, - } +fn test_language_for_injection_string<'a>( + string: &str, +) -> Option<(Language, &'a PropertySheet)> { + match string { + "javascript" => Some((get_language("javascript"), &JS_SHEET)), + "html" => Some((get_language("html"), &HTML_SHEET)), + _ => None, } } @@ -145,11 +138,11 @@ fn to_html<'a>( property_sheet: &'a PropertySheet, ) -> Result, String> { highlight_html( - &TestLanguageRegistry, src.as_bytes(), language, property_sheet, - |scope| SCOPE_CLASS_STRINGS[scope as usize].as_str(), + &test_language_for_injection_string, + &|scope| SCOPE_CLASS_STRINGS[scope as usize].as_str(), ) } @@ -162,10 +155,10 @@ fn to_token_vector<'a>( let mut scopes = Vec::new(); let mut line = Vec::new(); for event in highlight( - &TestLanguageRegistry, src.as_bytes(), language, property_sheet, + &test_language_for_injection_string, )? { match event { HighlightEvent::ScopeStart(s) => scopes.push(s), diff --git a/highlight/src/lib.rs b/highlight/src/lib.rs index bdf35b9f..453685f4 100644 --- a/highlight/src/lib.rs +++ b/highlight/src/lib.rs @@ -9,13 +9,6 @@ use std::str; use std::usize; use tree_sitter::{Language, Node, Parser, Point, PropertySheet, Range, Tree, TreePropertyCursor}; -pub trait LanguageRegistry { - fn language_for_injection_string<'a>( - &'a self, - s: &str, - ) -> Option<(Language, &'a PropertySheet)>; -} - #[derive(Debug)] enum TreeStep { Child { @@ -87,8 +80,11 @@ struct Layer<'a> { at_node_end: bool, } -struct Highlighter<'a, T: LanguageRegistry> { - language_registry: &'a T, +struct Highlighter<'a, T> +where + T: Fn(&str) -> Option<(Language, &'a PropertySheet)>, +{ + injection_callback: &'a T, source: &'a [u8], source_offset: usize, parser: Parser, @@ -349,12 +345,15 @@ impl Properties { } } -impl<'a, T: LanguageRegistry> Highlighter<'a, T> { +impl<'a, F> Highlighter<'a, F> +where + F: Fn(&str) -> Option<(Language, &'a PropertySheet)>, +{ fn new( - language_registry: &'a T, source: &'a [u8], language: Language, property_sheet: &'a PropertySheet, + injection_callback: &'a F, ) -> Result { let mut parser = Parser::new(); parser.set_language(language)?; @@ -362,7 +361,7 @@ impl<'a, T: LanguageRegistry> Highlighter<'a, T> { .parse(source, None) .ok_or_else(|| format!("Tree-sitter: failed to parse"))?; Ok(Self { - language_registry, + injection_callback, source, source_offset: 0, parser, @@ -457,7 +456,7 @@ impl<'a, T: LanguageRegistry> Highlighter<'a, T> { // on the text of some node in the syntax tree. fn injection_language_string( &self, - node: &Node, + node: &Node<'a>, language: &InjectionLanguage, ) -> Option { match language { @@ -556,10 +555,7 @@ impl<'a, T: LanguageRegistry> Highlighter<'a, T> { } fn add_layer(&mut self, language_string: &str, ranges: Vec) { - if let Some((language, property_sheet)) = self - .language_registry - .language_for_injection_string(language_string) - { + if let Some((language, property_sheet)) = (self.injection_callback)(language_string) { self.parser .set_language(language) .expect("Failed to set language"); @@ -579,7 +575,9 @@ impl<'a, T: LanguageRegistry> Highlighter<'a, T> { } } -impl<'a, T: LanguageRegistry> Iterator for Highlighter<'a, T> { +impl<'a, T: Fn(&str) -> Option<(Language, &'a PropertySheet)>> Iterator + for Highlighter<'a, T> +{ type Item = HighlightEvent<'a>; fn next(&mut self) -> Option { @@ -738,23 +736,32 @@ impl<'de> Deserialize<'de> for Scope { } } -pub fn highlight<'a, T: LanguageRegistry>( - language_registry: &'a T, +pub trait HTMLAttributeCallback<'a>: Fn(Scope) -> &'a str {} + +pub fn highlight<'a, F>( source: &'a [u8], language: Language, property_sheet: &'a PropertySheet, -) -> Result> + 'a, String> { - Highlighter::new(language_registry, source, language, property_sheet) + injection_callback: &'a F, +) -> Result> + 'a, String> +where + F: Fn(&str) -> Option<(Language, &'a PropertySheet)>, +{ + Highlighter::new(source, language, property_sheet, injection_callback) } -pub fn highlight_html<'a, T: LanguageRegistry, F: Fn(Scope) -> &'a str>( - language_registry: &'a T, +pub fn highlight_html<'a, F1, F2>( source: &'a [u8], language: Language, property_sheet: &'a PropertySheet, - attribute_callback: F, -) -> Result, String> { - let highlighter = Highlighter::new(language_registry, source, language, property_sheet)?; + injection_callback: &'a F1, + attribute_callback: &'a F2, +) -> Result, String> +where + F1: Fn(&str) -> Option<(Language, &'a PropertySheet)>, + F2: Fn(Scope) -> &'a str, +{ + let highlighter = Highlighter::new(source, language, property_sheet, injection_callback)?; let mut renderer = HtmlRenderer::new(attribute_callback); let mut scopes = Vec::new(); for event in highlighter { @@ -782,7 +789,10 @@ struct HtmlRenderer<'a, F: Fn(Scope) -> &'a str> { attribute_callback: F, } -impl<'a, F: Fn(Scope) -> &'a str> HtmlRenderer<'a, F> { +impl<'a, F> HtmlRenderer<'a, F> +where + F: Fn(Scope) -> &'a str, +{ fn new(attribute_callback: F) -> Self { HtmlRenderer { result: Vec::new(),