use libloading::{Library, Symbol};
use std::collections::HashMap;
use std::path::{Path, PathBuf};
use std::sync::{Arc, RwLock};
use tree_sitter::Language;
use tree_sitter_language::LanguageFn;
#[derive(Debug, thiserror::Error)]
pub enum GrammarLoadError {
#[error("grammar '{0}' not found in search paths")]
NotFound(String),
#[error("failed to load grammar '{grammar}': {detail}")]
LoadFailed {
grammar: String,
detail: String,
},
}
struct LoadedGrammar {
_library: Library,
language: Language,
}
pub struct GrammarLoader {
search_paths: Vec<PathBuf>,
cache: RwLock<HashMap<String, Arc<LoadedGrammar>>>,
highlight_cache: RwLock<HashMap<String, Arc<String>>>,
injection_cache: RwLock<HashMap<String, Arc<String>>>,
locals_cache: RwLock<HashMap<String, Arc<String>>>,
complexity_cache: RwLock<HashMap<String, Arc<String>>>,
calls_cache: RwLock<HashMap<String, Arc<String>>>,
types_cache: RwLock<HashMap<String, Arc<String>>>,
tags_cache: RwLock<HashMap<String, Arc<String>>>,
imports_cache: RwLock<HashMap<String, Arc<String>>>,
decorations_cache: RwLock<HashMap<String, Arc<String>>>,
test_regions_cache: RwLock<HashMap<String, Arc<String>>>,
cfg_cache: RwLock<HashMap<String, Arc<String>>>,
compiled_query_cache: RwLock<HashMap<String, Arc<tree_sitter::Query>>>,
}
impl GrammarLoader {
pub fn new() -> Self {
let mut paths = Vec::new();
if let Ok(env_path) = std::env::var("NORMALIZE_GRAMMAR_PATH") {
for p in env_path.split(':') {
if !p.is_empty() {
paths.push(PathBuf::from(p));
}
}
}
if let Some(config) = dirs::config_dir() {
paths.push(config.join("normalize/grammars"));
}
Self {
search_paths: paths,
cache: RwLock::new(HashMap::new()),
highlight_cache: RwLock::new(HashMap::new()),
injection_cache: RwLock::new(HashMap::new()),
locals_cache: RwLock::new(HashMap::new()),
complexity_cache: RwLock::new(HashMap::new()),
calls_cache: RwLock::new(HashMap::new()),
types_cache: RwLock::new(HashMap::new()),
tags_cache: RwLock::new(HashMap::new()),
imports_cache: RwLock::new(HashMap::new()),
decorations_cache: RwLock::new(HashMap::new()),
test_regions_cache: RwLock::new(HashMap::new()),
cfg_cache: RwLock::new(HashMap::new()),
compiled_query_cache: RwLock::new(HashMap::new()),
}
}
pub fn with_paths(paths: Vec<PathBuf>) -> Self {
Self {
search_paths: paths,
cache: RwLock::new(HashMap::new()),
highlight_cache: RwLock::new(HashMap::new()),
injection_cache: RwLock::new(HashMap::new()),
locals_cache: RwLock::new(HashMap::new()),
complexity_cache: RwLock::new(HashMap::new()),
calls_cache: RwLock::new(HashMap::new()),
types_cache: RwLock::new(HashMap::new()),
tags_cache: RwLock::new(HashMap::new()),
imports_cache: RwLock::new(HashMap::new()),
decorations_cache: RwLock::new(HashMap::new()),
test_regions_cache: RwLock::new(HashMap::new()),
cfg_cache: RwLock::new(HashMap::new()),
compiled_query_cache: RwLock::new(HashMap::new()),
}
}
pub fn add_path(&mut self, path: PathBuf) {
self.search_paths.push(path);
}
pub fn get(&self, name: &str) -> Result<Language, GrammarLoadError> {
if let Some(loaded) = self
.cache
.read()
.unwrap_or_else(|e| e.into_inner())
.get(name)
{
return Ok(loaded.language.clone());
}
self.load_external(name)
}
pub fn get_highlights(&self, name: &str) -> Option<Arc<String>> {
if let Some(query) = self
.highlight_cache
.read()
.unwrap_or_else(|e| e.into_inner())
.get(name)
{
return Some(Arc::clone(query));
}
self.load_query(name, "highlights", &self.highlight_cache)
}
pub fn get_injections(&self, name: &str) -> Option<Arc<String>> {
if let Some(query) = self
.injection_cache
.read()
.unwrap_or_else(|e| e.into_inner())
.get(name)
{
return Some(Arc::clone(query));
}
self.load_query(name, "injections", &self.injection_cache)
}
pub fn get_locals(&self, name: &str) -> Option<Arc<String>> {
if let Some(query) = self
.locals_cache
.read()
.unwrap_or_else(|e| e.into_inner())
.get(name)
{
return Some(Arc::clone(query));
}
self.load_query(name, "locals", &self.locals_cache)
}
pub fn get_complexity(&self, name: &str) -> Option<Arc<String>> {
if let Some(query) = self
.complexity_cache
.read()
.unwrap_or_else(|e| e.into_inner())
.get(name)
{
return Some(Arc::clone(query));
}
self.load_query(name, "complexity", &self.complexity_cache)
.or_else(|| {
let content = bundled_complexity_query(name)?;
let query = Arc::new(content.to_string());
self.complexity_cache
.write()
.unwrap_or_else(|e| e.into_inner())
.insert(name.to_string(), Arc::clone(&query));
Some(query)
})
}
pub fn get_calls(&self, name: &str) -> Option<Arc<String>> {
if let Some(query) = self
.calls_cache
.read()
.unwrap_or_else(|e| e.into_inner())
.get(name)
{
return Some(Arc::clone(query));
}
self.load_query(name, "calls", &self.calls_cache)
.or_else(|| {
let content = bundled_calls_query(name)?;
let query = Arc::new(content.to_string());
self.calls_cache
.write()
.unwrap_or_else(|e| e.into_inner())
.insert(name.to_string(), Arc::clone(&query));
Some(query)
})
}
pub fn get_types(&self, name: &str) -> Option<Arc<String>> {
if let Some(query) = self
.types_cache
.read()
.unwrap_or_else(|e| e.into_inner())
.get(name)
{
return Some(Arc::clone(query));
}
if let Some(q) = self.load_query(name, "types", &self.types_cache) {
return Some(q);
}
let bundled = bundled_types_query(name)?;
let query = Arc::new(bundled.to_string());
self.types_cache
.write()
.unwrap_or_else(|e| e.into_inner())
.insert(name.to_string(), Arc::clone(&query));
Some(query)
}
pub fn get_tags(&self, name: &str) -> Option<Arc<String>> {
if let Some(query) = self
.tags_cache
.read()
.unwrap_or_else(|e| e.into_inner())
.get(name)
{
return Some(Arc::clone(query));
}
if let Some(q) = self.load_query(name, "tags", &self.tags_cache) {
return Some(q);
}
let bundled = bundled_tags_query(name)?;
let query = Arc::new(bundled.to_string());
self.tags_cache
.write()
.unwrap_or_else(|e| e.into_inner())
.insert(name.to_string(), Arc::clone(&query));
Some(query)
}
pub fn get_imports(&self, name: &str) -> Option<Arc<String>> {
if let Some(query) = self
.imports_cache
.read()
.unwrap_or_else(|e| e.into_inner())
.get(name)
{
return Some(Arc::clone(query));
}
if let Some(q) = self.load_query(name, "imports", &self.imports_cache) {
return Some(q);
}
let bundled = bundled_imports_query(name)?;
let query = Arc::new(bundled.to_string());
self.imports_cache
.write()
.unwrap_or_else(|e| e.into_inner())
.insert(name.to_string(), Arc::clone(&query));
Some(query)
}
pub fn get_decorations(&self, name: &str) -> Option<Arc<String>> {
if let Some(query) = self
.decorations_cache
.read()
.unwrap_or_else(|e| e.into_inner())
.get(name)
{
return Some(Arc::clone(query));
}
if let Some(q) = self.load_query(name, "decorations", &self.decorations_cache) {
return Some(q);
}
let bundled = bundled_decorations_query(name)?;
let query = Arc::new(bundled.to_string());
self.decorations_cache
.write()
.unwrap_or_else(|e| e.into_inner())
.insert(name.to_string(), Arc::clone(&query));
Some(query)
}
pub fn get_test_regions(&self, name: &str) -> Option<Arc<String>> {
if let Some(query) = self
.test_regions_cache
.read()
.unwrap_or_else(|e| e.into_inner())
.get(name)
{
return Some(Arc::clone(query));
}
if let Some(q) = self.load_query(name, "test_regions", &self.test_regions_cache) {
return Some(q);
}
let bundled = bundled_test_regions_query(name)?;
let query = Arc::new(bundled.to_string());
self.test_regions_cache
.write()
.unwrap_or_else(|e| e.into_inner())
.insert(name.to_string(), Arc::clone(&query));
Some(query)
}
pub fn get_cfg(&self, name: &str) -> Option<Arc<String>> {
if let Some(query) = self
.cfg_cache
.read()
.unwrap_or_else(|e| e.into_inner())
.get(name)
{
return Some(Arc::clone(query));
}
self.load_query(name, "cfg", &self.cfg_cache).or_else(|| {
let content = bundled_cfg_query(name)?;
let query = Arc::new(content.to_string());
self.cfg_cache
.write()
.unwrap_or_else(|e| e.into_inner())
.insert(name.to_string(), Arc::clone(&query));
Some(query)
})
}
fn load_query(
&self,
name: &str,
query_type: &str,
cache: &RwLock<HashMap<String, Arc<String>>>,
) -> Option<Arc<String>> {
let scm_name = format!("{name}.{query_type}.scm");
for search_path in &self.search_paths {
let scm_path = search_path.join(&scm_name);
if scm_path.exists()
&& let Ok(content) = std::fs::read_to_string(&scm_path)
{
let query = Arc::new(content);
cache
.write()
.unwrap_or_else(|e| e.into_inner())
.insert(name.to_string(), Arc::clone(&query));
return Some(query);
}
}
None
}
pub fn get_compiled_query(
&self,
grammar_name: &str,
query_type: &str,
query_str: &str,
) -> Option<Arc<tree_sitter::Query>> {
let key = format!("{grammar_name}:{query_type}");
{
let cache = self
.compiled_query_cache
.read()
.unwrap_or_else(|e| e.into_inner());
if let Some(q) = cache.get(&key) {
return Some(Arc::clone(q));
}
}
let grammar = self.get(grammar_name).ok()?;
let compiled = tree_sitter::Query::new(&grammar, query_str).ok()?;
let arc = Arc::new(compiled);
self.compiled_query_cache
.write()
.unwrap_or_else(|e| e.into_inner())
.insert(key, Arc::clone(&arc));
Some(arc)
}
fn load_external(&self, name: &str) -> Result<Language, GrammarLoadError> {
let lib_name = grammar_lib_name(name);
for search_path in &self.search_paths {
let lib_path = search_path.join(&lib_name);
if lib_path.exists() {
return self.load_from_path(name, &lib_path);
}
}
Err(GrammarLoadError::NotFound(name.to_string()))
}
fn load_from_path(&self, name: &str, path: &Path) -> Result<Language, GrammarLoadError> {
let library = unsafe {
Library::new(path).map_err(|e| {
log::debug!("Failed to load grammar at {}: {}", path.display(), e);
GrammarLoadError::LoadFailed {
grammar: name.to_string(),
detail: e.to_string(),
}
})?
};
let symbol_name = grammar_symbol_name(name);
let language = unsafe {
let func: Result<Symbol<unsafe extern "C" fn() -> *const ()>, _> =
library.get(symbol_name.as_bytes());
match func {
Ok(f) => {
let lang_fn = LanguageFn::from_raw(*f);
Language::new(lang_fn)
}
Err(e) => {
log::debug!(
"Grammar '{}' at {} missing symbol '{}': {}",
name,
path.display(),
symbol_name,
e
);
return Err(GrammarLoadError::LoadFailed {
grammar: name.to_string(),
detail: format!("symbol '{}' not found: {}", symbol_name, e),
});
}
}
};
let loaded = Arc::new(LoadedGrammar {
_library: library,
language: language.clone(),
});
self.cache
.write()
.unwrap_or_else(|e| e.into_inner())
.insert(name.to_string(), loaded);
Ok(language)
}
pub fn available_external(&self) -> Vec<String> {
let mut grammars = Vec::new();
let ext = grammar_extension();
for path in &self.search_paths {
if let Ok(entries) = std::fs::read_dir(path) {
for entry in entries.flatten() {
let name = entry.file_name();
let name_str = name.to_string_lossy();
if name_str.ends_with(ext) {
let grammar_name = name_str.trim_end_matches(ext);
if !grammars.contains(&grammar_name.to_string()) {
grammars.push(grammar_name.to_string());
}
}
}
}
}
grammars.sort();
grammars
}
pub fn available_external_with_paths(&self) -> Vec<(String, std::path::PathBuf)> {
let mut grammars: Vec<(String, std::path::PathBuf)> = Vec::new();
let ext = grammar_extension();
for dir in &self.search_paths {
if let Ok(entries) = std::fs::read_dir(dir) {
for entry in entries.flatten() {
let name = entry.file_name();
let name_str = name.to_string_lossy();
if name_str.ends_with(ext) {
let grammar_name = name_str.trim_end_matches(ext).to_string();
if !grammars.iter().any(|(n, _)| n == &grammar_name) {
grammars.push((grammar_name, entry.path()));
}
}
}
}
}
grammars.sort_by(|a, b| a.0.cmp(&b.0));
grammars
}
}
impl Default for GrammarLoader {
fn default() -> Self {
Self::new()
}
}
fn grammar_lib_name(name: &str) -> String {
let ext = grammar_extension();
format!("{name}{ext}")
}
fn grammar_symbol_name(name: &str) -> String {
match name {
"rust" => return "tree_sitter_rust_orchard".to_string(),
"vb" => return "tree_sitter_vb_dotnet".to_string(),
_ => {}
}
let normalized = name.replace('-', "_");
format!("tree_sitter_{normalized}")
}
fn bundled_types_query(name: &str) -> Option<&'static str> {
match name {
"rust" => Some(include_str!("queries/rust.types.scm")),
"typescript" => Some(include_str!("queries/typescript.types.scm")),
"tsx" => Some(include_str!("queries/tsx.types.scm")),
"python" => Some(include_str!("queries/python.types.scm")),
"java" => Some(include_str!("queries/java.types.scm")),
"go" => Some(include_str!("queries/go.types.scm")),
"c" => Some(include_str!("queries/c.types.scm")),
"cpp" => Some(include_str!("queries/cpp.types.scm")),
"kotlin" => Some(include_str!("queries/kotlin.types.scm")),
"swift" => Some(include_str!("queries/swift.types.scm")),
"c-sharp" => Some(include_str!("queries/c-sharp.types.scm")),
"scala" => Some(include_str!("queries/scala.types.scm")),
"haskell" => Some(include_str!("queries/haskell.types.scm")),
"ruby" => Some(include_str!("queries/ruby.types.scm")),
"dart" => Some(include_str!("queries/dart.types.scm")),
"elixir" => Some(include_str!("queries/elixir.types.scm")),
"ocaml" => Some(include_str!("queries/ocaml.types.scm")),
"erlang" => Some(include_str!("queries/erlang.types.scm")),
"zig" => Some(include_str!("queries/zig.types.scm")),
"fsharp" => Some(include_str!("queries/fsharp.types.scm")),
"gleam" => Some(include_str!("queries/gleam.types.scm")),
"julia" => Some(include_str!("queries/julia.types.scm")),
"r" => Some(include_str!("queries/r.types.scm")),
"d" => Some(include_str!("queries/d.types.scm")),
"objc" => Some(include_str!("queries/objc.types.scm")),
"vb" => Some(include_str!("queries/vb.types.scm")),
"groovy" => Some(include_str!("queries/groovy.types.scm")),
"ada" => Some(include_str!("queries/ada.types.scm")),
"agda" => Some(include_str!("queries/agda.types.scm")),
"elm" => Some(include_str!("queries/elm.types.scm")),
"idris" => Some(include_str!("queries/idris.types.scm")),
"lean" => Some(include_str!("queries/lean.types.scm")),
"php" => Some(include_str!("queries/php.types.scm")),
"powershell" => Some(include_str!("queries/powershell.types.scm")),
"rescript" => Some(include_str!("queries/rescript.types.scm")),
"verilog" => Some(include_str!("queries/verilog.types.scm")),
"vhdl" => Some(include_str!("queries/vhdl.types.scm")),
"sql" => Some(include_str!("queries/sql.types.scm")),
"hcl" => Some(include_str!("queries/hcl.types.scm")),
"glsl" => Some(include_str!("queries/glsl.types.scm")),
"hlsl" => Some(include_str!("queries/hlsl.types.scm")),
"clojure" => Some(include_str!("queries/clojure.types.scm")),
"commonlisp" => Some(include_str!("queries/commonlisp.types.scm")),
"elisp" => Some(include_str!("queries/elisp.types.scm")),
"javascript" => Some(include_str!("queries/javascript.types.scm")),
"lua" => Some(include_str!("queries/lua.types.scm")),
"scheme" => Some(include_str!("queries/scheme.types.scm")),
"graphql" => Some(include_str!("queries/graphql.types.scm")),
"nix" => Some(include_str!("queries/nix.types.scm")),
"starlark" => Some(include_str!("queries/starlark.types.scm")),
"matlab" => Some(include_str!("queries/matlab.types.scm")),
"tlaplus" => Some(include_str!("queries/tlaplus.types.scm")),
"typst" => Some(include_str!("queries/typst.types.scm")),
_ => None,
}
}
fn bundled_tags_query(name: &str) -> Option<&'static str> {
match name {
"rust" => Some(include_str!("queries/rust.tags.scm")),
"python" => Some(include_str!("queries/python.tags.scm")),
"javascript" => Some(include_str!("queries/javascript.tags.scm")),
"typescript" => Some(include_str!("queries/typescript.tags.scm")),
"tsx" => Some(include_str!("queries/tsx.tags.scm")),
"go" => Some(include_str!("queries/go.tags.scm")),
"java" => Some(include_str!("queries/java.tags.scm")),
"c" => Some(include_str!("queries/c.tags.scm")),
"cpp" => Some(include_str!("queries/cpp.tags.scm")),
"ruby" => Some(include_str!("queries/ruby.tags.scm")),
"kotlin" => Some(include_str!("queries/kotlin.tags.scm")),
"scala" => Some(include_str!("queries/scala.tags.scm")),
"elixir" => Some(include_str!("queries/elixir.tags.scm")),
"swift" => Some(include_str!("queries/swift.tags.scm")),
"haskell" => Some(include_str!("queries/haskell.tags.scm")),
"dart" => Some(include_str!("queries/dart.tags.scm")),
"ocaml" => Some(include_str!("queries/ocaml.tags.scm")),
"fsharp" => Some(include_str!("queries/fsharp.tags.scm")),
"gleam" => Some(include_str!("queries/gleam.tags.scm")),
"zig" => Some(include_str!("queries/zig.tags.scm")),
"julia" => Some(include_str!("queries/julia.tags.scm")),
"erlang" => Some(include_str!("queries/erlang.tags.scm")),
"lua" => Some(include_str!("queries/lua.tags.scm")),
"php" => Some(include_str!("queries/php.tags.scm")),
"perl" => Some(include_str!("queries/perl.tags.scm")),
"r" => Some(include_str!("queries/r.tags.scm")),
"groovy" => Some(include_str!("queries/groovy.tags.scm")),
"c-sharp" => Some(include_str!("queries/c-sharp.tags.scm")),
"d" => Some(include_str!("queries/d.tags.scm")),
"graphql" => Some(include_str!("queries/graphql.tags.scm")),
"objc" => Some(include_str!("queries/objc.tags.scm")),
"vb" => Some(include_str!("queries/vb.tags.scm")),
"powershell" => Some(include_str!("queries/powershell.tags.scm")),
"clojure" => Some(include_str!("queries/clojure.tags.scm")),
"commonlisp" => Some(include_str!("queries/commonlisp.tags.scm")),
"scheme" => Some(include_str!("queries/scheme.tags.scm")),
"elisp" => Some(include_str!("queries/elisp.tags.scm")),
"bash" => Some(include_str!("queries/bash.tags.scm")),
"fish" => Some(include_str!("queries/fish.tags.scm")),
"zsh" => Some(include_str!("queries/zsh.tags.scm")),
"ada" => Some(include_str!("queries/ada.tags.scm")),
"idris" => Some(include_str!("queries/idris.tags.scm")),
"lean" => Some(include_str!("queries/lean.tags.scm")),
"rescript" => Some(include_str!("queries/rescript.tags.scm")),
"elm" => Some(include_str!("queries/elm.tags.scm")),
"markdown" => Some(include_str!("queries/markdown.tags.scm")),
"nix" => Some(include_str!("queries/nix.tags.scm")),
"prolog" => Some(include_str!("queries/prolog.tags.scm")),
"agda" => Some(include_str!("queries/agda.tags.scm")),
"awk" => Some(include_str!("queries/awk.tags.scm")),
"cmake" => Some(include_str!("queries/cmake.tags.scm")),
"glsl" => Some(include_str!("queries/glsl.tags.scm")),
"hcl" => Some(include_str!("queries/hcl.tags.scm")),
"hlsl" => Some(include_str!("queries/hlsl.tags.scm")),
"jq" => Some(include_str!("queries/jq.tags.scm")),
"matlab" => Some(include_str!("queries/matlab.tags.scm")),
"meson" => Some(include_str!("queries/meson.tags.scm")),
"nginx" => Some(include_str!("queries/nginx.tags.scm")),
"scss" => Some(include_str!("queries/scss.tags.scm")),
"sql" => Some(include_str!("queries/sql.tags.scm")),
"starlark" => Some(include_str!("queries/starlark.tags.scm")),
"svelte" => Some(include_str!("queries/svelte.tags.scm")),
"tlaplus" => Some(include_str!("queries/tlaplus.tags.scm")),
"typst" => Some(include_str!("queries/typst.tags.scm")),
"verilog" => Some(include_str!("queries/verilog.tags.scm")),
"vhdl" => Some(include_str!("queries/vhdl.tags.scm")),
"vim" => Some(include_str!("queries/vim.tags.scm")),
"vue" => Some(include_str!("queries/vue.tags.scm")),
"jinja2" => Some(include_str!("queries/jinja2.tags.scm")),
"json" => Some(include_str!("queries/json.tags.scm")),
"toml" => Some(include_str!("queries/toml.tags.scm")),
"yaml" => Some(include_str!("queries/yaml.tags.scm")),
"css" => Some(include_str!("queries/css.tags.scm")),
"html" => Some(include_str!("queries/html.tags.scm")),
"xml" => Some(include_str!("queries/xml.tags.scm")),
"thrift" => Some(include_str!("queries/thrift.tags.scm")),
"dockerfile" => Some(include_str!("queries/dockerfile.tags.scm")),
"caddy" => Some(include_str!("queries/caddy.tags.scm")),
_ => None,
}
}
fn grammar_extension() -> &'static str {
if cfg!(target_os = "macos") {
".dylib"
} else if cfg!(target_os = "windows") {
".dll"
} else {
".so"
}
}
fn bundled_complexity_query(name: &str) -> Option<&'static str> {
match name {
"rust" => Some(include_str!("queries/rust.complexity.scm")),
"python" => Some(include_str!("queries/python.complexity.scm")),
"go" => Some(include_str!("queries/go.complexity.scm")),
"javascript" => Some(include_str!("queries/javascript.complexity.scm")),
"typescript" => Some(include_str!("queries/typescript.complexity.scm")),
"tsx" => Some(include_str!("queries/tsx.complexity.scm")),
"java" => Some(include_str!("queries/java.complexity.scm")),
"c" => Some(include_str!("queries/c.complexity.scm")),
"cpp" => Some(include_str!("queries/cpp.complexity.scm")),
"ruby" => Some(include_str!("queries/ruby.complexity.scm")),
"kotlin" => Some(include_str!("queries/kotlin.complexity.scm")),
"swift" => Some(include_str!("queries/swift.complexity.scm")),
"c-sharp" => Some(include_str!("queries/c-sharp.complexity.scm")),
"bash" => Some(include_str!("queries/bash.complexity.scm")),
"lua" => Some(include_str!("queries/lua.complexity.scm")),
"elixir" => Some(include_str!("queries/elixir.complexity.scm")),
"scala" => Some(include_str!("queries/scala.complexity.scm")),
"dart" => Some(include_str!("queries/dart.complexity.scm")),
"zig" => Some(include_str!("queries/zig.complexity.scm")),
"ocaml" => Some(include_str!("queries/ocaml.complexity.scm")),
"erlang" => Some(include_str!("queries/erlang.complexity.scm")),
"php" => Some(include_str!("queries/php.complexity.scm")),
"haskell" => Some(include_str!("queries/haskell.complexity.scm")),
"r" => Some(include_str!("queries/r.complexity.scm")),
"julia" => Some(include_str!("queries/julia.complexity.scm")),
"perl" => Some(include_str!("queries/perl.complexity.scm")),
"groovy" => Some(include_str!("queries/groovy.complexity.scm")),
"elm" => Some(include_str!("queries/elm.complexity.scm")),
"powershell" => Some(include_str!("queries/powershell.complexity.scm")),
"fish" => Some(include_str!("queries/fish.complexity.scm")),
"fsharp" => Some(include_str!("queries/fsharp.complexity.scm")),
"gleam" => Some(include_str!("queries/gleam.complexity.scm")),
"clojure" => Some(include_str!("queries/clojure.complexity.scm")),
"commonlisp" => Some(include_str!("queries/commonlisp.complexity.scm")),
"scheme" => Some(include_str!("queries/scheme.complexity.scm")),
"d" => Some(include_str!("queries/d.complexity.scm")),
"objc" => Some(include_str!("queries/objc.complexity.scm")),
"vb" => Some(include_str!("queries/vb.complexity.scm")),
"elisp" => Some(include_str!("queries/elisp.complexity.scm")),
"hcl" => Some(include_str!("queries/hcl.complexity.scm")),
"matlab" => Some(include_str!("queries/matlab.complexity.scm")),
"nix" => Some(include_str!("queries/nix.complexity.scm")),
"sql" => Some(include_str!("queries/sql.complexity.scm")),
"starlark" => Some(include_str!("queries/starlark.complexity.scm")),
"vim" => Some(include_str!("queries/vim.complexity.scm")),
"zsh" => Some(include_str!("queries/zsh.complexity.scm")),
"rescript" => Some(include_str!("queries/rescript.complexity.scm")),
"idris" => Some(include_str!("queries/idris.complexity.scm")),
"lean" => Some(include_str!("queries/lean.complexity.scm")),
"ada" => Some(include_str!("queries/ada.complexity.scm")),
"agda" => Some(include_str!("queries/agda.complexity.scm")),
"awk" => Some(include_str!("queries/awk.complexity.scm")),
"cmake" => Some(include_str!("queries/cmake.complexity.scm")),
"glsl" => Some(include_str!("queries/glsl.complexity.scm")),
"graphql" => Some(include_str!("queries/graphql.complexity.scm")),
"hlsl" => Some(include_str!("queries/hlsl.complexity.scm")),
"jq" => Some(include_str!("queries/jq.complexity.scm")),
"meson" => Some(include_str!("queries/meson.complexity.scm")),
"nginx" => Some(include_str!("queries/nginx.complexity.scm")),
"prolog" => Some(include_str!("queries/prolog.complexity.scm")),
"scss" => Some(include_str!("queries/scss.complexity.scm")),
"svelte" => Some(include_str!("queries/svelte.complexity.scm")),
"tlaplus" => Some(include_str!("queries/tlaplus.complexity.scm")),
"typst" => Some(include_str!("queries/typst.complexity.scm")),
"verilog" => Some(include_str!("queries/verilog.complexity.scm")),
"vhdl" => Some(include_str!("queries/vhdl.complexity.scm")),
"vue" => Some(include_str!("queries/vue.complexity.scm")),
"batch" => Some(include_str!("queries/batch.complexity.scm")),
"thrift" => Some(include_str!("queries/thrift.complexity.scm")),
"jinja2" => Some(include_str!("queries/jinja2.complexity.scm")),
_ => None,
}
}
fn bundled_calls_query(name: &str) -> Option<&'static str> {
match name {
"python" => Some(include_str!("queries/python.calls.scm")),
"rust" => Some(include_str!("queries/rust.calls.scm")),
"typescript" => Some(include_str!("queries/typescript.calls.scm")),
"tsx" => Some(include_str!("queries/tsx.calls.scm")),
"javascript" => Some(include_str!("queries/javascript.calls.scm")),
"java" => Some(include_str!("queries/java.calls.scm")),
"go" => Some(include_str!("queries/go.calls.scm")),
"c" => Some(include_str!("queries/c.calls.scm")),
"cpp" => Some(include_str!("queries/cpp.calls.scm")),
"ruby" => Some(include_str!("queries/ruby.calls.scm")),
"kotlin" => Some(include_str!("queries/kotlin.calls.scm")),
"swift" => Some(include_str!("queries/swift.calls.scm")),
"c-sharp" => Some(include_str!("queries/c-sharp.calls.scm")),
"bash" => Some(include_str!("queries/bash.calls.scm")),
"scala" => Some(include_str!("queries/scala.calls.scm")),
"elixir" => Some(include_str!("queries/elixir.calls.scm")),
"lua" => Some(include_str!("queries/lua.calls.scm")),
"dart" => Some(include_str!("queries/dart.calls.scm")),
"graphql" => Some(include_str!("queries/graphql.calls.scm")),
"ocaml" => Some(include_str!("queries/ocaml.calls.scm")),
"erlang" => Some(include_str!("queries/erlang.calls.scm")),
"zig" => Some(include_str!("queries/zig.calls.scm")),
"julia" => Some(include_str!("queries/julia.calls.scm")),
"r" => Some(include_str!("queries/r.calls.scm")),
"haskell" => Some(include_str!("queries/haskell.calls.scm")),
"php" => Some(include_str!("queries/php.calls.scm")),
"perl" => Some(include_str!("queries/perl.calls.scm")),
"fsharp" => Some(include_str!("queries/fsharp.calls.scm")),
"gleam" => Some(include_str!("queries/gleam.calls.scm")),
"groovy" => Some(include_str!("queries/groovy.calls.scm")),
"clojure" => Some(include_str!("queries/clojure.calls.scm")),
"d" => Some(include_str!("queries/d.calls.scm")),
"objc" => Some(include_str!("queries/objc.calls.scm")),
"elisp" => Some(include_str!("queries/elisp.calls.scm")),
"hcl" => Some(include_str!("queries/hcl.calls.scm")),
"matlab" => Some(include_str!("queries/matlab.calls.scm")),
"nix" => Some(include_str!("queries/nix.calls.scm")),
"starlark" => Some(include_str!("queries/starlark.calls.scm")),
"vim" => Some(include_str!("queries/vim.calls.scm")),
"zsh" => Some(include_str!("queries/zsh.calls.scm")),
"rescript" => Some(include_str!("queries/rescript.calls.scm")),
"prolog" => Some(include_str!("queries/prolog.calls.scm")),
"sql" => Some(include_str!("queries/sql.calls.scm")),
"ada" => Some(include_str!("queries/ada.calls.scm")),
"agda" => Some(include_str!("queries/agda.calls.scm")),
"awk" => Some(include_str!("queries/awk.calls.scm")),
"batch" => Some(include_str!("queries/batch.calls.scm")),
"cmake" => Some(include_str!("queries/cmake.calls.scm")),
"elm" => Some(include_str!("queries/elm.calls.scm")),
"fish" => Some(include_str!("queries/fish.calls.scm")),
"idris" => Some(include_str!("queries/idris.calls.scm")),
"lean" => Some(include_str!("queries/lean.calls.scm")),
"meson" => Some(include_str!("queries/meson.calls.scm")),
"powershell" => Some(include_str!("queries/powershell.calls.scm")),
"scheme" => Some(include_str!("queries/scheme.calls.scm")),
"thrift" => Some(include_str!("queries/thrift.calls.scm")),
"tlaplus" => Some(include_str!("queries/tlaplus.calls.scm")),
"verilog" => Some(include_str!("queries/verilog.calls.scm")),
"vhdl" => Some(include_str!("queries/vhdl.calls.scm")),
"vb" => Some(include_str!("queries/vb.calls.scm")),
"commonlisp" => Some(include_str!("queries/commonlisp.calls.scm")),
"scss" => Some(include_str!("queries/scss.calls.scm")),
"glsl" => Some(include_str!("queries/glsl.calls.scm")),
"hlsl" => Some(include_str!("queries/hlsl.calls.scm")),
"typst" => Some(include_str!("queries/typst.calls.scm")),
"svelte" => Some(include_str!("queries/svelte.calls.scm")),
"vue" => Some(include_str!("queries/vue.calls.scm")),
"jq" => Some(include_str!("queries/jq.calls.scm")),
"jinja2" => Some(include_str!("queries/jinja2.calls.scm")),
"nginx" => Some(include_str!("queries/nginx.calls.scm")),
_ => None,
}
}
fn bundled_imports_query(name: &str) -> Option<&'static str> {
match name {
"python" => Some(include_str!("queries/python.imports.scm")),
"javascript" => Some(include_str!("queries/javascript.imports.scm")),
"go" => Some(include_str!("queries/go.imports.scm")),
"lua" => Some(include_str!("queries/lua.imports.scm")),
"rust" => Some(include_str!("queries/rust.imports.scm")),
"typescript" => Some(include_str!("queries/typescript.imports.scm")),
"tsx" => Some(include_str!("queries/tsx.imports.scm")),
"java" => Some(include_str!("queries/java.imports.scm")),
"kotlin" => Some(include_str!("queries/kotlin.imports.scm")),
"c-sharp" => Some(include_str!("queries/c-sharp.imports.scm")),
"ruby" => Some(include_str!("queries/ruby.imports.scm")),
"swift" => Some(include_str!("queries/swift.imports.scm")),
"scala" => Some(include_str!("queries/scala.imports.scm")),
"elixir" => Some(include_str!("queries/elixir.imports.scm")),
"dart" => Some(include_str!("queries/dart.imports.scm")),
"php" => Some(include_str!("queries/php.imports.scm")),
"c" => Some(include_str!("queries/c.imports.scm")),
"cpp" => Some(include_str!("queries/cpp.imports.scm")),
"bash" => Some(include_str!("queries/bash.imports.scm")),
"zsh" => Some(include_str!("queries/zsh.imports.scm")),
"fish" => Some(include_str!("queries/fish.imports.scm")),
"perl" => Some(include_str!("queries/perl.imports.scm")),
"r" => Some(include_str!("queries/r.imports.scm")),
"haskell" => Some(include_str!("queries/haskell.imports.scm")),
"ocaml" => Some(include_str!("queries/ocaml.imports.scm")),
"fsharp" => Some(include_str!("queries/fsharp.imports.scm")),
"erlang" => Some(include_str!("queries/erlang.imports.scm")),
"gleam" => Some(include_str!("queries/gleam.imports.scm")),
"zig" => Some(include_str!("queries/zig.imports.scm")),
"julia" => Some(include_str!("queries/julia.imports.scm")),
"groovy" => Some(include_str!("queries/groovy.imports.scm")),
"clojure" => Some(include_str!("queries/clojure.imports.scm")),
"commonlisp" => Some(include_str!("queries/commonlisp.imports.scm")),
"scheme" => Some(include_str!("queries/scheme.imports.scm")),
"elisp" => Some(include_str!("queries/elisp.imports.scm")),
"d" => Some(include_str!("queries/d.imports.scm")),
"objc" => Some(include_str!("queries/objc.imports.scm")),
"vb" => Some(include_str!("queries/vb.imports.scm")),
"powershell" => Some(include_str!("queries/powershell.imports.scm")),
"vim" => Some(include_str!("queries/vim.imports.scm")),
"matlab" => Some(include_str!("queries/matlab.imports.scm")),
"nix" => Some(include_str!("queries/nix.imports.scm")),
"starlark" => Some(include_str!("queries/starlark.imports.scm")),
"rescript" => Some(include_str!("queries/rescript.imports.scm")),
"idris" => Some(include_str!("queries/idris.imports.scm")),
"ada" => Some(include_str!("queries/ada.imports.scm")),
"agda" => Some(include_str!("queries/agda.imports.scm")),
"asciidoc" => Some(include_str!("queries/asciidoc.imports.scm")),
"caddy" => Some(include_str!("queries/caddy.imports.scm")),
"capnp" => Some(include_str!("queries/capnp.imports.scm")),
"cmake" => Some(include_str!("queries/cmake.imports.scm")),
"devicetree" => Some(include_str!("queries/devicetree.imports.scm")),
"dockerfile" => Some(include_str!("queries/dockerfile.imports.scm")),
"elm" => Some(include_str!("queries/elm.imports.scm")),
"hcl" => Some(include_str!("queries/hcl.imports.scm")),
"hlsl" => Some(include_str!("queries/hlsl.imports.scm")),
"jq" => Some(include_str!("queries/jq.imports.scm")),
"lean" => Some(include_str!("queries/lean.imports.scm")),
"meson" => Some(include_str!("queries/meson.imports.scm")),
"nginx" => Some(include_str!("queries/nginx.imports.scm")),
"ninja" => Some(include_str!("queries/ninja.imports.scm")),
"prolog" => Some(include_str!("queries/prolog.imports.scm")),
"awk" => Some(include_str!("queries/awk.imports.scm")),
"css" => Some(include_str!("queries/css.imports.scm")),
"glsl" => Some(include_str!("queries/glsl.imports.scm")),
"html" => Some(include_str!("queries/html.imports.scm")),
"jinja2" => Some(include_str!("queries/jinja2.imports.scm")),
"scss" => Some(include_str!("queries/scss.imports.scm")),
"thrift" => Some(include_str!("queries/thrift.imports.scm")),
"tlaplus" => Some(include_str!("queries/tlaplus.imports.scm")),
"typst" => Some(include_str!("queries/typst.imports.scm")),
"verilog" => Some(include_str!("queries/verilog.imports.scm")),
"vhdl" => Some(include_str!("queries/vhdl.imports.scm")),
"wit" => Some(include_str!("queries/wit.imports.scm")),
_ => None,
}
}
fn bundled_decorations_query(name: &str) -> Option<&'static str> {
match name {
"rust" => Some(include_str!("queries/rust.decorations.scm")),
"python" => Some(include_str!("queries/python.decorations.scm")),
"javascript" => Some(include_str!("queries/javascript.decorations.scm")),
"typescript" => Some(include_str!("queries/typescript.decorations.scm")),
"tsx" => Some(include_str!("queries/tsx.decorations.scm")),
"java" => Some(include_str!("queries/java.decorations.scm")),
"kotlin" => Some(include_str!("queries/kotlin.decorations.scm")),
"scala" => Some(include_str!("queries/scala.decorations.scm")),
"c-sharp" => Some(include_str!("queries/c-sharp.decorations.scm")),
"php" => Some(include_str!("queries/php.decorations.scm")),
"swift" => Some(include_str!("queries/swift.decorations.scm")),
"dart" => Some(include_str!("queries/dart.decorations.scm")),
"ocaml" => Some(include_str!("queries/ocaml.decorations.scm")),
"rescript" => Some(include_str!("queries/rescript.decorations.scm")),
"fsharp" => Some(include_str!("queries/fsharp.decorations.scm")),
"elixir" => Some(include_str!("queries/elixir.decorations.scm")),
"erlang" => Some(include_str!("queries/erlang.decorations.scm")),
"gleam" => Some(include_str!("queries/gleam.decorations.scm")),
"lean" => Some(include_str!("queries/lean.decorations.scm")),
"groovy" => Some(include_str!("queries/groovy.decorations.scm")),
"vb" => Some(include_str!("queries/vb.decorations.scm")),
"haskell" => Some(include_str!("queries/haskell.decorations.scm")),
"go" => Some(include_str!("queries/go.decorations.scm")),
"c" => Some(include_str!("queries/c.decorations.scm")),
"cpp" => Some(include_str!("queries/cpp.decorations.scm")),
"objc" => Some(include_str!("queries/objc.decorations.scm")),
"ruby" => Some(include_str!("queries/ruby.decorations.scm")),
"r" => Some(include_str!("queries/r.decorations.scm")),
"lua" => Some(include_str!("queries/lua.decorations.scm")),
"zig" => Some(include_str!("queries/zig.decorations.scm")),
"idris" => Some(include_str!("queries/idris.decorations.scm")),
"agda" => Some(include_str!("queries/agda.decorations.scm")),
"elm" => Some(include_str!("queries/elm.decorations.scm")),
"julia" => Some(include_str!("queries/julia.decorations.scm")),
"perl" => Some(include_str!("queries/perl.decorations.scm")),
"verilog" => Some(include_str!("queries/verilog.decorations.scm")),
"vhdl" => Some(include_str!("queries/vhdl.decorations.scm")),
"ada" => Some(include_str!("queries/ada.decorations.scm")),
"capnp" => Some(include_str!("queries/capnp.decorations.scm")),
"thrift" => Some(include_str!("queries/thrift.decorations.scm")),
"graphql" => Some(include_str!("queries/graphql.decorations.scm")),
"wit" => Some(include_str!("queries/wit.decorations.scm")),
"clojure" => Some(include_str!("queries/clojure.decorations.scm")),
"scheme" => Some(include_str!("queries/scheme.decorations.scm")),
"prolog" => Some(include_str!("queries/prolog.decorations.scm")),
_ => None,
}
}
fn bundled_test_regions_query(name: &str) -> Option<&'static str> {
match name {
"rust" => Some(include_str!("queries/rust.test_regions.scm")),
_ => None,
}
}
fn bundled_cfg_query(name: &str) -> Option<&'static str> {
match name {
"rust" => Some(include_str!("queries/rust.cfg.scm")),
"python" => Some(include_str!("queries/python.cfg.scm")),
"go" => Some(include_str!("queries/go.cfg.scm")),
"typescript" => Some(include_str!("queries/typescript.cfg.scm")),
"tsx" => Some(include_str!("queries/tsx.cfg.scm")),
"javascript" => Some(include_str!("queries/javascript.cfg.scm")),
"java" => Some(include_str!("queries/java.cfg.scm")),
"c" => Some(include_str!("queries/c.cfg.scm")),
"cpp" => Some(include_str!("queries/cpp.cfg.scm")),
"objc" => Some(include_str!("queries/objc.cfg.scm")),
"c-sharp" => Some(include_str!("queries/c-sharp.cfg.scm")),
"kotlin" => Some(include_str!("queries/kotlin.cfg.scm")),
"swift" => Some(include_str!("queries/swift.cfg.scm")),
"dart" => Some(include_str!("queries/dart.cfg.scm")),
"scala" => Some(include_str!("queries/scala.cfg.scm")),
"groovy" => Some(include_str!("queries/groovy.cfg.scm")),
"vb" => Some(include_str!("queries/vb.cfg.scm")),
"haskell" => Some(include_str!("queries/haskell.cfg.scm")),
"ocaml" => Some(include_str!("queries/ocaml.cfg.scm")),
"fsharp" => Some(include_str!("queries/fsharp.cfg.scm")),
"elixir" => Some(include_str!("queries/elixir.cfg.scm")),
"erlang" => Some(include_str!("queries/erlang.cfg.scm")),
"clojure" => Some(include_str!("queries/clojure.cfg.scm")),
"gleam" => Some(include_str!("queries/gleam.cfg.scm")),
"rescript" => Some(include_str!("queries/rescript.cfg.scm")),
"idris" => Some(include_str!("queries/idris.cfg.scm")),
"agda" => Some(include_str!("queries/agda.cfg.scm")),
"lean" => Some(include_str!("queries/lean.cfg.scm")),
"commonlisp" => Some(include_str!("queries/commonlisp.cfg.scm")),
"scheme" => Some(include_str!("queries/scheme.cfg.scm")),
"elisp" => Some(include_str!("queries/elisp.cfg.scm")),
"ruby" => Some(include_str!("queries/ruby.cfg.scm")),
"lua" => Some(include_str!("queries/lua.cfg.scm")),
"php" => Some(include_str!("queries/php.cfg.scm")),
"perl" => Some(include_str!("queries/perl.cfg.scm")),
"bash" => Some(include_str!("queries/bash.cfg.scm")),
"fish" => Some(include_str!("queries/fish.cfg.scm")),
"awk" => Some(include_str!("queries/awk.cfg.scm")),
"zsh" => Some(include_str!("queries/zsh.cfg.scm")),
"powershell" => Some(include_str!("queries/powershell.cfg.scm")),
"batch" => Some(include_str!("queries/batch.cfg.scm")),
"vim" => Some(include_str!("queries/vim.cfg.scm")),
"zig" => Some(include_str!("queries/zig.cfg.scm")),
"ada" => Some(include_str!("queries/ada.cfg.scm")),
"d" => Some(include_str!("queries/d.cfg.scm")),
"prolog" => Some(include_str!("queries/prolog.cfg.scm")),
"r" => Some(include_str!("queries/r.cfg.scm")),
"julia" => Some(include_str!("queries/julia.cfg.scm")),
"matlab" => Some(include_str!("queries/matlab.cfg.scm")),
"glsl" => Some(include_str!("queries/glsl.cfg.scm")),
"hlsl" => Some(include_str!("queries/hlsl.cfg.scm")),
"verilog" => Some(include_str!("queries/verilog.cfg.scm")),
"vhdl" => Some(include_str!("queries/vhdl.cfg.scm")),
"nix" => Some(include_str!("queries/nix.cfg.scm")),
"hcl" => Some(include_str!("queries/hcl.cfg.scm")),
"starlark" => Some(include_str!("queries/starlark.cfg.scm")),
"elm" => Some(include_str!("queries/elm.cfg.scm")),
"jinja2" => Some(include_str!("queries/jinja2.cfg.scm")),
"svelte" => Some(include_str!("queries/svelte.cfg.scm")),
"vue" => Some(include_str!("queries/vue.cfg.scm")),
"cmake" => Some(include_str!("queries/cmake.cfg.scm")),
"meson" => Some(include_str!("queries/meson.cfg.scm")),
"tlaplus" => Some(include_str!("queries/tlaplus.cfg.scm")),
"jq" => Some(include_str!("queries/jq.cfg.scm")),
_ => None,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_grammar_lib_name() {
let name = grammar_lib_name("python");
assert!(name.starts_with("python."));
}
#[test]
fn test_grammar_symbol_name() {
assert_eq!(grammar_symbol_name("python"), "tree_sitter_python");
assert_eq!(grammar_symbol_name("rust"), "tree_sitter_rust_orchard");
assert_eq!(grammar_symbol_name("ssh-config"), "tree_sitter_ssh_config");
assert_eq!(grammar_symbol_name("vb"), "tree_sitter_vb_dotnet");
}
#[test]
fn test_bundled_tags_queries() {
for lang in &[
"rust",
"python",
"javascript",
"typescript",
"tsx",
"go",
"java",
"c",
"cpp",
"ruby",
"kotlin",
"scala",
"elixir",
"swift",
"haskell",
"dart",
"ocaml",
"fsharp",
"gleam",
"zig",
"julia",
"erlang",
"lua",
"php",
"perl",
"r",
"groovy",
"d",
"objc",
"vb",
"powershell",
"clojure",
"commonlisp",
"scheme",
"elisp",
"bash",
"fish",
"zsh",
"ada",
"idris",
"lean",
"rescript",
"elm",
] {
let query = bundled_tags_query(lang);
assert!(query.is_some(), "Missing bundled tags query for {lang}");
assert!(
!query.unwrap().is_empty(),
"Empty bundled tags query for {lang}"
);
}
}
#[test]
fn test_bundled_types_queries() {
for lang in &[
"rust",
"python",
"typescript",
"tsx",
"java",
"go",
"c",
"cpp",
"kotlin",
"swift",
"c-sharp",
"scala",
"haskell",
"ruby",
"dart",
"elixir",
"ocaml",
"erlang",
"zig",
"fsharp",
"gleam",
"julia",
"r",
"d",
"objc",
"vb",
"groovy",
"ada",
"agda",
"elm",
"idris",
"lean",
"php",
"powershell",
"rescript",
"verilog",
"vhdl",
"sql",
"hcl",
"glsl",
"hlsl",
"clojure",
"commonlisp",
"elisp",
"javascript",
"lua",
"scheme",
"graphql",
"nix",
"starlark",
"matlab",
"tlaplus",
"typst",
] {
let query = bundled_types_query(lang);
assert!(query.is_some(), "Missing bundled types query for {lang}");
assert!(
!query.unwrap().is_empty(),
"Empty bundled types query for {lang}"
);
}
}
#[test]
fn test_bundled_complexity_queries() {
for lang in &[
"rust",
"python",
"go",
"javascript",
"typescript",
"tsx",
"java",
"c",
"cpp",
"ruby",
"kotlin",
"swift",
"c-sharp",
"bash",
"lua",
"elixir",
"scala",
"dart",
"zig",
"ocaml",
"erlang",
"php",
"haskell",
"r",
"julia",
"perl",
"groovy",
"elm",
"powershell",
"fish",
"fsharp",
"gleam",
"clojure",
"commonlisp",
"scheme",
"d",
"objc",
"vb",
"elisp",
"hcl",
"matlab",
"nix",
"sql",
"starlark",
"vim",
"zsh",
"rescript",
"idris",
"lean",
] {
let query = bundled_complexity_query(lang);
assert!(
query.is_some(),
"Missing bundled complexity query for {lang}"
);
assert!(
!query.unwrap().is_empty(),
"Empty bundled complexity query for {lang}"
);
}
}
#[test]
fn test_bundled_calls_queries() {
for lang in &[
"python",
"rust",
"typescript",
"tsx",
"javascript",
"java",
"go",
"c",
"cpp",
"ruby",
"kotlin",
"swift",
"c-sharp",
"bash",
"scala",
"elixir",
"lua",
"dart",
"ocaml",
"erlang",
"zig",
"julia",
"r",
"haskell",
"php",
"perl",
"fsharp",
"gleam",
"groovy",
"clojure",
"d",
"objc",
"elisp",
"hcl",
"matlab",
"nix",
"starlark",
"vim",
"zsh",
"rescript",
"prolog",
"sql",
"ada",
"agda",
"awk",
"batch",
"cmake",
"elm",
"fish",
"idris",
"lean",
"meson",
"powershell",
"scheme",
"thrift",
"tlaplus",
"verilog",
"vhdl",
"vb",
"commonlisp",
"scss",
"jinja2",
"nginx",
] {
let query = bundled_calls_query(lang);
assert!(query.is_some(), "Missing bundled calls query for {lang}");
assert!(
!query.unwrap().is_empty(),
"Empty bundled calls query for {lang}"
);
}
}
#[test]
fn test_bundled_imports_queries() {
for lang in &[
"awk",
"python",
"javascript",
"go",
"lua",
"rust",
"typescript",
"tsx",
"java",
"kotlin",
"c-sharp",
"ruby",
"swift",
"scala",
"elixir",
"dart",
"php",
"c",
"cpp",
"bash",
"zsh",
"fish",
"perl",
"r",
"haskell",
"ocaml",
"fsharp",
"erlang",
"gleam",
"zig",
"julia",
"groovy",
"clojure",
"commonlisp",
"scheme",
"elisp",
"d",
"objc",
"vb",
"powershell",
"vim",
"matlab",
"nix",
"starlark",
"rescript",
"idris",
"ada",
"agda",
"asciidoc",
"caddy",
"capnp",
"cmake",
"devicetree",
"dockerfile",
"elm",
"hcl",
"hlsl",
"jq",
"lean",
"meson",
"nginx",
"ninja",
"prolog",
"css",
"glsl",
"html",
"jinja2",
"scss",
"thrift",
"tlaplus",
"typst",
"verilog",
"vhdl",
"wit",
] {
let query = bundled_imports_query(lang);
assert!(query.is_some(), "Missing bundled imports query for {lang}");
assert!(
!query.unwrap().is_empty(),
"Empty bundled imports query for {lang}"
);
}
}
#[test]
fn test_get_imports_returns_bundled() {
let loader = GrammarLoader::with_paths(vec![]);
assert!(loader.get_imports("awk").is_some());
assert!(loader.get_imports("python").is_some());
assert!(loader.get_imports("javascript").is_some());
assert!(loader.get_imports("go").is_some());
assert!(loader.get_imports("lua").is_some());
assert!(loader.get_imports("rust").is_some());
assert!(loader.get_imports("typescript").is_some());
assert!(loader.get_imports("tsx").is_some());
assert!(loader.get_imports("java").is_some());
assert!(loader.get_imports("kotlin").is_some());
assert!(loader.get_imports("c-sharp").is_some());
assert!(loader.get_imports("ruby").is_some());
assert!(loader.get_imports("swift").is_some());
assert!(loader.get_imports("scala").is_some());
assert!(loader.get_imports("elixir").is_some());
assert!(loader.get_imports("dart").is_some());
assert!(loader.get_imports("php").is_some());
assert!(loader.get_imports("c").is_some());
assert!(loader.get_imports("cpp").is_some());
assert!(loader.get_imports("bash").is_some());
assert!(loader.get_imports("zsh").is_some());
assert!(loader.get_imports("fish").is_some());
assert!(loader.get_imports("perl").is_some());
assert!(loader.get_imports("r").is_some());
assert!(loader.get_imports("haskell").is_some());
assert!(loader.get_imports("ocaml").is_some());
assert!(loader.get_imports("fsharp").is_some());
assert!(loader.get_imports("erlang").is_some());
assert!(loader.get_imports("gleam").is_some());
assert!(loader.get_imports("zig").is_some());
assert!(loader.get_imports("julia").is_some());
assert!(loader.get_imports("groovy").is_some());
assert!(loader.get_imports("clojure").is_some());
assert!(loader.get_imports("commonlisp").is_some());
assert!(loader.get_imports("scheme").is_some());
assert!(loader.get_imports("elisp").is_some());
assert!(loader.get_imports("d").is_some());
assert!(loader.get_imports("objc").is_some());
assert!(loader.get_imports("vb").is_some());
assert!(loader.get_imports("powershell").is_some());
assert!(loader.get_imports("vim").is_some());
assert!(loader.get_imports("matlab").is_some());
assert!(loader.get_imports("nix").is_some());
assert!(loader.get_imports("starlark").is_some());
assert!(loader.get_imports("rescript").is_some());
assert!(loader.get_imports("idris").is_some());
assert!(loader.get_imports("ada").is_some());
assert!(loader.get_imports("agda").is_some());
assert!(loader.get_imports("asciidoc").is_some());
assert!(loader.get_imports("caddy").is_some());
assert!(loader.get_imports("capnp").is_some());
assert!(loader.get_imports("cmake").is_some());
assert!(loader.get_imports("devicetree").is_some());
assert!(loader.get_imports("dockerfile").is_some());
assert!(loader.get_imports("elm").is_some());
assert!(loader.get_imports("hcl").is_some());
assert!(loader.get_imports("hlsl").is_some());
assert!(loader.get_imports("jq").is_some());
assert!(loader.get_imports("lean").is_some());
assert!(loader.get_imports("meson").is_some());
assert!(loader.get_imports("nginx").is_some());
assert!(loader.get_imports("ninja").is_some());
assert!(loader.get_imports("prolog").is_some());
assert!(loader.get_imports("css").is_some());
assert!(loader.get_imports("glsl").is_some());
assert!(loader.get_imports("html").is_some());
assert!(loader.get_imports("jinja2").is_some());
assert!(loader.get_imports("scss").is_some());
assert!(loader.get_imports("thrift").is_some());
assert!(loader.get_imports("tlaplus").is_some());
assert!(loader.get_imports("typst").is_some());
assert!(loader.get_imports("verilog").is_some());
assert!(loader.get_imports("vhdl").is_some());
assert!(loader.get_imports("wit").is_some());
assert!(loader.get_imports("unknown-lang-xyz").is_none());
}
#[test]
fn test_get_tags_returns_bundled() {
let loader = GrammarLoader::with_paths(vec![]);
assert!(loader.get_tags("rust").is_some());
assert!(loader.get_tags("python").is_some());
assert!(loader.get_tags("go").is_some());
assert!(loader.get_tags("unknown-lang-xyz").is_none());
}
#[test]
fn test_tags_queries_compile() {
let loader = GrammarLoader::new();
let langs = [
"zig",
"clojure",
"scheme",
"nix",
"prolog",
"toml",
"json",
"yaml",
"css",
"html",
"xml",
"thrift",
"dockerfile",
"caddy",
];
for lang in langs {
let tags = loader.get_tags(lang);
assert!(
tags.is_some(),
"{lang}: no tags query found (missing from bundled_tags_query)"
);
let tags_str = tags.unwrap();
let grammar = loader.get(lang).ok();
if grammar.is_none() {
eprintln!("{lang}: grammar .so not found, skipping compilation check");
continue;
}
let result = tree_sitter::Query::new(&grammar.unwrap(), &tags_str);
assert!(
result.is_ok(),
"{lang}: tags query compilation failed: {:?}",
result.err()
);
}
}
#[test]
fn test_scheme_node_kinds() {
let loader = GrammarLoader::new();
let grammar = loader.get("scheme").ok();
if grammar.is_none() {
eprintln!("scheme: grammar .so not found or load failed");
return;
}
eprintln!("scheme: grammar loaded ok");
let g = grammar.unwrap();
let test_cases = [
("list", "(list) @x"),
("symbol", "(symbol) @x"),
("named_node", "(named_node) @x"),
("identifier", "(identifier) @x"),
];
for (name, query_str) in test_cases {
match tree_sitter::Query::new(&g, query_str) {
Ok(_) => eprintln!("scheme node '{name}': valid"),
Err(e) => eprintln!("scheme node '{name}': INVALID - {e:?}"),
}
}
use tree_sitter::Parser;
let src = "(define (add a b) (+ a b))\n(define (multiply a b) (* a b))\n";
let mut parser = Parser::new();
parser.set_language(&g).unwrap();
let tree = parser.parse(src, None).unwrap();
eprintln!("scheme sexp: {}", tree.root_node().to_sexp());
fn walk(node: tree_sitter::Node, depth: usize) {
let prefix = " ".repeat(depth);
eprintln!(
"{prefix}kind={} named={} text_len={}",
node.kind(),
node.is_named(),
node.byte_range().len()
);
let mut cursor = node.walk();
for child in node.children(&mut cursor) {
walk(child, depth + 1);
}
}
walk(tree.root_node(), 0);
}
#[test]
fn test_get_types_returns_bundled() {
let loader = GrammarLoader::with_paths(vec![]);
assert!(loader.get_types("rust").is_some());
assert!(loader.get_types("python").is_some());
assert!(loader.get_types("java").is_some());
assert!(loader.get_types("go").is_some());
assert!(loader.get_types("c").is_some());
assert!(loader.get_types("cpp").is_some());
assert!(loader.get_types("kotlin").is_some());
assert!(loader.get_types("swift").is_some());
assert!(loader.get_types("c-sharp").is_some());
assert!(loader.get_types("unknown-lang-xyz").is_none());
}
#[test]
fn test_get_calls_returns_bundled() {
let loader = GrammarLoader::with_paths(vec![]);
assert!(loader.get_calls("rust").is_some());
assert!(loader.get_calls("python").is_some());
assert!(loader.get_calls("go").is_some());
assert!(loader.get_calls("c").is_some());
assert!(loader.get_calls("cpp").is_some());
assert!(loader.get_calls("ruby").is_some());
assert!(loader.get_calls("kotlin").is_some());
assert!(loader.get_calls("swift").is_some());
assert!(loader.get_calls("c-sharp").is_some());
assert!(loader.get_calls("bash").is_some());
assert!(loader.get_calls("unknown-lang-xyz").is_none());
}
#[test]
fn test_load_from_env() {
let grammar_path = std::env::current_dir().unwrap().join("target/grammars");
if !grammar_path.exists() {
eprintln!("Skipping: run `cargo xtask build-grammars` first");
return;
}
unsafe {
std::env::set_var("NORMALIZE_GRAMMAR_PATH", grammar_path.to_str().unwrap());
}
let loader = GrammarLoader::new();
let ext = grammar_extension();
if grammar_path.join(format!("python{ext}")).exists() {
let lang = loader.get("python").ok();
assert!(lang.is_some(), "Failed to load python grammar");
}
unsafe {
std::env::remove_var("NORMALIZE_GRAMMAR_PATH");
}
}
}