use std::path::Path;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum CloneLanguage {
Rust,
Python,
Java,
JavaScript,
TypeScript,
Tsx,
}
impl CloneLanguage {
#[must_use]
pub fn from_path(path: &Path) -> Option<Self> {
let ext = path.extension()?.to_str()?;
match ext {
"rs" => Some(Self::Rust),
"py" => Some(Self::Python),
"java" => Some(Self::Java),
"js" | "jsx" | "mjs" | "cjs" => Some(Self::JavaScript),
"ts" => Some(Self::TypeScript),
"tsx" => Some(Self::Tsx),
_ => None,
}
}
#[must_use]
pub fn language(self) -> tree_sitter::Language {
match self {
Self::Rust => tree_sitter_rust::LANGUAGE.into(),
Self::Python => tree_sitter_python::LANGUAGE.into(),
Self::Java => tree_sitter_java::LANGUAGE.into(),
Self::JavaScript => tree_sitter_javascript::LANGUAGE.into(),
Self::TypeScript => tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into(),
Self::Tsx => tree_sitter_typescript::LANGUAGE_TSX.into(),
}
}
#[must_use]
pub fn skip_kinds(self) -> &'static [&'static str] {
match self {
Self::Rust => &[
"identifier",
"type_identifier",
"field_identifier",
"primitive_type",
"integer_literal",
"float_literal",
"string_literal",
"char_literal",
"boolean_literal",
"raw_string_literal",
"byte_literal",
"byte_string_literal",
"line_comment",
"block_comment",
],
Self::Python => &[
"identifier",
"integer",
"float",
"string",
"true",
"false",
"none",
"concatenated_string",
"comment",
],
Self::Java => &[
"identifier",
"type_identifier",
"decimal_integer_literal",
"hex_integer_literal",
"decimal_floating_point_literal",
"string_literal",
"character_literal",
"true",
"false",
"null_literal",
"line_comment",
"block_comment",
],
Self::JavaScript | Self::TypeScript | Self::Tsx => &[
"identifier",
"type_identifier",
"property_identifier",
"shorthand_property_identifier",
"number",
"string",
"template_string",
"true",
"false",
"null",
"undefined",
"regex",
"comment",
"html_comment",
],
}
}
#[must_use]
pub fn comment_kinds(self) -> &'static [&'static str] {
match self {
Self::Rust | Self::Java => &["line_comment", "block_comment"],
Self::Python => &["comment"],
Self::JavaScript | Self::TypeScript | Self::Tsx => &["comment", "html_comment"],
}
}
#[must_use]
pub fn function_kinds(self) -> &'static [&'static str] {
match self {
Self::Rust => &[
"function_item",
"function_signature_item",
"closure_expression",
],
Self::Python => &["function_definition"],
Self::Java => &["method_declaration", "constructor_declaration"],
Self::JavaScript | Self::TypeScript | Self::Tsx => &[
"function_declaration",
"method_definition",
"arrow_function",
"function_expression",
"generator_function",
"generator_function_declaration",
],
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::path::Path;
#[test]
fn extension_dispatch() {
assert_eq!(
CloneLanguage::from_path(Path::new("a.rs")),
Some(CloneLanguage::Rust)
);
assert_eq!(
CloneLanguage::from_path(Path::new("a.py")),
Some(CloneLanguage::Python)
);
assert_eq!(
CloneLanguage::from_path(Path::new("a.java")),
Some(CloneLanguage::Java)
);
assert_eq!(
CloneLanguage::from_path(Path::new("a.js")),
Some(CloneLanguage::JavaScript)
);
assert_eq!(
CloneLanguage::from_path(Path::new("a.mjs")),
Some(CloneLanguage::JavaScript)
);
assert_eq!(
CloneLanguage::from_path(Path::new("a.cjs")),
Some(CloneLanguage::JavaScript)
);
assert_eq!(
CloneLanguage::from_path(Path::new("a.jsx")),
Some(CloneLanguage::JavaScript)
);
assert_eq!(
CloneLanguage::from_path(Path::new("a.ts")),
Some(CloneLanguage::TypeScript)
);
assert_eq!(
CloneLanguage::from_path(Path::new("a.tsx")),
Some(CloneLanguage::Tsx)
);
assert_eq!(CloneLanguage::from_path(Path::new("a.txt")), None);
}
#[test]
fn tsx_grammar_parses_jsx() {
let lang = CloneLanguage::Tsx.language();
let mut parser = tree_sitter::Parser::new();
parser.set_language(&lang).expect("set TSX language");
let src = "export const View = (n: number) => <div className=\"x\">{n}</div>;";
let tree = parser.parse(src, None).expect("parse TSX");
assert!(
!tree.root_node().has_error(),
"TSX grammar must accept JSX tags; got: {}",
tree.root_node().to_sexp()
);
}
#[test]
fn jsx_grammar_parses_jsx() {
let lang = CloneLanguage::JavaScript.language();
let mut parser = tree_sitter::Parser::new();
parser.set_language(&lang).expect("set JS language");
let src = "export const View = ({n}) => <div className=\"x\">{n}</div>;";
let tree = parser.parse(src, None).expect("parse JSX");
assert!(
!tree.root_node().has_error(),
"JavaScript grammar must accept JSX; got: {}",
tree.root_node().to_sexp()
);
}
}