use tree_sitter::Language as TsLanguage;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Lang {
Rust,
Python,
JavaScript,
TypeScript,
}
pub(crate) struct LangSpec {
pub def_kinds: &'static [&'static str],
pub elision: &'static str,
pub symbol_kinds: &'static [&'static str],
pub keeps_docstring: bool,
pub ref_ident_kinds: &'static [&'static str],
pub binder_kinds: &'static [&'static str],
}
impl Lang {
pub fn from_path(path: &str) -> Option<Lang> {
let ext = path.rsplit('.').next()?.to_ascii_lowercase();
Some(match ext.as_str() {
"rs" => Lang::Rust,
"py" | "pyi" => Lang::Python,
"js" | "jsx" | "mjs" | "cjs" => Lang::JavaScript,
"ts" | "tsx" | "mts" | "cts" => Lang::TypeScript,
_ => return None,
})
}
pub(crate) fn ts_language(self) -> TsLanguage {
match self {
Lang::Rust => tree_sitter_rust::LANGUAGE.into(),
Lang::Python => tree_sitter_python::LANGUAGE.into(),
Lang::JavaScript => tree_sitter_javascript::LANGUAGE.into(),
Lang::TypeScript => tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into(),
}
}
pub(crate) fn spec(self) -> LangSpec {
match self {
Lang::Rust => LangSpec {
def_kinds: &["function_item"],
elision: "{ … }",
symbol_kinds: &[
"function_item",
"struct_item",
"enum_item",
"trait_item",
"type_item",
"const_item",
"static_item",
],
keeps_docstring: false,
ref_ident_kinds: &["identifier", "type_identifier"],
binder_kinds: &["parameter", "let_declaration", "closure_parameters"],
},
Lang::Python => LangSpec {
def_kinds: &["function_definition"],
elision: "...",
symbol_kinds: &["function_definition", "class_definition"],
keeps_docstring: true,
ref_ident_kinds: &["identifier"],
binder_kinds: &["parameters", "lambda_parameters"],
},
Lang::JavaScript => LangSpec {
def_kinds: &[
"function_declaration",
"method_definition",
"function_expression",
],
elision: "{ … }",
symbol_kinds: &[
"function_declaration",
"method_definition",
"class_declaration",
],
keeps_docstring: false,
ref_ident_kinds: &["identifier"],
binder_kinds: &["formal_parameters", "variable_declarator"],
},
Lang::TypeScript => LangSpec {
def_kinds: &[
"function_declaration",
"method_definition",
"function_expression",
],
elision: "{ … }",
symbol_kinds: &[
"function_declaration",
"method_definition",
"class_declaration",
"interface_declaration",
"type_alias_declaration",
],
keeps_docstring: false,
ref_ident_kinds: &["identifier", "type_identifier"],
binder_kinds: &[
"formal_parameters",
"variable_declarator",
"required_parameter",
"optional_parameter",
],
},
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn detects_languages_by_extension() {
assert_eq!(Lang::from_path("src/main.rs"), Some(Lang::Rust));
assert_eq!(Lang::from_path("a/b/c.py"), Some(Lang::Python));
assert_eq!(Lang::from_path("x.JSX"), Some(Lang::JavaScript));
assert_eq!(Lang::from_path("x.tsx"), Some(Lang::TypeScript));
assert_eq!(Lang::from_path("README.md"), None);
assert_eq!(Lang::from_path("noext"), None);
}
}