diff --git a/Cargo.toml b/Cargo.toml index 5c5bba7f..fcee9656 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,12 +1,12 @@ [package] name = "rowan" -version = "0.15.15" +version = "0.15.16" authors = ["Aleksey Kladov "] repository = "https://github.com/rust-analyzer/rowan" license = "MIT OR Apache-2.0" description = "Library for generic lossless syntax trees" edition = "2021" - +rust-version = "1.77.0" exclude = [".github/", "bors.toml", "rustfmt.toml"] [workspace] @@ -18,7 +18,6 @@ hashbrown = { version = "0.14.3", features = [ "inline-more", ], default-features = false } text-size = "1.1.0" -memoffset = "0.9" countme = "3.0.0" serde = { version = "1.0.89", optional = true, default-features = false } diff --git a/examples/math.rs b/examples/math.rs index 930c591b..e3c35056 100644 --- a/examples/math.rs +++ b/examples/math.rs @@ -111,7 +111,7 @@ impl> Parser { } fn print(indent: usize, element: SyntaxElement) { - let kind: SyntaxKind = element.kind().into(); + let kind: SyntaxKind = element.kind(); print!("{:indent$}", "", indent = indent); match element { NodeOrToken::Node(node) => { diff --git a/src/api.rs b/src/api.rs index 605fcc50..cd86d9d8 100644 --- a/src/api.rs +++ b/src/api.rs @@ -98,9 +98,13 @@ impl SyntaxNode { pub fn new_root(green: GreenNode) -> SyntaxNode { SyntaxNode::from(cursor::SyntaxNode::new_root(green)) } + pub fn new_root_mut(green: GreenNode) -> SyntaxNode { + SyntaxNode::from(cursor::SyntaxNode::new_root_mut(green)) + } + /// Returns a green tree, equal to the green tree this node - /// belongs two, except with this node substitute. The complexity - /// of operation is proportional to the depth of the tree + /// belongs to, except with this node substituted. The complexity + /// of the operation is proportional to the depth of the tree. pub fn replace_with(&self, replacement: GreenNode) -> GreenNode { self.raw.replace_with(replacement) } @@ -144,6 +148,13 @@ impl SyntaxNode { pub fn first_child(&self) -> Option> { self.raw.first_child().map(Self::from) } + + pub fn first_child_by_kind(&self, matcher: &impl Fn(L::Kind) -> bool) -> Option> { + self.raw + .first_child_by_kind(&|raw_kind| matcher(L::kind_from_raw(raw_kind))) + .map(Self::from) + } + pub fn last_child(&self) -> Option> { self.raw.last_child().map(Self::from) } @@ -151,6 +162,16 @@ impl SyntaxNode { pub fn first_child_or_token(&self) -> Option> { self.raw.first_child_or_token().map(NodeOrToken::from) } + + pub fn first_child_or_token_by_kind( + &self, + matcher: &impl Fn(L::Kind) -> bool, + ) -> Option> { + self.raw + .first_child_or_token_by_kind(&|raw_kind| matcher(L::kind_from_raw(raw_kind))) + .map(NodeOrToken::from) + } + pub fn last_child_or_token(&self) -> Option> { self.raw.last_child_or_token().map(NodeOrToken::from) } @@ -158,6 +179,16 @@ impl SyntaxNode { pub fn next_sibling(&self) -> Option> { self.raw.next_sibling().map(Self::from) } + + pub fn next_sibling_by_kind( + &self, + matcher: &impl Fn(L::Kind) -> bool, + ) -> Option> { + self.raw + .next_sibling_by_kind(&|raw_kind| matcher(L::kind_from_raw(raw_kind))) + .map(Self::from) + } + pub fn prev_sibling(&self) -> Option> { self.raw.prev_sibling().map(Self::from) } @@ -165,6 +196,16 @@ impl SyntaxNode { pub fn next_sibling_or_token(&self) -> Option> { self.raw.next_sibling_or_token().map(NodeOrToken::from) } + + pub fn next_sibling_or_token_by_kind( + &self, + matcher: &impl Fn(L::Kind) -> bool, + ) -> Option> { + self.raw + .next_sibling_or_token_by_kind(&|raw_kind| matcher(L::kind_from_raw(raw_kind))) + .map(NodeOrToken::from) + } + pub fn prev_sibling_or_token(&self) -> Option> { self.raw.prev_sibling_or_token().map(NodeOrToken::from) } @@ -210,7 +251,7 @@ impl SyntaxNode { } /// Find a token in the subtree corresponding to this node, which covers the offset. - /// Precondition: offset must be withing node's range. + /// Precondition: offset must be within node's range. pub fn token_at_offset(&self, offset: TextSize) -> TokenAtOffset> { self.raw.token_at_offset(offset).map(SyntaxToken::from) } @@ -218,7 +259,7 @@ impl SyntaxNode { /// Return the deepest node or token in the current subtree that fully /// contains the range. If the range is empty and is contained in two leaf /// nodes, either one can be returned. Precondition: range must be contained - /// withing the current node + /// within the current node pub fn covering_element(&self, range: TextRange) -> SyntaxElement { NodeOrToken::from(self.raw.covering_element(range)) } @@ -244,6 +285,10 @@ impl SyntaxNode { SyntaxNode::from(self.raw.clone_for_update()) } + pub fn is_mutable(&self) -> bool { + self.raw.is_mutable() + } + pub fn detach(&self) { self.raw.detach() } @@ -256,8 +301,8 @@ impl SyntaxNode { impl SyntaxToken { /// Returns a green tree, equal to the green tree this token - /// belongs two, except with this token substitute. The complexity - /// of operation is proportional to the depth of the tree + /// belongs to, except with this token substituted. The complexity + /// of the operation is proportional to the depth of the tree. pub fn replace_with(&self, new_token: GreenToken) -> GreenNode { self.raw.replace_with(new_token) } @@ -395,6 +440,15 @@ impl Iterator for SyntaxNodeChildren { } } +impl SyntaxNodeChildren { + pub fn by_kind(self, matcher: impl Fn(L::Kind) -> bool) -> impl Iterator> { + self.raw + .by_kind(move |raw_kind| matcher(L::kind_from_raw(raw_kind))) + .into_iter() + .map(SyntaxNode::from) + } +} + #[derive(Debug, Clone)] pub struct SyntaxElementChildren { raw: cursor::SyntaxElementChildren, @@ -408,6 +462,15 @@ impl Iterator for SyntaxElementChildren { } } +impl SyntaxElementChildren { + pub fn by_kind( + self, + matcher: impl Fn(L::Kind) -> bool, + ) -> impl Iterator> { + self.raw.by_kind(move |raw_kind| matcher(L::kind_from_raw(raw_kind))).map(NodeOrToken::from) + } +} + pub struct Preorder { raw: cursor::Preorder, _p: PhantomData, diff --git a/src/arc.rs b/src/arc.rs index 34883129..cb6758e6 100644 --- a/src/arc.rs +++ b/src/arc.rs @@ -4,7 +4,7 @@ use std::{ cmp::Ordering, hash::{Hash, Hasher}, marker::PhantomData, - mem::{self, ManuallyDrop}, + mem::{self, offset_of, ManuallyDrop}, ops::Deref, ptr, sync::atomic::{ @@ -13,8 +13,6 @@ use std::{ }, }; -use memoffset::offset_of; - /// A soft limit on the amount of references that may be made to an `Arc`. /// /// Going above this limit will abort your program (although not @@ -83,7 +81,7 @@ impl Arc { /// allocation #[inline] pub(crate) fn ptr_eq(this: &Self, other: &Self) -> bool { - this.ptr() == other.ptr() + std::ptr::addr_eq(this.ptr(), other.ptr()) } pub(crate) fn ptr(&self) -> *mut ArcInner { @@ -257,12 +255,9 @@ impl Deref for HeaderSlice { type Target = HeaderSlice; fn deref(&self) -> &Self::Target { - unsafe { - let len = self.length; - let fake_slice: *const [T] = - ptr::slice_from_raw_parts(self as *const _ as *const T, len); - &*(fake_slice as *const HeaderSlice) - } + let len = self.length; + let fake_slice: *const [T] = ptr::slice_from_raw_parts(self as *const _ as *const T, len); + unsafe { &*(fake_slice as *const HeaderSlice) } } } diff --git a/src/ast.rs b/src/ast.rs index 856a9f63..e142395b 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -55,6 +55,11 @@ pub trait AstNode { } /// A "pointer" to a [`SyntaxNode`], via location in the source code. +/// +/// ## Note +/// Since the location is source code dependent, this must not be used +/// with mutable syntax trees. Any changes made in such trees causes +/// the pointed node's source location to change, invalidating the pointer. #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub struct SyntaxNodePtr { kind: L::Kind, @@ -63,7 +68,10 @@ pub struct SyntaxNodePtr { impl SyntaxNodePtr { /// Returns a [`SyntaxNodePtr`] for the node. + /// + /// Panics if the provided node is mutable pub fn new(node: &SyntaxNode) -> Self { + assert!(!node.is_mutable(), "tree is mutable"); Self { kind: node.kind(), range: node.text_range() } } @@ -82,10 +90,13 @@ impl SyntaxNodePtr { /// Also returns `None` if `root` is not actually a root (i.e. it has a /// parent). /// + /// NOTE: If this function is called on a mutable tree, it will panic + /// /// The complexity is linear in the depth of the tree and logarithmic in /// tree width. As most trees are shallow, thinking about this as /// `O(log(N))` in the size of the tree is not too wrong! pub fn try_to_node(&self, root: &SyntaxNode) -> Option> { + assert!(!root.is_mutable(), "tree is mutable"); if root.parent().is_some() { return None; } @@ -113,13 +124,21 @@ impl SyntaxNodePtr { } /// Like [`SyntaxNodePtr`], but remembers the type of node. +/// +/// ## Note +/// As with [`SyntaxNodePtr`], this must not be used on mutable +/// syntax trees, since any mutation can cause the pointed node's +/// source location to change, invalidating the pointer pub struct AstPtr { raw: SyntaxNodePtr, } impl AstPtr { /// Returns an [`AstPtr`] for the node. + /// + /// Panics if the provided node is mutable pub fn new(node: &N) -> Self { + // The above mentioned panic is handled by SyntaxNodePtr Self { raw: SyntaxNodePtr::new(node.syntax()) } } @@ -129,8 +148,9 @@ impl AstPtr { } /// Given the root node containing the node `n` that `self` is a pointer to, - /// returns `n` if possible. See [`SyntaxNodePtr::try_to_node`]. + /// returns `n` if possible. Panics if `root` is mutable. See [`SyntaxNodePtr::try_to_node`]. pub fn try_to_node(&self, root: &SyntaxNode) -> Option { + // The above mentioned panic is handled by SyntaxNodePtr N::cast(self.raw.try_to_node(root)?) } @@ -215,3 +235,53 @@ pub mod support { parent.children_with_tokens().filter_map(|it| it.into_token()).find(|it| it.kind() == kind) } } + +#[cfg(test)] +mod tests { + use crate::{GreenNodeBuilder, Language, SyntaxKind, SyntaxNode}; + + use super::SyntaxNodePtr; + + #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] + struct TestLanguage; + impl Language for TestLanguage { + type Kind = SyntaxKind; + + fn kind_from_raw(raw: SyntaxKind) -> Self::Kind { + raw + } + + fn kind_to_raw(kind: Self::Kind) -> SyntaxKind { + kind + } + } + + fn build_immut_tree() -> SyntaxNode { + // Creates a single-node tree + let mut builder = GreenNodeBuilder::new(); + builder.start_node(SyntaxKind(0)); + builder.finish_node(); + + SyntaxNode::::new_root(builder.finish()) + } + + #[test] + #[should_panic = "tree is mutable"] + fn ensure_mut_panic_on_create() { + // Make a mutable version + let tree = build_immut_tree().clone_for_update(); + + SyntaxNodePtr::new(&tree); + } + + #[test] + #[should_panic = "tree is mutable"] + fn ensure_mut_panic_on_deref() { + let tree = build_immut_tree(); + let tree_mut = tree.clone_for_update(); + + // Create on immutable, convert on mutable + let syn_ptr = SyntaxNodePtr::new(&tree); + syn_ptr.to_node(&tree_mut); + } +} diff --git a/src/cursor.rs b/src/cursor.rs index 671be234..a6d84c68 100644 --- a/src/cursor.rs +++ b/src/cursor.rs @@ -376,11 +376,26 @@ impl NodeData { } fn next_sibling(&self) -> Option { - let mut siblings = self.green_siblings().enumerate(); + let siblings = self.green_siblings().enumerate(); + let index = self.index() as usize; + + siblings.skip(index + 1).find_map(|(index, child)| { + child.as_ref().into_node().and_then(|green| { + let parent = self.parent_node()?; + let offset = parent.offset() + child.rel_offset(); + Some(SyntaxNode::new_child(green, parent, index as u32, offset)) + }) + }) + } + + fn next_sibling_by_kind(&self, matcher: &impl Fn(SyntaxKind) -> bool) -> Option { + let siblings = self.green_siblings().enumerate(); let index = self.index() as usize; - siblings.nth(index); - siblings.find_map(|(index, child)| { + siblings.skip(index + 1).find_map(|(index, child)| { + if !matcher(child.as_ref().kind()) { + return None; + } child.as_ref().into_node().and_then(|green| { let parent = self.parent_node()?; let offset = parent.offset() + child.rel_offset(); @@ -388,12 +403,12 @@ impl NodeData { }) }) } + fn prev_sibling(&self) -> Option { - let mut rev_siblings = self.green_siblings().enumerate().rev(); - let index = rev_siblings.len().checked_sub(self.index() as usize + 1)?; + let rev_siblings = self.green_siblings().enumerate().rev(); + let index = rev_siblings.len().checked_sub(self.index() as usize)?; - rev_siblings.nth(index); - rev_siblings.find_map(|(index, child)| { + rev_siblings.skip(index + 1).find_map(|(index, child)| { child.as_ref().into_node().and_then(|green| { let parent = self.parent_node()?; let offset = parent.offset() + child.rel_offset(); @@ -412,6 +427,24 @@ impl NodeData { Some(SyntaxElement::new(child.as_ref(), parent, index as u32, offset)) }) } + + fn next_sibling_or_token_by_kind( + &self, + matcher: &impl Fn(SyntaxKind) -> bool, + ) -> Option { + let siblings = self.green_siblings().enumerate(); + let index = self.index() as usize; + + siblings.skip(index + 1).find_map(|(index, child)| { + if !matcher(child.as_ref().kind()) { + return None; + } + let parent = self.parent_node()?; + let offset = parent.offset() + child.rel_offset(); + Some(SyntaxElement::new(child.as_ref(), parent, index as u32, offset)) + }) + } + fn prev_sibling_or_token(&self) -> Option { let mut siblings = self.green_siblings().enumerate(); let index = self.index().checked_sub(1)? as usize; @@ -431,67 +464,63 @@ impl NodeData { None => return, }; - unsafe { - sll::adjust(self, self.index() + 1, Delta::Sub(1)); - let parent = parent_ptr.as_ref(); - sll::unlink(&parent.first, self); - - // Add strong ref to green - match self.green().to_owned() { - NodeOrToken::Node(it) => { - GreenNode::into_raw(it); - } - NodeOrToken::Token(it) => { - GreenToken::into_raw(it); - } - } + sll::adjust(self, self.index() + 1, Delta::Sub(1)); + let parent = unsafe { parent_ptr.as_ref() }; + sll::unlink(&parent.first, self); - match parent.green() { - NodeOrToken::Node(green) => { - let green = green.remove_child(self.index() as usize); - parent.respine(green) - } - NodeOrToken::Token(_) => unreachable!(), + // Add strong ref to green + match self.green().to_owned() { + NodeOrToken::Node(it) => { + GreenNode::into_raw(it); + } + NodeOrToken::Token(it) => { + GreenToken::into_raw(it); } + } - if parent.dec_rc() { - free(parent_ptr) + match parent.green() { + NodeOrToken::Node(green) => { + let green = green.remove_child(self.index() as usize); + unsafe { parent.respine(green) } } + NodeOrToken::Token(_) => unreachable!(), + } + + if parent.dec_rc() { + unsafe { free(parent_ptr) } } } fn attach_child(&self, index: usize, child: &NodeData) { assert!(self.mutable && child.mutable && child.parent().is_none()); assert!(self.rc.get() > 0 && child.rc.get() > 0); - unsafe { - child.index.set(index as u32); - child.parent.set(Some(self.into())); - self.inc_rc(); + child.index.set(index as u32); + child.parent.set(Some(self.into())); + self.inc_rc(); - if !self.first.get().is_null() { - sll::adjust(&*self.first.get(), index as u32, Delta::Add(1)); - } + if !self.first.get().is_null() { + sll::adjust(unsafe { &*self.first.get() }, index as u32, Delta::Add(1)); + } - match sll::link(&self.first, child) { - sll::AddToSllResult::AlreadyInSll(_) => { - panic!("Child already in sorted linked list") - } - it => it.add_to_sll(child), + match sll::link(&self.first, child) { + sll::AddToSllResult::AlreadyInSll(_) => { + panic!("Child already in sorted linked list") } + it => it.add_to_sll(child), + } - match self.green() { - NodeOrToken::Node(green) => { - // Child is root, so it ownes the green node. Steal it! - let child_green = match &child.green { - Green::Node { ptr } => GreenNode::from_raw(ptr.get()).into(), - Green::Token { ptr } => GreenToken::from_raw(*ptr).into(), - }; + match self.green() { + NodeOrToken::Node(green) => { + // Child is root, so it owns the green node. Steal it! + let child_green = match &child.green { + Green::Node { ptr } => unsafe { GreenNode::from_raw(ptr.get()).into() }, + Green::Token { ptr } => unsafe { GreenToken::from_raw(*ptr).into() }, + }; - let green = green.insert_child(index, child_green); - self.respine(green); - } - NodeOrToken::Token(_) => unreachable!(), + let green = green.insert_child(index, child_green); + unsafe { self.respine(green) }; } + NodeOrToken::Token(_) => unreachable!(), } } unsafe fn respine(&self, mut new_green: GreenNode) { @@ -544,6 +573,10 @@ impl SyntaxNode { SyntaxNode { ptr: NodeData::new(Some(parent), index, offset, green, mutable) } } + pub fn is_mutable(&self) -> bool { + self.data().mutable + } + pub fn clone_for_update(&self) -> SyntaxNode { assert!(!self.data().mutable); match self.parent() { @@ -564,6 +597,20 @@ impl SyntaxNode { unsafe { self.ptr.as_ref() } } + #[inline] + fn can_take_ptr(&self) -> bool { + self.data().rc.get() == 1 && !self.data().mutable + } + + #[inline] + fn take_ptr(self) -> ptr::NonNull { + assert!(self.can_take_ptr()); + let ret = self.ptr; + // don't change the refcount when self gets dropped + std::mem::forget(self); + ret + } + pub fn replace_with(&self, replacement: GreenNode) -> GreenNode { assert_eq!(self.kind(), replacement.kind()); match &self.parent() { @@ -647,6 +694,23 @@ impl SyntaxNode { }) }) } + + pub fn first_child_by_kind(&self, matcher: &impl Fn(SyntaxKind) -> bool) -> Option { + self.green_ref().children().raw.enumerate().find_map(|(index, child)| { + if !matcher(child.as_ref().kind()) { + return None; + } + child.as_ref().into_node().map(|green| { + SyntaxNode::new_child( + green, + self.clone(), + index as u32, + self.offset() + child.rel_offset(), + ) + }) + }) + } + pub fn last_child(&self) -> Option { self.green_ref().children().raw.enumerate().rev().find_map(|(index, child)| { child.as_ref().into_node().map(|green| { @@ -665,6 +729,24 @@ impl SyntaxNode { SyntaxElement::new(child.as_ref(), self.clone(), 0, self.offset() + child.rel_offset()) }) } + + pub fn first_child_or_token_by_kind( + &self, + matcher: &impl Fn(SyntaxKind) -> bool, + ) -> Option { + self.green_ref().children().raw.enumerate().find_map(|(index, child)| { + if !matcher(child.as_ref().kind()) { + return None; + } + Some(SyntaxElement::new( + child.as_ref(), + self.clone(), + index as u32, + self.offset() + child.rel_offset(), + )) + }) + } + pub fn last_child_or_token(&self) -> Option { self.green_ref().children().raw.enumerate().next_back().map(|(index, child)| { SyntaxElement::new( @@ -676,9 +758,55 @@ impl SyntaxNode { }) } + // if possible (i.e. unshared), consume self and advance it to point to the next sibling + // this way, we can reuse the previously allocated buffer + pub fn to_next_sibling(self) -> Option { + if !self.can_take_ptr() { + // cannot mutate in-place + return self.next_sibling(); + } + + let mut ptr = self.take_ptr(); + let data = unsafe { ptr.as_mut() }; + assert!(data.rc.get() == 1); + + let parent = data.parent_node()?; + let parent_offset = parent.offset(); + let siblings = parent.green_ref().children().raw.enumerate(); + let index = data.index() as usize; + + siblings + .skip(index + 1) + .find_map(|(index, child)| { + child + .as_ref() + .into_node() + .and_then(|green| Some((green, index as u32, child.rel_offset()))) + }) + .and_then(|(green, index, rel_offset)| { + data.index.set(index); + data.offset = parent_offset + rel_offset; + data.green = Green::Node { ptr: Cell::new(green.into()) }; + Some(SyntaxNode { ptr }) + }) + .or_else(|| { + data.dec_rc(); + unsafe { free(ptr) }; + None + }) + } + pub fn next_sibling(&self) -> Option { self.data().next_sibling() } + + pub fn next_sibling_by_kind( + &self, + matcher: &impl Fn(SyntaxKind) -> bool, + ) -> Option { + self.data().next_sibling_by_kind(matcher) + } + pub fn prev_sibling(&self) -> Option { self.data().prev_sibling() } @@ -686,6 +814,14 @@ impl SyntaxNode { pub fn next_sibling_or_token(&self) -> Option { self.data().next_sibling_or_token() } + + pub fn next_sibling_or_token_by_kind( + &self, + matcher: &impl Fn(SyntaxKind) -> bool, + ) -> Option { + self.data().next_sibling_or_token_by_kind(matcher) + } + pub fn prev_sibling_or_token(&self) -> Option { self.data().prev_sibling_or_token() } @@ -853,6 +989,20 @@ impl SyntaxToken { unsafe { self.ptr.as_ref() } } + #[inline] + fn can_take_ptr(&self) -> bool { + self.data().rc.get() == 1 && !self.data().mutable + } + + #[inline] + fn take_ptr(self) -> ptr::NonNull { + assert!(self.can_take_ptr()); + let ret = self.ptr; + // don't change the refcount when self gets dropped + std::mem::forget(self); + ret + } + pub fn replace_with(&self, replacement: GreenToken) -> GreenNode { assert_eq!(self.kind(), replacement.kind()); let parent = self.parent().unwrap(); @@ -910,6 +1060,14 @@ impl SyntaxToken { pub fn next_sibling_or_token(&self) -> Option { self.data().next_sibling_or_token() } + + pub fn next_sibling_or_token_by_kind( + &self, + matcher: &impl Fn(SyntaxKind) -> bool, + ) -> Option { + self.data().next_sibling_or_token_by_kind(matcher) + } + pub fn prev_sibling_or_token(&self) -> Option { self.data().prev_sibling_or_token() } @@ -1028,6 +1186,71 @@ impl SyntaxElement { NodeOrToken::Token(it) => it.next_sibling_or_token(), } } + + fn can_take_ptr(&self) -> bool { + match self { + NodeOrToken::Node(it) => it.can_take_ptr(), + NodeOrToken::Token(it) => it.can_take_ptr(), + } + } + + fn take_ptr(self) -> ptr::NonNull { + match self { + NodeOrToken::Node(it) => it.take_ptr(), + NodeOrToken::Token(it) => it.take_ptr(), + } + } + + // if possible (i.e. unshared), consume self and advance it to point to the next sibling + // this way, we can reuse the previously allocated buffer + pub fn to_next_sibling_or_token(self) -> Option { + if !self.can_take_ptr() { + // cannot mutate in-place + return self.next_sibling_or_token(); + } + + let mut ptr = self.take_ptr(); + let data = unsafe { ptr.as_mut() }; + + let parent = data.parent_node()?; + let parent_offset = parent.offset(); + let siblings = parent.green_ref().children().raw.enumerate(); + let index = data.index() as usize; + + siblings + .skip(index + 1) + .find_map(|(index, green)| { + data.index.set(index as u32); + data.offset = parent_offset + green.rel_offset(); + + match green.as_ref() { + NodeOrToken::Node(node) => { + data.green = Green::Node { ptr: Cell::new(node.into()) }; + Some(SyntaxElement::Node(SyntaxNode { ptr })) + } + NodeOrToken::Token(token) => { + data.green = Green::Token { ptr: token.into() }; + Some(SyntaxElement::Token(SyntaxToken { ptr })) + } + } + }) + .or_else(|| { + data.dec_rc(); + unsafe { free(ptr) }; + None + }) + } + + pub fn next_sibling_or_token_by_kind( + &self, + matcher: &impl Fn(SyntaxKind) -> bool, + ) -> Option { + match self { + NodeOrToken::Node(it) => it.next_sibling_or_token_by_kind(matcher), + NodeOrToken::Token(it) => it.next_sibling_or_token_by_kind(matcher), + } + } + pub fn prev_sibling_or_token(&self) -> Option { match self { NodeOrToken::Node(it) => it.prev_sibling_or_token(), @@ -1133,20 +1356,59 @@ impl From for SyntaxElement { #[derive(Clone, Debug)] pub struct SyntaxNodeChildren { + parent: SyntaxNode, next: Option, + next_initialized: bool, } impl SyntaxNodeChildren { fn new(parent: SyntaxNode) -> SyntaxNodeChildren { - SyntaxNodeChildren { next: parent.first_child() } + SyntaxNodeChildren { parent, next: None, next_initialized: false } + } + + pub fn by_kind bool>(self, matcher: F) -> SyntaxNodeChildrenByKind { + if !self.next_initialized { + SyntaxNodeChildrenByKind { next: self.parent.first_child_by_kind(&matcher), matcher } + } else { + SyntaxNodeChildrenByKind { + next: self.next.and_then(|node| { + if matcher(node.kind()) { + Some(node) + } else { + node.next_sibling_by_kind(&matcher) + } + }), + matcher, + } + } } } impl Iterator for SyntaxNodeChildren { + type Item = SyntaxNode; + fn next(&mut self) -> Option { + if !self.next_initialized { + self.next = self.parent.first_child(); + self.next_initialized = true; + } else { + self.next = self.next.take().and_then(|next| next.to_next_sibling()); + } + + self.next.clone() + } +} + +#[derive(Clone, Debug)] +pub struct SyntaxNodeChildrenByKind bool> { + next: Option, + matcher: F, +} + +impl bool> Iterator for SyntaxNodeChildrenByKind { type Item = SyntaxNode; fn next(&mut self) -> Option { self.next.take().map(|next| { - self.next = next.next_sibling(); + self.next = next.next_sibling_by_kind(&self.matcher); next }) } @@ -1154,20 +1416,62 @@ impl Iterator for SyntaxNodeChildren { #[derive(Clone, Debug)] pub struct SyntaxElementChildren { + parent: SyntaxNode, next: Option, + next_initialized: bool, } impl SyntaxElementChildren { fn new(parent: SyntaxNode) -> SyntaxElementChildren { - SyntaxElementChildren { next: parent.first_child_or_token() } + SyntaxElementChildren { parent, next: None, next_initialized: false } + } + + pub fn by_kind bool>(self, matcher: F) -> SyntaxElementChildrenByKind { + if !self.next_initialized { + SyntaxElementChildrenByKind { + next: self.parent.first_child_or_token_by_kind(&matcher), + matcher, + } + } else { + SyntaxElementChildrenByKind { + next: self.next.and_then(|node| { + if matcher(node.kind()) { + Some(node) + } else { + node.next_sibling_or_token_by_kind(&matcher) + } + }), + matcher, + } + } } } impl Iterator for SyntaxElementChildren { + type Item = SyntaxElement; + fn next(&mut self) -> Option { + if !self.next_initialized { + self.next = self.parent.first_child_or_token(); + self.next_initialized = true; + } else { + self.next = self.next.take().and_then(|next| next.to_next_sibling_or_token()); + } + + self.next.clone() + } +} + +#[derive(Clone, Debug)] +pub struct SyntaxElementChildrenByKind bool> { + next: Option, + matcher: F, +} + +impl bool> Iterator for SyntaxElementChildrenByKind { type Item = SyntaxElement; fn next(&mut self) -> Option { self.next.take().map(|next| { - self.next = next.next_sibling_or_token(); + self.next = next.next_sibling_or_token_by_kind(&self.matcher); next }) } diff --git a/src/green/builder.rs b/src/green/builder.rs index 1dbc8bb0..03d66df5 100644 --- a/src/green/builder.rs +++ b/src/green/builder.rs @@ -1,3 +1,5 @@ +use std::num::NonZeroUsize; + use crate::{ cow_mut::CowMut, green::{node_cache::NodeCache, GreenElement, GreenNode, SyntaxKind}, @@ -6,7 +8,17 @@ use crate::{ /// A checkpoint for maybe wrapping a node. See `GreenNodeBuilder::checkpoint` for details. #[derive(Clone, Copy, Debug)] -pub struct Checkpoint(usize); +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 + } +} /// A builder for a green tree. #[derive(Default, Debug)] @@ -82,14 +94,14 @@ impl GreenNodeBuilder<'_> { /// ``` #[inline] pub fn checkpoint(&self) -> Checkpoint { - Checkpoint(self.children.len()) + Checkpoint::new(self.children.len()) } /// Wrap the previous branch marked by `checkpoint` in a new branch and /// make it current. #[inline] pub fn start_node_at(&mut self, checkpoint: Checkpoint, kind: SyntaxKind) { - let Checkpoint(checkpoint) = checkpoint; + let checkpoint = checkpoint.into_inner(); assert!( checkpoint <= self.children.len(), "checkpoint no longer valid, was finish_node called early?" diff --git a/src/green/node.rs b/src/green/node.rs index e94dc01c..69b1cf76 100644 --- a/src/green/node.rs +++ b/src/green/node.rs @@ -56,11 +56,9 @@ impl ToOwned for GreenNodeData { #[inline] fn to_owned(&self) -> GreenNode { - unsafe { - let green = GreenNode::from_raw(ptr::NonNull::from(self)); - let green = ManuallyDrop::new(green); - GreenNode::clone(&green) - } + let green = unsafe { GreenNode::from_raw(ptr::NonNull::from(self)) }; + let green = ManuallyDrop::new(green); + GreenNode::clone(&green) } } @@ -194,8 +192,8 @@ impl ops::Deref for GreenNode { #[inline] fn deref(&self) -> &GreenNodeData { + let repr: &Repr = &self.ptr; unsafe { - let repr: &Repr = &self.ptr; let repr: &ReprThin = &*(repr as *const Repr as *const ReprThin); mem::transmute::<&ReprThin, &GreenNodeData>(repr) } diff --git a/src/green/token.rs b/src/green/token.rs index 1a4548a4..18e1434c 100644 --- a/src/green/token.rs +++ b/src/green/token.rs @@ -1,6 +1,7 @@ use std::{ borrow::Borrow, fmt, + hash::{Hash, Hasher}, mem::{self, ManuallyDrop}, ops, ptr, }; @@ -21,6 +22,7 @@ struct GreenTokenHead { type Repr = HeaderSlice; type ReprThin = HeaderSlice; +#[derive(Eq)] #[repr(transparent)] pub struct GreenTokenData { data: ReprThin, @@ -32,6 +34,12 @@ impl PartialEq for GreenTokenData { } } +impl Hash for GreenTokenData { + fn hash(&self, state: &mut H) { + (*self.data).hash(state); + } +} + /// Leaf node in the immutable tree. #[derive(PartialEq, Eq, Hash, Clone)] #[repr(transparent)] @@ -44,11 +52,9 @@ impl ToOwned for GreenTokenData { #[inline] fn to_owned(&self) -> GreenToken { - unsafe { - let green = GreenToken::from_raw(ptr::NonNull::from(self)); - let green = ManuallyDrop::new(green); - GreenToken::clone(&green) - } + let green = unsafe { GreenToken::from_raw(ptr::NonNull::from(self)) }; + let green = ManuallyDrop::new(green); + GreenToken::clone(&green) } } @@ -143,3 +149,18 @@ impl ops::Deref for GreenToken { } } } + +#[cfg(test)] +mod tests { + use std::hash::{BuildHasher, RandomState}; + + use super::*; + + #[test] + fn hash_borrow() { + let owned = GreenToken::new(SyntaxKind(42), "foobar"); + let borrowed: &GreenTokenData = owned.borrow(); + let s = RandomState::new(); + assert_eq!(s.hash_one(&owned), s.hash_one(borrowed)); + } +} diff --git a/src/sll.rs b/src/sll.rs index 87d2f1f3..4a298390 100644 --- a/src/sll.rs +++ b/src/sll.rs @@ -84,13 +84,12 @@ pub(crate) fn unlink(head: &Cell<*const E>, elem: &E) { #[cold] pub(crate) fn link<'a, E: Elem>(head: &'a Cell<*const E>, elem: &E) -> AddToSllResult<'a, E> { + let old_head = head.get(); + // Case 1: empty head, replace it. + if old_head.is_null() { + return AddToSllResult::EmptyHead(head); + } unsafe { - let old_head = head.get(); - // Case 1: empty head, replace it. - if old_head.is_null() { - return AddToSllResult::EmptyHead(head); - } - // Case 2: we are smaller than the head, replace it. if elem.key() < (*old_head).key() { return AddToSllResult::SmallerThanHead(head);