use sipha::red::{SyntaxElement, SyntaxNode};
use sipha::types::IntoSyntaxKind;
use leekscript_core::doc_comment::parse_comment_content;
use leekscript_core::syntax::Kind;
use leekscript_core::Type;
use super::scope::{MemberVisibility, ScopeStore, SigMeta};
use super::type_expr::{find_type_expr_child, parse_type_expr, TypeExprResult};
pub(crate) fn seed_scope_from_signatures(store: &mut ScopeStore, signature_roots: &[SyntaxNode]) {
for root in signature_roots {
let file_nodes: Vec<SyntaxNode> = if root.kind_as::<Kind>() == Some(Kind::NodeSigFile) {
vec![root.clone()]
} else {
root.children()
.filter_map(|c| match c {
SyntaxElement::Node(n) if n.kind_as::<Kind>() == Some(Kind::NodeSigFile) => {
Some(n.clone())
}
_ => None,
})
.collect()
};
for file in file_nodes {
let children: Vec<SyntaxNode> = file
.children()
.filter_map(|c| match c {
SyntaxElement::Node(n) => Some(n),
_ => None,
})
.collect();
let mut i = 0;
while i < children.len() {
let n = &children[i];
let (meta, meta_skip) = sig_meta_from_following(&children, i);
if n.kind_as::<Kind>() == Some(Kind::NodeSigGlobal) {
if let Some(name) = sig_global_name(n) {
if let Some(type_node) = find_type_expr_child(n) {
if let TypeExprResult::Ok(ty) = parse_type_expr(&type_node) {
store.add_root_global_with_type(name.clone(), ty);
} else {
store.add_root_global(name.clone());
}
} else {
store.add_root_global(name.clone());
}
if meta.doc.is_some() {
store.set_root_global_meta(name, meta);
}
}
i += 1 + meta_skip;
} else if n.kind_as::<Kind>() == Some(Kind::NodeSigFunction) {
if let Some((name, min_arity, max_arity)) = sig_function_info(n) {
let (param_types, return_type) = sig_function_types(n);
if let Some(pt) = param_types {
store.add_root_function_with_types(
name.clone(),
min_arity,
max_arity,
sipha::types::Span::new(0, 0),
Some(pt),
return_type,
);
} else {
store.add_root_function(
name.clone(),
min_arity,
max_arity,
sipha::types::Span::new(0, 0),
);
}
if meta.doc.is_some() {
store.set_root_function_meta(name, meta);
}
}
i += 1 + meta_skip;
} else if n.kind_as::<Kind>() == Some(Kind::NodeSigClass) {
if let Some(class_name) = sig_class_name(n) {
store.add_root_class(class_name.clone(), sipha::types::Span::new(0, 0));
for method_node in n.find_all_nodes(Kind::NodeSigMethod.into_syntax_kind())
{
if let (Some(method_name), Some(param_types), Some(return_type)) = (
sig_method_name(&method_node),
sig_method_param_types(&method_node),
sig_method_return_type(&method_node),
) {
let ret = return_type;
if sig_method_is_static(&method_node) {
store.add_class_static_method(
&class_name,
method_name,
param_types,
ret,
MemberVisibility::Public,
);
} else {
store.add_class_method(
&class_name,
method_name,
param_types,
ret,
MemberVisibility::Public,
);
}
}
}
for field_node in n.find_all_nodes(Kind::NodeSigField.into_syntax_kind()) {
if let (Some(field_name), Some(ty)) =
(sig_field_name(&field_node), sig_field_type(&field_node))
{
if sig_field_is_static(&field_node) {
store.add_class_static_field(
&class_name,
field_name,
ty,
MemberVisibility::Public,
);
} else {
store.add_class_field(
&class_name,
field_name,
ty,
MemberVisibility::Public,
);
}
}
}
}
i += 1;
} else {
i += 1;
}
}
}
}
}
fn sig_meta_from_following(children: &[SyntaxNode], idx: usize) -> (SigMeta, usize) {
let mut meta = SigMeta::default();
let j = idx + 1;
if j < children.len() && children[j].kind_as::<Kind>() == Some(Kind::NodeSigDocBlock) {
meta.doc = sig_doc_block_to_comment(&children[j]);
meta.complexity = meta.doc.as_ref().and_then(|d| d.complexity);
(meta, 1)
} else {
(meta, 0)
}
}
fn sig_doc_block_to_comment(node: &SyntaxNode) -> Option<leekscript_core::doc_comment::DocComment> {
let tokens: Vec<_> = node.descendant_tokens();
let mut raw = String::new();
let mut is_block = false;
for t in &tokens {
if t.kind_as::<Kind>() == Some(Kind::TokSigDocLine) {
let s = t.text();
if !raw.is_empty() {
raw.push('\n');
}
raw.push_str(s);
} else if t.kind_as::<Kind>() == Some(Kind::TokSigDocBlock) {
raw = t.text().to_string();
is_block = true;
break;
}
}
if raw.is_empty() {
return None;
}
Some(parse_comment_content(raw.trim(), is_block))
}
fn sig_global_name(node: &SyntaxNode) -> Option<String> {
let tokens: Vec<_> = node
.descendant_tokens()
.into_iter()
.filter(|t| t.kind_as::<Kind>() == Some(Kind::TokIdent))
.collect();
tokens.last().map(|t| t.text().to_string())
}
fn sig_class_name(node: &SyntaxNode) -> Option<String> {
let tokens: Vec<String> = node
.descendant_tokens()
.iter()
.filter(|t| t.kind_as::<Kind>() == Some(Kind::TokIdent))
.map(|t| t.text().to_string())
.collect();
tokens
.iter()
.find(|s| s.as_str() != "class")
.or(tokens.first())
.cloned()
}
fn sig_method_is_static(node: &SyntaxNode) -> bool {
node.descendant_tokens()
.iter()
.any(|t| t.text() == "static")
}
fn sig_method_return_type(node: &SyntaxNode) -> Option<Type> {
super::type_expr::param_and_return_types(node, Kind::NodeSigParam).1
}
fn sig_method_name(node: &SyntaxNode) -> Option<String> {
let tokens: Vec<String> = node
.descendant_tokens()
.iter()
.filter(|t| !t.is_trivia())
.map(|t| t.text().to_string())
.collect();
let lparen_idx = tokens.iter().position(|s| s == "(")?;
tokens.into_iter().nth(lparen_idx.checked_sub(1)?)
}
fn sig_method_param_types(node: &SyntaxNode) -> Option<Vec<Type>> {
super::type_expr::param_and_return_types(node, Kind::NodeSigParam).0
}
fn sig_field_is_static(node: &SyntaxNode) -> bool {
node.descendant_tokens()
.iter()
.any(|t| t.text() == "static")
}
fn sig_field_type(node: &SyntaxNode) -> Option<Type> {
let te = find_type_expr_child(node)?;
match parse_type_expr(&te) {
TypeExprResult::Ok(t) => Some(t),
TypeExprResult::Err(_) => None,
}
}
fn sig_field_name(node: &SyntaxNode) -> Option<String> {
let idents: Vec<String> = node
.descendant_tokens()
.iter()
.filter(|t| !t.is_trivia() && t.kind_as::<Kind>() == Some(Kind::TokIdent))
.map(|t| t.text().to_string())
.collect();
idents.into_iter().last()
}
fn sig_function_info(node: &SyntaxNode) -> Option<(String, usize, usize)> {
let tokens: Vec<_> = node
.descendant_tokens()
.into_iter()
.filter(|t| t.kind_as::<Kind>() == Some(Kind::TokIdent))
.collect();
let name = tokens.first()?.text().to_string();
let params: Vec<_> = node
.child_nodes()
.filter(|n| n.kind_as::<Kind>() == Some(Kind::NodeSigParam))
.collect();
let max_arity = params.len();
let min_arity = params
.iter()
.filter(|p| !p.descendant_tokens().iter().any(|t| t.text() == "?"))
.count();
Some((name, min_arity, max_arity))
}
fn sig_function_types(node: &SyntaxNode) -> (Option<Vec<Type>>, Option<Type>) {
super::type_expr::param_and_return_types(node, Kind::NodeSigParam)
}