Skip to main content

oak_core/tree/
red_tree.rs

1//! Red-green tree implementation with position information for incremental parsing.
2//!
3//! This module provides the "red" side of the red-green tree architecture.
4
5use crate::{
6    Language,
7    tree::green_tree::{GreenNode, GreenTree},
8};
9use core::range::Range;
10use std::fmt;
11
12/// A red tree element with absolute position information.
13///
14/// Red trees are the "red" side of the red-green tree architecture. They are
15/// lazily computed from green trees and include absolute byte offsets in
16/// the source code.
17pub enum RedTree<'a, L: Language> {
18    /// A red node with child elements.
19    Node(RedNode<'a, L>),
20    /// A red leaf (token).
21    Leaf(RedLeaf<L>),
22}
23
24// Manually implement Clone/Copy to avoid L: Copy bound
25impl<'a, L: Language> Clone for RedTree<'a, L> {
26    fn clone(&self) -> Self {
27        *self
28    }
29}
30
31impl<'a, L: Language> Copy for RedTree<'a, L> {}
32
33impl<'a, L: Language> fmt::Debug for RedTree<'a, L> {
34    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
35        match self {
36            Self::Node(node) => fmt::Debug::fmt(node, f),
37            Self::Leaf(leaf) => fmt::Debug::fmt(leaf, f),
38        }
39    }
40}
41
42impl<'a, L: Language> PartialEq for RedTree<'a, L> {
43    fn eq(&self, other: &Self) -> bool {
44        match (self, other) {
45            (Self::Node(l0), Self::Node(r0)) => l0 == r0,
46            (Self::Leaf(l0), Self::Leaf(r0)) => l0 == r0,
47            _ => false,
48        }
49    }
50}
51
52impl<'a, L: Language> Eq for RedTree<'a, L> {}
53
54impl<'a, L: Language> RedTree<'a, L> {
55    /// Returns the absolute byte span of this red tree element.
56    ///
57    /// The span includes the start and end offsets in the source text.
58    #[inline]
59    pub fn span(&self) -> Range<usize> {
60        match self {
61            RedTree::Node(n) => n.span(),
62            RedTree::Leaf(t) => t.span,
63        }
64    }
65
66    /// Returns the kind of this red tree element.
67    ///
68    /// # Type Parameters
69    ///
70    /// * `T` - A type that can be converted from both node and token kinds.
71    pub fn kind<T>(&self) -> T
72    where
73        T: From<L::ElementType> + From<L::TokenType>,
74    {
75        match self {
76            RedTree::Node(n) => T::from(n.green.kind),
77            RedTree::Leaf(l) => T::from(l.kind),
78        }
79    }
80
81    /// Returns the text content of this red tree element from the source.
82    ///
83    /// # Arguments
84    ///
85    /// * `source` - The source text provider.
86    pub fn text<'s, S: crate::source::Source + ?Sized>(&self, source: &'s S) -> std::borrow::Cow<'s, str> {
87        source.get_text_in(self.span())
88    }
89
90    /// Returns an iterator over the child elements if this is a node.
91    ///
92    /// Returns an empty iterator if this is a leaf.
93    pub fn children(&self) -> RedChildren<'a, L> {
94        match self {
95            RedTree::Node(n) => n.children(),
96            RedTree::Leaf(_) => RedChildren::empty(),
97        }
98    }
99
100    /// Returns this element as a node if it is one.
101    pub fn as_node(&self) -> Option<RedNode<'a, L>> {
102        match self {
103            RedTree::Node(n) => Some(*n),
104            RedTree::Leaf(_) => None,
105        }
106    }
107
108    /// Returns this element as a leaf if it is one.
109    pub fn as_leaf(&self) -> Option<RedLeaf<L>> {
110        match self {
111            RedTree::Node(_) => None,
112            RedTree::Leaf(l) => Some(*l),
113        }
114    }
115}
116
117/// A red node that wraps a green node with absolute offset information.
118///
119/// Red nodes are position-aware views into the immutable green tree structure.
120/// They are small, copyable handles that can be used for traversal and
121/// analysis.
122pub struct RedNode<'a, L: Language> {
123    /// The underlying green node that contains the structural information.
124    pub green: &'a GreenNode<'a, L>,
125    /// The absolute byte offset of this node in the source text.
126    pub offset: usize,
127}
128
129// Manually implement Clone/Copy to avoid L: Copy bound
130impl<'a, L: Language> Clone for RedNode<'a, L> {
131    fn clone(&self) -> Self {
132        *self
133    }
134}
135
136impl<'a, L: Language> Copy for RedNode<'a, L> {}
137
138impl<'a, L: Language> PartialEq for RedNode<'a, L> {
139    fn eq(&self, other: &Self) -> bool {
140        self.green == other.green && self.offset == other.offset
141    }
142}
143
144impl<'a, L: Language> Eq for RedNode<'a, L> {}
145
146impl<'a, L: Language> fmt::Debug for RedNode<'a, L> {
147    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
148        f.debug_struct("RedNode").field("green", &self.green).field("offset", &self.offset).finish()
149    }
150}
151
152/// A red leaf kind with absolute position information.
153///
154/// Red leaves represent individual tokens with their location in the source.
155pub struct RedLeaf<L: Language> {
156    /// The token kind/category.
157    pub kind: L::TokenType,
158    /// The absolute byte span of this token in the source text.
159    pub span: Range<usize>,
160}
161
162// Manually implement Clone/Copy to avoid L: Copy bound
163impl<L: Language> Clone for RedLeaf<L> {
164    fn clone(&self) -> Self {
165        *self
166    }
167}
168
169impl<L: Language> Copy for RedLeaf<L> {}
170
171impl<L: Language> PartialEq for RedLeaf<L> {
172    fn eq(&self, other: &Self) -> bool {
173        self.kind == other.kind && self.span == other.span
174    }
175}
176
177impl<L: Language> Eq for RedLeaf<L> {}
178
179impl<L: Language> fmt::Debug for RedLeaf<L> {
180    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
181        f.debug_struct("RedLeaf").field("kind", &self.kind).field("span", &self.span).finish()
182    }
183}
184
185/// An iterator over the child elements of a red node.
186///
187/// This iterator lazily computes the absolute offsets of each child
188/// as it traverses the tree.
189pub struct RedChildren<'a, L: Language> {
190    /// The parent red node being iterated.
191    node: Option<RedNode<'a, L>>,
192    /// The current index in the children slice.
193    index: usize,
194    /// The current absolute byte offset.
195    offset: usize,
196}
197
198impl<'a, L: Language> RedChildren<'a, L> {
199    /// Creates an empty iterator.
200    pub fn empty() -> Self {
201        Self { node: None, index: 0, offset: 0 }
202    }
203}
204
205impl<'a, L: Language> RedNode<'a, L> {
206    /// Creates a new red node from a green node and absolute offset.
207    #[inline]
208    pub fn new(green: &'a GreenNode<'a, L>, offset: usize) -> Self {
209        Self { green, offset }
210    }
211
212    /// Returns the absolute byte span of this red node.
213    #[inline]
214    pub fn span(&self) -> Range<usize> {
215        Range { start: self.offset, end: self.offset + self.green.text_len() as usize }
216    }
217
218    /// Returns the element type of this red node.
219    #[inline]
220    pub fn element_type(&self) -> L::ElementType {
221        self.green.kind
222    }
223
224    /// Returns the underlying green node.
225    #[inline]
226    pub fn green(&self) -> &'a GreenNode<'a, L> {
227        self.green
228    }
229
230    /// Returns the kind of this red node.
231    pub fn kind<T>(&self) -> T
232    where
233        T: From<L::ElementType>,
234    {
235        T::from(self.green.kind)
236    }
237
238    /// Gets the child element at the specified index.
239    ///
240    /// # Arguments
241    ///
242    /// * `idx` - The zero-based index of the child.
243    ///
244    /// # Panics
245    ///
246    /// Panics if the index is out of bounds.
247    pub fn child_at(&self, idx: usize) -> RedTree<'a, L> {
248        let children = self.green.children();
249        let green_child = &children[idx];
250
251        // Calculate offset by summing lengths of previous siblings
252        let mut offset = self.offset;
253        for i in 0..idx {
254            offset += children[i].len() as usize
255        }
256
257        match green_child {
258            GreenTree::Node(n) => RedTree::Node(RedNode::new(n, offset)),
259            GreenTree::Leaf(t) => RedTree::Leaf(RedLeaf { kind: t.kind, span: Range { start: offset, end: offset + t.length as usize } }),
260        }
261    }
262
263    /// Returns an iterator over the child elements of this red node.
264    pub fn children(&self) -> RedChildren<'a, L> {
265        RedChildren { node: Some(*self), index: 0, offset: self.offset }
266    }
267
268    /// Finds the index of the child element containing the specified absolute byte offset.
269    ///
270    /// # Arguments
271    ///
272    /// * `offset` - The absolute byte offset to search for.
273    pub fn child_index_at_offset(&self, offset: usize) -> Option<usize> {
274        if offset < self.offset || offset >= self.offset + self.green.text_len() as usize {
275            return None;
276        }
277
278        let relative_offset = (offset - self.offset) as u32;
279        let mut current_pos = 0;
280
281        for (idx, child) in self.green.children().iter().enumerate() {
282            let len = child.len();
283            if relative_offset < current_pos + len {
284                return Some(idx);
285            }
286            current_pos += len
287        }
288
289        None
290    }
291
292    /// Finds the child element containing the specified absolute byte offset.
293    ///
294    /// # Arguments
295    ///
296    /// * `offset` - The absolute byte offset to search for.
297    pub fn child_at_offset(&self, offset: usize) -> Option<RedTree<'a, L>> {
298        self.child_index_at_offset(offset).map(|idx| self.child_at(idx))
299    }
300
301    /// Finds the leaf element at the specified absolute byte offset by traversing down the tree.
302    ///
303    /// This method performs a deep search, following child nodes until a leaf is found.
304    ///
305    /// # Arguments
306    ///
307    /// * `offset` - The absolute byte offset to search for.
308    pub fn leaf_at_offset(&self, offset: usize) -> Option<RedLeaf<L>> {
309        let mut current = *self;
310        loop {
311            match current.child_at_offset(offset)? {
312                RedTree::Node(n) => current = n,
313                RedTree::Leaf(l) => return Some(l),
314            }
315        }
316    }
317}
318
319impl<'a, L: Language> Iterator for RedChildren<'a, L> {
320    type Item = RedTree<'a, L>;
321
322    fn next(&mut self) -> Option<Self::Item> {
323        let node = self.node.as_ref()?;
324        let children = node.green.children();
325        if self.index >= children.len() {
326            return None;
327        }
328
329        let ch = &children[self.index];
330        let offset = self.offset;
331        let elem = match ch {
332            GreenTree::Node(n) => RedTree::Node(RedNode::new(n, offset)),
333            GreenTree::Leaf(t) => RedTree::Leaf(RedLeaf { kind: t.kind, span: Range { start: offset, end: offset + t.length as usize } }),
334        };
335
336        self.offset += ch.len() as usize;
337        self.index += 1;
338        Some(elem)
339    }
340}