pub(crate) const DEFINITION_KINDS: &[&str] = &[
"function_declaration",
"function_definition",
"function_item",
"method_definition",
"method_declaration",
"class_declaration",
"class_definition",
"struct_item",
"object_declaration",
"interface_declaration",
"trait_declaration",
"type_alias_declaration",
"type_item",
"enum_item",
"enum_declaration",
"lexical_declaration",
"variable_declaration",
"const_item",
"const_declaration",
"static_item",
"property_declaration",
"trait_item",
"impl_item",
"mod_item",
"namespace_definition",
"decorated_definition",
"type_declaration",
"export_statement",
];
pub(crate) fn extract_definition_name(node: tree_sitter::Node, lines: &[&str]) -> Option<String> {
for field in &["name", "identifier", "declarator"] {
if let Some(child) = node.child_by_field_name(field) {
let text = node_text_simple(child, lines);
if !text.is_empty() {
if child.kind().contains("declarator") {
if let Some(id) = child.child_by_field_name("name") {
return Some(node_text_simple(id, lines));
}
}
return Some(text);
}
}
}
if node.kind() == "export_statement" {
let mut cursor = node.walk();
for child in node.children(&mut cursor) {
if DEFINITION_KINDS.contains(&child.kind()) {
return extract_definition_name(child, lines);
}
}
}
None
}
pub(crate) fn node_text_simple(node: tree_sitter::Node, lines: &[&str]) -> String {
let row = node.start_position().row;
let col_start = node.start_position().column;
let end_row = node.end_position().row;
if row < lines.len() && row == end_row {
let col_end = node.end_position().column.min(lines[row].len());
lines[row][col_start..col_end].to_string()
} else if row < lines.len() {
lines[row][col_start..].to_string()
} else {
String::new()
}
}
pub(crate) fn extract_impl_trait(node: tree_sitter::Node, lines: &[&str]) -> Option<String> {
let trait_node = node.child_by_field_name("trait")?;
Some(node_text_simple(trait_node, lines))
}
pub(crate) fn extract_impl_type(node: tree_sitter::Node, lines: &[&str]) -> Option<String> {
let type_node = node.child_by_field_name("type")?;
Some(node_text_simple(type_node, lines))
}
pub(crate) fn extract_implemented_interfaces(
node: tree_sitter::Node,
lines: &[&str],
) -> Vec<String> {
let mut interfaces = Vec::new();
collect_implemented_interface_clauses(node, lines, &mut interfaces);
interfaces
}
fn collect_implemented_interface_clauses(
node: tree_sitter::Node,
lines: &[&str],
out: &mut Vec<String>,
) {
if node.kind() == "implements_clause" || node.kind() == "super_interfaces" {
collect_identifier_texts(node, lines, out);
return;
}
if node.kind() == "class_body" {
return;
}
let mut cursor = node.walk();
for child in node.children(&mut cursor) {
collect_implemented_interface_clauses(child, lines, out);
}
}
fn collect_identifier_texts(node: tree_sitter::Node, lines: &[&str], out: &mut Vec<String>) {
if node.kind().contains("identifier") {
let text = node_text_simple(node, lines);
if !text.is_empty() {
out.push(text);
}
}
let mut cursor = node.walk();
for child in node.children(&mut cursor) {
collect_identifier_texts(child, lines, out);
}
}
pub(crate) fn extract_base_list_targets(node: tree_sitter::Node, lines: &[&str]) -> Vec<String> {
let mut targets = Vec::new();
let mut cursor = node.walk();
for child in node.children(&mut cursor) {
if child.kind() == "base_list" {
collect_identifier_texts(child, lines, &mut targets);
}
}
targets
}
const ELIXIR_DEFINITION_TARGETS: &[&str] = &[
"defmodule",
"def",
"defp",
"defmacro",
"defmacrop",
"defguard",
"defguardp",
"defdelegate",
"defstruct",
"defexception",
"defprotocol",
"defimpl",
];
pub(crate) fn elixir_arguments(node: tree_sitter::Node) -> Option<tree_sitter::Node> {
let mut cursor = node.walk();
let result = node.children(&mut cursor).find(|c| c.kind() == "arguments");
result
}
pub(crate) fn is_elixir_definition(node: tree_sitter::Node, lines: &[&str]) -> bool {
if node.kind() != "call" {
return false;
}
let Some(target) = node.child_by_field_name("target") else {
return false;
};
let kw = node_text_simple(target, lines);
ELIXIR_DEFINITION_TARGETS.contains(&kw.as_str())
}
pub(crate) fn extract_elixir_definition_name(
node: tree_sitter::Node,
lines: &[&str],
) -> Option<String> {
let target = node.child_by_field_name("target")?;
let kw = node_text_simple(target, lines);
let args = elixir_arguments(node)?;
match kw.as_str() {
"defmodule" | "defprotocol" | "defimpl" => {
let mut cursor = args.walk();
for child in args.children(&mut cursor) {
if child.is_named() {
return Some(node_text_simple(child, lines));
}
}
None
}
"def" | "defp" | "defmacro" | "defmacrop" | "defguard" | "defguardp" | "defdelegate" => {
let mut cursor = args.walk();
for child in args.children(&mut cursor) {
if !child.is_named() {
continue;
}
return elixir_extract_func_head_name(child, lines);
}
None
}
"defstruct" | "defexception" => Some(kw.clone()),
_ => None,
}
}
pub(crate) fn elixir_extract_func_head_name(
node: tree_sitter::Node,
lines: &[&str],
) -> Option<String> {
match node.kind() {
"call" => node
.child_by_field_name("target")
.map(|t| node_text_simple(t, lines)),
"identifier" => Some(node_text_simple(node, lines)),
"binary_operator" => {
let left = node.child_by_field_name("left")?;
elixir_extract_func_head_name(left, lines)
}
_ => None,
}
}
pub(crate) fn elixir_definition_weight(node: tree_sitter::Node, lines: &[&str]) -> u16 {
let Some(target) = node.child_by_field_name("target") else {
return 50;
};
let kw = node_text_simple(target, lines);
match kw.as_str() {
"defmodule" | "defprotocol" | "def" | "defp" | "defmacro" | "defmacrop" | "defguard"
| "defguardp" | "defdelegate" => 100,
"defimpl" => 90,
"defstruct" | "defexception" => 80,
_ => 50,
}
}
pub(crate) fn definition_weight(kind: &str) -> u16 {
match kind {
"function_declaration"
| "function_definition"
| "function_item"
| "method_definition"
| "method_declaration"
| "class_declaration"
| "class_definition"
| "struct_item"
| "interface_declaration"
| "trait_declaration"
| "trait_item"
| "enum_item"
| "enum_declaration"
| "type_item"
| "type_declaration"
| "decorated_definition" => 100,
"impl_item" | "object_declaration" => 90,
"const_item" | "const_declaration" | "static_item" => 80,
"mod_item" | "namespace_definition" | "property_declaration" => 70,
"lexical_declaration" | "variable_declaration" => 40,
"export_statement" => 30,
_ => 50,
}
}