use tree_sitter::{Language, Query};
pub const RUST_SYM_Q: &str = "
(function_item (visibility_modifier)? @vis name: (identifier) @name) @def
(struct_item (visibility_modifier)? @vis name: (type_identifier) @name) @def
(enum_item (visibility_modifier)? @vis name: (type_identifier) @name) @def
(trait_item (visibility_modifier)? @vis name: (type_identifier) @name) @def
(impl_item type: (_) @name) @def
(type_item (visibility_modifier)? @vis name: (type_identifier) @name) @def
(const_item (visibility_modifier)? @vis name: (identifier) @name) @def
(static_item (visibility_modifier)? @vis name: (identifier) @name) @def
(mod_item (visibility_modifier)? @vis name: (identifier) @name) @def
(macro_definition name: (identifier) @name) @def
";
pub const PYTHON_SYM_Q: &str = "
(function_definition name: (identifier) @name) @def
(class_definition name: (identifier) @name) @def
";
pub const JS_SYM_Q: &str = "
(function_declaration name: (identifier) @name) @def
(class_declaration name: (identifier) @name) @def
(method_definition name: (property_identifier) @name) @def
(export_statement declaration: (function_declaration name: (identifier) @name)) @def
(export_statement declaration: (class_declaration name: (identifier) @name)) @def
(lexical_declaration (variable_declarator name: (identifier) @name)) @def
";
pub const TS_SYM_Q: &str = "
(function_declaration name: (identifier) @name) @def
(class_declaration name: (type_identifier) @name) @def
(method_definition name: (property_identifier) @name) @def
(interface_declaration name: (type_identifier) @name) @def
(type_alias_declaration name: (type_identifier) @name) @def
(export_statement declaration: (function_declaration name: (identifier) @name)) @def
(export_statement declaration: (class_declaration name: (type_identifier) @name)) @def
(lexical_declaration (variable_declarator name: (identifier) @name)) @def
";
pub const GO_SYM_Q: &str = "
(function_declaration name: (identifier) @name) @def
(method_declaration name: (field_identifier) @name) @def
(type_declaration (type_spec name: (type_identifier) @name)) @def
(const_declaration (const_spec name: (identifier) @name)) @def
";
#[must_use]
pub fn compile_query(lang: &Language, source: &str, label: &str) -> Option<Query> {
Query::new(lang, source)
.map_err(|e| tracing::warn!("{label} query compile failed: {e}"))
.ok()
}
#[must_use]
pub fn lang_for_ext(ext: &str) -> Option<Language> {
match ext {
"rs" => Some(tree_sitter_rust::LANGUAGE.into()),
"py" | "pyi" => Some(tree_sitter_python::LANGUAGE.into()),
"js" | "jsx" | "mjs" | "cjs" => Some(tree_sitter_javascript::LANGUAGE.into()),
"ts" | "tsx" | "mts" | "cts" => Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
"go" => Some(tree_sitter_go::LANGUAGE.into()),
"sh" | "bash" | "zsh" => Some(tree_sitter_bash::LANGUAGE.into()),
"toml" => Some(tree_sitter_toml_ng::LANGUAGE.into()),
"json" | "jsonc" => Some(tree_sitter_json::LANGUAGE.into()),
"md" | "markdown" => Some(tree_sitter_md::LANGUAGE.into()),
_ => None,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn lang_for_ext_covers_all_extension_groups() {
let supported = [
"rs", "py", "pyi", "js", "jsx", "mjs", "cjs", "ts", "tsx", "mts", "cts", "go", "sh",
"bash", "zsh", "toml", "json", "jsonc", "md", "markdown",
];
for ext in supported {
assert!(
lang_for_ext(ext).is_some(),
"expected .{ext} to be supported"
);
}
}
#[test]
fn lang_for_ext_unsupported_returns_none() {
assert!(lang_for_ext("xyz").is_none());
assert!(lang_for_ext("").is_none());
}
#[test]
fn lang_for_ext_aliases_match_within_group_and_differ_across_groups() {
assert_eq!(lang_for_ext("sh"), lang_for_ext("bash"));
assert_eq!(lang_for_ext("sh"), lang_for_ext("zsh"));
assert_eq!(lang_for_ext("json"), lang_for_ext("jsonc"));
assert_eq!(lang_for_ext("md"), lang_for_ext("markdown"));
assert_ne!(lang_for_ext("sh"), lang_for_ext("toml"));
assert_ne!(lang_for_ext("toml"), lang_for_ext("json"));
assert_ne!(lang_for_ext("json"), lang_for_ext("md"));
}
}