use crate::{
GreenNode, Language, NodeOrToken, ParsedChildren, SyntaxFactory, SyntaxKind, SyntaxNode,
cow_mut::CowMut,
green::{GreenElement, NodeCache, NodeCacheNodeEntryMut},
syntax::TriviaPiece,
};
use std::{marker::PhantomData, num::NonZeroUsize};
#[derive(Clone, Copy, Debug)]
pub struct Checkpoint(NonZeroUsize);
impl Checkpoint {
fn new(inner: usize) -> Self {
Self(NonZeroUsize::new(inner + 1).unwrap())
}
fn into_inner(self) -> usize {
self.0.get() - 1
}
}
#[derive(Debug)]
pub struct TreeBuilder<'cache, L: Language, S: SyntaxFactory<Kind = L::Kind>> {
cache: CowMut<'cache, NodeCache>,
parents: Vec<(L::Kind, usize)>,
children: Vec<(u64, GreenElement)>,
ph: PhantomData<S>,
}
impl<L: Language, S: SyntaxFactory<Kind = L::Kind>> Default for TreeBuilder<'_, L, S> {
fn default() -> Self {
Self {
cache: CowMut::default(),
parents: Vec::default(),
children: Vec::default(),
ph: PhantomData,
}
}
}
impl<L: Language, S: SyntaxFactory<Kind = L::Kind>> TreeBuilder<'_, L, S> {
pub fn new() -> TreeBuilder<'static, L, S> {
TreeBuilder::default()
}
pub fn with_cache(cache: &mut NodeCache) -> TreeBuilder<'_, L, S> {
cache.increment_generation();
TreeBuilder {
cache: CowMut::Borrowed(cache),
parents: Vec::new(),
children: Vec::new(),
ph: PhantomData,
}
}
pub fn wrap_with_node<F>(kind: L::Kind, build: F) -> SyntaxNode<L>
where
F: Fn(&mut Self),
{
let mut builder = TreeBuilder::<L, S>::new();
builder.start_node(kind);
build(&mut builder);
builder.finish_node();
builder.finish()
}
#[inline]
pub fn token(&mut self, kind: L::Kind, text: &str) -> &mut Self {
let (hash, token) = self.cache.token(kind.to_raw(), text);
self.children.push((hash, token.into()));
self
}
#[inline]
pub fn token_with_trivia(
&mut self,
kind: L::Kind,
text: &str,
leading: &[TriviaPiece],
trailing: &[TriviaPiece],
) {
let (hash, token) = self
.cache
.token_with_trivia(kind.to_raw(), text, leading, trailing);
self.children.push((hash, token.into()));
}
#[inline]
pub fn start_node(&mut self, kind: L::Kind) -> &mut Self {
let len = self.children.len();
self.parents.push((kind, len));
self
}
#[inline]
pub fn finish_node(&mut self) -> &mut Self {
let (kind, first_child) = self.parents.pop().unwrap();
let raw_kind = kind.to_raw();
let slots = &self.children[first_child..];
let node_entry = self.cache.node(raw_kind, slots);
let mut build_node = || {
let children = ParsedChildren::new(&mut self.children, first_child);
S::make_syntax(kind, children).into_green()
};
let (hash, node) = match node_entry {
NodeCacheNodeEntryMut::NoCache(hash) => (hash, build_node()),
NodeCacheNodeEntryMut::Vacant(entry) => {
let node = build_node();
let hash = entry.cache(node.clone());
(hash, node)
}
NodeCacheNodeEntryMut::Cached(cached) => {
self.children.truncate(first_child);
(cached.hash(), cached.node().to_owned())
}
};
self.children.push((hash, node.into()));
self
}
#[inline]
pub fn checkpoint(&self) -> Checkpoint {
Checkpoint::new(self.children.len())
}
#[inline]
pub fn start_node_at(&mut self, checkpoint: Checkpoint, kind: L::Kind) {
let checkpoint = checkpoint.into_inner();
assert!(
checkpoint <= self.children.len(),
"checkpoint no longer valid, was finish_node called early?"
);
if let Some(&(_, first_child)) = self.parents.last() {
assert!(
checkpoint >= first_child,
"checkpoint no longer valid, was an unmatched start_node_at called?"
);
}
self.parents.push((kind, checkpoint));
}
#[inline]
#[must_use]
pub fn finish(mut self) -> SyntaxNode<L> {
let root = SyntaxNode::new_root(self.finish_green());
self.cache.retain_cache();
root
}
#[must_use]
pub(crate) fn finish_green(&mut self) -> GreenNode {
assert_eq!(self.children.len(), 1);
match self.children.pop().unwrap().1 {
NodeOrToken::Node(node) => node,
_ => panic!(),
}
}
}
#[cfg(test)]
mod tests {
use crate::green::GreenElementRef;
use crate::raw_language::{RawLanguageKind, RawSyntaxTreeBuilder};
use crate::{GreenNodeData, GreenTokenData, NodeOrToken};
fn build_condition_with_missing_closing_parenthesis(builder: &mut RawSyntaxTreeBuilder) {
builder.start_node(RawLanguageKind::CONDITION);
builder.token(RawLanguageKind::L_PAREN_TOKEN, "(");
builder.start_node(RawLanguageKind::LITERAL_EXPRESSION);
builder.token(RawLanguageKind::STRING_TOKEN, "a");
builder.finish_node();
builder.finish_node();
}
#[test]
fn caches_identical_nodes_with_empty_slots() {
let mut builder = RawSyntaxTreeBuilder::new();
builder.start_node(RawLanguageKind::ROOT); build_condition_with_missing_closing_parenthesis(&mut builder);
build_condition_with_missing_closing_parenthesis(&mut builder);
builder.finish_node();
let root = builder.finish_green();
let first = root.children().next().unwrap();
let last = root.children().next_back().unwrap();
assert_eq!(first.element(), last.element());
assert_same_elements(first.element(), last.element());
}
#[test]
fn doesnt_cache_node_if_empty_slots_differ() {
let mut builder = RawSyntaxTreeBuilder::new();
builder.start_node(RawLanguageKind::ROOT); build_condition_with_missing_closing_parenthesis(&mut builder);
builder.start_node(RawLanguageKind::CONDITION);
builder.token(RawLanguageKind::L_PAREN_TOKEN, "(");
builder.start_node(RawLanguageKind::LITERAL_EXPRESSION);
builder.token(RawLanguageKind::STRING_TOKEN, "a");
builder.finish_node();
builder.token(RawLanguageKind::R_PAREN_TOKEN, ")");
builder.finish_node();
builder.finish_node();
let root = builder.finish_green();
let first_condition = root.children().next().unwrap();
let last_condition = root.children().next_back().unwrap();
assert_ne!(first_condition.element(), last_condition.element());
}
fn assert_same_elements(left: GreenElementRef<'_>, right: GreenElementRef<'_>) {
fn element_id(element: GreenElementRef<'_>) -> *const () {
match element {
NodeOrToken::Node(node) => node as *const GreenNodeData as *const (),
NodeOrToken::Token(token) => token as *const GreenTokenData as *const (),
}
}
assert_eq!(element_id(left), element_id(right),);
}
}