pub const ELEMENT_QUERY: &str = r#"
; Method definitions
(method name: (identifier) @func)
; Class and module definitions
(class name: (constant) @class)
(module name: (constant) @class)
; Constant assignments
(assignment left: (constant) @const)
; Attr declarations as functions
(call method: (identifier) @func (#eq? @func "attr_accessor"))
(call method: (identifier) @func (#eq? @func "attr_reader"))
(call method: (identifier) @func (#eq? @func "attr_writer"))
; Require statements
(call method: (identifier) @import (#eq? @import "require"))
(call method: (identifier) @import (#eq? @import "require_relative"))
(call method: (identifier) @import (#eq? @import "load"))
"#;
pub const CALL_QUERY: &str = r#"
; Method calls
(call method: (identifier) @method.call)
; Method calls with receiver
(call receiver: (_) method: (identifier) @method.call)
; Calls to constants (typically constructors)
(call receiver: (constant) @function.call)
; Identifier and constant references in argument lists
(argument_list (identifier) @identifier.reference)
(argument_list (constant) @identifier.reference)
; Binary expressions
(binary left: (identifier) @identifier.reference)
(binary right: (identifier) @identifier.reference)
(binary left: (constant) @identifier.reference)
(binary right: (constant) @identifier.reference)
; Assignment expressions
(assignment right: (identifier) @identifier.reference)
(assignment right: (constant) @identifier.reference)
"#;
pub const REFERENCE_QUERY: &str = r#"
; Instance methods within a class - capture class name, will find method via receiver lookup
(class
name: (constant) @method.receiver
(body_statement (method)))
; Class instantiation (ClassName.new)
(call
receiver: (constant) @struct.literal
method: (identifier) @method.name (#eq? @method.name "new"))
; Constant references as receivers (type usage)
(call
receiver: (constant) @field.type
method: (identifier))
"#;
pub fn find_method_for_receiver(
receiver_node: &tree_sitter::Node,
source: &str,
ast_recursion_limit: Option<usize>,
) -> Option<String> {
let max_depth = ast_recursion_limit.unwrap_or(10);
if receiver_node.kind() == "constant" {
let mut current = *receiver_node;
while let Some(parent) = current.parent() {
if parent.kind() == "class" {
return find_first_method_in_class(&parent, source, max_depth);
}
current = parent;
}
}
None
}
fn find_first_method_in_class(
class_node: &tree_sitter::Node,
source: &str,
max_depth: usize,
) -> Option<String> {
for i in 0..class_node.child_count() as u32 {
if let Some(child) = class_node.child(i)
&& child.kind() == "body_statement"
{
return find_method_in_body_with_depth(&child, source, 0, max_depth);
}
}
None
}
fn find_method_in_body_with_depth(
node: &tree_sitter::Node,
source: &str,
depth: usize,
max_depth: usize,
) -> Option<String> {
if depth >= max_depth {
return None;
}
for i in 0..node.child_count() as u32 {
if let Some(child) = node.child(i)
&& child.kind() == "method"
{
for j in 0..child.child_count() as u32 {
if let Some(name_node) = child.child(j)
&& name_node.kind() == "identifier"
{
return source.get(name_node.byte_range()).map(|s| s.to_string());
}
}
}
}
None
}