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