use crate::Language;
use tree_sitter::Node;
pub(crate) struct FunctionNodeTypes {
pub(crate) function: &'static str,
pub(crate) method: &'static str,
pub(crate) extra_function_kinds: &'static [&'static str],
}
pub(crate) fn is_inside_function_body(node: Node, language: Language) -> bool {
let body_kinds = get_body_node_kinds(language);
let fn_kinds = get_function_node_kinds(language);
let mut current = node.parent();
let mut depth = 0;
const MAX_PARENT_WALK: usize = 500;
while let Some(parent) = current {
depth += 1;
if depth > MAX_PARENT_WALK {
return false;
}
let kind = parent.kind();
if body_kinds.contains(&kind) {
return true;
}
if fn_kinds.contains(&kind) {
return true;
}
current = parent.parent();
}
false
}
fn get_body_node_kinds(language: Language) -> &'static [&'static str] {
match language {
Language::TypeScript | Language::JavaScript => &["statement_block"],
Language::Python | Language::Rust | Language::Go => &["block"],
Language::Java => &["block", "constructor_body"],
Language::C | Language::Cpp => &["compound_statement"],
Language::CSharp => &["block"],
Language::Ruby => &["body_statement"],
Language::Sql => &[], Language::Kotlin => &["function_body", "block"],
Language::Swift => &["function_body"],
Language::Markdown | Language::Json | Language::Yaml | Language::Toml => &[],
}
}
fn get_function_node_kinds(language: Language) -> &'static [&'static str] {
match language {
Language::Python => &["function_definition"],
_ => &[],
}
}
pub(crate) fn find_body_child(node: Node) -> Option<Node> {
let mut cursor = node.walk();
for child in node.children(&mut cursor) {
match child.kind() {
"statement_block" | "block" | "compound_statement" | "constructor_body"
| "body_statement" | "function_body" => return Some(child),
_ => continue,
}
}
None
}
pub(crate) fn node_kind_info(kind: &str) -> (&'static str, u8) {
match kind {
"type_alias_declaration" => ("type_alias_declaration", 5),
"interface_declaration" => ("interface_declaration", 5),
"struct_item" => ("struct_item", 5),
"trait_item" => ("trait_item", 5),
"enum_item" => ("enum_item", 5),
"enum_declaration" => ("enum_declaration", 5),
"struct_specifier" => ("struct_specifier", 5),
"enum_specifier" => ("enum_specifier", 5),
"type_definition" => ("type_definition", 5),
"type_item" => ("type_item", 5),
"type_alias_statement" => ("type_alias_statement", 5),
"type_declaration" => ("type_declaration", 5),
"using_declaration" => ("using_declaration", 5), "alias_declaration" => ("alias_declaration", 5), "class_definition" => ("class_definition", 5), "struct_declaration" => ("struct_declaration", 5), "create_table" => ("create_table", 5), "type_alias" => ("type_alias", 5), "object_declaration" => ("object_declaration", 5), "typealias_declaration" => ("typealias_declaration", 5), "protocol_declaration" => ("protocol_declaration", 5), "atx_heading" => ("atx_heading", 5),
"setext_heading" => ("setext_heading", 5),
"function_declaration" => ("function_declaration", 4),
"function_item" => ("function_item", 4),
"method_declaration" => ("method_declaration", 4),
"function_definition" => ("function_definition", 4),
"method_definition" => ("method_definition", 4),
"declaration" => ("declaration", 4),
"template_declaration" => ("template_declaration", 4),
"arrow_function" => ("arrow_function", 4),
"function_expression" => ("function_expression", 4),
"constructor_declaration" => ("constructor_declaration", 4), "method" => ("method", 4), "singleton_method" => ("singleton_method", 4), "init_declaration" => ("init_declaration", 4), "deinit_declaration" => ("deinit_declaration", 4), "secondary_constructor" => ("secondary_constructor", 4), "anonymous_initializer" => ("anonymous_initializer", 4),
"import_statement" => ("import_statement", 3),
"use_declaration" => ("use_declaration", 3),
"import_declaration" => ("import_declaration", 3),
"preproc_include" => ("preproc_include", 3),
"export_statement" => ("export_statement", 3),
"use_item" => ("use_item", 3),
"using_directive" => ("using_directive", 3), "call" => ("call", 3),
"import" => ("import", 3), "package_header" => ("package_header", 3),
"class_declaration" => ("class_declaration", 2),
"module_declaration" => ("module_declaration", 2),
"impl_item" => ("impl_item", 2),
"class_specifier" => ("class_specifier", 2),
"namespace_definition" => ("namespace_definition", 2),
"namespace_declaration" => ("namespace_declaration", 2), "interface_type" => ("interface_type", 2),
"struct_type" => ("struct_type", 2),
"class" => ("class", 2), "module" => ("module", 2), "statement" => ("statement", 2),
"program" => ("program", 1),
"source_file" => ("source_file", 1),
"expression_statement" => ("expression_statement", 1),
"lexical_declaration" => ("lexical_declaration", 1),
"variable_declaration" => ("variable_declaration", 1),
"comment" => ("comment", 1),
"line_comment" => ("line_comment", 1),
"block_comment" => ("block_comment", 1),
_ => ("unknown", 1),
}
}
pub(crate) fn to_static_node_kind(kind: &str) -> &'static str {
node_kind_info(kind).0
}
pub(crate) fn score_node_kind(kind: &str) -> u8 {
node_kind_info(kind).1
}
pub(crate) fn get_comment_prefix(language: Language) -> &'static str {
match language {
Language::TypeScript
| Language::JavaScript
| Language::Rust
| Language::Go
| Language::Java
| Language::C
| Language::Cpp
| Language::CSharp
| Language::Kotlin
| Language::Swift => "//",
Language::Python | Language::Ruby => "#",
Language::Sql => "--",
Language::Markdown => "<!--",
Language::Json => "//", Language::Yaml => "#",
Language::Toml => "#",
}
}
pub(crate) fn get_comment_suffix(language: Language) -> &'static str {
match language {
Language::Markdown => " -->",
_ => "",
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_score_node_kind_priority_5() {
assert_eq!(score_node_kind("type_alias_declaration"), 5);
assert_eq!(score_node_kind("interface_declaration"), 5);
assert_eq!(score_node_kind("struct_item"), 5);
assert_eq!(score_node_kind("trait_item"), 5);
assert_eq!(score_node_kind("enum_item"), 5);
assert_eq!(score_node_kind("class_definition"), 5); assert_eq!(score_node_kind("atx_heading"), 5);
}
#[test]
fn test_score_node_kind_priority_4() {
assert_eq!(score_node_kind("function_declaration"), 4);
assert_eq!(score_node_kind("function_item"), 4);
assert_eq!(score_node_kind("method_declaration"), 4);
assert_eq!(score_node_kind("function_definition"), 4);
}
#[test]
fn test_score_node_kind_priority_3() {
assert_eq!(score_node_kind("import_statement"), 3);
assert_eq!(score_node_kind("use_declaration"), 3);
assert_eq!(score_node_kind("import_declaration"), 3);
}
#[test]
fn test_score_node_kind_priority_2() {
assert_eq!(score_node_kind("class_declaration"), 2);
assert_eq!(score_node_kind("impl_item"), 2);
}
#[test]
fn test_score_node_kind_priority_1_default() {
assert_eq!(score_node_kind("source_file"), 1);
assert_eq!(score_node_kind("expression_statement"), 1);
assert_eq!(score_node_kind("unknown_node"), 1);
}
#[test]
fn test_node_kind_info_consistency() {
let known_kinds = [
"type_alias_declaration",
"interface_declaration",
"struct_item",
"trait_item",
"enum_item",
"enum_declaration",
"struct_specifier",
"enum_specifier",
"type_definition",
"type_item",
"type_alias_statement",
"type_declaration",
"using_declaration",
"alias_declaration",
"class_definition",
"struct_declaration",
"create_table",
"type_alias",
"object_declaration",
"typealias_declaration",
"protocol_declaration",
"atx_heading",
"setext_heading",
"function_declaration",
"function_item",
"method_declaration",
"function_definition",
"method_definition",
"declaration",
"template_declaration",
"arrow_function",
"function_expression",
"constructor_declaration",
"method",
"singleton_method",
"init_declaration",
"deinit_declaration",
"secondary_constructor",
"anonymous_initializer",
"import_statement",
"use_declaration",
"import_declaration",
"import",
"package_header",
"preproc_include",
"export_statement",
"use_item",
"using_directive",
"call",
"class_declaration",
"module_declaration",
"impl_item",
"class_specifier",
"namespace_definition",
"namespace_declaration",
"interface_type",
"struct_type",
"class",
"module",
"statement",
"program",
"source_file",
"expression_statement",
"lexical_declaration",
"variable_declaration",
"comment",
"line_comment",
"block_comment",
];
for kind in &known_kinds {
let static_str = to_static_node_kind(kind);
assert_ne!(
static_str, "unknown",
"Known kind '{}' should not map to 'unknown'",
kind
);
assert_eq!(
static_str, *kind,
"to_static_node_kind('{}') should return itself",
kind
);
assert_eq!(
score_node_kind(static_str),
score_node_kind(kind),
"Scoring should be idempotent through mapping for '{}'",
kind
);
}
}
#[test]
fn test_class_definition_is_priority_5() {
let (static_str, priority) = node_kind_info("class_definition");
assert_eq!(static_str, "class_definition");
assert_eq!(
priority, 5,
"class_definition should be Priority 5 (type-level)"
);
}
#[test]
fn test_comment_prefix() {
assert_eq!(get_comment_prefix(Language::TypeScript), "//");
assert_eq!(get_comment_prefix(Language::JavaScript), "//");
assert_eq!(get_comment_prefix(Language::Rust), "//");
assert_eq!(get_comment_prefix(Language::Go), "//");
assert_eq!(get_comment_prefix(Language::Java), "//");
assert_eq!(get_comment_prefix(Language::C), "//");
assert_eq!(get_comment_prefix(Language::Cpp), "//");
assert_eq!(get_comment_prefix(Language::CSharp), "//");
assert_eq!(get_comment_prefix(Language::Kotlin), "//");
assert_eq!(get_comment_prefix(Language::Swift), "//");
assert_eq!(get_comment_prefix(Language::Python), "#");
assert_eq!(get_comment_prefix(Language::Ruby), "#");
assert_eq!(get_comment_prefix(Language::Sql), "--");
assert_eq!(get_comment_prefix(Language::Markdown), "<!--");
assert_eq!(get_comment_prefix(Language::Json), "//");
assert_eq!(get_comment_prefix(Language::Yaml), "#");
assert_eq!(get_comment_prefix(Language::Toml), "#");
}
#[test]
fn test_comment_suffix() {
assert_eq!(get_comment_suffix(Language::TypeScript), "");
assert_eq!(get_comment_suffix(Language::JavaScript), "");
assert_eq!(get_comment_suffix(Language::Rust), "");
assert_eq!(get_comment_suffix(Language::Go), "");
assert_eq!(get_comment_suffix(Language::Java), "");
assert_eq!(get_comment_suffix(Language::C), "");
assert_eq!(get_comment_suffix(Language::Cpp), "");
assert_eq!(get_comment_suffix(Language::CSharp), "");
assert_eq!(get_comment_suffix(Language::Kotlin), "");
assert_eq!(get_comment_suffix(Language::Swift), "");
assert_eq!(get_comment_suffix(Language::Python), "");
assert_eq!(get_comment_suffix(Language::Ruby), "");
assert_eq!(get_comment_suffix(Language::Sql), "");
assert_eq!(get_comment_suffix(Language::Markdown), " -->");
assert_eq!(get_comment_suffix(Language::Json), "");
assert_eq!(get_comment_suffix(Language::Yaml), "");
assert_eq!(get_comment_suffix(Language::Toml), "");
}
}