diff --git a/Cargo.toml b/Cargo.toml index 8c8cd83..8a93690 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rowan" -version = "0.10.0" +version = "0.10.1" authors = ["Aleksey Kladov "] repository = "https://github.com/rust-analyzer/rowan" license = "MIT OR Apache-2.0" @@ -8,12 +8,12 @@ description = "Library for generic lossless syntax trees" edition = "2018" [dependencies] -erasable = "1.2.1" rustc-hash = "1.0.1" -serde = { version = "1.0.89", optional = true, default-features = false } -slice-dst = "1.4.1" smol_str = "0.1.10" -text-size = "1.0.0" +text-size = "1.1.0" +triomphe = "0.1.1" + +serde = { version = "1.0.89", optional = true, default-features = false } [dev-dependencies] m_lexer = "0.0.4" diff --git a/src/api.rs b/src/api.rs index 9fb1390..16daabe 100644 --- a/src/api.rs +++ b/src/api.rs @@ -149,6 +149,9 @@ impl SyntaxNode { pub fn new_root(green: GreenNode) -> SyntaxNode { SyntaxNode::from(cursor::SyntaxNode::new_root(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 pub fn replace_with(&self, replacement: GreenNode) -> GreenNode { self.raw.replace_with(replacement) } @@ -217,10 +220,12 @@ impl SyntaxNode { self.raw.prev_sibling_or_token().map(NodeOrToken::from) } + /// Return the leftmost token in the subtree of this node. pub fn first_token(&self) -> Option> { self.raw.first_token().map(SyntaxToken::from) } + /// Return the rightmost token in the subtree of this node. pub fn last_token(&self) -> Option> { self.raw.last_token().map(SyntaxToken::from) } @@ -244,24 +249,41 @@ impl SyntaxNode { self.raw.descendants_with_tokens().map(NodeOrToken::from) } + /// Traverse the subtree rooted at the current node (including the current + /// node) in preorder, excluding tokens. pub fn preorder(&self) -> impl Iterator>> { self.raw.preorder().map(|event| event.map(SyntaxNode::from)) } + /// Traverse the subtree rooted at the current node (including the current + /// node) in preorder, including tokens. pub fn preorder_with_tokens(&self) -> impl Iterator>> { self.raw.preorder_with_tokens().map(|event| event.map(NodeOrToken::from)) } + /// Find a token in the subtree corresponding to this node, which covers the offset. + /// Precondition: offset must be withing node's range. pub fn token_at_offset(&self, offset: TextSize) -> TokenAtOffset> { self.raw.token_at_offset(offset).map(SyntaxToken::from) } + /// 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 pub fn covering_element(&self, range: TextRange) -> SyntaxElement { NodeOrToken::from(self.raw.covering_element(range)) } + + pub fn child_or_token_at_range(&self, range: TextRange) -> Option> { + self.raw.child_or_token_at_range(range).map(SyntaxElement::from) + } } 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 pub fn replace_with(&self, new_token: GreenToken) -> GreenNode { self.raw.replace_with(new_token) } @@ -305,10 +327,12 @@ impl SyntaxToken { self.raw.siblings_with_tokens(direction).map(SyntaxElement::from) } + /// Next token in the tree (i.e, not necessary a sibling). pub fn next_token(&self) -> Option> { self.raw.next_token().map(SyntaxToken::from) } + /// Previous token in the tree (i.e, not necessary a sibling). pub fn prev_token(&self) -> Option> { self.raw.prev_token().map(SyntaxToken::from) } diff --git a/src/cursor.rs b/src/cursor.rs index db6901b..364a5cd 100644 --- a/src/cursor.rs +++ b/src/cursor.rs @@ -209,23 +209,12 @@ impl SyntaxNode { SyntaxNode::new(data) } - /// 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 pub fn replace_with(&self, replacement: GreenNode) -> GreenNode { assert_eq!(self.kind(), replacement.kind()); match self.0.kind.as_child() { None => replacement, Some((parent, me, _offset)) => { - let mut replacement = Some(replacement); - let children = parent.green().children().enumerate().map(|(i, child)| { - if i as u32 == me { - replacement.take().unwrap().into() - } else { - child.cloned() - } - }); - let new_parent = GreenNode::new(parent.kind(), children); + let new_parent = parent.green().replace_child(me as usize, replacement.into()); parent.replace_with(new_parent) } } @@ -342,13 +331,11 @@ impl SyntaxNode { Some(SyntaxElement::new(element, parent.clone(), index as u32, offset)) } - /// Return the leftmost token in the subtree of this node #[inline] pub fn first_token(&self) -> Option { self.first_child_or_token()?.first_token() } - /// Return the rightmost token in the subtree of this node #[inline] pub fn last_token(&self) -> Option { self.last_child_or_token()?.last_token() @@ -386,8 +373,6 @@ impl SyntaxNode { }) } - /// Traverse the subtree rooted at the current node (including the current - /// node) in preorder, excluding tokens. #[inline] pub fn preorder(&self) -> impl Iterator> { let this = self.clone(); @@ -411,8 +396,6 @@ impl SyntaxNode { }) } - /// Traverse the subtree rooted at the current node (including the current - /// node) in preorder, including tokens. #[inline] pub fn preorder_with_tokens<'a>(&'a self) -> impl Iterator> { let start: SyntaxElement = self.clone().into(); @@ -439,8 +422,6 @@ impl SyntaxNode { }) } - /// Find a token in the subtree corresponding to this node, which covers the offset. - /// Precondition: offset must be withing node's range. pub fn token_at_offset(&self, offset: TextSize) -> TokenAtOffset { // TODO: this could be faster if we first drill-down to node, and only // then switch to token search. We should also replace explicit @@ -478,10 +459,6 @@ 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 pub fn covering_element(&self, range: TextRange) -> SyntaxElement { let mut res: SyntaxElement = self.clone().into(); loop { @@ -493,18 +470,29 @@ impl SyntaxNode { ); res = match &res { NodeOrToken::Token(_) => return res, - NodeOrToken::Node(node) => { - match node - .children_with_tokens() - .find(|child| child.text_range().contains_range(range)) - { - Some(child) => child, - None => return res, - } - } + NodeOrToken::Node(node) => match node.child_or_token_at_range(range) { + Some(it) => it, + None => return res, + }, }; } } + + pub fn child_or_token_at_range(&self, range: TextRange) -> Option { + let start_offset = self.text_range().start(); + let (index, offset, child) = self.green().child_at_range(range - start_offset)?; + let index = index as u32; + let offset = offset + start_offset; + let res: SyntaxElement = match child { + NodeOrToken::Node(node) => { + let data = + NodeData::new(Kind::Child { parent: self.clone(), index, offset }, node.into()); + SyntaxNode::new(data).into() + } + NodeOrToken::Token(_token) => SyntaxToken::new(self.clone(), index, offset).into(), + }; + Some(res) + } } impl SyntaxToken { @@ -512,9 +500,6 @@ impl SyntaxToken { SyntaxToken { parent, index, offset } } - /// 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 pub fn replace_with(&self, replacement: GreenToken) -> GreenNode { assert_eq!(self.kind(), replacement.kind()); let mut replacement = Some(replacement); @@ -588,7 +573,6 @@ impl SyntaxToken { }) } - /// Next token in the tree (i.e, not necessary a sibling) pub fn next_token(&self) -> Option { match self.next_sibling_or_token() { Some(element) => element.first_token(), @@ -599,7 +583,7 @@ impl SyntaxToken { .and_then(|element| element.first_token()), } } - /// Previous token in the tree (i.e, not necessary a sibling) + pub fn prev_token(&self) -> Option { match self.prev_sibling_or_token() { Some(element) => element.last_token(), diff --git a/src/green.rs b/src/green.rs index 6b004ad..740d015 100644 --- a/src/green.rs +++ b/src/green.rs @@ -3,8 +3,8 @@ mod token; mod element; mod builder; +use self::element::GreenElement; pub(crate) use self::element::GreenElementRef; -use self::element::{GreenElement, PackedGreenElement}; pub use self::{ builder::{Checkpoint, GreenNodeBuilder, NodeCache}, @@ -26,7 +26,6 @@ mod tests { f::(); f::(); f::(); - f::(); } #[test] @@ -36,6 +35,5 @@ mod tests { eprintln!("GreenNode {}", size_of::()); eprintln!("GreenToken {}", size_of::()); eprintln!("GreenElement {}", size_of::()); - eprintln!("PackedGreenElement {}", size_of::()); } } diff --git a/src/green/element.rs b/src/green/element.rs index 67d23f3..db314f0 100644 --- a/src/green/element.rs +++ b/src/green/element.rs @@ -1,7 +1,3 @@ -use std::{fmt, hash, mem}; - -use erasable::ErasedPtr; - use crate::{ green::{GreenNode, GreenToken, SyntaxKind}, NodeOrToken, TextSize, @@ -10,11 +6,6 @@ use crate::{ pub(super) type GreenElement = NodeOrToken; pub(crate) type GreenElementRef<'a> = NodeOrToken<&'a GreenNode, &'a GreenToken>; -#[repr(transparent)] -pub(super) struct PackedGreenElement { - ptr: ErasedPtr, -} - impl From for GreenElement { #[inline] fn from(node: GreenNode) -> GreenElement { @@ -29,13 +20,6 @@ impl<'a> From<&'a GreenNode> for GreenElementRef<'a> { } } -impl From for PackedGreenElement { - #[inline] - fn from(node: GreenNode) -> PackedGreenElement { - unsafe { mem::transmute(node) } - } -} - impl From for GreenElement { #[inline] fn from(token: GreenToken) -> GreenElement { @@ -50,13 +34,6 @@ impl<'a> From<&'a GreenToken> for GreenElementRef<'a> { } } -impl From for PackedGreenElement { - #[inline] - fn from(token: GreenToken) -> PackedGreenElement { - unsafe { mem::transmute(token) } - } -} - impl GreenElement { /// Returns kind of this element. #[inline] @@ -90,121 +67,3 @@ impl GreenElementRef<'_> { } } } - -impl From for PackedGreenElement { - fn from(element: GreenElement) -> Self { - match element { - NodeOrToken::Node(node) => node.into(), - NodeOrToken::Token(token) => token.into(), - } - } -} - -impl From for GreenElement { - fn from(element: PackedGreenElement) -> Self { - if element.is_node() { - NodeOrToken::Node(element.into_node().unwrap()) - } else { - NodeOrToken::Token(element.into_token().unwrap()) - } - } -} - -impl PackedGreenElement { - fn is_node(&self) -> bool { - self.ptr.as_ptr() as usize & 1 == 0 - } - - pub(crate) fn as_node(&self) -> Option<&GreenNode> { - if self.is_node() { - unsafe { Some(&*(&self.ptr as *const ErasedPtr as *const GreenNode)) } - } else { - None - } - } - - pub(crate) fn into_node(self) -> Option { - if self.is_node() { - unsafe { Some(mem::transmute(self)) } - } else { - None - } - } - - pub(crate) fn as_token(&self) -> Option<&GreenToken> { - if !self.is_node() { - unsafe { Some(&*(&self.ptr as *const ErasedPtr as *const GreenToken)) } - } else { - None - } - } - - pub(crate) fn into_token(self) -> Option { - if !self.is_node() { - unsafe { Some(mem::transmute(self)) } - } else { - None - } - } - - pub(crate) fn as_ref(&self) -> GreenElementRef<'_> { - if self.is_node() { - NodeOrToken::Node(self.as_node().unwrap()) - } else { - NodeOrToken::Token(self.as_token().unwrap()) - } - } -} - -impl fmt::Debug for PackedGreenElement { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if self.is_node() { - self.as_node().unwrap().fmt(f) - } else { - self.as_token().unwrap().fmt(f) - } - } -} - -impl Eq for PackedGreenElement {} -impl PartialEq for PackedGreenElement { - fn eq(&self, other: &Self) -> bool { - self.as_node() == other.as_node() && self.as_token() == other.as_token() - } -} - -impl hash::Hash for PackedGreenElement { - fn hash(&self, state: &mut H) - where - H: hash::Hasher, - { - if self.is_node() { - self.as_node().unwrap().hash(state) - } else { - self.as_token().unwrap().hash(state) - } - } -} - -impl Drop for PackedGreenElement { - fn drop(&mut self) { - if self.is_node() { - PackedGreenElement { ptr: self.ptr }.into_node(); - } else { - PackedGreenElement { ptr: self.ptr }.into_token(); - } - } -} - -unsafe impl Send for PackedGreenElement -where - GreenToken: Send, - GreenNode: Send, -{ -} -unsafe impl Sync for PackedGreenElement -where - GreenToken: Sync, - GreenNode: Sync, -{ -} diff --git a/src/green/node.rs b/src/green/node.rs index 5e2687a..74b3a6e 100644 --- a/src/green/node.rs +++ b/src/green/node.rs @@ -1,14 +1,12 @@ -use std::{iter::FusedIterator, slice, sync::Arc}; +use std::{ffi::c_void, fmt, iter::FusedIterator, mem, slice}; -use erasable::Thin; -use slice_dst::SliceWithHeader; +use triomphe::{Arc, ThinArc}; use crate::{ - green::{GreenElement, GreenElementRef, PackedGreenElement, SyntaxKind}, - TextSize, + green::{GreenElement, GreenElementRef, SyntaxKind}, + GreenToken, NodeOrToken, TextRange, TextSize, }; -#[repr(align(2))] // NB: this is an at-least annotation #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub(super) struct GreenNodeHead { kind: SyntaxKind, @@ -17,12 +15,52 @@ pub(super) struct GreenNodeHead { /// Internal node in the immutable tree. /// It has other nodes and tokens as children. -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -#[repr(transparent)] +#[derive(Clone, PartialEq, Eq, Hash)] pub struct GreenNode { - pub(super) data: Thin>>, + data: ThinArc, +} + +impl fmt::Debug for GreenNode { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("GreenNode") + .field("kind", &self.kind()) + .field("text_len", &self.text_len()) + .field("n_children", &self.children().len()) + .finish() + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +enum GreenChild { + Node { offset_in_parent: TextSize, node: GreenNode }, + Token { offset_in_parent: TextSize, token: GreenToken }, +} + +impl GreenChild { + fn as_ref(&self) -> GreenElementRef { + match self { + GreenChild::Node { node, .. } => NodeOrToken::Node(node), + GreenChild::Token { token, .. } => NodeOrToken::Token(token), + } + } + fn offset_in_parent(&self) -> TextSize { + match self { + GreenChild::Node { offset_in_parent, .. } + | GreenChild::Token { offset_in_parent, .. } => *offset_in_parent, + } + } + fn range_in_parent(&self) -> TextRange { + let len = self.as_ref().text_len(); + TextRange::at(self.offset_in_parent(), len) + } } +#[cfg(target_pointer_width = "64")] +const _: i32 = { + let cond = mem::size_of::() == mem::size_of::() * 2; + 0 / cond as i32 +}; + impl GreenNode { /// Creates new Node. #[inline] @@ -32,30 +70,39 @@ impl GreenNode { I::IntoIter: ExactSizeIterator, { let mut text_len: TextSize = 0.into(); - let children = children - .into_iter() - .inspect(|it| text_len += it.text_len()) - .map(PackedGreenElement::from); - let mut data: Arc<_> = - SliceWithHeader::new(GreenNodeHead { kind, text_len: 0.into() }, children); + let children = children.into_iter().map(|el| { + let offset_in_parent = text_len; + text_len += el.text_len(); + match el { + NodeOrToken::Node(node) => GreenChild::Node { offset_in_parent, node }, + NodeOrToken::Token(token) => GreenChild::Token { offset_in_parent, token }, + } + }); + + let data = + ThinArc::from_header_and_iter(GreenNodeHead { kind, text_len: 0.into() }, children); // XXX: fixup `text_len` after construction, because we can't iterate // `children` twice. - Arc::get_mut(&mut data).unwrap().header.text_len = text_len; + let data = { + let mut data = Arc::from_thin(data); + Arc::get_mut(&mut data).unwrap().header.header.text_len = text_len; + Arc::into_thin(data) + }; - GreenNode { data: data.into() } + GreenNode { data } } /// Kind of this node. #[inline] pub fn kind(&self) -> SyntaxKind { - self.data.header.kind + self.data.header.header.kind } /// Returns the length of the text covered by this node. #[inline] pub fn text_len(&self) -> TextSize { - self.data.header.text_len + self.data.header.header.text_len } /// Children of this node. @@ -64,15 +111,44 @@ impl GreenNode { Children { inner: self.data.slice.iter() } } - pub(crate) fn ptr(&self) -> *const u8 { - let r: &SliceWithHeader<_, _> = &*self.data; - r as *const _ as _ + pub(crate) fn child_at_range( + &self, + range: TextRange, + ) -> Option<(usize, TextSize, GreenElementRef<'_>)> { + let idx = self + .data + .slice + .binary_search_by(|it| { + let child_range = it.range_in_parent(); + TextRange::ordering(child_range, range) + }) + // XXX: this handles empty ranges + .unwrap_or_else(|it| it.saturating_sub(1)); + let child = + &self.data.slice.get(idx).filter(|it| it.range_in_parent().contains_range(range))?; + Some((idx, child.offset_in_parent(), child.as_ref())) + } + + pub fn ptr(&self) -> *const c_void { + self.data.heap_ptr() + } + + pub(crate) fn replace_child(&self, idx: usize, new_child: GreenElement) -> GreenNode { + let mut replacement = Some(new_child); + let children = self.children().enumerate().map(|(i, child)| { + if i == idx { + replacement.take().unwrap() + } else { + child.cloned() + } + }); + GreenNode::new(self.kind(), children) } } #[derive(Debug, Clone)] pub struct Children<'a> { - inner: slice::Iter<'a, PackedGreenElement>, + inner: slice::Iter<'a, GreenChild>, } // NB: forward everything stable that iter::Slice specializes as of Rust 1.39.0 @@ -88,7 +164,7 @@ impl<'a> Iterator for Children<'a> { #[inline] fn next(&mut self) -> Option> { - self.inner.next().map(PackedGreenElement::as_ref) + self.inner.next().map(GreenChild::as_ref) } #[inline] @@ -106,7 +182,7 @@ impl<'a> Iterator for Children<'a> { #[inline] fn nth(&mut self, n: usize) -> Option { - self.inner.nth(n).map(PackedGreenElement::as_ref) + self.inner.nth(n).map(GreenChild::as_ref) } #[inline] @@ -133,12 +209,12 @@ impl<'a> Iterator for Children<'a> { impl<'a> DoubleEndedIterator for Children<'a> { #[inline] fn next_back(&mut self) -> Option { - self.inner.next_back().map(PackedGreenElement::as_ref) + self.inner.next_back().map(GreenChild::as_ref) } #[inline] fn nth_back(&mut self, n: usize) -> Option { - self.inner.nth_back(n).map(PackedGreenElement::as_ref) + self.inner.nth_back(n).map(GreenChild::as_ref) } #[inline] diff --git a/src/green/token.rs b/src/green/token.rs index be0fefd..0284e3c 100644 --- a/src/green/token.rs +++ b/src/green/token.rs @@ -1,66 +1,49 @@ -use std::{convert::TryFrom, fmt, hash, mem::ManuallyDrop, ptr, sync::Arc}; +use std::fmt; + +use triomphe::Arc; use crate::{green::SyntaxKind, SmolStr, TextSize}; -#[repr(align(2))] // NB: this is an at-least annotation -#[derive(Debug, PartialEq, Eq, Hash)] +#[derive(Debug, PartialEq, Eq, Hash, Clone)] struct GreenTokenData { kind: SyntaxKind, text: SmolStr, } /// Leaf node in the immutable tree. -#[repr(transparent)] +#[derive(PartialEq, Eq, Hash, Clone)] pub struct GreenToken { - ptr: ptr::NonNull, + data: Arc, } -unsafe impl Send for GreenToken {} // where GreenTokenData: Send + Sync -unsafe impl Sync for GreenToken {} // where GreenTokenData: Send + Sync - impl GreenToken { - fn add_tag(ptr: ptr::NonNull) -> ptr::NonNull { - unsafe { - let ptr = ((ptr.as_ptr() as usize) | 1) as *mut GreenTokenData; - ptr::NonNull::new_unchecked(ptr) - } - } - - fn remove_tag(ptr: ptr::NonNull) -> ptr::NonNull { - unsafe { - let ptr = ((ptr.as_ptr() as usize) & !1) as *mut GreenTokenData; - ptr::NonNull::new_unchecked(ptr) - } - } - fn data(&self) -> &GreenTokenData { - unsafe { &*Self::remove_tag(self.ptr).as_ptr() } + &*self.data } /// Creates new Token. #[inline] pub fn new(kind: SyntaxKind, text: SmolStr) -> GreenToken { - let ptr = Arc::into_raw(Arc::new(GreenTokenData { kind, text })); - let ptr = ptr::NonNull::new(ptr as *mut _).unwrap(); - GreenToken { ptr: Self::add_tag(ptr) } + let data = Arc::new(GreenTokenData { kind, text }); + GreenToken { data } } /// Kind of this Token. #[inline] pub fn kind(&self) -> SyntaxKind { - self.data().kind + self.data.kind } /// Text of this Token. #[inline] pub fn text(&self) -> &SmolStr { - &self.data().text + &self.data.text } /// Returns the length of the text covered by this token. #[inline] pub fn text_len(&self) -> TextSize { - TextSize::try_from(self.text().len()).unwrap() + TextSize::of(self.text().as_str()) } } @@ -70,39 +53,3 @@ impl fmt::Debug for GreenToken { f.debug_struct("GreenToken").field("kind", &data.kind).field("text", &data.text).finish() } } - -impl Clone for GreenToken { - fn clone(&self) -> Self { - let ptr = Self::remove_tag(self.ptr); - let ptr = unsafe { - let arc = ManuallyDrop::new(Arc::from_raw(ptr.as_ptr())); - Arc::into_raw(Arc::clone(&arc)) - }; - let ptr = ptr::NonNull::new(ptr as *mut _).unwrap(); - GreenToken { ptr: Self::add_tag(ptr) } - } -} - -impl Eq for GreenToken {} -impl PartialEq for GreenToken { - fn eq(&self, other: &Self) -> bool { - self.data() == other.data() - } -} - -impl hash::Hash for GreenToken { - fn hash(&self, state: &mut H) - where - H: hash::Hasher, - { - self.data().hash(state) - } -} - -impl Drop for GreenToken { - fn drop(&mut self) { - unsafe { - Arc::from_raw(Self::remove_tag(self.ptr).as_ptr()); - } - } -}