tree_house/
config.rs

1use once_cell::sync::Lazy;
2use regex::Regex;
3use tree_sitter::{query, Grammar};
4
5use crate::highlighter::{Highlight, HighlightQuery};
6use crate::injections_query::{InjectionLanguageMarker, InjectionsQuery};
7use crate::Language;
8
9use std::fmt::Write;
10
11#[derive(Debug)]
12pub struct LanguageConfig {
13    pub grammar: Grammar,
14    pub highlight_query: HighlightQuery,
15    pub injection_query: InjectionsQuery,
16}
17
18impl LanguageConfig {
19    pub fn new(
20        grammar: Grammar,
21        highlight_query_text: &str,
22        injection_query_text: &str,
23        local_query_text: &str,
24    ) -> Result<Self, query::ParseError> {
25        // NOTE: the injection queries are parsed first since the local query is passed as-is
26        // to `Query::new` in `InjectionsQuery::new`. This ensures that the more readable error
27        // bubbles up first if the locals queries have an issue.
28        let injection_query =
29            InjectionsQuery::new(grammar, injection_query_text, local_query_text)?;
30        let highlight_query = HighlightQuery::new(grammar, highlight_query_text, local_query_text)?;
31
32        Ok(Self {
33            grammar,
34            highlight_query,
35            injection_query,
36        })
37    }
38
39    pub fn configure(&self, mut f: impl FnMut(&str) -> Option<Highlight>) {
40        self.highlight_query.configure(&mut f);
41        self.injection_query.configure(&mut f);
42    }
43}
44
45static INHERITS_REGEX: Lazy<Regex> =
46    Lazy::new(|| Regex::new(r";+\s*inherits\s*:?\s*([a-z_,()-]+)\s*").unwrap());
47
48/// reads a query by invoking `read_query_text`, handles any `inherits` directives
49pub fn read_query(language: &str, mut read_query_text: impl FnMut(&str) -> String) -> String {
50    fn read_query_impl(language: &str, read_query_text: &mut impl FnMut(&str) -> String) -> String {
51        let query = read_query_text(language);
52
53        // replaces all "; inherits <language>(,<language>)*" with the queries of the given language(s)
54        INHERITS_REGEX
55            .replace_all(&query, |captures: &regex::Captures| {
56                captures[1]
57                    .split(',')
58                    .fold(String::new(), |mut output, language| {
59                        // `write!` to a String cannot fail.
60                        write!(
61                            output,
62                            "\n{}\n",
63                            read_query_impl(language, &mut *read_query_text)
64                        )
65                        .unwrap();
66                        output
67                    })
68            })
69            .into_owned()
70    }
71    read_query_impl(language, &mut read_query_text)
72}
73
74pub trait LanguageLoader {
75    fn language_for_marker(&self, marker: InjectionLanguageMarker) -> Option<Language>;
76    fn get_config(&self, lang: Language) -> Option<&LanguageConfig>;
77}
78
79impl<T> LanguageLoader for &'_ T
80where
81    T: LanguageLoader,
82{
83    fn language_for_marker(&self, marker: InjectionLanguageMarker) -> Option<Language> {
84        T::language_for_marker(self, marker)
85    }
86
87    fn get_config(&self, lang: Language) -> Option<&LanguageConfig> {
88        T::get_config(self, lang)
89    }
90}