oak_core/tree/
cursor.rs

1//! efficient Cursor for GreenNode traversal
2//!
3//! This module provides a cursor implementation for traversing GreenTrees
4//! in an Arena-based memory model. It tracks absolute offsets and allows
5//! efficient navigation (up, down, next sibling) without recursion.
6
7use crate::{
8    Language,
9    tree::{GreenNode, GreenTree},
10};
11
12/// A cursor for traversing a GreenTree.
13///
14/// It maintains a path from the root to the current node, allowing for
15/// `parent()` navigation and offset tracking.
16#[derive(Debug, Clone)]
17pub struct Cursor<'a, L: Language> {
18    /// The current element (Node or Leaf).
19    current: GreenTree<'a, L>,
20    /// The absolute start offset of the current element.
21    offset: usize,
22    /// The stack of parent nodes and the index of the current node within them.
23    /// Format: (Parent Node, Index in Parent, Parent Start Offset)
24    stack: Vec<(&'a GreenNode<'a, L>, usize, usize)>,
25}
26
27impl<'a, L: Language> Cursor<'a, L> {
28    /// Creates a new cursor starting at the given root node.
29    pub fn new(root: &'a GreenNode<'a, L>) -> Self {
30        Self { current: GreenTree::Node(root), offset: 0, stack: Vec::with_capacity(16) }
31    }
32
33    /// Returns the current element.
34    #[inline]
35    pub fn current(&self) -> GreenTree<'a, L> {
36        self.current
37    }
38
39    /// Returns the current element as a node, if it is one.
40    #[inline]
41    pub fn as_node(&self) -> Option<&'a GreenNode<'a, L>> {
42        match self.current {
43            GreenTree::Node(n) => Some(n),
44            GreenTree::Leaf(_) => None,
45        }
46    }
47
48    /// Returns the absolute start offset of the current element.
49    #[inline]
50    pub fn offset(&self) -> usize {
51        self.offset
52    }
53
54    /// Returns the text length of the current element.
55    #[inline]
56    pub fn len(&self) -> usize {
57        self.current.len() as usize
58    }
59
60    /// Returns the absolute end offset of the current element.
61    #[inline]
62    pub fn end_offset(&self) -> usize {
63        self.offset + self.len()
64    }
65
66    /// Tries to move to the first child of the current node.
67    /// Returns true if successful (current was a node with children).
68    pub fn step_into(&mut self) -> bool {
69        match self.current {
70            GreenTree::Node(node) => {
71                if let Some(first_child) = node.children.first() {
72                    self.stack.push((node, 0, self.offset));
73                    self.current = *first_child;
74                    // offset remains the same for the first child
75                    true
76                }
77                else {
78                    false
79                }
80            }
81            GreenTree::Leaf(_) => false,
82        }
83    }
84
85    /// Tries to move to the next sibling.
86    /// Returns true if successful.
87    pub fn step_over(&mut self) -> bool {
88        if let Some((parent, idx, _parent_offset)) = self.stack.last() {
89            let next_idx = idx + 1;
90            if let Some(sibling) = parent.children.get(next_idx) {
91                // Update offset: current offset + current len
92                self.offset += self.current.len() as usize;
93
94                // Update stack top
95                let last = self.stack.last_mut().unwrap();
96                last.1 = next_idx;
97
98                self.current = *sibling;
99                true
100            }
101            else {
102                false
103            }
104        }
105        else {
106            false
107        }
108    }
109
110    /// Tries to move to the parent node.
111    /// Returns true if successful (not at root).
112    pub fn step_out(&mut self) -> bool {
113        if let Some((parent, _, parent_offset)) = self.stack.pop() {
114            self.current = GreenTree::Node(parent);
115            self.offset = parent_offset;
116            true
117        }
118        else {
119            false
120        }
121    }
122
123    /// Pre-order traversal step: try into, then over, then out + over.
124    /// Returns true if moved to a new node, false if finished traversal.
125    pub fn step(&mut self) -> bool {
126        if self.step_into() {
127            return true;
128        }
129        if self.step_over() {
130            return true;
131        }
132        while self.step_out() {
133            if self.step_over() {
134                return true;
135            }
136        }
137        false
138    }
139
140    /// Skips the current subtree (like step_over), but if that fails, goes up and over.
141    /// Effectively "next node in post-order" or "next node not in this subtree".
142    pub fn step_next(&mut self) -> bool {
143        if self.step_over() {
144            true
145        }
146        else {
147            while self.step_out() {
148                if self.step_over() {
149                    return true;
150                }
151            }
152            false
153        }
154    }
155}