use tree_sitter::Node;
use super::super::{IntroKind, SKIP_KINDS, ScopeKind};
use super::Collector;
impl Collector<'_> {
pub(super) fn walk(&mut self, n: Node, scope: usize) {
let kind = n.kind();
if SKIP_KINDS.contains(&kind) {
return;
}
match kind {
"function_definition" | "class_definition" | "lambda" => {
self.nested_scope(n, scope, kind)
}
"assignment" | "augmented_assignment" | "named_expression" => self.write_head(n, scope),
"as_pattern" | "case_clause" | "for_statement" | "for_in_clause" => {
self.protocol_head(n, scope)
}
"keyword_argument" | "attribute" => self.operand_only(n, scope),
"identifier" => self.read_identifier(n, scope),
_ => self.walk_children(n, scope),
}
}
fn nested_scope(&mut self, n: Node, parent: usize, kind: &str) {
let (kind, skip_field) = match kind {
"function_definition" => (ScopeKind::Function, "name"),
"class_definition" => (ScopeKind::Class, "name"),
_ => (ScopeKind::Block, "parameters"),
};
let s = self.open_scope(kind, parent);
self.walk_except(n, s, skip_field);
}
fn write_head(&mut self, n: Node, scope: usize) {
match n.kind() {
"assignment" => self.walk_assignment(n, scope),
"augmented_assignment" => self.walk_augmented(n, scope),
_ => self.walk_named_expression(n, scope),
}
}
fn protocol_head(&mut self, n: Node, scope: usize) {
match n.kind() {
"as_pattern" => self.walk_as_pattern(n, scope),
"case_clause" => self.walk_case_clause(n, scope),
_ => self.walk_except(n, scope, "left"),
}
}
fn operand_only(&mut self, n: Node, scope: usize) {
let field = match n.kind() {
"attribute" => "object",
_ => "value",
};
if let Some(part) = n.child_by_field_name(field) {
self.walk(part, scope);
}
}
fn read_identifier(&mut self, n: Node, scope: usize) {
self.record_read(scope, &self.text(n).to_string(), n.start_byte());
}
pub(super) fn walk_children(&mut self, n: Node, scope: usize) {
for child in n.children(&mut n.walk()) {
self.walk(child, scope);
}
}
fn walk_except(&mut self, n: Node, scope: usize, skip_field: &str) {
let skipped = n.child_by_field_name(skip_field).map(|s| s.id());
for child in n.children(&mut n.walk()) {
if Some(child.id()) == skipped {
continue;
}
self.walk(child, scope);
}
}
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");
if let Some(left) = left {
if left.kind() == "identifier" {
self.bind_name(left, scope, IntroKind::Assign, right.map(|r| r.id()));
} else {
self.bind_targets(left, scope);
}
}
if let Some(right) = right {
self.walk(right, scope);
}
}
fn walk_augmented(&mut self, n: Node, scope: usize) {
let left = n.child_by_field_name("left");
let right = n.child_by_field_name("right");
if let Some(left) = left {
if left.kind() == "identifier" {
let name = self.text(left).to_string();
self.bind_name(left, scope, IntroKind::Binding, None);
self.record_read(scope, &name, left.start_byte() + 1);
} else {
self.walk_children(left, scope);
}
}
if let Some(right) = right {
self.walk(right, scope);
}
}
fn walk_named_expression(&mut self, n: Node, scope: usize) {
if let (Some(name_node), Some(value)) = (
n.child_by_field_name("name"),
n.child_by_field_name("value"),
) {
self.bind_name(name_node, scope, IntroKind::Assign, Some(value.id()));
self.walk(value, scope);
}
}
fn walk_as_pattern(&mut self, n: Node, scope: usize) {
let children: Vec<_> = n.children(&mut n.walk()).collect();
let mut after_as = false;
for child in children {
if child.kind() == "as" {
after_as = true;
} else if after_as {
self.bind_alias(child, scope);
} else {
self.walk(child, scope);
}
}
}
fn walk_case_clause(&mut self, n: Node, scope: usize) {
let children: Vec<_> = n.children(&mut n.walk()).collect();
for child in children {
if child.kind() == "case_pattern" {
self.bind_captures(child, scope);
} else {
self.walk(child, scope);
}
}
}
}