diff --git a/Cargo.toml b/Cargo.toml index 5c5bba7f..461b57cb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,27 +1,27 @@ [package] name = "rowan" -version = "0.15.15" +version = "0.16.2" 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" - +edition = "2024" +rust-version = "1.85.0" exclude = [".github/", "bors.toml", "rustfmt.toml"] [workspace] members = ["xtask"] [dependencies] -rustc-hash = "1.0.1" -hashbrown = { version = "0.14.3", features = [ - "inline-more", +rustc-hash = "2.1.1" +hashbrown = { version = "0.15.2", features = [ + "inline-more", + "raw-entry", ], default-features = false } -text-size = "1.1.0" -memoffset = "0.9" -countme = "3.0.0" +text-size = "1.1.1" +countme = "3.0.1" -serde = { version = "1.0.89", optional = true, default-features = false } +serde = { version = "1.0.218", optional = true, default-features = false } [dev-dependencies] m_lexer = "0.0.4" diff --git a/README.md b/README.md index f0d5b133..a5d61678 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,13 @@ # Rowan +[![docs.rs](https://docs.rs/rowan/badge.svg)](https://docs.rs/rowan/) [![Crates.io](https://img.shields.io/crates/v/rowan.svg)](https://crates.io/crates/rowan) [![Crates.io](https://img.shields.io/crates/d/rowan.svg)](https://crates.io/crates/rowan) Rowan is a library for lossless syntax trees, inspired in part by Swift's [libsyntax](https://github.com/apple/swift/tree/5e2c815edfd758f9b1309ce07bfc01c4bc20ec23/lib/Syntax). -A conceptual overview is available in the [rust-analyzer repo](https://github.com/rust-analyzer/rust-analyzer/blob/master/docs/dev/syntax.md). +A conceptual overview is available in the [rust-analyzer book](https://rust-analyzer.github.io/book/contributing/syntax.html). See `examples/s_expressions` for a tutorial, and [rust-analyzer](https://github.com/rust-analyzer/rust-analyzer/) for real-world usage. diff --git a/examples/math.rs b/examples/math.rs index 930c591b..3a451b6a 100644 --- a/examples/math.rs +++ b/examples/math.rs @@ -32,6 +32,7 @@ enum SyntaxKind { OPERATION, ROOT, } + use SyntaxKind::*; impl From for rowan::SyntaxKind { @@ -111,7 +112,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/examples/s_expressions.rs b/examples/s_expressions.rs index e7df4c64..34b3068b 100644 --- a/examples/s_expressions.rs +++ b/examples/s_expressions.rs @@ -7,7 +7,7 @@ //! //! It's suggested to read the conceptual overview of the design //! alongside this tutorial: -//! https://github.com/rust-analyzer/rust-analyzer/blob/master/docs/dev/syntax.md +//! https://rust-analyzer.github.io/book/contributing/syntax.html /// Let's start with defining all kinds of tokens and /// composite nodes. @@ -194,8 +194,10 @@ fn parse(text: &str) -> Parse { /// has identity semantics. type SyntaxNode = rowan::SyntaxNode; + #[allow(unused)] type SyntaxToken = rowan::SyntaxToken; + #[allow(unused)] type SyntaxElement = rowan::NodeOrToken; @@ -255,11 +257,7 @@ macro_rules! ast_node { impl $ast { #[allow(unused)] fn cast(node: SyntaxNode) -> Option { - if node.kind() == $kind { - Some(Self(node)) - } else { - None - } + if node.kind() == $kind { Some(Self(node)) } else { None } } } }; diff --git a/src/api.rs b/src/api.rs index 605fcc50..157bf14f 100644 --- a/src/api.rs +++ b/src/api.rs @@ -1,8 +1,8 @@ use std::{borrow::Cow, fmt, iter, marker::PhantomData, ops::Range}; use crate::{ - cursor, green::GreenTokenData, Direction, GreenNode, GreenNodeData, GreenToken, NodeOrToken, - SyntaxKind, SyntaxText, TextRange, TextSize, TokenAtOffset, WalkEvent, + Direction, GreenNode, GreenNodeData, GreenToken, NodeOrToken, SyntaxKind, SyntaxText, + TextRange, TextSize, TokenAtOffset, WalkEvent, cursor, green::GreenTokenData, }; pub trait Language: Sized + Copy + fmt::Debug + Eq + Ord + std::hash::Hash { @@ -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) } @@ -129,7 +133,7 @@ impl SyntaxNode { self.raw.parent().map(Self::from) } - pub fn ancestors(&self) -> impl Iterator> { + pub fn ancestors(&self) -> impl Iterator> + use { self.raw.ancestors().map(SyntaxNode::from) } @@ -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) } @@ -178,7 +219,7 @@ impl SyntaxNode { self.raw.last_token().map(SyntaxToken::from) } - pub fn siblings(&self, direction: Direction) -> impl Iterator> { + pub fn siblings(&self, direction: Direction) -> impl Iterator> + use { self.raw.siblings(direction).map(SyntaxNode::from) } @@ -189,11 +230,11 @@ impl SyntaxNode { self.raw.siblings_with_tokens(direction).map(SyntaxElement::from) } - pub fn descendants(&self) -> impl Iterator> { + pub fn descendants(&self) -> impl Iterator> + use { self.raw.descendants().map(SyntaxNode::from) } - pub fn descendants_with_tokens(&self) -> impl Iterator> { + pub fn descendants_with_tokens(&self) -> impl Iterator> + use { self.raw.descendants_with_tokens().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,20 +285,28 @@ 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() } - pub fn splice_children(&self, to_delete: Range, to_insert: Vec>) { - let to_insert = to_insert.into_iter().map(cursor::SyntaxElement::from).collect::>(); + pub fn splice_children>>( + &self, + to_delete: Range, + to_insert: I, + ) { + let to_insert = to_insert.into_iter().map(cursor::SyntaxElement::from); self.raw.splice_children(to_delete, to_insert) } } 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) } @@ -288,12 +337,12 @@ impl SyntaxToken { /// Iterator over all the ancestors of this token excluding itself. #[deprecated = "use `SyntaxToken::parent_ancestors` instead"] - pub fn ancestors(&self) -> impl Iterator> { + pub fn ancestors(&self) -> impl Iterator> + use { self.parent_ancestors() } /// Iterator over all the ancestors of this token excluding itself. - pub fn parent_ancestors(&self) -> impl Iterator> { + pub fn parent_ancestors(&self) -> impl Iterator> + use { self.raw.ancestors().map(SyntaxNode::from) } @@ -307,7 +356,7 @@ impl SyntaxToken { pub fn siblings_with_tokens( &self, direction: Direction, - ) -> impl Iterator> { + ) -> impl Iterator> + use { self.raw.siblings_with_tokens(direction).map(SyntaxElement::from) } @@ -354,7 +403,7 @@ impl SyntaxElement { } } - pub fn ancestors(&self) -> impl Iterator> { + pub fn ancestors(&self) -> impl Iterator> + use { let first = match self { NodeOrToken::Node(it) => Some(it.clone()), NodeOrToken::Token(it) => it.parent(), @@ -388,6 +437,12 @@ pub struct SyntaxNodeChildren { _p: PhantomData, } +impl Default for SyntaxNodeChildren { + fn default() -> Self { + Self { raw: Default::default(), _p: PhantomData } + } +} + impl Iterator for SyntaxNodeChildren { type Item = SyntaxNode; fn next(&mut self) -> Option { @@ -395,12 +450,24 @@ 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))).map(SyntaxNode::from) + } +} + #[derive(Debug, Clone)] pub struct SyntaxElementChildren { raw: cursor::SyntaxElementChildren, _p: PhantomData, } +impl Default for SyntaxElementChildren { + fn default() -> Self { + Self { raw: Default::default(), _p: PhantomData } + } +} + impl Iterator for SyntaxElementChildren { type Item = SyntaxElement; fn next(&mut self) -> Option { @@ -408,6 +475,16 @@ 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) + } +} + +#[derive(Debug, Clone)] pub struct Preorder { raw: cursor::Preorder, _p: PhantomData, @@ -426,6 +503,7 @@ impl Iterator for Preorder { } } +#[derive(Debug, Clone)] pub struct PreorderWithTokens { raw: cursor::PreorderWithTokens, _p: PhantomData, diff --git a/src/arc.rs b/src/arc.rs index 34883129..fa3774fd 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, ManuallyDrop, offset_of}, 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 @@ -57,14 +55,17 @@ impl Arc { pub(crate) unsafe fn from_raw(ptr: *const T) -> Self { // To find the corresponding pointer to the `ArcInner` we need // to subtract the offset of the `data` field from the pointer. - let ptr = (ptr as *const u8).sub(offset_of!(ArcInner, data)); - Arc { p: ptr::NonNull::new_unchecked(ptr as *mut ArcInner), phantom: PhantomData } + unsafe { + let ptr = (ptr as *const u8).sub(offset_of!(ArcInner, data)); + Arc { p: ptr::NonNull::new_unchecked(ptr as *mut ArcInner), phantom: PhantomData } + } } } impl Arc { #[inline] fn inner(&self) -> &ArcInner { + // SAFETY: // This unsafety is ok because while this arc is alive we're guaranteed // that the inner pointer is valid. Furthermore, we know that the // `ArcInner` structure itself is `Sync` because the inner data is @@ -76,14 +77,16 @@ impl Arc { // Non-inlined part of `drop`. Just invokes the destructor. #[inline(never)] unsafe fn drop_slow(&mut self) { - let _ = Box::from_raw(self.ptr()); + unsafe { + let _ = Box::from_raw(self.ptr()); + } } /// Test pointer equality between the two Arcs, i.e. they must be the _same_ /// 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 { @@ -197,10 +200,6 @@ impl PartialEq for Arc { fn eq(&self, other: &Arc) -> bool { Self::ptr_eq(self, other) || *(*self) == *(*other) } - - fn ne(&self, other: &Arc) -> bool { - !Self::ptr_eq(self, other) && *(*self) != *(*other) - } } impl PartialOrd for Arc { @@ -257,12 +256,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) } } } @@ -317,10 +313,7 @@ impl ThinArc { }; // Expose the transient Arc to the callback, which may clone it if it wants. - let result = f(&transient); - - // Forward the result. - result + f(&transient) } /// Creates a `ThinArc` for a HeaderSlice using the given header struct and diff --git a/src/ast.rs b/src/ast.rs index 856a9f63..292dba9f 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,14 +148,15 @@ 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)?) } /// Returns the underlying [`SyntaxNodePtr`]. pub fn syntax_node_ptr(&self) -> SyntaxNodePtr { - self.raw.clone() + self.raw } /// Casts this to an [`AstPtr`] to the given node type if possible. @@ -156,7 +176,7 @@ impl fmt::Debug for AstPtr { impl Clone for AstPtr { fn clone(&self) -> Self { - Self { raw: self.raw.clone() } + Self { raw: self.raw } } } @@ -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/cow_mut.rs b/src/cow_mut.rs index c50e25b7..418d7ee3 100644 --- a/src/cow_mut.rs +++ b/src/cow_mut.rs @@ -9,7 +9,7 @@ impl std::ops::Deref for CowMut<'_, T> { fn deref(&self) -> &T { match self { CowMut::Owned(it) => it, - CowMut::Borrowed(it) => *it, + CowMut::Borrowed(it) => it, } } } @@ -18,7 +18,7 @@ impl std::ops::DerefMut for CowMut<'_, T> { fn deref_mut(&mut self) -> &mut T { match self { CowMut::Owned(it) => it, - CowMut::Borrowed(it) => *it, + CowMut::Borrowed(it) => it, } } } diff --git a/src/cursor.rs b/src/cursor.rs index 671be234..d7ea5f08 100644 --- a/src/cursor.rs +++ b/src/cursor.rs @@ -95,11 +95,11 @@ use std::{ use countme::Count; use crate::{ + Direction, GreenNode, GreenToken, NodeOrToken, SyntaxText, TextRange, TextSize, TokenAtOffset, + WalkEvent, green::{GreenChild, GreenElementRef, GreenNodeData, GreenTokenData, SyntaxKind}, sll, utility_types::Delta, - Direction, GreenNode, GreenToken, NodeOrToken, SyntaxText, TextRange, TextSize, TokenAtOffset, - WalkEvent, }; enum Green { @@ -188,32 +188,34 @@ impl Drop for SyntaxToken { #[inline(never)] unsafe fn free(mut data: ptr::NonNull) { - loop { - debug_assert_eq!(data.as_ref().rc.get(), 0); - debug_assert!(data.as_ref().first.get().is_null()); - let node = Box::from_raw(data.as_ptr()); - match node.parent.take() { - Some(parent) => { - debug_assert!(parent.as_ref().rc.get() > 0); - if node.mutable { - sll::unlink(&parent.as_ref().first, &*node) - } - if parent.as_ref().dec_rc() { - data = parent; - } else { - break; - } - } - None => { - match &node.green { - Green::Node { ptr } => { - let _ = GreenNode::from_raw(ptr.get()); + unsafe { + loop { + debug_assert_eq!(data.as_ref().rc.get(), 0); + debug_assert!(data.as_ref().first.get().is_null()); + let node = Box::from_raw(data.as_ptr()); + match node.parent.take() { + Some(parent) => { + debug_assert!(parent.as_ref().rc.get() > 0); + if node.mutable { + sll::unlink(&parent.as_ref().first, &*node) } - Green::Token { ptr } => { - let _ = GreenToken::from_raw(*ptr); + if parent.as_ref().dec_rc() { + data = parent; + } else { + break; } } - break; + None => { + match &node.green { + Green::Node { ptr } => { + let _ = GreenNode::from_raw(ptr.get()); + } + Green::Token { ptr } => { + let _ = GreenToken::from_raw(*ptr); + } + } + break; + } } } } @@ -321,7 +323,7 @@ impl NodeData { fn green(&self) -> GreenElementRef<'_> { match &self.green { Green::Node { ptr } => GreenElementRef::Node(unsafe { &*ptr.get().as_ptr() }), - Green::Token { ptr } => GreenElementRef::Token(unsafe { &*ptr.as_ref() }), + Green::Token { ptr } => GreenElementRef::Token(unsafe { ptr.as_ref() }), } } #[inline] @@ -342,11 +344,7 @@ impl NodeData { #[inline] fn offset(&self) -> TextSize { - if self.mutable { - self.offset_mut() - } else { - self.offset - } + if self.mutable { self.offset_mut() } else { self.offset } } #[cold] @@ -376,11 +374,10 @@ 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.nth(index); - siblings.find_map(|(index, child)| { + 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(); @@ -388,12 +385,28 @@ impl NodeData { }) }) } + + 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.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(); + Some(SyntaxNode::new_child(green, parent, index as u32, offset)) + }) + }) + } + 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).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 +425,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,89 +462,87 @@ 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) { - let mut node = self; - loop { - let old_green = match &node.green { - Green::Node { ptr } => ptr.replace(ptr::NonNull::from(&*new_green)), - Green::Token { .. } => unreachable!(), - }; - match node.parent() { - Some(parent) => match parent.green() { - NodeOrToken::Node(parent_green) => { - new_green = - parent_green.replace_child(node.index() as usize, new_green.into()); - node = parent; + unsafe { + let mut node = self; + loop { + let old_green = match &node.green { + Green::Node { ptr } => ptr.replace(ptr::NonNull::from(&*new_green)), + Green::Token { .. } => unreachable!(), + }; + match node.parent() { + Some(parent) => match parent.green() { + NodeOrToken::Node(parent_green) => { + new_green = + parent_green.replace_child(node.index() as usize, new_green.into()); + node = parent; + } + _ => unreachable!(), + }, + None => { + mem::forget(new_green); + let _ = GreenNode::from_raw(old_green); + break; } - _ => unreachable!(), - }, - None => { - mem::forget(new_green); - let _ = GreenNode::from_raw(old_green); - break; } } } @@ -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() { @@ -621,7 +668,7 @@ impl SyntaxNode { } #[inline] - pub fn ancestors(&self) -> impl Iterator { + pub fn ancestors(&self) -> impl Iterator + use<> { iter::successors(Some(self.clone()), SyntaxNode::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,52 @@ 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().map(|green| (green, index as u32, child.rel_offset())) + }) + .map(|(green, index, rel_offset)| { + data.index.set(index); + data.offset = parent_offset + rel_offset; + data.green = Green::Node { ptr: Cell::new(green.into()) }; + 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 +811,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() } @@ -698,7 +831,7 @@ impl SyntaxNode { } #[inline] - pub fn siblings(&self, direction: Direction) -> impl Iterator { + pub fn siblings(&self, direction: Direction) -> impl Iterator + use<> { iter::successors(Some(self.clone()), move |node| match direction { Direction::Next => node.next_sibling(), Direction::Prev => node.prev_sibling(), @@ -709,7 +842,7 @@ impl SyntaxNode { pub fn siblings_with_tokens( &self, direction: Direction, - ) -> impl Iterator { + ) -> impl Iterator + use<> { let me: SyntaxElement = self.clone().into(); iter::successors(Some(me), move |el| match direction { Direction::Next => el.next_sibling_or_token(), @@ -718,7 +851,7 @@ impl SyntaxNode { } #[inline] - pub fn descendants(&self) -> impl Iterator { + pub fn descendants(&self) -> impl Iterator + use<> { self.preorder().filter_map(|event| match event { WalkEvent::Enter(node) => Some(node), WalkEvent::Leave(_) => None, @@ -726,7 +859,7 @@ impl SyntaxNode { } #[inline] - pub fn descendants_with_tokens(&self) -> impl Iterator { + pub fn descendants_with_tokens(&self) -> impl Iterator + use<> { self.preorder_with_tokens().filter_map(|event| match event { WalkEvent::Enter(it) => Some(it), WalkEvent::Leave(_) => None, @@ -806,7 +939,11 @@ impl SyntaxNode { }) } - pub fn splice_children(&self, to_delete: Range, to_insert: Vec) { + pub fn splice_children>( + &self, + to_delete: Range, + to_insert: I, + ) { assert!(self.data().mutable, "immutable tree: {}", self); for (i, child) in self.children_with_tokens().enumerate() { if to_delete.contains(&i) { @@ -853,6 +990,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(); @@ -903,13 +1054,21 @@ impl SyntaxToken { } #[inline] - pub fn ancestors(&self) -> impl Iterator { + pub fn ancestors(&self) -> impl Iterator + use<> { std::iter::successors(self.parent(), SyntaxNode::parent) } 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() } @@ -918,7 +1077,7 @@ impl SyntaxToken { pub fn siblings_with_tokens( &self, direction: Direction, - ) -> impl Iterator { + ) -> impl Iterator + use<> { let me: SyntaxElement = self.clone().into(); iter::successors(Some(me), move |el| match direction { Direction::Next => el.next_sibling_or_token(), @@ -959,12 +1118,8 @@ impl SyntaxElement { offset: TextSize, ) -> SyntaxElement { match element { - NodeOrToken::Node(node) => { - SyntaxNode::new_child(node, parent, index as u32, offset).into() - } - NodeOrToken::Token(token) => { - SyntaxToken::new(token, parent, index as u32, offset).into() - } + NodeOrToken::Node(node) => SyntaxNode::new_child(node, parent, index, offset).into(), + NodeOrToken::Token(token) => SyntaxToken::new(token, parent, index, offset).into(), } } @@ -1001,7 +1156,7 @@ impl SyntaxElement { } #[inline] - pub fn ancestors(&self) -> impl Iterator { + pub fn ancestors(&self) -> impl Iterator + use<> { let first = match self { NodeOrToken::Node(it) => Some(it.clone()), NodeOrToken::Token(it) => it.parent(), @@ -1028,6 +1183,73 @@ 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) + .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 })) + } + } + }) + .next() + .flatten() + .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(), @@ -1131,7 +1353,7 @@ impl From for SyntaxElement { // region: iterators -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Default)] pub struct SyntaxNodeChildren { next: Option, } @@ -1140,19 +1362,42 @@ impl SyntaxNodeChildren { fn new(parent: SyntaxNode) -> SyntaxNodeChildren { SyntaxNodeChildren { next: parent.first_child() } } + + pub fn by_kind bool>(self, matcher: F) -> SyntaxNodeChildrenByKind { + 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 { - self.next.take().map(|next| { - self.next = next.next_sibling(); - next - }) + let curr = self.next.take()?; + self.next = curr.next_sibling(); + Some(curr) } } #[derive(Clone, Debug)] +pub struct SyntaxNodeChildrenByKind bool> { + next: Option, + matcher: F, +} + +impl bool> Iterator for SyntaxNodeChildrenByKind { + type Item = SyntaxNode; + fn next(&mut self) -> Option { + let curr = self.next.take()?; + self.next = curr.next_sibling_by_kind(&self.matcher); + Some(curr) + } +} + +#[derive(Clone, Debug, Default)] pub struct SyntaxElementChildren { next: Option, } @@ -1161,18 +1406,46 @@ impl SyntaxElementChildren { fn new(parent: SyntaxNode) -> SyntaxElementChildren { SyntaxElementChildren { next: parent.first_child_or_token() } } + + pub fn by_kind bool>(self, matcher: F) -> SyntaxElementChildrenByKind { + 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 { - self.next.take().map(|next| { - self.next = next.next_sibling_or_token(); - next - }) + let curr = self.next.take()?; + self.next = curr.next_sibling_or_token(); + Some(curr) + } +} + +#[derive(Clone, Debug)] +pub struct SyntaxElementChildrenByKind bool> { + next: Option, + matcher: F, +} + +impl bool> Iterator for SyntaxElementChildrenByKind { + type Item = SyntaxElement; + fn next(&mut self) -> Option { + let curr = self.next.take()?; + self.next = curr.next_sibling_or_token_by_kind(&self.matcher); + Some(curr) } } +#[derive(Debug, Clone)] pub struct Preorder { start: SyntaxNode, next: Option>, @@ -1228,6 +1501,7 @@ impl Iterator for Preorder { } } +#[derive(Debug, Clone)] pub struct PreorderWithTokens { start: SyntaxElement, next: Option>, diff --git a/src/green/builder.rs b/src/green/builder.rs index 1dbc8bb0..a8becadc 100644 --- a/src/green/builder.rs +++ b/src/green/builder.rs @@ -1,12 +1,24 @@ +use std::num::NonZeroUsize; + use crate::{ - cow_mut::CowMut, - green::{node_cache::NodeCache, GreenElement, GreenNode, SyntaxKind}, NodeOrToken, + cow_mut::CowMut, + green::{GreenElement, GreenNode, SyntaxKind, node_cache::NodeCache}, }; /// 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/element.rs b/src/green/element.rs index 2d1ce1f6..29451579 100644 --- a/src/green/element.rs +++ b/src/green/element.rs @@ -1,8 +1,8 @@ use std::borrow::Cow; use crate::{ - green::{GreenNode, GreenToken, SyntaxKind}, GreenNodeData, NodeOrToken, TextSize, + green::{GreenNode, GreenToken, SyntaxKind}, }; use super::GreenTokenData; diff --git a/src/green/node.rs b/src/green/node.rs index e94dc01c..d056abb4 100644 --- a/src/green/node.rs +++ b/src/green/node.rs @@ -9,10 +9,9 @@ use std::{ use countme::Count; use crate::{ + GreenToken, NodeOrToken, TextRange, TextSize, arc::{Arc, HeaderSlice, ThinArc}, green::{GreenElement, GreenElementRef, SyntaxKind}, - utility_types::static_assert, - GreenToken, NodeOrToken, TextRange, TextSize, }; #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -23,12 +22,11 @@ pub(super) struct GreenNodeHead { } #[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[repr(u8)] pub(crate) enum GreenChild { Node { rel_offset: TextSize, node: GreenNode }, Token { rel_offset: TextSize, token: GreenToken }, } -#[cfg(target_pointer_width = "64")] -static_assert!(mem::size_of::() == mem::size_of::() * 2); type Repr = HeaderSlice; type ReprThin = HeaderSlice; @@ -56,18 +54,16 @@ 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) } } impl Borrow for GreenNode { #[inline] fn borrow(&self) -> &GreenNodeData { - &*self + self } } @@ -90,14 +86,14 @@ impl fmt::Debug for GreenNodeData { impl fmt::Debug for GreenNode { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let data: &GreenNodeData = &*self; + let data: &GreenNodeData = self; fmt::Debug::fmt(data, f) } } impl fmt::Display for GreenNode { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let data: &GreenNodeData = &*self; + let data: &GreenNodeData = self; fmt::Display::fmt(data, f) } } @@ -160,11 +156,7 @@ impl GreenNodeData { pub fn replace_child(&self, index: usize, new_child: GreenElement) -> GreenNode { let mut replacement = Some(new_child); let children = self.children().enumerate().map(|(i, child)| { - if i == index { - replacement.take().unwrap() - } else { - child.to_owned() - } + if i == index { replacement.take().unwrap() } else { child.to_owned() } }); GreenNode::new(self.kind(), children) } @@ -194,8 +186,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) } @@ -239,15 +231,17 @@ impl GreenNode { #[inline] pub(crate) fn into_raw(this: GreenNode) -> ptr::NonNull { let green = ManuallyDrop::new(this); - let green: &GreenNodeData = &*green; - ptr::NonNull::from(&*green) + let green: &GreenNodeData = &green; + ptr::NonNull::from(green) } #[inline] pub(crate) unsafe fn from_raw(ptr: ptr::NonNull) -> GreenNode { - let arc = Arc::from_raw(&ptr.as_ref().data as *const ReprThin); - let arc = mem::transmute::, ThinArc>(arc); - GreenNode { ptr: arc } + unsafe { + let arc = Arc::from_raw(&ptr.as_ref().data as *const ReprThin); + let arc = mem::transmute::, ThinArc>(arc); + GreenNode { ptr: arc } + } } } @@ -322,19 +316,19 @@ impl<'a> Iterator for Children<'a> { } #[inline] - fn fold(mut self, init: Acc, mut f: Fold) -> Acc + fn fold(self, init: Acc, mut f: Fold) -> Acc where Fold: FnMut(Acc, Self::Item) -> Acc, { let mut accum = init; - while let Some(x) = self.next() { + for x in self { accum = f(accum, x); } accum } } -impl<'a> DoubleEndedIterator for Children<'a> { +impl DoubleEndedIterator for Children<'_> { #[inline] fn next_back(&mut self) -> Option { self.raw.next_back().map(GreenChild::as_ref) @@ -359,3 +353,16 @@ impl<'a> DoubleEndedIterator for Children<'a> { } impl FusedIterator for Children<'_> {} + +#[cfg(test)] +mod test { + + #[test] + #[cfg(target_pointer_width = "64")] + fn check_green_child_size() { + use super::GreenChild; + use std::mem; + + assert_eq!(mem::size_of::(), mem::size_of::() * 2); + } +} diff --git a/src/green/node_cache.rs b/src/green/node_cache.rs index c73f3e69..89740bf6 100644 --- a/src/green/node_cache.rs +++ b/src/green/node_cache.rs @@ -3,8 +3,8 @@ use rustc_hash::FxHasher; use std::hash::{BuildHasherDefault, Hash, Hasher}; use crate::{ - green::GreenElementRef, GreenNode, GreenNodeData, GreenToken, GreenTokenData, NodeOrToken, - SyntaxKind, + GreenNode, GreenNodeData, GreenToken, GreenTokenData, NodeOrToken, SyntaxKind, + green::GreenElementRef, }; use super::element::GreenElement; diff --git a/src/green/token.rs b/src/green/token.rs index 1a4548a4..9e6a1f04 100644 --- a/src/green/token.rs +++ b/src/green/token.rs @@ -8,9 +8,9 @@ use std::{ use countme::Count; use crate::{ + TextSize, arc::{Arc, HeaderSlice, ThinArc}, green::SyntaxKind, - TextSize, }; #[derive(PartialEq, Eq, Hash)] @@ -44,18 +44,16 @@ 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) } } impl Borrow for GreenToken { #[inline] fn borrow(&self) -> &GreenTokenData { - &*self + self } } @@ -70,14 +68,14 @@ impl fmt::Debug for GreenTokenData { impl fmt::Debug for GreenToken { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let data: &GreenTokenData = &*self; + let data: &GreenTokenData = self; fmt::Debug::fmt(data, f) } } impl fmt::Display for GreenToken { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let data: &GreenTokenData = &*self; + let data: &GreenTokenData = self; fmt::Display::fmt(data, f) } } @@ -119,14 +117,25 @@ impl GreenToken { #[inline] pub(crate) fn into_raw(this: GreenToken) -> ptr::NonNull { let green = ManuallyDrop::new(this); - let green: &GreenTokenData = &*green; - ptr::NonNull::from(&*green) + let green: &GreenTokenData = &green; + ptr::NonNull::from(green) } + /// # Safety + /// + /// This function uses `unsafe` code to create an `Arc` from a raw pointer and then transmutes it into a `ThinArc`. + /// + /// - The raw pointer must be valid and correctly aligned for the type `ReprThin`. + /// - The lifetime of the raw pointer must outlive the lifetime of the `Arc` created from it. + /// - The transmute operation must be safe, meaning that the memory layout of `Arc` must be compatible with `ThinArc`. + /// + /// Failure to uphold these invariants can lead to undefined behavior. #[inline] pub(crate) unsafe fn from_raw(ptr: ptr::NonNull) -> GreenToken { - let arc = Arc::from_raw(&ptr.as_ref().data as *const ReprThin); - let arc = mem::transmute::, ThinArc>(arc); + let arc = unsafe { + let arc = Arc::from_raw(&ptr.as_ref().data as *const ReprThin); + mem::transmute::, ThinArc>(arc) + }; GreenToken { ptr: arc } } } diff --git a/src/serde_impls.rs b/src/serde_impls.rs index aea5d88b..529303b9 100644 --- a/src/serde_impls.rs +++ b/src/serde_impls.rs @@ -2,8 +2,8 @@ use serde::ser::{Serialize, SerializeMap, SerializeSeq, Serializer}; use std::fmt; use crate::{ - api::{Language, SyntaxNode, SyntaxToken}, NodeOrToken, + api::{Language, SyntaxNode, SyntaxToken}, }; struct SerDisplay(T); diff --git a/src/sll.rs b/src/sll.rs index 87d2f1f3..69476606 100644 --- a/src/sll.rs +++ b/src/sll.rs @@ -3,6 +3,15 @@ use std::{cell::Cell, cmp::Ordering, ptr}; use crate::utility_types::Delta; + +/// # Safety +/// +/// Implementors of this trait must ensure that the pointers returned by +/// `prev` and `next` are valid and properly initialized. The pointers must +/// point to valid instances of the implementing type or be null pointers. +/// Additionally, the `key` method must return a valid reference to a `Cell`. +/// +/// Failure to uphold these invariants can result in undefined behavior. pub(crate) unsafe trait Elem { fn prev(&self) -> &Cell<*const Self>; fn next(&self) -> &Cell<*const Self>; @@ -17,7 +26,7 @@ pub(crate) enum AddToSllResult<'a, E: Elem> { AlreadyInSll(*const E), } -impl<'a, E: Elem> AddToSllResult<'a, E> { +impl AddToSllResult<'_, E> { pub(crate) fn add_to_sll(&self, elem_ptr: *const E) { unsafe { (*elem_ptr).prev().set(elem_ptr); @@ -55,11 +64,7 @@ pub(crate) fn init<'a, E: Elem>( head: Option<&'a Cell<*const E>>, elem: &E, ) -> AddToSllResult<'a, E> { - if let Some(head) = head { - link(head, elem) - } else { - AddToSllResult::NoHead - } + if let Some(head) = head { link(head, elem) } else { AddToSllResult::NoHead } } #[cold] @@ -84,13 +89,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); diff --git a/src/syntax_text.rs b/src/syntax_text.rs index 3ab3f6cb..7e91ba21 100644 --- a/src/syntax_text.rs +++ b/src/syntax_text.rs @@ -1,8 +1,8 @@ use std::fmt; use crate::{ - cursor::{SyntaxNode, SyntaxToken}, TextRange, TextSize, + cursor::{SyntaxNode, SyntaxToken}, }; #[derive(Clone)] @@ -43,7 +43,6 @@ impl SyntaxText { } pub fn char_at(&self, offset: TextSize) -> Option { - let offset = offset.into(); let mut start: TextSize = 0.into(); let res = self.try_for_each_chunk(|chunk| { let end = start + TextSize::of(chunk); @@ -97,13 +96,16 @@ impl SyntaxText { pub fn for_each_chunk(&self, mut f: F) { enum Void {} - match self.try_for_each_chunk(|chunk| Ok::<(), Void>(f(chunk))) { + match self.try_for_each_chunk(|chunk| { + f(chunk); + Ok::<(), Void>(()) + }) { Ok(()) => (), Err(void) => match void {}, } } - fn tokens_with_ranges(&self) -> impl Iterator { + fn tokens_with_ranges(&self) -> impl Iterator + use<> { let text_range = self.range; self.node.descendants_with_tokens().filter_map(|element| element.into_token()).filter_map( move |token| { @@ -266,7 +268,7 @@ mod private { #[cfg(test)] mod tests { - use crate::{green::SyntaxKind, GreenNodeBuilder}; + use crate::{GreenNodeBuilder, green::SyntaxKind}; use super::*; @@ -274,7 +276,7 @@ mod tests { let mut builder = GreenNodeBuilder::new(); builder.start_node(SyntaxKind(62)); for &chunk in chunks.iter() { - builder.token(SyntaxKind(92), chunk.into()) + builder.token(SyntaxKind(92), chunk) } builder.finish_node(); SyntaxNode::new_root(builder.finish()) @@ -288,7 +290,7 @@ mod tests { let expected = t1.to_string() == t2.to_string(); let actual = t1 == t2; assert_eq!(expected, actual, "`{}` (SyntaxText) `{}` (SyntaxText)", t1, t2); - let actual = t1 == &*t2.to_string(); + let actual = t1 == *t2.to_string(); assert_eq!(expected, actual, "`{}` (SyntaxText) `{}` (&str)", t1, t2); } fn check(t1: &[&str], t2: &[&str]) { diff --git a/src/utility_types.rs b/src/utility_types.rs index 817add72..656f9d4b 100644 --- a/src/utility_types.rs +++ b/src/utility_types.rs @@ -43,8 +43,8 @@ impl NodeOrToken { impl NodeOrToken { pub(crate) fn as_deref(&self) -> NodeOrToken<&N::Target, &T::Target> { match self { - NodeOrToken::Node(node) => NodeOrToken::Node(&*node), - NodeOrToken::Token(token) => NodeOrToken::Token(&*token), + NodeOrToken::Node(node) => NodeOrToken::Node(node), + NodeOrToken::Token(token) => NodeOrToken::Token(token), } } } @@ -149,14 +149,6 @@ impl Iterator for TokenAtOffset { impl ExactSizeIterator for TokenAtOffset {} -macro_rules! _static_assert { - ($expr:expr) => { - const _: i32 = 0 / $expr as i32; - }; -} - -pub(crate) use _static_assert as static_assert; - #[derive(Copy, Clone, Debug)] pub(crate) enum Delta { Add(T), diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml index f87c7f46..8a76f529 100644 --- a/xtask/Cargo.toml +++ b/xtask/Cargo.toml @@ -3,7 +3,8 @@ name = "xtask" version = "0.0.0" publish = false authors = ["Aleksey Kladov "] -edition = "2021" +edition = "2024" [dependencies] -xaction = "0.2" +anyhow = "1.0.96" +xshell = "0.2.7" diff --git a/xtask/src/main.rs b/xtask/src/main.rs index d8ee44bb..f14e1eaf 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -1,6 +1,13 @@ -use std::env; +use std::{ + env, + path::PathBuf, + sync::atomic::{AtomicBool, Ordering}, + time::{Duration, Instant}, +}; -use xaction::{cargo_toml, cmd, git, section, Result}; +use anyhow::anyhow; +use xshell::{Shell, cmd}; +pub type Result = anyhow::Result; fn main() { if let Err(err) = try_main() { @@ -9,44 +16,163 @@ fn main() { } } +pub struct Section { + name: &'static str, + start: Instant, +} + +pub struct CargoToml { + path: PathBuf, + contents: String, +} + +impl CargoToml { + pub fn version(&self) -> Result<&str> { + self.get("version") + } + + fn get(&self, field: &str) -> Result<&str> { + for line in self.contents.lines() { + let words = line.split_ascii_whitespace().collect::>(); + match words.as_slice() { + [n, "=", v, ..] if n.trim() == field => { + assert!(v.starts_with('"') && v.ends_with('"')); + return Ok(&v[1..v.len() - 1]); + } + _ => (), + } + } + Err(anyhow!("can't find `{}` in {}", field, self.path.display()))? + } + + pub fn publish(&self, sh: &mut Shell) -> Result<()> { + let token = env::var("CRATES_IO_TOKEN").unwrap_or("no token".to_string()); + let dry_run = dry_run(); + cmd!(sh, "cargo publish --token {token} {dry_run...}").run()?; + Ok(()) + } + + pub fn publish_all(&self, dirs: &[&str], sh: &mut Shell) -> Result<()> { + let token = env::var("CRATES_IO_TOKEN").unwrap_or("no token".to_string()); + if dry_run().is_none() { + for &dir in dirs { + for _ in 0..20 { + std::thread::sleep(Duration::from_secs(10)); + if cmd!( + sh, + "cargo publish --manifest-path {dir}'/Cargo.toml' --token {token} --dry-run" + ) + .run() + .is_ok() + { + break; + } + } + cmd!(sh, "cargo publish --manifest-path {dir}'/Cargo.toml' --token {token}") + .run()?; + } + } + Ok(()) + } +} + +fn dry_run() -> Option<&'static str> { + let dry_run = DRY_RUN.load(Ordering::Relaxed); + if dry_run { Some("--dry-run") } else { None } +} + +pub fn section(name: &'static str) -> Section { + Section::new(name) +} + +pub fn cargo_toml() -> Result { + let manifest_dir = env::var("CARGO_MANIFEST_DIR")?; + let path = PathBuf::from(manifest_dir).join("Cargo.toml"); + let contents = std::fs::read_to_string(&path)?; + Ok(CargoToml { path, contents }) +} + +static DRY_RUN: AtomicBool = AtomicBool::new(false); +pub fn set_dry_run(yes: bool) { + DRY_RUN.store(yes, Ordering::Relaxed) +} + fn try_main() -> Result<()> { + let mut sh = Shell::new()?; let subcommand = std::env::args().nth(1); match subcommand { Some(it) if it == "ci" => (), _ => { print_usage(); - Err("invalid arguments")? + Err(anyhow!("invalid arguments"))? } } - let cargo_toml = cargo_toml()?; - - { - let _s = section("BUILD"); - cmd!("cargo test --workspace --no-run").run()?; - } - { let _s = section("TEST"); - cmd!("cargo test --workspace -- --nocapture").run()?; + for &release in &[None, Some("--release")] { + cmd!(sh, "cargo test {release...} --workspace -- --nocapture").run()?; + } } let version = cargo_toml.version()?; let tag = format!("v{}", version); - let dry_run = - env::var("CI").is_err() || git::has_tag(&tag)? || git::current_branch()? != "master"; - xaction::set_dry_run(dry_run); + let dry_run = env::var("CI").is_err() + || git::has_tag(&tag, &mut sh)? + || git::current_branch(&mut sh)? != "master"; + set_dry_run(dry_run); { let _s = section("PUBLISH"); - cargo_toml.publish()?; - git::tag(&tag)?; - git::push_tags()?; + cargo_toml.publish(&mut sh)?; + git::tag(&tag, &mut sh)?; + git::push_tags(&mut sh)?; } Ok(()) } +pub mod git { + use xshell::{Shell, cmd}; + + use super::{Result, dry_run}; + + pub fn current_branch(sh: &mut Shell) -> Result { + let res = cmd!(sh, "git branch --show-current").read()?; + Ok(res) + } + + pub fn tag_list(sh: &mut Shell) -> Result> { + let tags = cmd!(sh, "git tag --list").read()?; + let res = tags.lines().map(|it| it.trim().to_string()).collect(); + Ok(res) + } + + pub fn has_tag(tag: &str, sh: &mut Shell) -> Result { + let res = tag_list(sh)?.iter().any(|it| it == tag); + Ok(res) + } + + pub fn tag(tag: &str, sh: &mut Shell) -> Result<()> { + if dry_run().is_some() { + return Ok(()); + } + cmd!(sh, "git tag {tag}").run()?; + Ok(()) + } + + pub fn push_tags(sh: &mut Shell) -> Result<()> { + // `git push --tags --dry-run` exists, but it will fail with permissions + // error for forks. + if dry_run().is_some() { + return Ok(()); + } + + cmd!(sh, "git push --tags").run()?; + Ok(()) + } +} + fn print_usage() { eprintln!( "\ @@ -57,3 +183,18 @@ SUBCOMMANDS: " ) } + +impl Section { + fn new(name: &'static str) -> Section { + println!("::group::{}", name); + let start = Instant::now(); + Section { name, start } + } +} + +impl Drop for Section { + fn drop(&mut self) { + eprintln!("{}: {:.2?}", self.name, self.start.elapsed()); + println!("::endgroup::"); + } +} diff --git a/xtask/tests/tidy.rs b/xtask/tests/tidy.rs index 97c423a1..60579e6b 100644 --- a/xtask/tests/tidy.rs +++ b/xtask/tests/tidy.rs @@ -1,6 +1,7 @@ -use xaction::cmd; +use xshell::{Shell, cmd}; #[test] fn test_formatting() { - cmd!("cargo fmt --all -- --check").run().unwrap() + let sh = Shell::new().unwrap(); + cmd!(sh, "cargo fmt --all -- --check").run().unwrap() }