use tree_sitter::Node;
use crate::scope_model::walk::{Backend, Spec, dispatch};
use crate::scope_model::{IntroKind, Model, Write};
static SWIFT_SPEC: Spec = Spec {
skip_kinds: &["import_declaration"],
block_scoped: &[
"statement_block",
"class_body",
"struct_body",
"enum_case_block",
"function_declaration",
"init_declaration",
"closure_expression",
],
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),
_ => self.walk_children(n, scope),
}
}
}
impl SwiftCollector<'_> {
fn swift_bind(&mut self, n: Node, scope: usize) {
let name = n
.child_by_field_name("name")
.and_then(|p| p.named_children(&mut p.walk()).next());
let Some(name) = name else {
return;
};
self.bind_var(
name,
scope,
Write::assign(
name.start_byte(),
name.id(),
n.child_by_field_name("value").map(|v| v.id()),
),
IntroKind::Assign,
);
if let Some(value) = n.child_by_field_name("value") {
dispatch(self, value, scope);
}
}
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);
}
}
}