use tree_sitter::Node;
use super::ScopeKind;
pub use super::backend::Backend;
pub struct Spec {
pub skip_kinds: &'static [&'static str],
pub block_scoped: &'static [&'static str],
pub function_kinds: &'static [&'static str],
pub read_kinds: &'static [&'static str],
pub exclude_fields: &'static [(&'static str, &'static str)],
}
pub fn dispatch(b: &mut impl Backend, n: Node, scope: usize) -> bool {
let kind = n.kind();
let spec = b.spec();
if spec.skip_kinds.contains(&kind) {
return true;
}
if spec.read_kinds.contains(&kind) {
record_read(b, n, scope);
return true;
}
if walk_excluding_field_slot(b, n, scope, kind, spec) {
return true;
}
match boundary_kind(spec, &kind) {
Some(boundary) => {
let s = b.model().open_scope(boundary, scope);
custom_children(b, n, s);
}
None => b.custom(n, scope),
}
true
}
fn record_read(b: &mut impl Backend, n: Node, scope: usize) {
let name = b.text_of(n).to_string();
b.model().record_read(scope, &name, n.start_byte());
}
fn walk_excluding_field_slot(
b: &mut impl Backend,
n: Node,
scope: usize,
kind: &str,
spec: &Spec,
) -> bool {
let Some((_, field)) = spec.exclude_fields.iter().find(|(k, _)| *k == kind) else {
return false;
};
let Some(excluded) = n.child_by_field_name(field).map(|c| c.id()) else {
return false;
};
let mut cursor = n.walk();
for child in n.children(&mut cursor) {
if child.id() != excluded {
b.custom(child, scope);
}
}
true
}
fn boundary_kind(spec: &Spec, kind: &str) -> Option<ScopeKind> {
if spec.block_scoped.contains(&kind) {
Some(ScopeKind::Block)
} else if spec.function_kinds.contains(&kind) {
Some(ScopeKind::Function)
} else {
None
}
}
fn custom_children(b: &mut impl Backend, n: Node, scope: usize) {
let mut cursor = n.walk();
for child in n.children(&mut cursor) {
b.custom(child, scope);
}
}