use tree_sitter::Node;
use super::text::{named_children, text};
pub(super) fn delegate_argument_param_count(argument: Node, src: &[u8]) -> Option<usize> {
if argument.child_by_field_name("name").is_some() {
return None;
}
let children = named_children(argument);
if let Some(lambda) = children.iter().find(|c| c.kind() == "lambda_expression") {
let params = lambda.child_by_field_name("parameters")?;
return Some(if params.kind() == "implicit_parameter" {
1
} else {
named_children(params).len()
});
}
match children.as_slice() {
[identifier] if identifier.kind() == "identifier" && argument.child_count() == 1 => {
local_function_param_count(*identifier, src)
}
_ => None,
}
}
fn local_function_param_count(identifier: Node, src: &[u8]) -> Option<usize> {
let name = text(identifier, src);
let mut declared = Vec::new();
let mut node = identifier.parent();
while let Some(scope) = node {
match scope.kind() {
"block" => {
for statement in named_children(scope) {
match statement.kind() {
"local_function_statement" if name_is(statement, &name, src) => {
declared.push(statement);
}
"local_declaration_statement" if declares_local(statement, &name, src) => {
return None;
}
_ => {}
}
}
}
"lambda_expression" | "anonymous_method_expression" | "local_function_statement" => {
if parameters(scope)
.iter()
.any(|p| parameter_is(*p, &name, src))
{
return None;
}
}
"foreach_statement" => {
if scope
.child_by_field_name("left")
.is_some_and(|left| text(left, src) == name)
{
return None;
}
}
"declaration_list" | "compilation_unit" | "global_statement" => break,
_ => {}
}
node = scope.parent();
}
match declared.as_slice() {
[function] => Some(parameters(*function).len()),
_ => None,
}
}
fn name_is(node: Node, name: &str, src: &[u8]) -> bool {
node.child_by_field_name("name")
.is_some_and(|n| text(n, src) == name)
}
fn parameter_is(parameter: Node, name: &str, src: &[u8]) -> bool {
if parameter.kind() == "implicit_parameter" {
text(parameter, src) == name
} else {
name_is(parameter, name, src)
}
}
fn parameters(function: Node) -> Vec<Node> {
match function.child_by_field_name("parameters") {
Some(p) if p.kind() == "implicit_parameter" => vec![p],
Some(list) => named_children(list)
.into_iter()
.filter(|c| c.kind() == "parameter")
.collect(),
None => Vec::new(),
}
}
fn declares_local(statement: Node, name: &str, src: &[u8]) -> bool {
named_children(statement)
.into_iter()
.filter(|c| c.kind() == "variable_declaration")
.flat_map(named_children)
.any(|d| d.kind() == "variable_declarator" && name_is(d, name, src))
}