diff --git a/highlighter/src/config.rs b/highlighter/src/config.rs index 20e6e38..25a02e9 100644 --- a/highlighter/src/config.rs +++ b/highlighter/src/config.rs @@ -45,7 +45,12 @@ impl LanguageConfig { static INHERITS_REGEX: Lazy = Lazy::new(|| Regex::new(r";+\s*inherits\s*:?\s*([a-z_,()-]+)\s*").unwrap()); -/// reads a query by invoking `read_query_text`, handles any `inherits` directives +static EXTENDS_REGEX: Lazy = Lazy::new(|| Regex::new(r";+\s*extends\s*").unwrap()); + +/// reads a query by invoking `read_query_text`, handles any `inherits` directives. +/// +/// if you need to also handle `extends` directives, you can use the [`read_query_extends`] +/// function. pub fn read_query(language: &str, mut read_query_text: impl FnMut(&str) -> String) -> String { fn read_query_impl(language: &str, read_query_text: &mut impl FnMut(&str) -> String) -> String { let query = read_query_text(language); @@ -71,6 +76,53 @@ pub fn read_query(language: &str, mut read_query_text: impl FnMut(&str) -> Strin read_query_impl(language, &mut read_query_text) } +/// gets a list of queries in priority order from highest to lowest by invoking +/// `read_lang_queries`, handles any `extends` and `inherits` directives. +/// +/// this function is very similar to [`read_query`], however it also handles `extends` +/// directives in addition to `inherits`. +pub fn read_query_extends(language: &str, mut read_lang_queries: impl FnMut(&str) -> I) -> String +where + I: Iterator, +{ + fn read_query_impl(read_lang_queries: &mut impl FnMut(&str) -> I, mut queries: I) -> String + where + I: Iterator, + { + let Some(mut query) = queries.next() else { + return String::new(); + }; + + // replace all "; extends" with the queries of the current language, one precedence level up + if let Some(m) = EXTENDS_REGEX.find(&query) { + let q = read_query_impl(read_lang_queries, queries); + query.replace_range(m.range(), &q); + } + + // replaces all "; inherits (,)*" with the queries of the given language(s) + INHERITS_REGEX + .replace_all(&query, |captures: ®ex::Captures| { + captures[1] + .split(',') + .fold(String::new(), |mut output, language| { + let queries = read_lang_queries(language); + // `write!` to a String cannot fail. + write!( + output, + "\n{}\n", + read_query_impl(&mut *read_lang_queries, queries) + ) + .unwrap(); + output + }) + }) + .into_owned() + } + + let queries = read_lang_queries(language); + read_query_impl(&mut read_lang_queries, queries) +} + pub trait LanguageLoader { fn language_for_marker(&self, marker: InjectionLanguageMarker) -> Option; fn get_config(&self, lang: Language) -> Option<&LanguageConfig>; diff --git a/highlighter/src/lib.rs b/highlighter/src/lib.rs index 43796cb..55c1433 100644 --- a/highlighter/src/lib.rs +++ b/highlighter/src/lib.rs @@ -8,7 +8,7 @@ use std::hash::{Hash, Hasher}; use std::time::Duration; use tree_sitter::{IncompatibleGrammarError, Node, Tree}; -pub use crate::config::{read_query, LanguageConfig, LanguageLoader}; +pub use crate::config::{read_query, read_query_extends, LanguageConfig, LanguageLoader}; pub use crate::injections_query::{InjectionLanguageMarker, InjectionsQuery}; use crate::parse::LayerUpdateFlags; pub use crate::tree_cursor::TreeCursor;