use tree_sitter::Node;
use crate::core::signatures::{Signature, compact_params};
use super::super::helpers::strip_parens;
pub(crate) fn lua_function(node: &Node, name: &str, source: &[u8]) -> Signature {
let is_method = node
.child_by_field_name("name")
.is_some_and(|n| n.kind() == "method_index_expression");
Signature {
kind: if is_method { "method" } else { "fn" },
name: name.to_string(),
params: compact_params(&strip_parens(¶ms_text(node, source))),
return_type: luau_return_type(node, source),
is_async: false,
is_exported: !is_local_decl(node, source),
indent: 0,
..Signature::no_span()
}
}
pub(crate) fn lua_assigned_function(node: &Node, name: &str, source: &[u8]) -> Signature {
let params = find_function_definition(node)
.map(|f| compact_params(&strip_parens(¶ms_text(&f, source))))
.unwrap_or_default();
Signature {
kind: "fn",
name: name.to_string(),
params,
return_type: String::new(),
is_async: false,
is_exported: !is_local_decl(node, source),
indent: 0,
..Signature::no_span()
}
}
pub(crate) fn luau_type(node: &Node, name: &str, source: &[u8]) -> Signature {
Signature {
kind: "type",
name: name.to_string(),
params: String::new(),
return_type: String::new(),
is_async: false,
is_exported: has_leading_keyword(node, source, "export"),
indent: 0,
..Signature::no_span()
}
}
fn params_text(node: &Node, source: &[u8]) -> String {
node.child_by_field_name("parameters")
.and_then(|p| p.utf8_text(source).ok())
.unwrap_or("")
.to_string()
}
fn luau_return_type(node: &Node, source: &[u8]) -> String {
let Some(params_end) = node.child_by_field_name("parameters").map(|p| p.end_byte()) else {
return String::new();
};
let mut cursor = node.walk();
for child in node.children(&mut cursor) {
if child.start_byte() >= params_end
&& child.kind() != "block"
&& (child.kind() == "type" || child.kind().ends_with("_type"))
&& let Ok(t) = child.utf8_text(source)
{
return t.trim().to_string();
}
}
String::new()
}
fn find_function_definition<'a>(node: &Node<'a>) -> Option<Node<'a>> {
let mut stack = vec![*node];
while let Some(n) = stack.pop() {
if n.kind() == "function_definition" {
return Some(n);
}
let mut cursor = n.walk();
for child in n.children(&mut cursor) {
stack.push(child);
}
}
None
}
fn is_local_decl(node: &Node, source: &[u8]) -> bool {
has_leading_keyword(node, source, "local")
}
fn has_leading_keyword(node: &Node, source: &[u8], keyword: &str) -> bool {
if node
.utf8_text(source)
.is_ok_and(|t| t.trim_start().starts_with(keyword))
{
return true;
}
std::str::from_utf8(&source[..node.start_byte()])
.ok()
.and_then(|p| {
p.trim_end()
.rsplit(|c: char| !(c.is_alphanumeric() || c == '_'))
.next()
.map(|w| w == keyword)
})
.unwrap_or(false)
}