use std::collections::HashSet;
use tree_sitter::Language;
pub struct LanguageSpec {
pub extensions: &'static [&'static str],
pub symbol_query: &'static str,
pub import_query: &'static str,
pub call_query: &'static str,
}
const PYTHON: LanguageSpec = LanguageSpec {
extensions: &[".py", ".pyi"],
symbol_query: r#"
(function_definition name: (identifier) @name) @definition.function
(class_definition name: (identifier) @name) @definition.class
"#,
import_query: r#"
(import_statement) @import
(import_from_statement) @import
"#,
call_query: r#"
(call function: (identifier) @name) @call
(call function: (attribute attribute: (identifier) @name)) @call
"#,
};
const JAVASCRIPT: LanguageSpec = LanguageSpec {
extensions: &[".js", ".jsx", ".mjs", ".cjs"],
symbol_query: r#"
(function_declaration name: (identifier) @name) @definition.function
(class_declaration name: (identifier) @name) @definition.class
(method_definition name: (property_identifier) @name) @definition.method
(export_statement declaration: (function_declaration name: (identifier) @name)) @definition.function
(export_statement declaration: (class_declaration name: (identifier) @name)) @definition.class
(lexical_declaration (variable_declarator name: (identifier) @name value: (arrow_function))) @definition.function
"#,
import_query: r#"
(import_statement) @import
"#,
call_query: r#"
(call_expression function: (identifier) @name) @call
(call_expression function: (member_expression property: (property_identifier) @name)) @call
"#,
};
const TYPESCRIPT: LanguageSpec = LanguageSpec {
extensions: &[".ts", ".tsx"],
symbol_query: r#"
(function_declaration name: (identifier) @name) @definition.function
(class_declaration name: (type_identifier) @name) @definition.class
(method_definition name: (property_identifier) @name) @definition.method
(interface_declaration name: (type_identifier) @name) @definition.type
(type_alias_declaration name: (type_identifier) @name) @definition.type
(enum_declaration name: (identifier) @name) @definition.type
(lexical_declaration (variable_declarator name: (identifier) @name value: (arrow_function))) @definition.function
(export_statement declaration: (function_declaration name: (identifier) @name)) @definition.function
(export_statement declaration: (class_declaration name: (type_identifier) @name)) @definition.class
(export_statement declaration: (interface_declaration name: (type_identifier) @name)) @definition.type
(export_statement declaration: (type_alias_declaration name: (type_identifier) @name)) @definition.type
(export_statement declaration: (enum_declaration name: (identifier) @name)) @definition.type
(export_statement declaration: (lexical_declaration (variable_declarator name: (identifier) @name value: (arrow_function)))) @definition.function
"#,
import_query: r#"
(import_statement) @import
"#,
call_query: r#"
(call_expression function: (identifier) @name) @call
(call_expression function: (member_expression property: (property_identifier) @name)) @call
"#,
};
const GO: LanguageSpec = LanguageSpec {
extensions: &[".go"],
symbol_query: r#"
(function_declaration name: (identifier) @name) @definition.function
(method_declaration name: (field_identifier) @name) @definition.method
(type_declaration (type_spec name: (type_identifier) @name)) @definition.type
"#,
import_query: r#"
(import_declaration) @import
"#,
call_query: r#"
(call_expression function: (identifier) @name) @call
(call_expression function: (selector_expression field: (field_identifier) @name)) @call
"#,
};
const RUST: LanguageSpec = LanguageSpec {
extensions: &[".rs"],
symbol_query: r#"
(function_item name: (identifier) @name) @definition.function
(struct_item name: (type_identifier) @name) @definition.class
(enum_item name: (type_identifier) @name) @definition.type
(trait_item name: (type_identifier) @name) @definition.type
(impl_item type: (type_identifier) @name) @definition.class
(type_item name: (type_identifier) @name) @definition.type
"#,
import_query: r#"
(use_declaration) @import
"#,
call_query: r#"
(call_expression function: (identifier) @name) @call
(call_expression function: (scoped_identifier name: (identifier) @name)) @call
(call_expression function: (field_expression field: (field_identifier) @name)) @call
"#,
};
const JAVA: LanguageSpec = LanguageSpec {
extensions: &[".java"],
symbol_query: r#"
(method_declaration name: (identifier) @name) @definition.method
(class_declaration name: (identifier) @name) @definition.class
(interface_declaration name: (identifier) @name) @definition.type
(enum_declaration name: (identifier) @name) @definition.type
(constructor_declaration name: (identifier) @name) @definition.method
"#,
import_query: r#"
(import_declaration) @import
"#,
call_query: r#"
(method_invocation name: (identifier) @name) @call
"#,
};
const PHP: LanguageSpec = LanguageSpec {
extensions: &[".php"],
symbol_query: r#"
(function_definition name: (name) @name) @definition.function
(class_declaration name: (name) @name) @definition.class
(method_declaration name: (name) @name) @definition.method
(interface_declaration name: (name) @name) @definition.type
(trait_declaration name: (name) @name) @definition.type
"#,
import_query: r#"
(namespace_use_declaration) @import
"#,
call_query: r#"
(function_call_expression function: (name) @name) @call
(member_call_expression name: (name) @name) @call
"#,
};
const DART: LanguageSpec = LanguageSpec {
extensions: &[".dart"],
symbol_query: r#"
(function_signature name: (identifier) @name) @definition.function
(class_definition name: (identifier) @name) @definition.class
(method_signature (function_signature (identifier) @name)) @definition.method
(enum_declaration name: (identifier) @name) @definition.type
"#,
import_query: r#"
(import_or_export) @import
"#,
call_query: "",
};
const CSHARP: LanguageSpec = LanguageSpec {
extensions: &[".cs"],
symbol_query: r#"
(method_declaration name: (identifier) @name) @definition.method
(class_declaration name: (identifier) @name) @definition.class
(interface_declaration name: (identifier) @name) @definition.type
(struct_declaration name: (identifier) @name) @definition.type
(enum_declaration name: (identifier) @name) @definition.type
(constructor_declaration name: (identifier) @name) @definition.method
"#,
import_query: r#"
(using_directive) @import
"#,
call_query: r#"
(invocation_expression function: (identifier) @name) @call
(invocation_expression function: (member_access_expression name: (identifier) @name)) @call
"#,
};
const C_LANG: LanguageSpec = LanguageSpec {
extensions: &[".c", ".h"],
symbol_query: r#"
(function_definition declarator: (function_declarator declarator: (identifier) @name)) @definition.function
(struct_specifier name: (type_identifier) @name) @definition.type
(enum_specifier name: (type_identifier) @name) @definition.type
(type_definition declarator: (type_identifier) @name) @definition.type
"#,
import_query: r#"
(preproc_include) @import
"#,
call_query: r#"
(call_expression function: (identifier) @name) @call
"#,
};
const CPP: LanguageSpec = LanguageSpec {
extensions: &[".cpp", ".cc", ".cxx", ".hpp", ".hxx", ".hh"],
symbol_query: r#"
(function_definition declarator: (function_declarator declarator: (identifier) @name)) @definition.function
(function_definition declarator: (function_declarator declarator: (qualified_identifier name: (identifier) @name))) @definition.function
(class_specifier name: (type_identifier) @name) @definition.class
(struct_specifier name: (type_identifier) @name) @definition.type
"#,
import_query: r#"
(preproc_include) @import
"#,
call_query: r#"
(call_expression function: (identifier) @name) @call
(call_expression function: (field_expression field: (field_identifier) @name)) @call
"#,
};
const ELIXIR: LanguageSpec = LanguageSpec {
extensions: &[".ex", ".exs"],
symbol_query: r#"
(call target: (identifier) @_keyword (#any-of? @_keyword "def" "defp" "defmacro") (arguments (identifier) @name)) @definition.function
(call target: (identifier) @_keyword (#any-of? @_keyword "defmodule") (arguments (alias) @name)) @definition.class
"#,
import_query: r#"
(call target: (identifier) @_keyword (#any-of? @_keyword "import" "alias" "use" "require")) @import
"#,
call_query: "",
};
const RUBY: LanguageSpec = LanguageSpec {
extensions: &[".rb", ".rake", ".gemspec"],
symbol_query: r#"
(method name: (identifier) @name) @definition.function
(singleton_method name: (identifier) @name) @definition.function
(class name: (constant) @name) @definition.class
(module name: (constant) @name) @definition.class
"#,
import_query: r#"
(call method: (identifier) @_m (#any-of? @_m "require" "require_relative" "include")) @import
"#,
call_query: r#"
(call method: (identifier) @name) @call
"#,
};
const MARKDOWN: LanguageSpec = LanguageSpec {
extensions: &[".md", ".markdown"],
symbol_query: r#"
(atx_heading heading_content: (_) @name) @definition.section
(setext_heading heading_content: (_) @name) @definition.section
"#,
import_query: "",
call_query: "",
};
const YAML: LanguageSpec = LanguageSpec {
extensions: &[".yaml", ".yml"],
symbol_query: r#"
(block_mapping_pair key: (_) @name) @definition.property
"#,
import_query: "",
call_query: "",
};
const JSON_LANG: LanguageSpec = LanguageSpec {
extensions: &[".json", ".jsonc"],
symbol_query: r#"
(pair key: (string (string_content) @name)) @definition.property
"#,
import_query: "",
call_query: "",
};
const SWIFT: LanguageSpec = LanguageSpec {
extensions: &[".swift"],
symbol_query: r#"
(function_declaration name: (simple_identifier) @name) @definition.function
(class_declaration name: (type_identifier) @name) @definition.class
(protocol_declaration name: (type_identifier) @name) @definition.type
(struct_declaration name: (type_identifier) @name) @definition.type
(enum_declaration name: (type_identifier) @name) @definition.type
"#,
import_query: r#"
(import_declaration) @import
"#,
call_query: r#"
(call_expression (simple_identifier) @name) @call
"#,
};
const SPECS: &[(&str, &LanguageSpec)] = &[
("python", &PYTHON),
("javascript", &JAVASCRIPT),
("typescript", &TYPESCRIPT),
("go", &GO),
("rust", &RUST),
("java", &JAVA),
("php", &PHP),
("dart", &DART),
("csharp", &CSHARP),
("c", &C_LANG),
("cpp", &CPP),
("elixir", &ELIXIR),
("ruby", &RUBY),
("swift", &SWIFT),
("markdown", &MARKDOWN),
("yaml", &YAML),
("json", &JSON_LANG),
];
pub fn detect_language(file_path: &str) -> Option<&'static str> {
let path = std::path::Path::new(file_path);
let ext = path
.extension()
.map(|e| format!(".{}", e.to_string_lossy().to_lowercase()))?;
for (name, spec) in SPECS {
if spec.extensions.contains(&ext.as_str()) {
return Some(name);
}
}
None
}
pub fn get_spec(lang: &str) -> Option<&'static LanguageSpec> {
SPECS
.iter()
.find(|(name, _)| *name == lang)
.map(|(_, s)| *s)
}
pub fn get_ts_language(lang: &str) -> Option<Language> {
let lang_fn = match lang {
"python" => tree_sitter_python::LANGUAGE,
"javascript" => tree_sitter_javascript::LANGUAGE,
"typescript" => tree_sitter_typescript::LANGUAGE_TYPESCRIPT,
"go" => tree_sitter_go::LANGUAGE,
"rust" => tree_sitter_rust::LANGUAGE,
"java" => tree_sitter_java::LANGUAGE,
"c" => tree_sitter_c::LANGUAGE,
"cpp" => tree_sitter_cpp::LANGUAGE,
"csharp" => tree_sitter_c_sharp::LANGUAGE,
"ruby" => tree_sitter_ruby::LANGUAGE,
"php" => tree_sitter_php::LANGUAGE_PHP,
"swift" => tree_sitter_swift::LANGUAGE,
"kotlin" => return None,
"dart" => tree_sitter_dart::LANGUAGE,
"elixir" => tree_sitter_elixir::LANGUAGE,
"json" => tree_sitter_json::LANGUAGE,
"yaml" => tree_sitter_yaml::LANGUAGE,
"markdown" => tree_sitter_md::LANGUAGE,
_ => return None,
};
Some(lang_fn.into())
}
pub fn supported_extensions() -> HashSet<&'static str> {
let mut exts = HashSet::new();
for (_, spec) in SPECS {
for ext in spec.extensions {
exts.insert(*ext);
}
}
exts
}