use std::sync::{Arc, LazyLock, RwLock};
use ahash::AHashMap;
use tree_sitter::Query;
use crate::error::Error;
#[cfg_attr(alef, alef(skip))]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum QueryKind {
Highlights,
Injections,
Locals,
Tags,
Indents,
Folds,
}
#[allow(clippy::type_complexity)]
static QUERY_CACHE: LazyLock<RwLock<AHashMap<(String, QueryKind), Option<Arc<Query>>>>> =
LazyLock::new(|| RwLock::new(AHashMap::new()));
fn source_for(language: &str, kind: QueryKind) -> Option<&'static str> {
match kind {
QueryKind::Highlights => crate::queries::get_highlights_query(language),
QueryKind::Injections => crate::queries::get_injections_query(language),
QueryKind::Locals => crate::queries::get_locals_query(language),
QueryKind::Tags => crate::queries::get_tags_query(language),
QueryKind::Indents => crate::queries::get_indents_query(language),
QueryKind::Folds => crate::queries::get_folds_query(language),
}
}
#[cfg_attr(alef, alef(skip))]
pub fn get_query(language: &str, kind: QueryKind) -> Result<Option<Arc<Query>>, Error> {
let lang = crate::registry::resolve_alias(language);
{
let cache = QUERY_CACHE.read().map_err(|e| Error::LockPoisoned(e.to_string()))?;
if let Some(entry) = cache.get(&(lang.to_string(), kind)) {
return Ok(entry.clone());
}
}
let compiled: Option<Arc<Query>> = match source_for(lang, kind) {
None => None, Some(src) => {
let language = crate::get_language(lang)?;
let query = Query::new(&language, src)
.map_err(|e| Error::QueryError(format!("failed to compile {kind:?} query for '{lang}': {e}")))?;
Some(Arc::new(query))
}
};
let mut cache = QUERY_CACHE.write().map_err(|e| Error::LockPoisoned(e.to_string()))?;
let entry = cache.entry((lang.to_string(), kind)).or_insert(compiled);
Ok(entry.clone())
}
#[cfg(test)]
mod tests {
use super::*;
const ALL_KINDS: [QueryKind; 6] = [
QueryKind::Highlights,
QueryKind::Injections,
QueryKind::Locals,
QueryKind::Tags,
QueryKind::Indents,
QueryKind::Folds,
];
#[test]
fn missing_query_returns_none_and_is_negatively_cached() {
for _ in 0..2 {
for kind in ALL_KINDS {
assert!(get_query("definitely_not_a_language", kind).unwrap().is_none());
}
}
}
}