use tree_sitter::Node;
use crate::scope_model::walk::{Backend, Spec, dispatch};
use crate::scope_model::{IntroKind, Model, ScopeKind, Write, child_of_kind};
static SPEC: Spec = Spec {
skip_kinds: &[
"parameter_list",
"field_declaration",
"using_directive",
"namespace_declaration",
"file_scoped_namespace_declaration",
"qualified_name",
],
block_scoped: &[
"block",
"lambda_expression",
"anonymous_method_expression",
"switch_body",
],
function_kinds: &[
"method_declaration",
"constructor_declaration",
"accessor_declaration",
],
read_kinds: &["identifier"],
exclude_fields: &[("member_access_expression", "name")],
};
pub(super) fn collect(root: Node, src: &[u8]) -> Vec<crate::scope_model::Scope> {
let mut c = Collector {
src,
model: Model::rooted(),
};
dispatch(&mut c, root, 0);
c.model.scopes
}
struct Collector<'a> {
src: &'a [u8],
model: Model,
}
impl Backend for Collector<'_> {
fn spec(&self) -> &'static Spec {
&SPEC
}
fn model(&mut self) -> &mut Model {
&mut self.model
}
fn text_of(&self, n: Node) -> &str {
n.utf8_text(self.src).unwrap_or("")
}
fn custom(&mut self, n: Node, scope: usize) {
match n.kind() {
"variable_declaration" => self.bind_variable_declarations(n, scope),
"assignment_expression" => self.walk_assignment(n, scope),
"declaration_expression" => {
if let Some(d) = n.child_by_field_name("name") {
self.bind_var(
d,
scope,
Write::rewrite(d.start_byte(), d.id()),
IntroKind::Binding,
);
}
}
"foreach_statement" => {
let s = self.model.open_scope(ScopeKind::Block, scope);
self.walk_children_excluding_field(n, s, "left");
}
"catch_clause" => self.bind_catch_clause(n, scope),
_ => self.walk_children(n, scope),
}
}
}
impl Collector<'_> {
fn bind_variable_declarations(&mut self, n: Node, scope: usize) {
let mut cursor = n.walk();
for child in n.children(&mut cursor) {
if child.kind() == "variable_declarator" {
self.bind_declarator_with_rhs_field(child, scope);
} else {
dispatch(self, child, scope);
}
}
}
fn bind_catch_clause(&mut self, n: Node, scope: usize) {
let s = self.model.open_scope(ScopeKind::Block, scope);
let decl = child_of_kind(n, "catch_declaration");
if let Some(decl) = decl
&& let Some(name) = decl.child_by_field_name("name")
{
self.bind_var(
name,
s,
Write::rewrite(name.start_byte(), name.id()),
IntroKind::Binding,
);
}
let skipped = decl.map(|d| d.id());
let mut cursor = n.walk();
for child in n.children(&mut cursor) {
if Some(child.id()) != skipped {
dispatch(self, child, s);
}
}
}
fn walk_assignment(&mut self, n: Node, scope: usize) {
let left = n.child_by_field_name("left");
let right = n.child_by_field_name("right");
let plain = n
.child_by_field_name("operator")
.and_then(|o| o.utf8_text(self.src).ok())
== Some("=");
if let Some(left) = left {
if left.kind() == "identifier" {
self.rebind_local(left, scope, plain, right.map(|r| r.id()));
} else {
self.walk_children(left, scope);
}
}
if let Some(right) = right {
dispatch(self, right, scope);
}
}
}