use std::{ fmt, sync::Arc, hash::{Hasher, Hash}, ops::Range, }; use smol_str::SmolStr; use { yellow::{GreenNode, RedNode, TreeRoot, SyntaxRoot, RedPtr, RefRoot, OwnedRoot, SyntaxText}, SyntaxKind::{self, *}, TextRange, TextUnit, }; #[derive(Clone, Copy)] pub struct SyntaxNode { pub(crate) root: R, // Guaranteed to not dangle, because `root` holds a // strong reference to red's ancestor red: RedPtr, } unsafe impl Send for SyntaxNode {} unsafe impl Sync for SyntaxNode {} impl PartialEq> for SyntaxNode { fn eq(&self, other: &SyntaxNode) -> bool { self.red == other.red } } impl Eq for SyntaxNode {} impl Hash for SyntaxNode { fn hash(&self, state: &mut H) { self.red.hash(state) } } pub type SyntaxNodeRef<'a> = SyntaxNode>; #[test] fn syntax_node_ref_is_copy() { fn assert_copy(){} assert_copy::() } #[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)] pub struct SyntaxError { pub msg: String, pub offset: TextUnit, } impl SyntaxNode { pub(crate) fn new_owned(root: SyntaxRoot) -> Self { let root = OwnedRoot(Arc::new(root)); let red = RedPtr::new(&root.syntax_root().red); SyntaxNode { root, red } } } impl<'a> SyntaxNode> { pub(crate) fn leaf_text_ref(self) -> Option<&'a SmolStr> { let red = unsafe { self.red.get(self.root.syntax_root()) }; red.green().leaf_text_ref() } } impl SyntaxNode { pub fn borrowed<'a>(&'a self) -> SyntaxNodeRef<'a> { SyntaxNode { root: self.root.borrowed(), red: self.red, } } pub fn owned(&self) -> SyntaxNode { SyntaxNode { root: self.root.owned(), red: self.red, } } pub fn kind(&self) -> SyntaxKind { self.red().green().kind() } pub fn range(&self) -> TextRange { let red = self.red(); TextRange::offset_len(red.start_offset(), red.green().text_len()) } pub fn text(&self) -> SyntaxText { SyntaxText::new(self.borrowed()) } pub fn children(&self) -> SyntaxNodeChildren { SyntaxNodeChildren { parent: self.clone(), iter: (0..self.red().n_children()) } } pub fn parent(&self) -> Option> { let parent = self.red().parent()?; Some(SyntaxNode { root: self.root.clone(), red: parent, }) } pub fn first_child(&self) -> Option> { let red = self.red().get_child(0)?; Some(SyntaxNode { root: self.root.clone(), red }) } pub fn last_child(&self) -> Option> { let n = self.red().n_children(); let n = n.checked_sub(1)?; let red = self.red().get_child(n)?; Some(SyntaxNode { root: self.root.clone(), red }) } pub fn next_sibling(&self) -> Option> { let red = self.red(); let parent = self.parent()?; let next_sibling_idx = red.index_in_parent()? + 1; let sibling_red = parent.red().get_child(next_sibling_idx)?; Some(SyntaxNode { root: self.root.clone(), red: sibling_red, }) } pub fn prev_sibling(&self) -> Option> { let red = self.red(); let parent = self.parent()?; let prev_sibling_idx = red.index_in_parent()?.checked_sub(1)?; let sibling_red = parent.red().get_child(prev_sibling_idx)?; Some(SyntaxNode { root: self.root.clone(), red: sibling_red, }) } pub fn is_leaf(&self) -> bool { self.first_child().is_none() } pub fn leaf_text(&self) -> Option { self.borrowed().leaf_text_ref().map(|it| it.clone()) } pub(crate) fn replace_with(&self, green: GreenNode) -> GreenNode { assert_eq!(self.kind(), green.kind()); match self.parent() { None => green, Some(parent) => { let children: Vec<_> = parent.children().map(|child| { if child == *self { green.clone() } else { child.red().green().clone() } }).collect(); let new_parent = GreenNode::new_branch( parent.kind(), children.into_boxed_slice(), ); parent.replace_with(new_parent) }, } } fn red(&self) -> &RedNode { unsafe { self.red.get(self.root.syntax_root()) } } } impl fmt::Debug for SyntaxNode { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { write!(fmt, "{:?}@{:?}", self.kind(), self.range())?; if has_short_text(self.kind()) { write!(fmt, " \"{}\"", self.text())?; } Ok(()) } } #[derive(Debug)] pub struct SyntaxNodeChildren { parent: SyntaxNode, iter: Range, } impl Iterator for SyntaxNodeChildren { type Item = SyntaxNode; fn next(&mut self) -> Option> { self.iter.next().map(|i| { let red = self.parent.red(); SyntaxNode { root: self.parent.root.clone(), red: red.get_child(i).unwrap(), } }) } } fn has_short_text(kind: SyntaxKind) -> bool { match kind { IDENT | LIFETIME | INT_NUMBER | FLOAT_NUMBER => true, _ => false, } }