use tree_sitter::Node;
use crate::scope_model::walk::{Backend, Spec, dispatch};
use crate::scope_model::{IntroKind, Model, ScopeKind, Write};
const TYPE_BODIES: &[&str] = &["class_body", "enum_class_body", "protocol_body"];
static SWIFT_SPEC: Spec = Spec {
skip_kinds: &["import_declaration"],
block_scoped: &[
"class_body",
"enum_class_body",
"protocol_body",
"function_declaration",
"init_declaration",
"lambda_literal",
"computed_property",
"computed_getter",
"computed_setter",
"computed_modify",
"willset_didset_block",
"catch_block",
],
function_kinds: &[],
read_kinds: &["simple_identifier"],
exclude_fields: &[],
};
pub(super) fn swift_collect(root: Node, src: &[u8]) -> Vec<crate::scope_model::Scope> {
let mut c = SwiftCollector {
src,
model: Model::rooted(),
};
dispatch(&mut c, root, 0);
c.model.scopes
}
struct SwiftCollector<'a> {
src: &'a [u8],
model: Model,
}
impl Backend for SwiftCollector<'_> {
fn spec(&self) -> &'static Spec {
&SWIFT_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() {
"property_declaration" => self.swift_bind(n, scope),
"navigation_suffix" => self.walk_children_excluding_field(n, scope, "suffix"),
"assignment" => self.walk_assignment(n, scope),
"for_statement" => {
let s = self.model.open_scope(ScopeKind::Block, scope);
self.walk_children_excluding_field(n, s, "item");
}
_ => self.walk_children(n, scope),
}
}
}
impl SwiftCollector<'_> {
fn swift_bind(&mut self, n: Node, scope: usize) {
if !is_type_member(n) {
if let Some(name) = n
.child_by_field_name("name")
.and_then(|p| p.named_children(&mut p.walk()).next())
{
self.bind_var(
name,
scope,
Write::assign(
name.start_byte(),
name.id(),
n.child_by_field_name("value").map(|v| v.id()),
),
IntroKind::Assign,
);
}
}
self.walk_children_excluding_field(n, scope, "name");
}
fn walk_assignment(&mut self, n: Node, scope: usize) {
let left = n
.child_by_field_name("left")
.or_else(|| n.child_by_field_name("target"));
let right = n
.child_by_field_name("right")
.or_else(|| n.child_by_field_name("result"));
let plain = n
.child_by_field_name("operator")
.map_or(false, |o| self.text_of(o) == "=");
if let Some(left) = left {
if left.kind() == "simple_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);
}
}
}
fn is_type_member(n: Node) -> bool {
n.parent()
.is_some_and(|p| TYPE_BODIES.iter().any(|k| p.kind() == *k))
}