use crate::{
project::syntax::Syntax,
syntax::{
cst::{
kind::TreeKind, tree::{NodeHandle, NodeRef}, view::{self, Ident, View}
},
location::{OriginTable, Pos, Span}, parser::token::Tt,
},
util::data::either::Either,
};
pub fn node_is_ref(node: NodeRef) -> bool {
node.is_tt(Tt::Ident)
}
pub fn node_is_def(node: NodeRef) -> bool {
let Some(kind) = node.open_kind() else {
return false;
};
use TreeKind::*;
match kind {
#[rustfmt::skip]
Package | StmtFunction | FunctionParam | StmtInterface | StmtModule |
TypedefEnum | TypedefStruct | TypedefUnion | TypedefAlias | ModuleParam |
ModulePortParam | TypedefParam | StmtMethod | StmtTypeclass => true,
VarInit => {
let init = view::VarInit::cast(node);
init.target().and_then(|t| t.left()).is_some()
}
DeclPattern => {
let pat = view::DeclPattern::cast(node);
pat.name().and_then(Either::right).is_some()
}
_ => false,
}
}
pub fn node_is_implicit_def(node: NodeRef) -> Option<Ident> {
use view::*;
match node.open_kind()? {
TreeKind::TypeVar => TypeVar::cast(node).name(),
_ => None,
}
}
pub fn selection_span(node: NodeRef, ot: &OriginTable) -> Option<Span> {
use view::*;
let Some(kind) = node.open_kind() else {
return node.span(ot);
};
match kind {
TreeKind::Package => Package::cast(node)
.name()
.and_then(|n| n.syntax().span(ot))
.or_else(|| node.span(ot).map(|s| s.empty_at_start())),
TreeKind::FunctionParam => FunctionParam::cast(node)
.typed_name()
.and_then(|n| n.name())
.and_then(|n| n.syntax().span(ot)),
TreeKind::StmtFunction => StmtFunction::cast(node)
.typed_name()
.and_then(|tn| tn.name())
.and_then(|n| n.syntax().span(ot)),
TreeKind::StmtMethod => StmtMethod::cast(node)
.typed_name()
.and_then(|tn| tn.name())
.and_then(|n| n.syntax().span(ot)),
TreeKind::StmtInterface => StmtInterface::cast(node)
.name()
.and_then(|n| n.syntax().span(ot)),
TreeKind::StmtModule => StmtModule::cast(node)
.name()
.and_then(|n| n.syntax().span(ot)),
TreeKind::ModuleParam => ModuleParam::cast(node)
.typed_name()
.and_then(|n| n.name())
.and_then(|n| n.syntax().span(ot)),
TreeKind::ModulePortParam => ModulePortParam::cast(node)
.name()
.and_then(|n| n.syntax().span(ot)),
TreeKind::StmtTypeclass => StmtTypeclass::cast(node)
.name()
.and_then(|n| n.syntax().span(ot)),
TreeKind::VarInit => VarInit::cast(node)
.target()
.and_then(Either::left)
.and_then(|n| n.syntax().span(ot)),
TreeKind::TypedefEnum => TypedefEnum::cast(node)
.name()
.and_then(|n| n.syntax().span(ot)),
TreeKind::TypedefStruct => TypedefStruct::cast(node)
.name()
.and_then(|n| n.syntax().span(ot)),
TreeKind::TypedefUnion => TypedefUnion::cast(node)
.name()
.and_then(|n| n.syntax().span(ot)),
TreeKind::TypedefAlias => TypedefAlias::cast(node)
.name()
.and_then(|n| n.syntax().span(ot)),
TreeKind::TypedefParam => TypedefParam::cast(node)
.name()
.and_then(|n| n.syntax().span(ot)),
_ => node.span(ot),
}
}
pub fn node_of_interest(syntax: &Syntax, pos: Pos) -> Option<NodeHandle> {
fn walk(syntax: &Syntax, pos: &Pos, node: NodeRef) -> Option<NodeHandle> {
let ot = &syntax.ctx.origins;
if !node
.span(ot)
.and_then(|s| s.contains(pos, ot))
.unwrap_or(false)
{
return None;
}
if selection_span(node, ot)
.and_then(|s| s.contains(pos, ot))
.unwrap_or(false)
&& (node_is_ref(node) || node_is_def(node) || node_is_implicit_def(node).is_some())
{
return Some(node.handle());
}
node.children().find_map(|c| walk(syntax, pos, c))
}
walk(syntax, &pos, syntax.cst.root())
}