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