use tree_sitter::Node;
mod bash;
mod cpp;
mod css;
mod go;
mod java;
mod javascript;
mod json;
mod lua;
mod markdown;
mod php;
#[cfg(test)]
mod php_test;
mod python;
pub mod resolution_utils;
mod ruby;
mod rust;
mod svelte;
mod typescript;
pub use bash::Bash;
pub use cpp::Cpp;
pub use css::Css;
pub use go::Go;
pub use java::Java;
pub use javascript::JavaScript;
pub use json::Json;
pub use lua::Lua;
pub use markdown::Markdown;
pub use php::Php;
pub use python::Python;
pub use ruby::Ruby;
pub use rust::Rust;
pub use svelte::Svelte;
pub use typescript::TypeScript;
pub trait Language: Send + Sync {
fn name(&self) -> &'static str;
fn get_ts_language(&self) -> tree_sitter::Language;
fn get_meaningful_kinds(&self) -> Vec<&'static str>;
fn extract_symbols(&self, node: Node, contents: &str) -> Vec<String>;
fn extract_identifiers(&self, node: Node, contents: &str, symbols: &mut Vec<String>);
fn extract_imports_exports(&self, node: Node, contents: &str) -> (Vec<String>, Vec<String>) {
let _ = (node, contents);
(Vec::new(), Vec::new())
}
fn extract_function_calls(&self, node: Node, contents: &str) -> Vec<String> {
let _ = (node, contents);
Vec::new()
}
fn are_node_types_equivalent(&self, type1: &str, type2: &str) -> bool {
type1 == type2
}
fn get_node_type_description(&self, node_type: &str) -> &'static str {
match node_type {
t if t.contains("function") => "function declarations",
t if t.contains("method") => "function declarations",
t if t.contains("class") => "class/interface declarations",
t if t.contains("struct") => "type definitions",
t if t.contains("enum") => "type definitions",
t if t.contains("mod") || t.contains("module") => "module declarations",
t if t.contains("const") => "constant declarations",
t if t.contains("var") || t.contains("let") => "variable declarations",
t if t.contains("type") => "type declarations",
t if t.contains("trait") => "trait declarations",
t if t.contains("impl") => "implementation blocks",
t if t.contains("macro") => "macro definitions",
t if t.contains("namespace") => "namespace declarations",
t if t.contains("comment") => "comments",
_ => "declarations",
}
}
fn resolve_import(
&self,
import_path: &str,
source_file: &str,
all_files: &[String],
) -> Option<String>;
fn get_file_extensions(&self) -> Vec<&'static str>;
}
pub fn get_language(name: &str) -> Option<Box<dyn Language>> {
match name {
"rust" => Some(Box::new(Rust {})),
"javascript" => Some(Box::new(JavaScript {})),
"typescript" => Some(Box::new(TypeScript {})),
"python" => Some(Box::new(Python {})),
"go" => Some(Box::new(Go {})),
"java" => Some(Box::new(Java {})),
"cpp" => Some(Box::new(Cpp {})),
"php" => Some(Box::new(Php {})),
"bash" => Some(Box::new(Bash {})),
"ruby" => Some(Box::new(Ruby {})),
"lua" => Some(Box::new(Lua {})),
"json" => Some(Box::new(Json {})),
"svelte" => Some(Box::new(Svelte {})),
"css" => Some(Box::new(Css {})),
"markdown" => Some(Box::new(Markdown {})),
_ => None,
}
}
pub fn deduplicate_symbols(symbols: &mut Vec<String>) {
symbols.sort();
symbols.dedup();
}
pub fn extract_identifiers_default<F>(
node: Node,
contents: &str,
symbols: &mut Vec<String>,
should_include: F,
) where
F: Fn(&str, &str) -> bool + Copy,
{
let kind = node.kind();
if let Ok(text) = node.utf8_text(contents.as_bytes()) {
let trimmed = text.trim();
if !trimmed.is_empty()
&& should_include(kind, trimmed)
&& !symbols.contains(&trimmed.to_string())
{
symbols.push(trimmed.to_string());
}
}
let mut cursor = node.walk();
if cursor.goto_first_child() {
loop {
extract_identifiers_default(cursor.node(), contents, symbols, should_include);
if !cursor.goto_next_sibling() {
break;
}
}
}
}
pub fn check_semantic_groups(type1: &str, type2: &str, semantic_groups: &[&[&str]]) -> bool {
if type1 == type2 {
return true;
}
for group in semantic_groups {
let contains_type1 = group.contains(&type1);
let contains_type2 = group.contains(&type2);
if contains_type1 && contains_type2 {
return true;
}
}
false
}
pub fn extract_symbol_by_kind(node: Node, contents: &str, target_kind: &str) -> Option<String> {
for child in node.children(&mut node.walk()) {
if child.kind() == target_kind {
if let Ok(text) = child.utf8_text(contents.as_bytes()) {
return Some(text.to_string());
}
}
}
None
}
pub fn extract_callee_identifiers(text: &str) -> Vec<String> {
let trimmed = text.trim();
if trimmed.is_empty() {
return Vec::new();
}
let mut results = Vec::new();
for segment in trimmed.split(['.', ':']) {
let seg = segment.trim();
if !seg.is_empty() && seg != "self" && seg != "Self" && seg != "this" && seg != "super" {
results.push(seg.to_string());
}
}
results
}
pub fn extract_symbol_by_kinds(
node: Node,
contents: &str,
target_kinds: &[&str],
) -> Option<String> {
for child in node.children(&mut node.walk()) {
if target_kinds.contains(&child.kind())
|| target_kinds.iter().any(|k| child.kind().contains(k))
{
if let Ok(text) = child.utf8_text(contents.as_bytes()) {
return Some(text.to_string());
}
}
}
None
}