1use libloading::{Library, Symbol};
43use std::collections::HashMap;
44use std::path::{Path, PathBuf};
45use std::sync::{Arc, RwLock};
46use tree_sitter::Language;
47use tree_sitter_language::LanguageFn;
48
49#[derive(Debug, thiserror::Error)]
51pub enum GrammarLoadError {
52 #[error("grammar '{0}' not found in search paths")]
54 NotFound(String),
55 #[error("failed to load grammar '{grammar}': {detail}")]
58 LoadFailed {
59 grammar: String,
61 detail: String,
63 },
64}
65
66struct LoadedGrammar {
72 _library: Library,
74 language: Language,
76}
77
78pub struct GrammarLoader {
80 search_paths: Vec<PathBuf>,
82 cache: RwLock<HashMap<String, Arc<LoadedGrammar>>>,
84 highlight_cache: RwLock<HashMap<String, Arc<String>>>,
86 injection_cache: RwLock<HashMap<String, Arc<String>>>,
88 locals_cache: RwLock<HashMap<String, Arc<String>>>,
90 complexity_cache: RwLock<HashMap<String, Arc<String>>>,
92 calls_cache: RwLock<HashMap<String, Arc<String>>>,
94 types_cache: RwLock<HashMap<String, Arc<String>>>,
96 tags_cache: RwLock<HashMap<String, Arc<String>>>,
98 imports_cache: RwLock<HashMap<String, Arc<String>>>,
100 decorations_cache: RwLock<HashMap<String, Arc<String>>>,
102 test_regions_cache: RwLock<HashMap<String, Arc<String>>>,
104 compiled_query_cache: RwLock<HashMap<String, Arc<tree_sitter::Query>>>,
106}
107
108impl GrammarLoader {
109 pub fn new() -> Self {
115 let mut paths = Vec::new();
116
117 if let Ok(env_path) = std::env::var("NORMALIZE_GRAMMAR_PATH") {
119 for p in env_path.split(':') {
120 if !p.is_empty() {
121 paths.push(PathBuf::from(p));
122 }
123 }
124 }
125
126 if let Some(config) = dirs::config_dir() {
128 paths.push(config.join("normalize/grammars"));
129 }
130
131 Self {
132 search_paths: paths,
133 cache: RwLock::new(HashMap::new()),
134 highlight_cache: RwLock::new(HashMap::new()),
135 injection_cache: RwLock::new(HashMap::new()),
136 locals_cache: RwLock::new(HashMap::new()),
137 complexity_cache: RwLock::new(HashMap::new()),
138 calls_cache: RwLock::new(HashMap::new()),
139 types_cache: RwLock::new(HashMap::new()),
140 tags_cache: RwLock::new(HashMap::new()),
141 imports_cache: RwLock::new(HashMap::new()),
142 decorations_cache: RwLock::new(HashMap::new()),
143 test_regions_cache: RwLock::new(HashMap::new()),
144 compiled_query_cache: RwLock::new(HashMap::new()),
145 }
146 }
147
148 pub fn with_paths(paths: Vec<PathBuf>) -> Self {
150 Self {
151 search_paths: paths,
152 cache: RwLock::new(HashMap::new()),
153 highlight_cache: RwLock::new(HashMap::new()),
154 injection_cache: RwLock::new(HashMap::new()),
155 locals_cache: RwLock::new(HashMap::new()),
156 complexity_cache: RwLock::new(HashMap::new()),
157 calls_cache: RwLock::new(HashMap::new()),
158 types_cache: RwLock::new(HashMap::new()),
159 tags_cache: RwLock::new(HashMap::new()),
160 imports_cache: RwLock::new(HashMap::new()),
161 decorations_cache: RwLock::new(HashMap::new()),
162 test_regions_cache: RwLock::new(HashMap::new()),
163 compiled_query_cache: RwLock::new(HashMap::new()),
164 }
165 }
166
167 pub fn add_path(&mut self, path: PathBuf) {
169 self.search_paths.push(path);
170 }
171
172 pub fn get(&self, name: &str) -> Result<Language, GrammarLoadError> {
178 if let Some(loaded) = self
180 .cache
181 .read()
182 .unwrap_or_else(|e| e.into_inner())
183 .get(name)
184 {
185 return Ok(loaded.language.clone());
186 }
187
188 self.load_external(name)
189 }
190
191 pub fn get_highlights(&self, name: &str) -> Option<Arc<String>> {
196 if let Some(query) = self
198 .highlight_cache
199 .read()
200 .unwrap_or_else(|e| e.into_inner())
201 .get(name)
202 {
203 return Some(Arc::clone(query));
204 }
205
206 self.load_query(name, "highlights", &self.highlight_cache)
207 }
208
209 pub fn get_injections(&self, name: &str) -> Option<Arc<String>> {
214 if let Some(query) = self
216 .injection_cache
217 .read()
218 .unwrap_or_else(|e| e.into_inner())
219 .get(name)
220 {
221 return Some(Arc::clone(query));
222 }
223
224 self.load_query(name, "injections", &self.injection_cache)
225 }
226
227 pub fn get_locals(&self, name: &str) -> Option<Arc<String>> {
232 if let Some(query) = self
234 .locals_cache
235 .read()
236 .unwrap_or_else(|e| e.into_inner())
237 .get(name)
238 {
239 return Some(Arc::clone(query));
240 }
241
242 self.load_query(name, "locals", &self.locals_cache)
243 }
244
245 pub fn get_complexity(&self, name: &str) -> Option<Arc<String>> {
252 if let Some(query) = self
254 .complexity_cache
255 .read()
256 .unwrap_or_else(|e| e.into_inner())
257 .get(name)
258 {
259 return Some(Arc::clone(query));
260 }
261
262 self.load_query(name, "complexity", &self.complexity_cache)
264 .or_else(|| {
265 let content = bundled_complexity_query(name)?;
266 let query = Arc::new(content.to_string());
267 self.complexity_cache
268 .write()
269 .unwrap_or_else(|e| e.into_inner())
270 .insert(name.to_string(), Arc::clone(&query));
271 Some(query)
272 })
273 }
274
275 pub fn get_calls(&self, name: &str) -> Option<Arc<String>> {
282 if let Some(query) = self
284 .calls_cache
285 .read()
286 .unwrap_or_else(|e| e.into_inner())
287 .get(name)
288 {
289 return Some(Arc::clone(query));
290 }
291
292 self.load_query(name, "calls", &self.calls_cache)
294 .or_else(|| {
295 let content = bundled_calls_query(name)?;
296 let query = Arc::new(content.to_string());
297 self.calls_cache
298 .write()
299 .unwrap_or_else(|e| e.into_inner())
300 .insert(name.to_string(), Arc::clone(&query));
301 Some(query)
302 })
303 }
304
305 pub fn get_types(&self, name: &str) -> Option<Arc<String>> {
310 if let Some(query) = self
312 .types_cache
313 .read()
314 .unwrap_or_else(|e| e.into_inner())
315 .get(name)
316 {
317 return Some(Arc::clone(query));
318 }
319
320 if let Some(q) = self.load_query(name, "types", &self.types_cache) {
322 return Some(q);
323 }
324
325 let bundled = bundled_types_query(name)?;
327 let query = Arc::new(bundled.to_string());
328 self.types_cache
329 .write()
330 .unwrap_or_else(|e| e.into_inner())
331 .insert(name.to_string(), Arc::clone(&query));
332 Some(query)
333 }
334
335 pub fn get_tags(&self, name: &str) -> Option<Arc<String>> {
344 if let Some(query) = self
346 .tags_cache
347 .read()
348 .unwrap_or_else(|e| e.into_inner())
349 .get(name)
350 {
351 return Some(Arc::clone(query));
352 }
353
354 if let Some(q) = self.load_query(name, "tags", &self.tags_cache) {
356 return Some(q);
357 }
358
359 let bundled = bundled_tags_query(name)?;
361 let query = Arc::new(bundled.to_string());
362 self.tags_cache
363 .write()
364 .unwrap_or_else(|e| e.into_inner())
365 .insert(name.to_string(), Arc::clone(&query));
366 Some(query)
367 }
368
369 pub fn get_imports(&self, name: &str) -> Option<Arc<String>> {
374 if let Some(query) = self
376 .imports_cache
377 .read()
378 .unwrap_or_else(|e| e.into_inner())
379 .get(name)
380 {
381 return Some(Arc::clone(query));
382 }
383
384 if let Some(q) = self.load_query(name, "imports", &self.imports_cache) {
386 return Some(q);
387 }
388
389 let bundled = bundled_imports_query(name)?;
391 let query = Arc::new(bundled.to_string());
392 self.imports_cache
393 .write()
394 .unwrap_or_else(|e| e.into_inner())
395 .insert(name.to_string(), Arc::clone(&query));
396 Some(query)
397 }
398
399 pub fn get_decorations(&self, name: &str) -> Option<Arc<String>> {
406 if let Some(query) = self
408 .decorations_cache
409 .read()
410 .unwrap_or_else(|e| e.into_inner())
411 .get(name)
412 {
413 return Some(Arc::clone(query));
414 }
415
416 if let Some(q) = self.load_query(name, "decorations", &self.decorations_cache) {
418 return Some(q);
419 }
420
421 let bundled = bundled_decorations_query(name)?;
423 let query = Arc::new(bundled.to_string());
424 self.decorations_cache
425 .write()
426 .unwrap_or_else(|e| e.into_inner())
427 .insert(name.to_string(), Arc::clone(&query));
428 Some(query)
429 }
430
431 pub fn get_test_regions(&self, name: &str) -> Option<Arc<String>> {
442 if let Some(query) = self
444 .test_regions_cache
445 .read()
446 .unwrap_or_else(|e| e.into_inner())
447 .get(name)
448 {
449 return Some(Arc::clone(query));
450 }
451
452 if let Some(q) = self.load_query(name, "test_regions", &self.test_regions_cache) {
454 return Some(q);
455 }
456
457 let bundled = bundled_test_regions_query(name)?;
459 let query = Arc::new(bundled.to_string());
460 self.test_regions_cache
461 .write()
462 .unwrap_or_else(|e| e.into_inner())
463 .insert(name.to_string(), Arc::clone(&query));
464 Some(query)
465 }
466
467 fn load_query(
469 &self,
470 name: &str,
471 query_type: &str,
472 cache: &RwLock<HashMap<String, Arc<String>>>,
473 ) -> Option<Arc<String>> {
474 let scm_name = format!("{name}.{query_type}.scm");
475
476 for search_path in &self.search_paths {
477 let scm_path = search_path.join(&scm_name);
478 if scm_path.exists()
479 && let Ok(content) = std::fs::read_to_string(&scm_path)
480 {
481 let query = Arc::new(content);
482
483 cache
485 .write()
486 .unwrap_or_else(|e| e.into_inner())
487 .insert(name.to_string(), Arc::clone(&query));
488
489 return Some(query);
490 }
491 }
492
493 None
494 }
495
496 pub fn get_compiled_query(
504 &self,
505 grammar_name: &str,
506 query_type: &str,
507 query_str: &str,
508 ) -> Option<Arc<tree_sitter::Query>> {
509 let key = format!("{grammar_name}:{query_type}");
510
511 {
513 let cache = self
514 .compiled_query_cache
515 .read()
516 .unwrap_or_else(|e| e.into_inner());
517 if let Some(q) = cache.get(&key) {
518 return Some(Arc::clone(q));
519 }
520 }
521
522 let grammar = self.get(grammar_name).ok()?;
524 let compiled = tree_sitter::Query::new(&grammar, query_str).ok()?;
525 let arc = Arc::new(compiled);
526
527 self.compiled_query_cache
528 .write()
529 .unwrap_or_else(|e| e.into_inner())
530 .insert(key, Arc::clone(&arc));
531
532 Some(arc)
533 }
534
535 fn load_external(&self, name: &str) -> Result<Language, GrammarLoadError> {
537 let lib_name = grammar_lib_name(name);
538
539 for search_path in &self.search_paths {
540 let lib_path = search_path.join(&lib_name);
541 if lib_path.exists() {
542 return self.load_from_path(name, &lib_path);
543 }
544 }
545
546 Err(GrammarLoadError::NotFound(name.to_string()))
547 }
548
549 fn load_from_path(&self, name: &str, path: &Path) -> Result<Language, GrammarLoadError> {
551 let library = unsafe {
556 Library::new(path).map_err(|e| {
557 log::debug!("Failed to load grammar at {}: {}", path.display(), e);
558 GrammarLoadError::LoadFailed {
559 grammar: name.to_string(),
560 detail: e.to_string(),
561 }
562 })?
563 };
564
565 let symbol_name = grammar_symbol_name(name);
566 let language = unsafe {
572 let func: Result<Symbol<unsafe extern "C" fn() -> *const ()>, _> =
573 library.get(symbol_name.as_bytes());
574 match func {
575 Ok(f) => {
576 let lang_fn = LanguageFn::from_raw(*f);
577 Language::new(lang_fn)
578 }
579 Err(e) => {
580 log::debug!(
581 "Grammar '{}' at {} missing symbol '{}': {}",
582 name,
583 path.display(),
584 symbol_name,
585 e
586 );
587 return Err(GrammarLoadError::LoadFailed {
588 grammar: name.to_string(),
589 detail: format!("symbol '{}' not found: {}", symbol_name, e),
590 });
591 }
592 }
593 };
594
595 let loaded = Arc::new(LoadedGrammar {
597 _library: library,
598 language: language.clone(),
599 });
600
601 self.cache
602 .write()
603 .unwrap_or_else(|e| e.into_inner())
604 .insert(name.to_string(), loaded);
605
606 Ok(language)
607 }
608
609 pub fn available_external(&self) -> Vec<String> {
611 let mut grammars = Vec::new();
612 let ext = grammar_extension();
613
614 for path in &self.search_paths {
615 if let Ok(entries) = std::fs::read_dir(path) {
616 for entry in entries.flatten() {
617 let name = entry.file_name();
618 let name_str = name.to_string_lossy();
619 if name_str.ends_with(ext) {
620 let grammar_name = name_str.trim_end_matches(ext);
621 if !grammars.contains(&grammar_name.to_string()) {
622 grammars.push(grammar_name.to_string());
623 }
624 }
625 }
626 }
627 }
628
629 grammars.sort();
630 grammars
631 }
632
633 pub fn available_external_with_paths(&self) -> Vec<(String, std::path::PathBuf)> {
635 let mut grammars: Vec<(String, std::path::PathBuf)> = Vec::new();
636 let ext = grammar_extension();
637
638 for dir in &self.search_paths {
639 if let Ok(entries) = std::fs::read_dir(dir) {
640 for entry in entries.flatten() {
641 let name = entry.file_name();
642 let name_str = name.to_string_lossy();
643 if name_str.ends_with(ext) {
644 let grammar_name = name_str.trim_end_matches(ext).to_string();
645 if !grammars.iter().any(|(n, _)| n == &grammar_name) {
646 grammars.push((grammar_name, entry.path()));
647 }
648 }
649 }
650 }
651 }
652
653 grammars.sort_by(|a, b| a.0.cmp(&b.0));
654 grammars
655 }
656}
657
658impl Default for GrammarLoader {
659 fn default() -> Self {
660 Self::new()
661 }
662}
663
664fn grammar_lib_name(name: &str) -> String {
666 let ext = grammar_extension();
667 format!("{name}{ext}")
668}
669
670fn grammar_symbol_name(name: &str) -> String {
672 match name {
674 "rust" => return "tree_sitter_rust_orchard".to_string(),
675 "vb" => return "tree_sitter_vb_dotnet".to_string(),
676 _ => {}
677 }
678 let normalized = name.replace('-', "_");
680 format!("tree_sitter_{normalized}")
681}
682
683fn bundled_types_query(name: &str) -> Option<&'static str> {
686 match name {
687 "rust" => Some(include_str!("queries/rust.types.scm")),
688 "typescript" => Some(include_str!("queries/typescript.types.scm")),
689 "tsx" => Some(include_str!("queries/tsx.types.scm")),
690 "python" => Some(include_str!("queries/python.types.scm")),
691 "java" => Some(include_str!("queries/java.types.scm")),
692 "go" => Some(include_str!("queries/go.types.scm")),
693 "c" => Some(include_str!("queries/c.types.scm")),
694 "cpp" => Some(include_str!("queries/cpp.types.scm")),
695 "kotlin" => Some(include_str!("queries/kotlin.types.scm")),
696 "swift" => Some(include_str!("queries/swift.types.scm")),
697 "c-sharp" => Some(include_str!("queries/c-sharp.types.scm")),
698 "scala" => Some(include_str!("queries/scala.types.scm")),
699 "haskell" => Some(include_str!("queries/haskell.types.scm")),
700 "ruby" => Some(include_str!("queries/ruby.types.scm")),
701 "dart" => Some(include_str!("queries/dart.types.scm")),
702 "elixir" => Some(include_str!("queries/elixir.types.scm")),
703 "ocaml" => Some(include_str!("queries/ocaml.types.scm")),
704 "erlang" => Some(include_str!("queries/erlang.types.scm")),
705 "zig" => Some(include_str!("queries/zig.types.scm")),
706 "fsharp" => Some(include_str!("queries/fsharp.types.scm")),
707 "gleam" => Some(include_str!("queries/gleam.types.scm")),
708 "julia" => Some(include_str!("queries/julia.types.scm")),
709 "r" => Some(include_str!("queries/r.types.scm")),
710 "d" => Some(include_str!("queries/d.types.scm")),
711 "objc" => Some(include_str!("queries/objc.types.scm")),
712 "vb" => Some(include_str!("queries/vb.types.scm")),
713 "groovy" => Some(include_str!("queries/groovy.types.scm")),
714 "ada" => Some(include_str!("queries/ada.types.scm")),
715 "agda" => Some(include_str!("queries/agda.types.scm")),
716 "elm" => Some(include_str!("queries/elm.types.scm")),
717 "idris" => Some(include_str!("queries/idris.types.scm")),
718 "lean" => Some(include_str!("queries/lean.types.scm")),
719 "php" => Some(include_str!("queries/php.types.scm")),
720 "powershell" => Some(include_str!("queries/powershell.types.scm")),
721 "rescript" => Some(include_str!("queries/rescript.types.scm")),
722 "verilog" => Some(include_str!("queries/verilog.types.scm")),
723 "vhdl" => Some(include_str!("queries/vhdl.types.scm")),
724 "sql" => Some(include_str!("queries/sql.types.scm")),
725 "hcl" => Some(include_str!("queries/hcl.types.scm")),
726 "glsl" => Some(include_str!("queries/glsl.types.scm")),
727 "hlsl" => Some(include_str!("queries/hlsl.types.scm")),
728 "clojure" => Some(include_str!("queries/clojure.types.scm")),
729 "commonlisp" => Some(include_str!("queries/commonlisp.types.scm")),
730 "elisp" => Some(include_str!("queries/elisp.types.scm")),
731 "javascript" => Some(include_str!("queries/javascript.types.scm")),
732 "lua" => Some(include_str!("queries/lua.types.scm")),
733 "scheme" => Some(include_str!("queries/scheme.types.scm")),
734 "graphql" => Some(include_str!("queries/graphql.types.scm")),
735 "nix" => Some(include_str!("queries/nix.types.scm")),
736 "starlark" => Some(include_str!("queries/starlark.types.scm")),
737 "matlab" => Some(include_str!("queries/matlab.types.scm")),
738 "tlaplus" => Some(include_str!("queries/tlaplus.types.scm")),
739 "typst" => Some(include_str!("queries/typst.types.scm")),
740 _ => None,
741 }
742}
743
744fn bundled_tags_query(name: &str) -> Option<&'static str> {
750 match name {
751 "rust" => Some(include_str!("queries/rust.tags.scm")),
752 "python" => Some(include_str!("queries/python.tags.scm")),
753 "javascript" => Some(include_str!("queries/javascript.tags.scm")),
754 "typescript" => Some(include_str!("queries/typescript.tags.scm")),
755 "tsx" => Some(include_str!("queries/tsx.tags.scm")),
756 "go" => Some(include_str!("queries/go.tags.scm")),
757 "java" => Some(include_str!("queries/java.tags.scm")),
758 "c" => Some(include_str!("queries/c.tags.scm")),
759 "cpp" => Some(include_str!("queries/cpp.tags.scm")),
760 "ruby" => Some(include_str!("queries/ruby.tags.scm")),
761 "kotlin" => Some(include_str!("queries/kotlin.tags.scm")),
762 "scala" => Some(include_str!("queries/scala.tags.scm")),
763 "elixir" => Some(include_str!("queries/elixir.tags.scm")),
764 "swift" => Some(include_str!("queries/swift.tags.scm")),
765 "haskell" => Some(include_str!("queries/haskell.tags.scm")),
766 "dart" => Some(include_str!("queries/dart.tags.scm")),
767 "ocaml" => Some(include_str!("queries/ocaml.tags.scm")),
768 "fsharp" => Some(include_str!("queries/fsharp.tags.scm")),
769 "gleam" => Some(include_str!("queries/gleam.tags.scm")),
770 "zig" => Some(include_str!("queries/zig.tags.scm")),
771 "julia" => Some(include_str!("queries/julia.tags.scm")),
772 "erlang" => Some(include_str!("queries/erlang.tags.scm")),
773 "lua" => Some(include_str!("queries/lua.tags.scm")),
774 "php" => Some(include_str!("queries/php.tags.scm")),
775 "perl" => Some(include_str!("queries/perl.tags.scm")),
776 "r" => Some(include_str!("queries/r.tags.scm")),
777 "groovy" => Some(include_str!("queries/groovy.tags.scm")),
778 "c-sharp" => Some(include_str!("queries/c-sharp.tags.scm")),
779 "d" => Some(include_str!("queries/d.tags.scm")),
780 "graphql" => Some(include_str!("queries/graphql.tags.scm")),
781 "objc" => Some(include_str!("queries/objc.tags.scm")),
782 "vb" => Some(include_str!("queries/vb.tags.scm")),
783 "powershell" => Some(include_str!("queries/powershell.tags.scm")),
784 "clojure" => Some(include_str!("queries/clojure.tags.scm")),
785 "commonlisp" => Some(include_str!("queries/commonlisp.tags.scm")),
786 "scheme" => Some(include_str!("queries/scheme.tags.scm")),
787 "elisp" => Some(include_str!("queries/elisp.tags.scm")),
788 "bash" => Some(include_str!("queries/bash.tags.scm")),
789 "fish" => Some(include_str!("queries/fish.tags.scm")),
790 "zsh" => Some(include_str!("queries/zsh.tags.scm")),
791 "ada" => Some(include_str!("queries/ada.tags.scm")),
792 "idris" => Some(include_str!("queries/idris.tags.scm")),
793 "lean" => Some(include_str!("queries/lean.tags.scm")),
794 "rescript" => Some(include_str!("queries/rescript.tags.scm")),
795 "elm" => Some(include_str!("queries/elm.tags.scm")),
796 "markdown" => Some(include_str!("queries/markdown.tags.scm")),
797 "nix" => Some(include_str!("queries/nix.tags.scm")),
798 "prolog" => Some(include_str!("queries/prolog.tags.scm")),
799 "agda" => Some(include_str!("queries/agda.tags.scm")),
800 "awk" => Some(include_str!("queries/awk.tags.scm")),
801 "cmake" => Some(include_str!("queries/cmake.tags.scm")),
802 "glsl" => Some(include_str!("queries/glsl.tags.scm")),
803 "hcl" => Some(include_str!("queries/hcl.tags.scm")),
804 "hlsl" => Some(include_str!("queries/hlsl.tags.scm")),
805 "jq" => Some(include_str!("queries/jq.tags.scm")),
806 "matlab" => Some(include_str!("queries/matlab.tags.scm")),
807 "meson" => Some(include_str!("queries/meson.tags.scm")),
808 "nginx" => Some(include_str!("queries/nginx.tags.scm")),
809 "scss" => Some(include_str!("queries/scss.tags.scm")),
810 "sql" => Some(include_str!("queries/sql.tags.scm")),
811 "starlark" => Some(include_str!("queries/starlark.tags.scm")),
812 "svelte" => Some(include_str!("queries/svelte.tags.scm")),
813 "tlaplus" => Some(include_str!("queries/tlaplus.tags.scm")),
814 "typst" => Some(include_str!("queries/typst.tags.scm")),
815 "verilog" => Some(include_str!("queries/verilog.tags.scm")),
816 "vhdl" => Some(include_str!("queries/vhdl.tags.scm")),
817 "vim" => Some(include_str!("queries/vim.tags.scm")),
818 "vue" => Some(include_str!("queries/vue.tags.scm")),
819 "jinja2" => Some(include_str!("queries/jinja2.tags.scm")),
820 "json" => Some(include_str!("queries/json.tags.scm")),
821 "toml" => Some(include_str!("queries/toml.tags.scm")),
822 "yaml" => Some(include_str!("queries/yaml.tags.scm")),
823 "css" => Some(include_str!("queries/css.tags.scm")),
824 "html" => Some(include_str!("queries/html.tags.scm")),
825 "xml" => Some(include_str!("queries/xml.tags.scm")),
826 "thrift" => Some(include_str!("queries/thrift.tags.scm")),
827 "dockerfile" => Some(include_str!("queries/dockerfile.tags.scm")),
828 "caddy" => Some(include_str!("queries/caddy.tags.scm")),
829 _ => None,
830 }
831}
832
833fn grammar_extension() -> &'static str {
835 if cfg!(target_os = "macos") {
836 ".dylib"
837 } else if cfg!(target_os = "windows") {
838 ".dll"
839 } else {
840 ".so"
841 }
842}
843
844fn bundled_complexity_query(name: &str) -> Option<&'static str> {
849 match name {
850 "rust" => Some(include_str!("queries/rust.complexity.scm")),
851 "python" => Some(include_str!("queries/python.complexity.scm")),
852 "go" => Some(include_str!("queries/go.complexity.scm")),
853 "javascript" => Some(include_str!("queries/javascript.complexity.scm")),
854 "typescript" => Some(include_str!("queries/typescript.complexity.scm")),
855 "tsx" => Some(include_str!("queries/tsx.complexity.scm")),
856 "java" => Some(include_str!("queries/java.complexity.scm")),
857 "c" => Some(include_str!("queries/c.complexity.scm")),
858 "cpp" => Some(include_str!("queries/cpp.complexity.scm")),
859 "ruby" => Some(include_str!("queries/ruby.complexity.scm")),
860 "kotlin" => Some(include_str!("queries/kotlin.complexity.scm")),
861 "swift" => Some(include_str!("queries/swift.complexity.scm")),
862 "c-sharp" => Some(include_str!("queries/c-sharp.complexity.scm")),
863 "bash" => Some(include_str!("queries/bash.complexity.scm")),
864 "lua" => Some(include_str!("queries/lua.complexity.scm")),
865 "elixir" => Some(include_str!("queries/elixir.complexity.scm")),
866 "scala" => Some(include_str!("queries/scala.complexity.scm")),
867 "dart" => Some(include_str!("queries/dart.complexity.scm")),
868 "zig" => Some(include_str!("queries/zig.complexity.scm")),
869 "ocaml" => Some(include_str!("queries/ocaml.complexity.scm")),
870 "erlang" => Some(include_str!("queries/erlang.complexity.scm")),
871 "php" => Some(include_str!("queries/php.complexity.scm")),
872 "haskell" => Some(include_str!("queries/haskell.complexity.scm")),
873 "r" => Some(include_str!("queries/r.complexity.scm")),
874 "julia" => Some(include_str!("queries/julia.complexity.scm")),
875 "perl" => Some(include_str!("queries/perl.complexity.scm")),
876 "groovy" => Some(include_str!("queries/groovy.complexity.scm")),
877 "elm" => Some(include_str!("queries/elm.complexity.scm")),
878 "powershell" => Some(include_str!("queries/powershell.complexity.scm")),
879 "fish" => Some(include_str!("queries/fish.complexity.scm")),
880 "fsharp" => Some(include_str!("queries/fsharp.complexity.scm")),
881 "gleam" => Some(include_str!("queries/gleam.complexity.scm")),
882 "clojure" => Some(include_str!("queries/clojure.complexity.scm")),
883 "commonlisp" => Some(include_str!("queries/commonlisp.complexity.scm")),
884 "scheme" => Some(include_str!("queries/scheme.complexity.scm")),
885 "d" => Some(include_str!("queries/d.complexity.scm")),
886 "objc" => Some(include_str!("queries/objc.complexity.scm")),
887 "vb" => Some(include_str!("queries/vb.complexity.scm")),
888 "elisp" => Some(include_str!("queries/elisp.complexity.scm")),
889 "hcl" => Some(include_str!("queries/hcl.complexity.scm")),
890 "matlab" => Some(include_str!("queries/matlab.complexity.scm")),
891 "nix" => Some(include_str!("queries/nix.complexity.scm")),
892 "sql" => Some(include_str!("queries/sql.complexity.scm")),
893 "starlark" => Some(include_str!("queries/starlark.complexity.scm")),
894 "vim" => Some(include_str!("queries/vim.complexity.scm")),
895 "zsh" => Some(include_str!("queries/zsh.complexity.scm")),
896 "rescript" => Some(include_str!("queries/rescript.complexity.scm")),
897 "idris" => Some(include_str!("queries/idris.complexity.scm")),
898 "lean" => Some(include_str!("queries/lean.complexity.scm")),
899 "ada" => Some(include_str!("queries/ada.complexity.scm")),
900 "agda" => Some(include_str!("queries/agda.complexity.scm")),
901 "awk" => Some(include_str!("queries/awk.complexity.scm")),
902 "cmake" => Some(include_str!("queries/cmake.complexity.scm")),
903 "glsl" => Some(include_str!("queries/glsl.complexity.scm")),
904 "graphql" => Some(include_str!("queries/graphql.complexity.scm")),
905 "hlsl" => Some(include_str!("queries/hlsl.complexity.scm")),
906 "jq" => Some(include_str!("queries/jq.complexity.scm")),
907 "meson" => Some(include_str!("queries/meson.complexity.scm")),
908 "nginx" => Some(include_str!("queries/nginx.complexity.scm")),
909 "prolog" => Some(include_str!("queries/prolog.complexity.scm")),
910 "scss" => Some(include_str!("queries/scss.complexity.scm")),
911 "svelte" => Some(include_str!("queries/svelte.complexity.scm")),
912 "tlaplus" => Some(include_str!("queries/tlaplus.complexity.scm")),
913 "typst" => Some(include_str!("queries/typst.complexity.scm")),
914 "verilog" => Some(include_str!("queries/verilog.complexity.scm")),
915 "vhdl" => Some(include_str!("queries/vhdl.complexity.scm")),
916 "vue" => Some(include_str!("queries/vue.complexity.scm")),
917 "batch" => Some(include_str!("queries/batch.complexity.scm")),
918 "thrift" => Some(include_str!("queries/thrift.complexity.scm")),
919 "jinja2" => Some(include_str!("queries/jinja2.complexity.scm")),
920 _ => None,
921 }
922}
923
924fn bundled_calls_query(name: &str) -> Option<&'static str> {
926 match name {
927 "python" => Some(include_str!("queries/python.calls.scm")),
928 "rust" => Some(include_str!("queries/rust.calls.scm")),
929 "typescript" => Some(include_str!("queries/typescript.calls.scm")),
930 "tsx" => Some(include_str!("queries/tsx.calls.scm")),
931 "javascript" => Some(include_str!("queries/javascript.calls.scm")),
932 "java" => Some(include_str!("queries/java.calls.scm")),
933 "go" => Some(include_str!("queries/go.calls.scm")),
934 "c" => Some(include_str!("queries/c.calls.scm")),
935 "cpp" => Some(include_str!("queries/cpp.calls.scm")),
936 "ruby" => Some(include_str!("queries/ruby.calls.scm")),
937 "kotlin" => Some(include_str!("queries/kotlin.calls.scm")),
938 "swift" => Some(include_str!("queries/swift.calls.scm")),
939 "c-sharp" => Some(include_str!("queries/c-sharp.calls.scm")),
940 "bash" => Some(include_str!("queries/bash.calls.scm")),
941 "scala" => Some(include_str!("queries/scala.calls.scm")),
942 "elixir" => Some(include_str!("queries/elixir.calls.scm")),
943 "lua" => Some(include_str!("queries/lua.calls.scm")),
944 "dart" => Some(include_str!("queries/dart.calls.scm")),
945 "graphql" => Some(include_str!("queries/graphql.calls.scm")),
946 "ocaml" => Some(include_str!("queries/ocaml.calls.scm")),
947 "erlang" => Some(include_str!("queries/erlang.calls.scm")),
948 "zig" => Some(include_str!("queries/zig.calls.scm")),
949 "julia" => Some(include_str!("queries/julia.calls.scm")),
950 "r" => Some(include_str!("queries/r.calls.scm")),
951 "haskell" => Some(include_str!("queries/haskell.calls.scm")),
952 "php" => Some(include_str!("queries/php.calls.scm")),
953 "perl" => Some(include_str!("queries/perl.calls.scm")),
954 "fsharp" => Some(include_str!("queries/fsharp.calls.scm")),
955 "gleam" => Some(include_str!("queries/gleam.calls.scm")),
956 "groovy" => Some(include_str!("queries/groovy.calls.scm")),
957 "clojure" => Some(include_str!("queries/clojure.calls.scm")),
958 "d" => Some(include_str!("queries/d.calls.scm")),
959 "objc" => Some(include_str!("queries/objc.calls.scm")),
960 "elisp" => Some(include_str!("queries/elisp.calls.scm")),
961 "hcl" => Some(include_str!("queries/hcl.calls.scm")),
962 "matlab" => Some(include_str!("queries/matlab.calls.scm")),
963 "nix" => Some(include_str!("queries/nix.calls.scm")),
964 "starlark" => Some(include_str!("queries/starlark.calls.scm")),
965 "vim" => Some(include_str!("queries/vim.calls.scm")),
966 "zsh" => Some(include_str!("queries/zsh.calls.scm")),
967 "rescript" => Some(include_str!("queries/rescript.calls.scm")),
968 "prolog" => Some(include_str!("queries/prolog.calls.scm")),
969 "sql" => Some(include_str!("queries/sql.calls.scm")),
970 "ada" => Some(include_str!("queries/ada.calls.scm")),
971 "agda" => Some(include_str!("queries/agda.calls.scm")),
972 "awk" => Some(include_str!("queries/awk.calls.scm")),
973 "batch" => Some(include_str!("queries/batch.calls.scm")),
974 "cmake" => Some(include_str!("queries/cmake.calls.scm")),
975 "elm" => Some(include_str!("queries/elm.calls.scm")),
976 "fish" => Some(include_str!("queries/fish.calls.scm")),
977 "idris" => Some(include_str!("queries/idris.calls.scm")),
978 "lean" => Some(include_str!("queries/lean.calls.scm")),
979 "meson" => Some(include_str!("queries/meson.calls.scm")),
980 "powershell" => Some(include_str!("queries/powershell.calls.scm")),
981 "scheme" => Some(include_str!("queries/scheme.calls.scm")),
982 "thrift" => Some(include_str!("queries/thrift.calls.scm")),
983 "tlaplus" => Some(include_str!("queries/tlaplus.calls.scm")),
984 "verilog" => Some(include_str!("queries/verilog.calls.scm")),
985 "vhdl" => Some(include_str!("queries/vhdl.calls.scm")),
986 "vb" => Some(include_str!("queries/vb.calls.scm")),
987 "commonlisp" => Some(include_str!("queries/commonlisp.calls.scm")),
988 "scss" => Some(include_str!("queries/scss.calls.scm")),
989 "glsl" => Some(include_str!("queries/glsl.calls.scm")),
990 "hlsl" => Some(include_str!("queries/hlsl.calls.scm")),
991 "typst" => Some(include_str!("queries/typst.calls.scm")),
992 "svelte" => Some(include_str!("queries/svelte.calls.scm")),
993 "vue" => Some(include_str!("queries/vue.calls.scm")),
994 "jq" => Some(include_str!("queries/jq.calls.scm")),
995 "jinja2" => Some(include_str!("queries/jinja2.calls.scm")),
996 "nginx" => Some(include_str!("queries/nginx.calls.scm")),
997 _ => None,
998 }
999}
1000
1001fn bundled_imports_query(name: &str) -> Option<&'static str> {
1003 match name {
1004 "python" => Some(include_str!("queries/python.imports.scm")),
1005 "javascript" => Some(include_str!("queries/javascript.imports.scm")),
1006 "go" => Some(include_str!("queries/go.imports.scm")),
1007 "lua" => Some(include_str!("queries/lua.imports.scm")),
1008 "rust" => Some(include_str!("queries/rust.imports.scm")),
1009 "typescript" => Some(include_str!("queries/typescript.imports.scm")),
1010 "tsx" => Some(include_str!("queries/tsx.imports.scm")),
1011 "java" => Some(include_str!("queries/java.imports.scm")),
1012 "kotlin" => Some(include_str!("queries/kotlin.imports.scm")),
1013 "c-sharp" => Some(include_str!("queries/c-sharp.imports.scm")),
1014 "ruby" => Some(include_str!("queries/ruby.imports.scm")),
1015 "swift" => Some(include_str!("queries/swift.imports.scm")),
1016 "scala" => Some(include_str!("queries/scala.imports.scm")),
1017 "elixir" => Some(include_str!("queries/elixir.imports.scm")),
1018 "dart" => Some(include_str!("queries/dart.imports.scm")),
1019 "php" => Some(include_str!("queries/php.imports.scm")),
1020 "c" => Some(include_str!("queries/c.imports.scm")),
1021 "cpp" => Some(include_str!("queries/cpp.imports.scm")),
1022 "bash" => Some(include_str!("queries/bash.imports.scm")),
1023 "zsh" => Some(include_str!("queries/zsh.imports.scm")),
1024 "fish" => Some(include_str!("queries/fish.imports.scm")),
1025 "perl" => Some(include_str!("queries/perl.imports.scm")),
1026 "r" => Some(include_str!("queries/r.imports.scm")),
1027 "haskell" => Some(include_str!("queries/haskell.imports.scm")),
1028 "ocaml" => Some(include_str!("queries/ocaml.imports.scm")),
1029 "fsharp" => Some(include_str!("queries/fsharp.imports.scm")),
1030 "erlang" => Some(include_str!("queries/erlang.imports.scm")),
1031 "gleam" => Some(include_str!("queries/gleam.imports.scm")),
1032 "zig" => Some(include_str!("queries/zig.imports.scm")),
1033 "julia" => Some(include_str!("queries/julia.imports.scm")),
1034 "groovy" => Some(include_str!("queries/groovy.imports.scm")),
1035 "clojure" => Some(include_str!("queries/clojure.imports.scm")),
1036 "commonlisp" => Some(include_str!("queries/commonlisp.imports.scm")),
1037 "scheme" => Some(include_str!("queries/scheme.imports.scm")),
1038 "elisp" => Some(include_str!("queries/elisp.imports.scm")),
1039 "d" => Some(include_str!("queries/d.imports.scm")),
1040 "objc" => Some(include_str!("queries/objc.imports.scm")),
1041 "vb" => Some(include_str!("queries/vb.imports.scm")),
1042 "powershell" => Some(include_str!("queries/powershell.imports.scm")),
1043 "vim" => Some(include_str!("queries/vim.imports.scm")),
1044 "matlab" => Some(include_str!("queries/matlab.imports.scm")),
1045 "nix" => Some(include_str!("queries/nix.imports.scm")),
1046 "starlark" => Some(include_str!("queries/starlark.imports.scm")),
1047 "rescript" => Some(include_str!("queries/rescript.imports.scm")),
1048 "idris" => Some(include_str!("queries/idris.imports.scm")),
1049 "ada" => Some(include_str!("queries/ada.imports.scm")),
1050 "agda" => Some(include_str!("queries/agda.imports.scm")),
1051 "asciidoc" => Some(include_str!("queries/asciidoc.imports.scm")),
1052 "caddy" => Some(include_str!("queries/caddy.imports.scm")),
1053 "capnp" => Some(include_str!("queries/capnp.imports.scm")),
1054 "cmake" => Some(include_str!("queries/cmake.imports.scm")),
1055 "devicetree" => Some(include_str!("queries/devicetree.imports.scm")),
1056 "dockerfile" => Some(include_str!("queries/dockerfile.imports.scm")),
1057 "elm" => Some(include_str!("queries/elm.imports.scm")),
1058 "hcl" => Some(include_str!("queries/hcl.imports.scm")),
1059 "hlsl" => Some(include_str!("queries/hlsl.imports.scm")),
1060 "jq" => Some(include_str!("queries/jq.imports.scm")),
1061 "lean" => Some(include_str!("queries/lean.imports.scm")),
1062 "meson" => Some(include_str!("queries/meson.imports.scm")),
1063 "nginx" => Some(include_str!("queries/nginx.imports.scm")),
1064 "ninja" => Some(include_str!("queries/ninja.imports.scm")),
1065 "prolog" => Some(include_str!("queries/prolog.imports.scm")),
1066 "awk" => Some(include_str!("queries/awk.imports.scm")),
1067 "css" => Some(include_str!("queries/css.imports.scm")),
1068 "glsl" => Some(include_str!("queries/glsl.imports.scm")),
1069 "html" => Some(include_str!("queries/html.imports.scm")),
1070 "jinja2" => Some(include_str!("queries/jinja2.imports.scm")),
1071 "scss" => Some(include_str!("queries/scss.imports.scm")),
1072 "thrift" => Some(include_str!("queries/thrift.imports.scm")),
1073 "tlaplus" => Some(include_str!("queries/tlaplus.imports.scm")),
1074 "typst" => Some(include_str!("queries/typst.imports.scm")),
1075 "verilog" => Some(include_str!("queries/verilog.imports.scm")),
1076 "vhdl" => Some(include_str!("queries/vhdl.imports.scm")),
1077 "wit" => Some(include_str!("queries/wit.imports.scm")),
1078 _ => None,
1079 }
1080}
1081
1082fn bundled_decorations_query(name: &str) -> Option<&'static str> {
1087 match name {
1088 "rust" => Some(include_str!("queries/rust.decorations.scm")),
1089 "python" => Some(include_str!("queries/python.decorations.scm")),
1090 "javascript" => Some(include_str!("queries/javascript.decorations.scm")),
1091 "typescript" => Some(include_str!("queries/typescript.decorations.scm")),
1092 "tsx" => Some(include_str!("queries/tsx.decorations.scm")),
1093 "java" => Some(include_str!("queries/java.decorations.scm")),
1094 "kotlin" => Some(include_str!("queries/kotlin.decorations.scm")),
1095 "scala" => Some(include_str!("queries/scala.decorations.scm")),
1096 "c-sharp" => Some(include_str!("queries/c-sharp.decorations.scm")),
1097 "php" => Some(include_str!("queries/php.decorations.scm")),
1098 "swift" => Some(include_str!("queries/swift.decorations.scm")),
1099 "dart" => Some(include_str!("queries/dart.decorations.scm")),
1100 "ocaml" => Some(include_str!("queries/ocaml.decorations.scm")),
1101 "rescript" => Some(include_str!("queries/rescript.decorations.scm")),
1102 "fsharp" => Some(include_str!("queries/fsharp.decorations.scm")),
1103 "elixir" => Some(include_str!("queries/elixir.decorations.scm")),
1104 "erlang" => Some(include_str!("queries/erlang.decorations.scm")),
1105 "gleam" => Some(include_str!("queries/gleam.decorations.scm")),
1106 "lean" => Some(include_str!("queries/lean.decorations.scm")),
1107 "groovy" => Some(include_str!("queries/groovy.decorations.scm")),
1108 "vb" => Some(include_str!("queries/vb.decorations.scm")),
1109 "haskell" => Some(include_str!("queries/haskell.decorations.scm")),
1110 "go" => Some(include_str!("queries/go.decorations.scm")),
1111 "c" => Some(include_str!("queries/c.decorations.scm")),
1112 "cpp" => Some(include_str!("queries/cpp.decorations.scm")),
1113 "objc" => Some(include_str!("queries/objc.decorations.scm")),
1114 "ruby" => Some(include_str!("queries/ruby.decorations.scm")),
1115 "r" => Some(include_str!("queries/r.decorations.scm")),
1116 "lua" => Some(include_str!("queries/lua.decorations.scm")),
1117 "zig" => Some(include_str!("queries/zig.decorations.scm")),
1118 "idris" => Some(include_str!("queries/idris.decorations.scm")),
1119 "agda" => Some(include_str!("queries/agda.decorations.scm")),
1120 "elm" => Some(include_str!("queries/elm.decorations.scm")),
1121 "julia" => Some(include_str!("queries/julia.decorations.scm")),
1122 "perl" => Some(include_str!("queries/perl.decorations.scm")),
1123 "verilog" => Some(include_str!("queries/verilog.decorations.scm")),
1124 "vhdl" => Some(include_str!("queries/vhdl.decorations.scm")),
1125 "ada" => Some(include_str!("queries/ada.decorations.scm")),
1126 "capnp" => Some(include_str!("queries/capnp.decorations.scm")),
1127 "thrift" => Some(include_str!("queries/thrift.decorations.scm")),
1128 "graphql" => Some(include_str!("queries/graphql.decorations.scm")),
1129 "wit" => Some(include_str!("queries/wit.decorations.scm")),
1130 "clojure" => Some(include_str!("queries/clojure.decorations.scm")),
1131 "scheme" => Some(include_str!("queries/scheme.decorations.scm")),
1132 "prolog" => Some(include_str!("queries/prolog.decorations.scm")),
1133 _ => None,
1134 }
1135}
1136
1137fn bundled_test_regions_query(name: &str) -> Option<&'static str> {
1145 match name {
1146 "rust" => Some(include_str!("queries/rust.test_regions.scm")),
1147 _ => None,
1148 }
1149}
1150
1151#[cfg(test)]
1152mod tests {
1153 use super::*;
1154
1155 #[test]
1156 fn test_grammar_lib_name() {
1157 let name = grammar_lib_name("python");
1158 assert!(name.starts_with("python."));
1159 }
1160
1161 #[test]
1162 fn test_grammar_symbol_name() {
1163 assert_eq!(grammar_symbol_name("python"), "tree_sitter_python");
1164 assert_eq!(grammar_symbol_name("rust"), "tree_sitter_rust_orchard");
1165 assert_eq!(grammar_symbol_name("ssh-config"), "tree_sitter_ssh_config");
1166 assert_eq!(grammar_symbol_name("vb"), "tree_sitter_vb_dotnet");
1167 }
1168
1169 #[test]
1170 fn test_bundled_tags_queries() {
1171 for lang in &[
1172 "rust",
1173 "python",
1174 "javascript",
1175 "typescript",
1176 "tsx",
1177 "go",
1178 "java",
1179 "c",
1180 "cpp",
1181 "ruby",
1182 "kotlin",
1183 "scala",
1184 "elixir",
1185 "swift",
1186 "haskell",
1187 "dart",
1188 "ocaml",
1189 "fsharp",
1190 "gleam",
1191 "zig",
1192 "julia",
1193 "erlang",
1194 "lua",
1195 "php",
1196 "perl",
1197 "r",
1198 "groovy",
1199 "d",
1200 "objc",
1201 "vb",
1202 "powershell",
1203 "clojure",
1204 "commonlisp",
1205 "scheme",
1206 "elisp",
1207 "bash",
1208 "fish",
1209 "zsh",
1210 "ada",
1211 "idris",
1212 "lean",
1213 "rescript",
1214 "elm",
1215 ] {
1216 let query = bundled_tags_query(lang);
1217 assert!(query.is_some(), "Missing bundled tags query for {lang}");
1218 assert!(
1219 !query.unwrap().is_empty(),
1220 "Empty bundled tags query for {lang}"
1221 );
1222 }
1223 }
1224
1225 #[test]
1226 fn test_bundled_types_queries() {
1227 for lang in &[
1228 "rust",
1229 "python",
1230 "typescript",
1231 "tsx",
1232 "java",
1233 "go",
1234 "c",
1235 "cpp",
1236 "kotlin",
1237 "swift",
1238 "c-sharp",
1239 "scala",
1240 "haskell",
1241 "ruby",
1242 "dart",
1243 "elixir",
1244 "ocaml",
1245 "erlang",
1246 "zig",
1247 "fsharp",
1248 "gleam",
1249 "julia",
1250 "r",
1251 "d",
1252 "objc",
1253 "vb",
1254 "groovy",
1255 "ada",
1256 "agda",
1257 "elm",
1258 "idris",
1259 "lean",
1260 "php",
1261 "powershell",
1262 "rescript",
1263 "verilog",
1264 "vhdl",
1265 "sql",
1266 "hcl",
1267 "glsl",
1268 "hlsl",
1269 "clojure",
1270 "commonlisp",
1271 "elisp",
1272 "javascript",
1273 "lua",
1274 "scheme",
1275 "graphql",
1276 "nix",
1277 "starlark",
1278 "matlab",
1279 "tlaplus",
1280 "typst",
1281 ] {
1282 let query = bundled_types_query(lang);
1283 assert!(query.is_some(), "Missing bundled types query for {lang}");
1284 assert!(
1285 !query.unwrap().is_empty(),
1286 "Empty bundled types query for {lang}"
1287 );
1288 }
1289 }
1290
1291 #[test]
1292 fn test_bundled_complexity_queries() {
1293 for lang in &[
1294 "rust",
1295 "python",
1296 "go",
1297 "javascript",
1298 "typescript",
1299 "tsx",
1300 "java",
1301 "c",
1302 "cpp",
1303 "ruby",
1304 "kotlin",
1305 "swift",
1306 "c-sharp",
1307 "bash",
1308 "lua",
1309 "elixir",
1310 "scala",
1311 "dart",
1312 "zig",
1313 "ocaml",
1314 "erlang",
1315 "php",
1316 "haskell",
1317 "r",
1318 "julia",
1319 "perl",
1320 "groovy",
1321 "elm",
1322 "powershell",
1323 "fish",
1324 "fsharp",
1325 "gleam",
1326 "clojure",
1327 "commonlisp",
1328 "scheme",
1329 "d",
1330 "objc",
1331 "vb",
1332 "elisp",
1333 "hcl",
1334 "matlab",
1335 "nix",
1336 "sql",
1337 "starlark",
1338 "vim",
1339 "zsh",
1340 "rescript",
1341 "idris",
1342 "lean",
1343 ] {
1344 let query = bundled_complexity_query(lang);
1345 assert!(
1346 query.is_some(),
1347 "Missing bundled complexity query for {lang}"
1348 );
1349 assert!(
1350 !query.unwrap().is_empty(),
1351 "Empty bundled complexity query for {lang}"
1352 );
1353 }
1354 }
1355
1356 #[test]
1357 fn test_bundled_calls_queries() {
1358 for lang in &[
1359 "python",
1360 "rust",
1361 "typescript",
1362 "tsx",
1363 "javascript",
1364 "java",
1365 "go",
1366 "c",
1367 "cpp",
1368 "ruby",
1369 "kotlin",
1370 "swift",
1371 "c-sharp",
1372 "bash",
1373 "scala",
1374 "elixir",
1375 "lua",
1376 "dart",
1377 "ocaml",
1378 "erlang",
1379 "zig",
1380 "julia",
1381 "r",
1382 "haskell",
1383 "php",
1384 "perl",
1385 "fsharp",
1386 "gleam",
1387 "groovy",
1388 "clojure",
1389 "d",
1390 "objc",
1391 "elisp",
1392 "hcl",
1393 "matlab",
1394 "nix",
1395 "starlark",
1396 "vim",
1397 "zsh",
1398 "rescript",
1399 "prolog",
1400 "sql",
1401 "ada",
1402 "agda",
1403 "awk",
1404 "batch",
1405 "cmake",
1406 "elm",
1407 "fish",
1408 "idris",
1409 "lean",
1410 "meson",
1411 "powershell",
1412 "scheme",
1413 "thrift",
1414 "tlaplus",
1415 "verilog",
1416 "vhdl",
1417 "vb",
1418 "commonlisp",
1419 "scss",
1420 "jinja2",
1421 "nginx",
1422 ] {
1423 let query = bundled_calls_query(lang);
1424 assert!(query.is_some(), "Missing bundled calls query for {lang}");
1425 assert!(
1426 !query.unwrap().is_empty(),
1427 "Empty bundled calls query for {lang}"
1428 );
1429 }
1430 }
1431
1432 #[test]
1433 fn test_bundled_imports_queries() {
1434 for lang in &[
1435 "awk",
1436 "python",
1437 "javascript",
1438 "go",
1439 "lua",
1440 "rust",
1441 "typescript",
1442 "tsx",
1443 "java",
1444 "kotlin",
1445 "c-sharp",
1446 "ruby",
1447 "swift",
1448 "scala",
1449 "elixir",
1450 "dart",
1451 "php",
1452 "c",
1453 "cpp",
1454 "bash",
1455 "zsh",
1456 "fish",
1457 "perl",
1458 "r",
1459 "haskell",
1460 "ocaml",
1461 "fsharp",
1462 "erlang",
1463 "gleam",
1464 "zig",
1465 "julia",
1466 "groovy",
1467 "clojure",
1468 "commonlisp",
1469 "scheme",
1470 "elisp",
1471 "d",
1472 "objc",
1473 "vb",
1474 "powershell",
1475 "vim",
1476 "matlab",
1477 "nix",
1478 "starlark",
1479 "rescript",
1480 "idris",
1481 "ada",
1482 "agda",
1483 "asciidoc",
1484 "caddy",
1485 "capnp",
1486 "cmake",
1487 "devicetree",
1488 "dockerfile",
1489 "elm",
1490 "hcl",
1491 "hlsl",
1492 "jq",
1493 "lean",
1494 "meson",
1495 "nginx",
1496 "ninja",
1497 "prolog",
1498 "css",
1499 "glsl",
1500 "html",
1501 "jinja2",
1502 "scss",
1503 "thrift",
1504 "tlaplus",
1505 "typst",
1506 "verilog",
1507 "vhdl",
1508 "wit",
1509 ] {
1510 let query = bundled_imports_query(lang);
1511 assert!(query.is_some(), "Missing bundled imports query for {lang}");
1512 assert!(
1513 !query.unwrap().is_empty(),
1514 "Empty bundled imports query for {lang}"
1515 );
1516 }
1517 }
1518
1519 #[test]
1520 fn test_get_imports_returns_bundled() {
1521 let loader = GrammarLoader::with_paths(vec![]);
1522 assert!(loader.get_imports("awk").is_some());
1523 assert!(loader.get_imports("python").is_some());
1524 assert!(loader.get_imports("javascript").is_some());
1525 assert!(loader.get_imports("go").is_some());
1526 assert!(loader.get_imports("lua").is_some());
1527 assert!(loader.get_imports("rust").is_some());
1528 assert!(loader.get_imports("typescript").is_some());
1529 assert!(loader.get_imports("tsx").is_some());
1530 assert!(loader.get_imports("java").is_some());
1531 assert!(loader.get_imports("kotlin").is_some());
1532 assert!(loader.get_imports("c-sharp").is_some());
1533 assert!(loader.get_imports("ruby").is_some());
1534 assert!(loader.get_imports("swift").is_some());
1535 assert!(loader.get_imports("scala").is_some());
1536 assert!(loader.get_imports("elixir").is_some());
1537 assert!(loader.get_imports("dart").is_some());
1538 assert!(loader.get_imports("php").is_some());
1539 assert!(loader.get_imports("c").is_some());
1540 assert!(loader.get_imports("cpp").is_some());
1541 assert!(loader.get_imports("bash").is_some());
1542 assert!(loader.get_imports("zsh").is_some());
1543 assert!(loader.get_imports("fish").is_some());
1544 assert!(loader.get_imports("perl").is_some());
1545 assert!(loader.get_imports("r").is_some());
1546 assert!(loader.get_imports("haskell").is_some());
1547 assert!(loader.get_imports("ocaml").is_some());
1548 assert!(loader.get_imports("fsharp").is_some());
1549 assert!(loader.get_imports("erlang").is_some());
1550 assert!(loader.get_imports("gleam").is_some());
1551 assert!(loader.get_imports("zig").is_some());
1552 assert!(loader.get_imports("julia").is_some());
1553 assert!(loader.get_imports("groovy").is_some());
1554 assert!(loader.get_imports("clojure").is_some());
1555 assert!(loader.get_imports("commonlisp").is_some());
1556 assert!(loader.get_imports("scheme").is_some());
1557 assert!(loader.get_imports("elisp").is_some());
1558 assert!(loader.get_imports("d").is_some());
1559 assert!(loader.get_imports("objc").is_some());
1560 assert!(loader.get_imports("vb").is_some());
1561 assert!(loader.get_imports("powershell").is_some());
1562 assert!(loader.get_imports("vim").is_some());
1563 assert!(loader.get_imports("matlab").is_some());
1564 assert!(loader.get_imports("nix").is_some());
1565 assert!(loader.get_imports("starlark").is_some());
1566 assert!(loader.get_imports("rescript").is_some());
1567 assert!(loader.get_imports("idris").is_some());
1568 assert!(loader.get_imports("ada").is_some());
1569 assert!(loader.get_imports("agda").is_some());
1570 assert!(loader.get_imports("asciidoc").is_some());
1571 assert!(loader.get_imports("caddy").is_some());
1572 assert!(loader.get_imports("capnp").is_some());
1573 assert!(loader.get_imports("cmake").is_some());
1574 assert!(loader.get_imports("devicetree").is_some());
1575 assert!(loader.get_imports("dockerfile").is_some());
1576 assert!(loader.get_imports("elm").is_some());
1577 assert!(loader.get_imports("hcl").is_some());
1578 assert!(loader.get_imports("hlsl").is_some());
1579 assert!(loader.get_imports("jq").is_some());
1580 assert!(loader.get_imports("lean").is_some());
1581 assert!(loader.get_imports("meson").is_some());
1582 assert!(loader.get_imports("nginx").is_some());
1583 assert!(loader.get_imports("ninja").is_some());
1584 assert!(loader.get_imports("prolog").is_some());
1585 assert!(loader.get_imports("css").is_some());
1586 assert!(loader.get_imports("glsl").is_some());
1587 assert!(loader.get_imports("html").is_some());
1588 assert!(loader.get_imports("jinja2").is_some());
1589 assert!(loader.get_imports("scss").is_some());
1590 assert!(loader.get_imports("thrift").is_some());
1591 assert!(loader.get_imports("tlaplus").is_some());
1592 assert!(loader.get_imports("typst").is_some());
1593 assert!(loader.get_imports("verilog").is_some());
1594 assert!(loader.get_imports("vhdl").is_some());
1595 assert!(loader.get_imports("wit").is_some());
1596 assert!(loader.get_imports("unknown-lang-xyz").is_none());
1597 }
1598
1599 #[test]
1600 fn test_get_tags_returns_bundled() {
1601 let loader = GrammarLoader::with_paths(vec![]);
1602 assert!(loader.get_tags("rust").is_some());
1603 assert!(loader.get_tags("python").is_some());
1604 assert!(loader.get_tags("go").is_some());
1605 assert!(loader.get_tags("unknown-lang-xyz").is_none());
1606 }
1607
1608 #[test]
1609 fn test_tags_queries_compile() {
1610 let loader = GrammarLoader::new();
1611 let langs = [
1612 "zig",
1613 "clojure",
1614 "scheme",
1615 "nix",
1616 "prolog",
1617 "toml",
1618 "json",
1619 "yaml",
1620 "css",
1621 "html",
1622 "xml",
1623 "thrift",
1624 "dockerfile",
1625 "caddy",
1626 ];
1627 for lang in langs {
1628 let tags = loader.get_tags(lang);
1629 assert!(
1630 tags.is_some(),
1631 "{lang}: no tags query found (missing from bundled_tags_query)"
1632 );
1633 let tags_str = tags.unwrap();
1634 let grammar = loader.get(lang).ok();
1635 if grammar.is_none() {
1636 eprintln!("{lang}: grammar .so not found, skipping compilation check");
1637 continue;
1638 }
1639 let result = tree_sitter::Query::new(&grammar.unwrap(), &tags_str);
1640 assert!(
1641 result.is_ok(),
1642 "{lang}: tags query compilation failed: {:?}",
1643 result.err()
1644 );
1645 }
1646 }
1647
1648 #[test]
1649 fn test_scheme_node_kinds() {
1650 let loader = GrammarLoader::new();
1651 let grammar = loader.get("scheme").ok();
1652 if grammar.is_none() {
1653 eprintln!("scheme: grammar .so not found or load failed");
1654 return;
1655 }
1656 eprintln!("scheme: grammar loaded ok");
1657 let g = grammar.unwrap();
1658 let test_cases = [
1660 ("list", "(list) @x"),
1661 ("symbol", "(symbol) @x"),
1662 ("named_node", "(named_node) @x"),
1663 ("identifier", "(identifier) @x"),
1664 ];
1665 for (name, query_str) in test_cases {
1666 match tree_sitter::Query::new(&g, query_str) {
1667 Ok(_) => eprintln!("scheme node '{name}': valid"),
1668 Err(e) => eprintln!("scheme node '{name}': INVALID - {e:?}"),
1669 }
1670 }
1671 use tree_sitter::Parser;
1673 let src = "(define (add a b) (+ a b))\n(define (multiply a b) (* a b))\n";
1674 let mut parser = Parser::new();
1675 parser.set_language(&g).unwrap();
1676 let tree = parser.parse(src, None).unwrap();
1677 eprintln!("scheme sexp: {}", tree.root_node().to_sexp());
1678 fn walk(node: tree_sitter::Node, depth: usize) {
1680 let prefix = " ".repeat(depth);
1681 eprintln!(
1682 "{prefix}kind={} named={} text_len={}",
1683 node.kind(),
1684 node.is_named(),
1685 node.byte_range().len()
1686 );
1687 let mut cursor = node.walk();
1688 for child in node.children(&mut cursor) {
1689 walk(child, depth + 1);
1690 }
1691 }
1692 walk(tree.root_node(), 0);
1693 }
1694
1695 #[test]
1696 fn test_get_types_returns_bundled() {
1697 let loader = GrammarLoader::with_paths(vec![]);
1698 assert!(loader.get_types("rust").is_some());
1699 assert!(loader.get_types("python").is_some());
1700 assert!(loader.get_types("java").is_some());
1701 assert!(loader.get_types("go").is_some());
1702 assert!(loader.get_types("c").is_some());
1703 assert!(loader.get_types("cpp").is_some());
1704 assert!(loader.get_types("kotlin").is_some());
1705 assert!(loader.get_types("swift").is_some());
1706 assert!(loader.get_types("c-sharp").is_some());
1707 assert!(loader.get_types("unknown-lang-xyz").is_none());
1708 }
1709
1710 #[test]
1711 fn test_get_calls_returns_bundled() {
1712 let loader = GrammarLoader::with_paths(vec![]);
1713 assert!(loader.get_calls("rust").is_some());
1714 assert!(loader.get_calls("python").is_some());
1715 assert!(loader.get_calls("go").is_some());
1716 assert!(loader.get_calls("c").is_some());
1717 assert!(loader.get_calls("cpp").is_some());
1718 assert!(loader.get_calls("ruby").is_some());
1719 assert!(loader.get_calls("kotlin").is_some());
1720 assert!(loader.get_calls("swift").is_some());
1721 assert!(loader.get_calls("c-sharp").is_some());
1722 assert!(loader.get_calls("bash").is_some());
1723 assert!(loader.get_calls("unknown-lang-xyz").is_none());
1724 }
1725
1726 #[test]
1727 fn test_load_from_env() {
1728 let grammar_path = std::env::current_dir().unwrap().join("target/grammars");
1730
1731 if !grammar_path.exists() {
1732 eprintln!("Skipping: run `cargo xtask build-grammars` first");
1733 return;
1734 }
1735
1736 unsafe {
1738 std::env::set_var("NORMALIZE_GRAMMAR_PATH", grammar_path.to_str().unwrap());
1739 }
1740
1741 let loader = GrammarLoader::new();
1742
1743 let ext = grammar_extension();
1745 if grammar_path.join(format!("python{ext}")).exists() {
1746 let lang = loader.get("python").ok();
1747 assert!(lang.is_some(), "Failed to load python grammar");
1748 }
1749
1750 unsafe {
1753 std::env::remove_var("NORMALIZE_GRAMMAR_PATH");
1754 }
1755 }
1756}