use crate::types::{FieldId, FromSyntaxKind, SyntaxKind, TreeEvent};
use std::sync::Arc;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct GreenToken {
pub kind: SyntaxKind,
pub text_len: u32,
pub is_trivia: bool,
pub text: Arc<str>,
}
impl GreenToken {
#[must_use]
pub fn new(kind: SyntaxKind, text: &str, is_trivia: bool) -> Arc<Self> {
Arc::new(Self {
kind,
text_len: u32::try_from(text.len()).unwrap_or(u32::MAX),
is_trivia,
text: text.into(),
})
}
#[must_use]
#[inline]
pub fn text(&self) -> &str {
&self.text
}
#[must_use]
#[inline]
pub fn space(kind: SyntaxKind) -> Arc<Self> {
Self::new(kind, " ", true)
}
#[must_use]
#[inline]
pub fn newline(kind: SyntaxKind) -> Arc<Self> {
Self::new(kind, "\n", true)
}
#[must_use]
#[inline]
pub fn kind_as<K: FromSyntaxKind>(&self) -> Option<K> {
K::from_syntax_kind(self.kind)
}
}
#[derive(Clone, Debug)]
pub struct GreenNode {
pub kind: SyntaxKind,
pub text_len: u32,
pub children: Box<[GreenElement]>,
pub child_fields: Option<Box<[Option<FieldId>]>>,
}
impl GreenNode {
#[must_use]
pub fn new(kind: SyntaxKind, children: Vec<GreenElement>) -> Arc<Self> {
let text_len = children.iter().map(GreenElement::text_len).sum();
Arc::new(Self {
kind,
text_len,
children: children.into(),
child_fields: None,
})
}
#[must_use]
pub fn new_with_fields(
kind: SyntaxKind,
children_with_fields: Vec<(Option<FieldId>, GreenElement)>,
) -> Arc<Self> {
let text_len = children_with_fields.iter().map(|(_, e)| e.text_len()).sum();
let child_fields: Box<[Option<FieldId>]> = children_with_fields
.iter()
.map(|(f, _)| *f)
.collect::<Vec<_>>()
.into();
let children: Box<[GreenElement]> = children_with_fields
.into_iter()
.map(|(_, e)| e)
.collect::<Vec<_>>()
.into();
let has_any_field = child_fields.iter().any(Option::is_some);
Arc::new(Self {
kind,
text_len,
children,
child_fields: if has_any_field {
Some(child_fields)
} else {
None
},
})
}
#[must_use]
#[inline]
pub fn child_field(&self, i: usize) -> Option<FieldId> {
self.child_fields
.as_ref()
.and_then(|f| f.get(i).copied())
.flatten()
}
#[must_use]
#[inline]
pub fn kind_as<K: FromSyntaxKind>(&self) -> Option<K> {
K::from_syntax_kind(self.kind)
}
#[must_use]
pub fn collect_text(&self) -> String {
let mut out = String::with_capacity(self.text_len as usize);
collect_text_into(self, &mut out);
out
}
}
fn collect_text_into(node: &GreenNode, out: &mut String) {
for child in &node.children {
match child {
GreenElement::Node(n) => collect_text_into(n, out),
GreenElement::Token(t) => out.push_str(&t.text),
}
}
}
#[derive(Clone, Debug)]
pub enum GreenElement {
Node(Arc<GreenNode>),
Token(Arc<GreenToken>),
}
impl GreenElement {
#[must_use]
#[inline]
pub fn text_len(&self) -> u32 {
match self {
Self::Node(n) => n.text_len,
Self::Token(t) => t.text_len,
}
}
#[must_use]
#[inline]
pub fn is_trivia(&self) -> bool {
match self {
Self::Token(t) => t.is_trivia,
Self::Node(_) => false,
}
}
#[must_use]
#[inline]
pub fn kind(&self) -> SyntaxKind {
match self {
Self::Node(n) => n.kind,
Self::Token(t) => t.kind,
}
}
#[must_use]
#[inline]
pub fn kind_as<K: FromSyntaxKind>(&self) -> Option<K> {
K::from_syntax_kind(self.kind())
}
}
#[must_use]
pub fn build_green_tree(input: &[u8], events: &[TreeEvent]) -> Option<Arc<GreenNode>> {
type StackEntry = (
SyntaxKind,
Option<FieldId>,
Vec<(Option<FieldId>, GreenElement)>,
);
let mut stack: Vec<StackEntry> = Vec::new();
let mut roots: Vec<(Option<FieldId>, GreenElement)> = Vec::new();
for ev in events {
match *ev {
TreeEvent::NodeOpen { kind, field, .. } => {
let leading = if let Some((_, _, children)) = stack.last_mut() {
drain_trailing_trivia(children)
} else {
Vec::new()
};
stack.push((kind, field, leading));
}
TreeEvent::NodeClose { .. } => {
let (kind, my_field, children_with_fields) = stack.pop()?;
let node = GreenNode::new_with_fields(kind, children_with_fields);
push_element(&mut stack, &mut roots, (my_field, GreenElement::Node(node)));
}
TreeEvent::Token {
kind,
start,
end,
is_trivia,
} => {
if start == end {
continue;
}
let text = input
.get(start as usize..end as usize)
.and_then(|b| std::str::from_utf8(b).ok())
.unwrap_or("");
let tok = GreenToken::new(kind, text, is_trivia);
push_element(&mut stack, &mut roots, (None, GreenElement::Token(tok)));
}
}
}
if !stack.is_empty() {
return None;
}
match roots.len() {
0 => None,
1 => {
let (_, elem) = roots.remove(0);
match elem {
GreenElement::Node(n) => Some(n),
GreenElement::Token(t) => {
Some(GreenNode::new(t.kind, vec![GreenElement::Token(t)]))
}
}
}
_ => {
let children_with_fields: Vec<(Option<FieldId>, GreenElement)> =
roots.into_iter().collect();
Some(GreenNode::new_with_fields(u16::MAX, children_with_fields))
}
}
}
type BuildElem = (Option<FieldId>, GreenElement);
type BuildStackEntry = (SyntaxKind, Option<FieldId>, Vec<BuildElem>);
fn drain_trailing_trivia(children: &mut Vec<BuildElem>) -> Vec<BuildElem> {
let n = children
.iter()
.rev()
.take_while(|(_, el)| el.is_trivia())
.count();
if n == 0 {
return Vec::new();
}
children.drain(children.len() - n..).collect()
}
#[inline]
fn push_element(stack: &mut [BuildStackEntry], roots: &mut Vec<BuildElem>, elem: BuildElem) {
match stack.last_mut() {
Some((_, _, children)) => children.push(elem),
None => roots.push(elem),
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::types::TreeEvent;
#[test]
fn build_green_tree_single_token() {
let input = b"abc";
let events = [TreeEvent::Token {
kind: 1,
start: 0,
end: 3,
is_trivia: false,
}];
let root = build_green_tree(input, &events).expect("valid events");
assert_eq!(root.kind, 1); assert_eq!(root.text_len, 3);
assert_eq!(root.children.len(), 1);
let token = match &root.children[0] {
GreenElement::Token(t) => t.as_ref(),
_ => panic!("expected token"),
};
assert_eq!(token.text(), "abc");
assert!(!token.is_trivia);
}
#[test]
fn build_green_tree_node_with_token() {
let input = b"x";
let events = [
TreeEvent::NodeOpen {
kind: 10,
field: None,
pos: 0,
},
TreeEvent::Token {
kind: 2,
start: 0,
end: 1,
is_trivia: false,
},
TreeEvent::NodeClose { pos: 1 },
];
let root = build_green_tree(input, &events).expect("valid events");
assert_eq!(root.kind, 10);
assert_eq!(root.text_len, 1);
assert_eq!(root.children.len(), 1);
if let GreenElement::Token(t) = &root.children[0] {
assert_eq!(t.text(), "x");
} else {
panic!("expected token");
}
}
#[test]
fn build_green_tree_empty_returns_none() {
let root = build_green_tree(b"", &[]);
assert!(root.is_none());
}
}