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.
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    /// Creates a new red tree from a green tree root, starting at offset 0.
56    pub fn new(root: &'a GreenNode<'a, L>) -> Self {
57        Self::Node(RedNode { green: root, offset: 0 })
58    }
59
60    /// Returns the absolute byte span of this red tree element.
61    ///
62    /// The span includes the start and end offsets in the source text.
63    #[inline]
64    pub fn span(&self) -> Range<usize> {
65        match self {
66            RedTree::Node(n) => n.span(),
67            RedTree::Leaf(t) => t.span,
68        }
69    }
70
71    /// Returns the kind of this red tree element.
72    ///
73    /// # Type Parameters
74    ///
75    /// * `T` - A type that can be converted from both node and token kinds.
76    pub fn kind<T>(&self) -> T
77    where
78        T: From<L::ElementType> + From<L::TokenType>,
79    {
80        match self {
81            RedTree::Node(n) => T::from(n.green.kind),
82            RedTree::Leaf(l) => T::from(l.kind),
83        }
84    }
85
86    /// Returns the text content of this red tree element from the source.
87    ///
88    /// # Arguments
89    ///
90    /// * `source` - The source text provider.
91    pub fn text<'s, S: crate::source::Source + ?Sized>(&self, source: &'s S) -> std::borrow::Cow<'s, str> {
92        source.get_text_in(self.span())
93    }
94
95    /// Returns an iterator over the child elements if this is a node.
96    ///
97    /// Returns an empty iterator if this is a token.
98    pub fn children(&self) -> RedChildren<'a, L> {
99        match self {
100            RedTree::Node(n) => n.children(),
101            RedTree::Leaf(_) => RedChildren::empty(),
102        }
103    }
104
105    /// Returns this element as a node if it is one.
106    pub fn as_node(&self) -> Option<RedNode<'a, L>> {
107        match self {
108            RedTree::Node(n) => Some(*n),
109            RedTree::Leaf(_) => None,
110        }
111    }
112
113    /// Returns this element as a token if it is one.
114    pub fn as_token(&self) -> Option<RedLeaf<L>> {
115        match self {
116            RedTree::Node(_) => None,
117            RedTree::Leaf(l) => Some(*l),
118        }
119    }
120
121    /// Alias for `as_token`.
122    #[deprecated(note = "Use `as_token` instead")]
123    pub fn as_leaf(&self) -> Option<RedLeaf<L>> {
124        self.as_token()
125    }
126}
127
128/// A red node that wraps a green node with absolute offset information.
129///
130/// Red nodes are position-aware views into the immutable green tree structure.
131/// They are small, copyable handles that can be used for traversal and
132/// analysis.
133///
134/// # Design Note: Reference vs Owned
135/// We store `&'a GreenNode<'a, L>` here instead of `GreenNode<'a, L>` to keep
136/// `RedNode` as small as possible (16 bytes: 8 for pointer + 8 for offset).
137/// This makes it efficient to pass `RedNode` by value during tree traversal.
138pub struct RedNode<'a, L: Language> {
139    /// The underlying green node that contains the structural information.
140    pub green: &'a GreenNode<'a, L>,
141    /// The absolute byte offset of this node in the source text.
142    pub offset: usize,
143}
144
145// Manually implement Clone/Copy to avoid L: Copy bound
146impl<'a, L: Language> Clone for RedNode<'a, L> {
147    fn clone(&self) -> Self {
148        *self
149    }
150}
151
152impl<'a, L: Language> Copy for RedNode<'a, L> {}
153
154impl<'a, L: Language> PartialEq for RedNode<'a, L> {
155    fn eq(&self, other: &Self) -> bool {
156        self.green == other.green && self.offset == other.offset
157    }
158}
159
160impl<'a, L: Language> Eq for RedNode<'a, L> {}
161
162impl<'a, L: Language> fmt::Debug for RedNode<'a, L> {
163    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
164        f.debug_struct("RedNode").field("green", &self.green).field("offset", &self.offset).finish()
165    }
166}
167
168/// A red leaf kind with absolute position information.
169///
170/// Red leaves represent individual tokens with their location in the source.
171pub struct RedLeaf<L: Language> {
172    /// The token kind/category.
173    pub kind: L::TokenType,
174    /// The absolute byte span of this token in the source text.
175    pub span: Range<usize>,
176}
177
178// Manually implement Clone/Copy to avoid L: Copy bound
179impl<L: Language> Clone for RedLeaf<L> {
180    fn clone(&self) -> Self {
181        *self
182    }
183}
184
185impl<L: Language> Copy for RedLeaf<L> {}
186
187impl<L: Language> RedLeaf<L> {
188    /// Returns the kind of this red leaf.
189    #[inline]
190    pub fn kind(&self) -> L::TokenType {
191        self.kind
192    }
193
194    /// Returns the absolute byte span of this red leaf.
195    #[inline]
196    pub fn span(&self) -> Range<usize> {
197        self.span.clone()
198    }
199
200    /// Returns the text content of this red leaf from the source.
201    pub fn text<'s, S: crate::source::Source + ?Sized>(&self, source: &'s S) -> std::borrow::Cow<'s, str> {
202        source.get_text_in(self.span())
203    }
204}
205
206impl<L: Language> PartialEq for RedLeaf<L> {
207    fn eq(&self, other: &Self) -> bool {
208        self.kind == other.kind && self.span == other.span
209    }
210}
211
212impl<L: Language> Eq for RedLeaf<L> {}
213
214impl<L: Language> fmt::Debug for RedLeaf<L> {
215    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
216        f.debug_struct("RedLeaf").field("kind", &self.kind).field("span", &self.span).finish()
217    }
218}
219
220/// An iterator over the child elements of a red node.
221///
222/// This iterator lazily computes the absolute offsets of each child
223/// as it traverses the tree.
224pub struct RedChildren<'a, L: Language> {
225    /// The parent red node being iterated.
226    node: Option<RedNode<'a, L>>,
227    /// The current index in the children slice.
228    index: usize,
229    /// The current absolute byte offset.
230    offset: usize,
231}
232
233impl<'a, L: Language> RedChildren<'a, L> {
234    /// Creates an empty iterator.
235    pub fn empty() -> Self {
236        Self { node: None, index: 0, offset: 0 }
237    }
238}
239
240impl<'a, L: Language> RedNode<'a, L> {
241    /// Returns the text content of this red node from the source.
242    pub fn text<'s, S: crate::source::Source + ?Sized>(&self, source: &'s S) -> std::borrow::Cow<'s, str> {
243        source.get_text_in(self.span())
244    }
245
246    /// Creates a new red node from a green node and absolute offset.
247    #[inline]
248    pub fn new(green: &'a GreenNode<'a, L>, offset: usize) -> Self {
249        Self { green, offset }
250    }
251
252    /// Returns the absolute byte span of this red node.
253    #[inline]
254    pub fn span(&self) -> Range<usize> {
255        Range { start: self.offset, end: self.offset + self.green.text_len() as usize }
256    }
257
258    /// Returns the element type of this red node.
259    #[inline]
260    pub fn element_type(&self) -> L::ElementType {
261        self.green.kind
262    }
263
264    /// Returns the underlying green node.
265    #[inline]
266    pub fn green(&self) -> &'a GreenNode<'a, L> {
267        self.green
268    }
269
270    /// Returns the kind of this red node.
271    pub fn kind<T>(&self) -> T
272    where
273        T: From<L::ElementType>,
274    {
275        T::from(self.green.kind)
276    }
277
278    /// Gets the child element at the specified index.
279    ///
280    /// # Arguments
281    ///
282    /// * `idx` - The zero-based index of the child.
283    ///
284    /// # Panics
285    ///
286    /// Panics if the index is out of bounds.
287    pub fn child_at(&self, idx: usize) -> RedTree<'a, L> {
288        let children = self.green.children();
289        let green_child = &children[idx];
290
291        // Calculate offset by summing lengths of previous siblings
292        let mut offset = self.offset;
293        for i in 0..idx {
294            offset += children[i].len() as usize
295        }
296
297        match green_child {
298            GreenTree::Node(n) => RedTree::Node(RedNode::new(n, offset)),
299            GreenTree::Leaf(t) => RedTree::Leaf(RedLeaf { kind: t.kind, span: Range { start: offset, end: offset + t.length as usize } }),
300        }
301    }
302
303    /// Returns an iterator over the child elements of this red node.
304    pub fn children(&self) -> RedChildren<'a, L> {
305        RedChildren { node: Some(*self), index: 0, offset: self.offset }
306    }
307
308    /// Finds the index of the child element containing the specified absolute byte offset.
309    ///
310    /// # Arguments
311    ///
312    /// * `offset` - The absolute byte offset to search for.
313    pub fn child_index_at_offset(&self, offset: usize) -> Option<usize> {
314        if offset < self.offset || offset >= self.offset + self.green.text_len() as usize {
315            return None;
316        }
317
318        let relative_offset = (offset - self.offset) as u32;
319        let mut current_pos = 0;
320
321        for (idx, child) in self.green.children().iter().enumerate() {
322            let len = child.len();
323            if relative_offset < current_pos + len {
324                return Some(idx);
325            }
326            current_pos += len
327        }
328
329        None
330    }
331
332    /// Finds the child element containing the specified absolute byte offset.
333    ///
334    /// # Arguments
335    ///
336    /// * `offset` - The absolute byte offset to search for.
337    pub fn child_at_offset(&self, offset: usize) -> Option<RedTree<'a, L>> {
338        self.child_index_at_offset(offset).map(|idx| self.child_at(idx))
339    }
340
341    /// Finds the leaf element at the specified absolute byte offset by traversing down the tree.
342    ///
343    /// This method performs a deep search, following child nodes until a leaf is found.
344    ///
345    /// # Arguments
346    ///
347    /// * `offset` - The absolute byte offset to search for.
348    pub fn leaf_at_offset(&self, offset: usize) -> Option<RedLeaf<L>> {
349        let mut current = *self;
350        loop {
351            match current.child_at_offset(offset)? {
352                RedTree::Node(n) => current = n,
353                RedTree::Leaf(l) => return Some(l),
354            }
355        }
356    }
357
358    /// Returns the first child token if any.
359    pub fn first_token(&self) -> Option<RedLeaf<L>> {
360        for child in self.children() {
361            match child {
362                RedTree::Node(_) => continue,
363                RedTree::Leaf(l) => return Some(l),
364            }
365        }
366        None
367    }
368}
369
370impl<'a, L: Language> Iterator for RedChildren<'a, L> {
371    type Item = RedTree<'a, L>;
372
373    fn next(&mut self) -> Option<Self::Item> {
374        let node = self.node.as_ref()?;
375        let children = node.green.children();
376        if self.index >= children.len() {
377            return None;
378        }
379
380        let ch = &children[self.index];
381        let offset = self.offset;
382        let elem = match ch {
383            GreenTree::Node(n) => RedTree::Node(RedNode::new(n, offset)),
384            GreenTree::Leaf(t) => RedTree::Leaf(RedLeaf { kind: t.kind, span: Range { start: offset, end: offset + t.length as usize } }),
385        };
386
387        self.offset += ch.len() as usize;
388        self.index += 1;
389        Some(elem)
390    }
391}