1use lanekeep_query::CompiledQuery;
29use std::collections::HashMap;
30
31use lanekeep_lang::binding::{Binding, BindingResolver};
32use tree_sitter::{Node, Tree};
33
34pub type Handle = u32;
36
37fn child_indices(node: Node<'_>) -> std::ops::Range<u32> {
42 0..u32::try_from(node.child_count()).unwrap_or(u32::MAX)
43}
44
45#[derive(Debug)]
47pub struct NodeArena {
48 tree: Tree,
49 source: String,
50 paths: Vec<Vec<u32>>,
52 by_id: HashMap<usize, Handle>,
54}
55
56impl NodeArena {
57 #[must_use]
61 pub fn new(tree: Tree, source: String) -> Self {
62 let root_id = tree.root_node().id();
63 let mut arena = Self {
64 tree,
65 source,
66 paths: Vec::new(),
67 by_id: HashMap::new(),
68 };
69 arena.paths.push(Vec::new());
70 arena.by_id.insert(root_id, 0);
71 arena
72 }
73
74 pub const ROOT: Handle = 0;
78
79 #[must_use]
81 pub fn source(&self) -> &str {
82 &self.source
83 }
84
85 #[must_use]
88 pub fn len(&self) -> usize {
89 self.paths.len()
90 }
91
92 #[must_use]
94 pub fn is_empty(&self) -> bool {
95 self.paths.len() <= 1
96 }
97
98 fn node_at(&self, path: &[u32]) -> Option<Node<'_>> {
100 let mut node = self.tree.root_node();
101 for index in path {
102 node = node.child(*index)?;
103 }
104 Some(node)
105 }
106
107 fn node(&self, handle: Handle) -> Option<Node<'_>> {
109 let path = self.paths.get(handle as usize)?;
110 self.node_at(path)
111 }
112
113 fn intern(&mut self, id: usize, path: Vec<u32>) -> Handle {
115 if let Some(existing) = self.by_id.get(&id) {
116 return *existing;
117 }
118 let handle = Handle::try_from(self.paths.len()).unwrap_or(Handle::MAX);
120 self.paths.push(path);
121 self.by_id.insert(id, handle);
122 handle
123 }
124
125 fn intern_child(&mut self, parent_path: &[u32], index: u32) -> Option<Handle> {
130 let mut path = parent_path.to_vec();
131 path.push(index);
132 let id = self.node_at(&path)?.id();
133 Some(self.intern(id, path))
134 }
135
136 #[must_use]
138 pub fn kind(&self, handle: Handle) -> Option<&'static str> {
139 self.node(handle).map(|node| node.kind())
140 }
141
142 #[must_use]
144 pub fn is_named(&self, handle: Handle) -> Option<bool> {
145 self.node(handle).map(|node| node.is_named())
146 }
147
148 #[must_use]
150 pub fn text(&self, handle: Handle) -> Option<&str> {
151 let node = self.node(handle)?;
152 self.source.get(node.byte_range())
153 }
154
155 #[must_use]
157 pub fn position(&self, handle: Handle) -> Option<(u32, u32)> {
158 let node = self.node(handle)?;
159 let start = node.start_position();
160 Some((
161 u32::try_from(start.row)
162 .unwrap_or(u32::MAX)
163 .saturating_add(1),
164 u32::try_from(start.column)
165 .unwrap_or(u32::MAX)
166 .saturating_add(1),
167 ))
168 }
169
170 #[must_use]
172 pub fn byte_range(&self, handle: Handle) -> Option<(usize, usize)> {
173 self.node(handle)
174 .map(|node| (node.start_byte(), node.end_byte()))
175 }
176
177 #[must_use]
182 pub fn resolve_binding(
183 &self,
184 handle: Handle,
185 resolver: &dyn BindingResolver,
186 ) -> Option<Binding> {
187 let node = self.node(handle)?;
188 resolver.resolve(&self.tree, &self.source, node)
189 }
190
191 #[must_use]
193 pub fn is_shadowed(&self, handle: Handle, resolver: &dyn BindingResolver) -> bool {
194 self.node(handle)
195 .is_some_and(|node| resolver.is_shadowed(&self.tree, &self.source, node))
196 }
197
198 #[must_use]
206 pub const fn tree(&self) -> &Tree {
207 &self.tree
208 }
209
210 #[must_use]
216 pub fn path_of(&self, node: Node<'_>) -> Option<Vec<u32>> {
217 let mut path = Vec::new();
219 let mut current = node;
220 while let Some(parent) = current.parent() {
221 let index = child_indices(parent)
222 .find(|i| parent.child(*i).is_some_and(|c| c.id() == current.id()))?;
223 path.push(index);
224 current = parent;
225 }
226 path.reverse();
227
228 if self
229 .node_at(&path)
230 .is_none_or(|found| found.id() != node.id())
231 {
232 return None;
233 }
234 Some(path)
235 }
236
237 pub fn intern_path(&mut self, path: Vec<u32>) -> Option<Handle> {
239 let id = self.node_at(&path)?.id();
240 Some(self.intern(id, path))
241 }
242
243 pub fn parent(&mut self, handle: Handle) -> Option<Handle> {
245 let path = self.paths.get(handle as usize)?.clone();
246 if path.is_empty() {
247 return None;
248 }
249
250 let parent_path = path[..path.len() - 1].to_vec();
251 let id = self.node_at(&parent_path)?.id();
252 Some(self.intern(id, parent_path))
253 }
254
255 pub fn children(&mut self, handle: Handle) -> Vec<Handle> {
257 self.children_matching(handle, false)
258 }
259
260 pub fn named_children(&mut self, handle: Handle) -> Vec<Handle> {
262 self.children_matching(handle, true)
263 }
264
265 fn children_matching(&mut self, handle: Handle, named_only: bool) -> Vec<Handle> {
266 let Some(path) = self.paths.get(handle as usize).cloned() else {
267 return Vec::new();
268 };
269
270 let indices: Vec<u32> = {
271 let Some(node) = self.node_at(&path) else {
272 return Vec::new();
273 };
274 child_indices(node)
275 .filter(|i| !named_only || node.child(*i).is_some_and(|c| c.is_named()))
276 .collect()
277 };
278
279 indices
280 .into_iter()
281 .filter_map(|i| self.intern_child(&path, i))
282 .collect()
283 }
284
285 #[must_use]
291 pub fn query_subtree(
292 &self,
293 handle: Handle,
294 query: &CompiledQuery,
295 ) -> Vec<Vec<(String, Vec<u32>)>> {
296 let Some(path) = self.paths.get(handle as usize) else {
297 return Vec::new();
298 };
299 let Some(node) = self.node_at(path) else {
300 return Vec::new();
301 };
302
303 let mut found = Vec::new();
304 query.for_each_match_in(node, self.source.as_bytes(), |m| {
305 found.push(
306 m.captures
307 .iter()
308 .filter_map(|(name, node)| {
309 self.path_of(*node).map(|path| ((*name).to_owned(), path))
310 })
311 .collect::<Vec<_>>(),
312 );
313 });
314 found
315 }
316
317 #[must_use]
324 pub fn closest_ancestor_paths(
325 &self,
326 handle: Handle,
327 query: &CompiledQuery,
328 ) -> Option<Vec<(String, Vec<u32>)>> {
329 let path = self.paths.get(handle as usize)?.clone();
330
331 for depth in (0..path.len()).rev() {
333 let Some(ancestor) = self.node_at(&path[..depth]) else {
334 continue;
335 };
336
337 let mut matched: Option<Vec<(String, Vec<u32>)>> = None;
338 query.for_each_match_in(ancestor, self.source.as_bytes(), |m| {
339 if matched.is_some() || !m.captures.iter().any(|(_, node)| *node == ancestor) {
340 return;
341 }
342 matched = Some(
343 m.captures
344 .iter()
345 .filter_map(|(name, node)| {
346 self.path_of(*node).map(|p| ((*name).to_owned(), p))
347 })
348 .collect(),
349 );
350 });
351
352 if matched.is_some() {
353 return matched;
354 }
355 }
356 None
357 }
358
359 pub fn ancestors(&mut self, handle: Handle) -> Vec<Handle> {
361 let Some(path) = self.paths.get(handle as usize).cloned() else {
362 return Vec::new();
363 };
364
365 let mut out = Vec::with_capacity(path.len());
366 for depth in (0..path.len()).rev() {
367 let ancestor_path = path[..depth].to_vec();
368 let Some(id) = self.node_at(&ancestor_path).map(|n| n.id()) else {
369 break;
370 };
371 out.push(self.intern(id, ancestor_path));
372 }
373 out
374 }
375}
376
377#[cfg(test)]
378mod tests {
379 use lanekeep_lang::Language;
380 use lanekeep_lang_js::TypeScript;
381
382 use super::*;
383
384 fn arena(source: &str) -> NodeArena {
385 let mut parser = tree_sitter::Parser::new();
386 parser
387 .set_language(&TypeScript.grammar())
388 .expect("grammar loads");
389 let tree = parser.parse(source, None).expect("parses");
390 NodeArena::new(tree, source.to_owned())
391 }
392
393 #[test]
394 fn the_root_is_always_handle_zero() {
395 let arena = arena("const x = 1;");
396 assert_eq!(NodeArena::ROOT, 0);
397 assert_eq!(arena.kind(0), Some("program"));
398 }
399
400 #[test]
401 fn resolves_kind_text_and_position() {
402 let mut arena = arena("const x = 1;\nconst y = 2;");
403 let statements = arena.named_children(NodeArena::ROOT);
404 assert_eq!(statements.len(), 2);
405
406 assert_eq!(arena.kind(statements[0]), Some("lexical_declaration"));
407 assert_eq!(arena.text(statements[0]), Some("const x = 1;"));
408 assert_eq!(arena.position(statements[0]), Some((1, 1)));
409 assert_eq!(arena.position(statements[1]), Some((2, 1)));
410 }
411
412 #[test]
413 fn walks_down_and_back_up() {
414 let mut arena = arena("const x = 1;");
415 let root = NodeArena::ROOT;
416 let declaration = arena.named_children(root)[0];
417 let declarator = arena.named_children(declaration)[0];
418
419 assert_eq!(arena.parent(declarator), Some(declaration));
420 assert_eq!(arena.parent(declaration), Some(root));
421 assert_eq!(arena.parent(root), None, "the root has no parent");
422 }
423
424 #[test]
425 fn handles_are_stable_for_the_same_node() {
426 let mut arena = arena("const x = 1;");
430 let root = NodeArena::ROOT;
431 let declaration = arena.named_children(root)[0];
432
433 let again = arena.named_children(root)[0];
434 assert_eq!(
435 declaration, again,
436 "the same child must intern to the same handle"
437 );
438
439 let declarator = arena.named_children(declaration)[0];
440 assert_eq!(
441 arena.parent(declarator),
442 Some(declaration),
443 "reaching a node from below must give the handle it already had"
444 );
445 }
446
447 #[test]
448 fn interning_is_lazy() {
449 let arena = arena("const a = 1; const b = 2; function c() { return [1,2,3] }");
452 assert!(
453 arena.is_empty(),
454 "only the root should be interned before any traversal"
455 );
456 assert_eq!(arena.len(), 1);
457 }
458
459 #[test]
460 fn only_touched_nodes_are_interned() {
461 let mut arena = arena("const a = 1; const b = 2; const c = 3;");
462 let before = arena.len();
463 let _ = arena.named_children(NodeArena::ROOT);
464 let after = arena.len();
465
466 assert!(after > before);
467 assert!(
468 after < 20,
469 "should intern three statements, not the whole tree: {after}"
470 );
471 }
472
473 #[test]
474 fn named_children_excludes_anonymous_tokens() {
475 let mut arena = arena("const x = 1;");
476 let declaration = arena.named_children(NodeArena::ROOT)[0];
477
478 let all = arena.children(declaration);
479 let named = arena.named_children(declaration);
480 assert!(all.len() > named.len(), "`const` and `;` are anonymous");
481 assert!(named.iter().all(|h| arena.is_named(*h) == Some(true)));
482 }
483
484 #[test]
485 fn ancestors_run_innermost_first_and_end_at_the_root() {
486 let mut arena = arena("function f() { return 1; }");
487 let root = NodeArena::ROOT;
488 let function = arena.named_children(root)[0];
489 let body = arena
490 .named_children(function)
491 .last()
492 .copied()
493 .expect("has a body");
494 let statement = arena.named_children(body)[0];
495
496 let ancestors = arena.ancestors(statement);
497 assert_eq!(ancestors.first(), Some(&body), "innermost first");
498 assert_eq!(ancestors.last(), Some(&root), "ending at the root");
499 assert!(ancestors.contains(&function));
500 }
501
502 #[test]
503 fn the_root_has_no_ancestors() {
504 let mut arena = arena("const x = 1;");
505 assert!(arena.ancestors(NodeArena::ROOT).is_empty());
506 }
507
508 #[test]
509 fn an_unknown_handle_yields_nothing_rather_than_panicking() {
510 let mut arena = arena("const x = 1;");
512 assert_eq!(arena.kind(9999), None);
513 assert_eq!(arena.text(9999), None);
514 assert_eq!(arena.position(9999), None);
515 assert_eq!(arena.parent(9999), None);
516 assert!(arena.children(9999).is_empty());
517 assert!(arena.ancestors(9999).is_empty());
518 }
519
520 #[test]
521 fn interns_a_node_reached_through_the_tree() {
522 let mut arena = arena("const x = 1;");
525
526 let (path, expected_kind) = {
527 let target = arena
528 .tree()
529 .root_node()
530 .child(0)
531 .and_then(|n| n.child(1))
532 .expect("has a declarator");
533 (arena.path_of(target).expect("has a path"), target.kind())
534 };
535
536 let handle = arena.intern_path(path.clone()).expect("interns");
537 assert_eq!(arena.kind(handle), Some(expected_kind));
538 assert_eq!(
539 arena.intern_path(path),
540 Some(handle),
541 "interning the same path twice must give the same handle"
542 );
543 }
544
545 #[test]
546 fn rejects_a_node_from_a_different_tree() {
547 let mut parser = tree_sitter::Parser::new();
550 parser
551 .set_language(&TypeScript.grammar())
552 .expect("grammar loads");
553 let other = parser
554 .parse("function totallyDifferent() { return 42 }", None)
555 .expect("parses");
556 let foreign = other.root_node().child(0).expect("has a child");
557
558 let arena = arena("const x = 1;");
559 assert_eq!(
560 arena.path_of(foreign),
561 None,
562 "a node from another tree must not be reducible to a path here"
563 );
564 }
565
566 #[test]
567 fn text_is_correct_for_multibyte_source() {
568 let mut arena = arena("const emoji = '🎯';\nconst after = 1;");
569 let statements = arena.named_children(NodeArena::ROOT);
570 assert_eq!(arena.text(statements[0]), Some("const emoji = '🎯';"));
571 assert_eq!(
572 arena.position(statements[1]),
573 Some((2, 1)),
574 "a multibyte character must not shift the following line"
575 );
576 }
577}