1use tree_sitter::{Node, Tree};
7
8#[derive(Debug, Default, Clone)]
9pub struct SymbolIndex {
10 pub defs: Vec<(String, usize, usize)>,
12 pub refs: Vec<(String, usize, usize)>,
13}
14
15impl SymbolIndex {
16 pub fn from_tree(tree: &Tree, source: &[u8]) -> Self {
17 let mut idx = SymbolIndex::default();
18 walk(tree.root_node(), source, &mut idx);
19 idx
20 }
21
22 pub fn first_definition(&self, name: &str) -> Option<(usize, usize)> {
24 self.defs
25 .iter()
26 .find(|(n, _, _)| n == name)
27 .map(|(_, s, e)| (*s, *e))
28 }
29
30 pub fn all_references(&self, name: &str) -> Vec<(usize, usize)> {
31 self.refs
32 .iter()
33 .filter(|(n, _, _)| n == name)
34 .map(|(_, s, e)| (*s, *e))
35 .collect()
36 }
37
38 pub fn reference_at(&self, byte_offset: usize) -> Option<&str> {
39 self.refs
40 .iter()
41 .find(|(_, s, e)| *s <= byte_offset && byte_offset < *e)
42 .map(|(n, _, _)| n.as_str())
43 }
44
45 pub fn definition_at(&self, byte_offset: usize) -> Option<&str> {
46 self.defs
47 .iter()
48 .find(|(_, s, e)| *s <= byte_offset && byte_offset < *e)
49 .map(|(n, _, _)| n.as_str())
50 }
51}
52
53fn walk(node: Node, source: &[u8], idx: &mut SymbolIndex) {
54 match node.kind() {
55 "pattern_reference" => {
56 if let Some(id) = node.child_by_field_name("identifier") {
57 if let Ok(text) = id.utf8_text(source) {
58 idx
59 .refs
60 .push((text.to_string(), id.start_byte(), id.end_byte()));
61 }
62 }
63 }
64 "node_pattern" => {
65 if let Some(id) = node.child_by_field_name("identifier") {
66 if let Ok(text) = id.utf8_text(source) {
67 let has_labels = node.child_by_field_name("labels").is_some();
68 let has_record = node.child_by_field_name("record").is_some();
69 if has_labels || has_record {
70 idx.defs
71 .push((text.to_string(), id.start_byte(), id.end_byte()));
72 } else if relationship_parent_field(node).is_some() {
73 idx.refs
75 .push((text.to_string(), id.start_byte(), id.end_byte()));
76 }
77 }
78 }
79 }
80 "subject_pattern" => {
81 if let Some(id) = node.child_by_field_name("identifier") {
82 if let Ok(text) = id.utf8_text(source) {
83 let has_labels = node.child_by_field_name("labels").is_some();
84 let has_record = node.child_by_field_name("record").is_some();
85 if has_labels || has_record {
86 idx.defs
87 .push((text.to_string(), id.start_byte(), id.end_byte()));
88 }
89 }
90 }
91 }
92 _ => {}
93 }
94 let mut c = node.walk();
95 for child in node.children(&mut c) {
96 walk(child, source, idx);
97 }
98}
99
100fn relationship_parent_field(node: Node) -> Option<&'static str> {
102 let parent = node.parent()?;
103 if parent.kind() != "relationship_pattern" {
104 return None;
105 }
106 for field in ["left", "right"] {
107 if let Some(ch) = parent.child_by_field_name(field) {
108 if ch.id() == node.id() {
109 return Some(field);
110 }
111 }
112 }
113 None
114}
115