use std::hash::{Hash, Hasher};
use rustc_hash::{FxHashMap, FxHasher};
use text_size::TextSize;
use crate::{
RawSyntaxKind,
Syntax,
green::{GreenElement, GreenNode, GreenToken},
interning::{Interner, TokenInterner, TokenKey, new_interner},
util::NodeOrToken,
utility_types::MaybeOwned,
};
use super::{node::GreenNodeHead, token::GreenTokenData};
const CHILDREN_CACHE_THRESHOLD: usize = 3;
#[derive(Debug)]
pub struct NodeCache<'i, I = TokenInterner> {
nodes: FxHashMap<GreenNodeHead, GreenNode>,
tokens: FxHashMap<GreenTokenData, GreenToken>,
interner: MaybeOwned<'i, I>,
}
impl NodeCache<'static> {
pub fn new() -> Self {
Self {
nodes: FxHashMap::default(),
tokens: FxHashMap::default(),
interner: MaybeOwned::Owned(new_interner()),
}
}
}
impl Default for NodeCache<'static> {
fn default() -> Self {
Self::new()
}
}
impl<'i, I> NodeCache<'i, I>
where
I: Interner<TokenKey>,
{
#[inline]
pub fn with_interner(interner: &'i mut I) -> Self {
Self {
nodes: FxHashMap::default(),
tokens: FxHashMap::default(),
interner: MaybeOwned::Borrowed(interner),
}
}
#[inline]
pub fn from_interner(interner: I) -> Self {
Self {
nodes: FxHashMap::default(),
tokens: FxHashMap::default(),
interner: MaybeOwned::Owned(interner),
}
}
#[inline]
pub fn interner(&self) -> &I {
&self.interner
}
#[inline]
pub fn interner_mut(&mut self) -> &mut I {
&mut self.interner
}
#[inline]
pub fn into_interner(self) -> Option<I> {
self.interner.into_owned()
}
fn node<S: Syntax>(&mut self, kind: S, all_children: &mut Vec<GreenElement>, offset: usize) -> GreenNode {
let kind = S::into_raw(kind);
let mut hasher = FxHasher::default();
let mut text_len: TextSize = 0.into();
for child in &all_children[offset..] {
text_len += child.text_len();
child.hash(&mut hasher);
}
let child_hash = hasher.finish() as u32;
let children = all_children.drain(offset..);
if children.len() <= CHILDREN_CACHE_THRESHOLD {
self.get_cached_node(kind, children, text_len, child_hash)
} else {
GreenNode::new_with_len_and_hash(kind, children, text_len, child_hash)
}
}
#[inline(always)]
fn intern(&mut self, text: &str) -> TokenKey {
self.interner.get_or_intern(text)
}
#[inline]
fn get_cached_node(
&mut self,
kind: RawSyntaxKind,
children: std::vec::Drain<'_, GreenElement>,
text_len: TextSize,
child_hash: u32,
) -> GreenNode {
let head = GreenNodeHead {
kind,
text_len,
child_hash,
};
self.nodes
.entry(head)
.or_insert_with_key(|head| GreenNode::from_head_and_children(head.clone(), children))
.clone()
}
fn token<S: Syntax>(&mut self, kind: S, text: Option<TokenKey>, len: u32) -> GreenToken {
let text_len = TextSize::from(len);
let kind = S::into_raw(kind);
let data = GreenTokenData { kind, text, text_len };
self.tokens
.entry(data)
.or_insert_with_key(|data| GreenToken::new(*data))
.clone()
}
}
#[derive(Clone, Copy, Debug)]
pub struct Checkpoint {
parent_idx: usize,
child_idx: usize,
}
#[derive(Debug)]
pub struct GreenNodeBuilder<'cache, 'interner, S: Syntax, I = TokenInterner> {
cache: MaybeOwned<'cache, NodeCache<'interner, I>>,
parents: Vec<(S, usize)>,
children: Vec<GreenElement>,
}
impl<S: Syntax> GreenNodeBuilder<'static, 'static, S> {
pub fn new() -> Self {
Self {
cache: MaybeOwned::Owned(NodeCache::new()),
parents: Vec::with_capacity(8),
children: Vec::with_capacity(8),
}
}
}
impl<S: Syntax> Default for GreenNodeBuilder<'static, 'static, S> {
fn default() -> Self {
Self::new()
}
}
impl<'cache, 'interner, S, I> GreenNodeBuilder<'cache, 'interner, S, I>
where
S: Syntax,
I: Interner<TokenKey>,
{
pub fn with_cache(cache: &'cache mut NodeCache<'interner, I>) -> Self {
Self {
cache: MaybeOwned::Borrowed(cache),
parents: Vec::with_capacity(8),
children: Vec::with_capacity(8),
}
}
pub fn from_cache(cache: NodeCache<'interner, I>) -> Self {
Self {
cache: MaybeOwned::Owned(cache),
parents: Vec::with_capacity(8),
children: Vec::with_capacity(8),
}
}
#[inline]
pub fn with_interner(interner: &'interner mut I) -> Self {
let cache = NodeCache::with_interner(interner);
Self::from_cache(cache)
}
#[inline]
pub fn from_interner(interner: I) -> Self {
let cache = NodeCache::from_interner(interner);
Self::from_cache(cache)
}
#[inline]
pub fn interner(&self) -> &I {
&self.cache.interner
}
#[inline]
pub fn interner_mut(&mut self) -> &mut I {
&mut self.cache.interner
}
#[inline]
pub fn token(&mut self, kind: S, text: &str) {
let token = match S::static_text(kind) {
Some(static_text) => {
debug_assert_eq!(
static_text, text,
r#"Received `{kind:?}` token which should have text "{static_text}", but "{text}" was given."#
);
self.cache.token::<S>(kind, None, static_text.len() as u32)
}
None => {
let len = text.len() as u32;
let text = self.cache.intern(text);
self.cache.token::<S>(kind, Some(text), len)
}
};
self.children.push(token.into());
}
#[inline]
pub fn static_token(&mut self, kind: S) {
let static_text = S::static_text(kind).unwrap_or_else(|| panic!("Missing static text for '{kind:?}'"));
let token = self.cache.token::<S>(kind, None, static_text.len() as u32);
self.children.push(token.into());
}
#[inline]
pub fn start_node(&mut self, kind: S) {
let len = self.children.len();
self.parents.push((kind, len));
}
#[inline]
pub fn finish_node(&mut self) {
let (kind, first_child) = self.parents.pop().unwrap();
let node = self.cache.node::<S>(kind, &mut self.children, first_child);
self.children.push(node.into());
}
#[inline]
pub fn checkpoint(&self) -> Checkpoint {
Checkpoint {
parent_idx: self.parents.len(),
child_idx: self.children.len(),
}
}
pub fn revert_to(&mut self, checkpoint: Checkpoint) {
let Checkpoint { parent_idx, child_idx } = checkpoint;
assert!(
parent_idx <= self.parents.len(),
"checkpoint no longer valid, was `finish_node` called early or did you already `revert_to`?"
);
assert!(
child_idx <= self.children.len(),
"checkpoint no longer valid after reverting to an earlier checkpoint"
);
if let Some(&(_, first_child)) = self.parents.last() {
assert!(
child_idx >= first_child,
"checkpoint no longer valid, was an unmatched start_node_at called?"
);
}
self.parents.truncate(parent_idx);
self.children.truncate(child_idx);
}
#[inline]
pub fn start_node_at(&mut self, checkpoint: Checkpoint, kind: S) {
let Checkpoint { parent_idx, child_idx } = checkpoint;
assert!(
parent_idx <= self.parents.len(),
"checkpoint no longer valid, was `finish_node` called early or did you already `revert_to`?"
);
assert!(
parent_idx >= self.parents.len(),
"checkpoint contains one or more unfinished nodes"
);
assert!(
child_idx <= self.children.len(),
"checkpoint no longer valid after reverting to an earlier checkpoint"
);
if let Some(&(_, first_child)) = self.parents.last() {
assert!(
child_idx >= first_child,
"checkpoint no longer valid, was an unmatched `start_node` or `start_node_at` called?"
);
}
self.parents.push((kind, child_idx));
}
#[inline]
pub fn finish(mut self) -> (GreenNode, Option<NodeCache<'interner, I>>) {
assert_eq!(self.children.len(), 1);
let cache = self.cache.into_owned();
match self.children.pop().unwrap() {
NodeOrToken::Node(node) => (node, cache),
NodeOrToken::Token(_) => panic!("called `finish` on a `GreenNodeBuilder` which only contained a token"),
}
}
}