use crate::{
Language,
tree::green_tree::{GreenNode, GreenTree},
};
use core::range::Range;
use std::fmt;
pub enum RedTree<'a, L: Language> {
Node(RedNode<'a, L>),
Leaf(RedLeaf<L>),
}
impl<'a, L: Language> Clone for RedTree<'a, L> {
fn clone(&self) -> Self {
*self
}
}
impl<'a, L: Language> Copy for RedTree<'a, L> {}
impl<'a, L: Language> fmt::Debug for RedTree<'a, L> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Node(node) => fmt::Debug::fmt(node, f),
Self::Leaf(leaf) => fmt::Debug::fmt(leaf, f),
}
}
}
impl<'a, L: Language> PartialEq for RedTree<'a, L> {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::Node(l0), Self::Node(r0)) => l0 == r0,
(Self::Leaf(l0), Self::Leaf(r0)) => l0 == r0,
_ => false,
}
}
}
impl<'a, L: Language> Eq for RedTree<'a, L> {}
impl<'a, L: Language> RedTree<'a, L> {
pub fn new(root: &'a GreenNode<'a, L>) -> Self {
Self::Node(RedNode { green: root, offset: 0 })
}
#[inline]
pub fn span(&self) -> Range<usize> {
match self {
RedTree::Node(n) => n.span(),
RedTree::Leaf(t) => t.span,
}
}
pub fn kind<T>(&self) -> T
where
T: From<L::ElementType> + From<L::TokenType>,
{
match self {
RedTree::Node(n) => T::from(n.green.kind),
RedTree::Leaf(l) => T::from(l.kind),
}
}
pub fn text<'s, S: crate::source::Source + ?Sized>(&self, source: &'s S) -> std::borrow::Cow<'s, str> {
source.get_text_in(self.span())
}
pub fn children(&self) -> RedChildren<'a, L> {
match self {
RedTree::Node(n) => n.children(),
RedTree::Leaf(_) => RedChildren::empty(),
}
}
pub fn as_node(&self) -> Option<RedNode<'a, L>> {
match self {
RedTree::Node(n) => Some(*n),
RedTree::Leaf(_) => None,
}
}
pub fn as_token(&self) -> Option<RedLeaf<L>> {
match self {
RedTree::Node(_) => None,
RedTree::Leaf(l) => Some(*l),
}
}
#[deprecated(note = "Use `as_token` instead")]
pub fn as_leaf(&self) -> Option<RedLeaf<L>> {
self.as_token()
}
}
pub struct RedNode<'a, L: Language> {
pub green: &'a GreenNode<'a, L>,
pub offset: usize,
}
impl<'a, L: Language> Clone for RedNode<'a, L> {
fn clone(&self) -> Self {
*self
}
}
impl<'a, L: Language> Copy for RedNode<'a, L> {}
impl<'a, L: Language> PartialEq for RedNode<'a, L> {
fn eq(&self, other: &Self) -> bool {
self.green == other.green && self.offset == other.offset
}
}
impl<'a, L: Language> Eq for RedNode<'a, L> {}
impl<'a, L: Language> fmt::Debug for RedNode<'a, L> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("RedNode").field("green", &self.green).field("offset", &self.offset).finish()
}
}
pub struct RedLeaf<L: Language> {
pub kind: L::TokenType,
pub span: Range<usize>,
}
impl<L: Language> Clone for RedLeaf<L> {
fn clone(&self) -> Self {
*self
}
}
impl<L: Language> Copy for RedLeaf<L> {}
impl<L: Language> RedLeaf<L> {
#[inline]
pub fn kind(&self) -> L::TokenType {
self.kind
}
#[inline]
pub fn span(&self) -> Range<usize> {
self.span.clone()
}
pub fn text<'s, S: crate::source::Source + ?Sized>(&self, source: &'s S) -> std::borrow::Cow<'s, str> {
source.get_text_in(self.span())
}
}
impl<L: Language> PartialEq for RedLeaf<L> {
fn eq(&self, other: &Self) -> bool {
self.kind == other.kind && self.span == other.span
}
}
impl<L: Language> Eq for RedLeaf<L> {}
impl<L: Language> fmt::Debug for RedLeaf<L> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("RedLeaf").field("kind", &self.kind).field("span", &self.span).finish()
}
}
pub struct RedChildren<'a, L: Language> {
node: Option<RedNode<'a, L>>,
index: usize,
offset: usize,
}
impl<'a, L: Language> RedChildren<'a, L> {
pub fn empty() -> Self {
Self { node: None, index: 0, offset: 0 }
}
}
impl<'a, L: Language> RedNode<'a, L> {
pub fn text<'s, S: crate::source::Source + ?Sized>(&self, source: &'s S) -> std::borrow::Cow<'s, str> {
source.get_text_in(self.span())
}
#[inline]
pub fn new(green: &'a GreenNode<'a, L>, offset: usize) -> Self {
Self { green, offset }
}
#[inline]
pub fn span(&self) -> Range<usize> {
Range { start: self.offset, end: self.offset + self.green.text_len() as usize }
}
#[inline]
pub fn element_type(&self) -> L::ElementType {
self.green.kind
}
#[inline]
pub fn green(&self) -> &'a GreenNode<'a, L> {
self.green
}
pub fn kind<T>(&self) -> T
where
T: From<L::ElementType>,
{
T::from(self.green.kind)
}
pub fn child_at(&self, idx: usize) -> RedTree<'a, L> {
let children = self.green.children();
let green_child = &children[idx];
let mut offset = self.offset;
for i in 0..idx {
offset += children[i].len() as usize
}
match green_child {
GreenTree::Node(n) => RedTree::Node(RedNode::new(n, offset)),
GreenTree::Leaf(t) => RedTree::Leaf(RedLeaf { kind: t.kind, span: Range { start: offset, end: offset + t.length as usize } }),
}
}
pub fn children(&self) -> RedChildren<'a, L> {
RedChildren { node: Some(*self), index: 0, offset: self.offset }
}
pub fn child_index_at_offset(&self, offset: usize) -> Option<usize> {
if offset < self.offset || offset >= self.offset + self.green.text_len() as usize {
return None;
}
let relative_offset = (offset - self.offset) as u32;
let mut current_pos = 0;
for (idx, child) in self.green.children().iter().enumerate() {
let len = child.len();
if relative_offset < current_pos + len {
return Some(idx);
}
current_pos += len
}
None
}
pub fn child_at_offset(&self, offset: usize) -> Option<RedTree<'a, L>> {
self.child_index_at_offset(offset).map(|idx| self.child_at(idx))
}
pub fn leaf_at_offset(&self, offset: usize) -> Option<RedLeaf<L>> {
let mut current = *self;
loop {
match current.child_at_offset(offset)? {
RedTree::Node(n) => current = n,
RedTree::Leaf(l) => return Some(l),
}
}
}
pub fn first_token(&self) -> Option<RedLeaf<L>> {
for child in self.children() {
match child {
RedTree::Node(_) => continue,
RedTree::Leaf(l) => return Some(l),
}
}
None
}
}
impl<'a, L: Language> Iterator for RedChildren<'a, L> {
type Item = RedTree<'a, L>;
fn next(&mut self) -> Option<Self::Item> {
let node = self.node.as_ref()?;
let children = node.green.children();
if self.index >= children.len() {
return None;
}
let ch = &children[self.index];
let offset = self.offset;
let elem = match ch {
GreenTree::Node(n) => RedTree::Node(RedNode::new(n, offset)),
GreenTree::Leaf(t) => RedTree::Leaf(RedLeaf { kind: t.kind, span: Range { start: offset, end: offset + t.length as usize } }),
};
self.offset += ch.len() as usize;
self.index += 1;
Some(elem)
}
}