From 02a429eab724af0228c6b3c41aed3a56f140c3f7 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Fri, 31 Jul 2026 11:20:56 +0530 Subject: [PATCH 1/5] Remove typed mutable syntax APIs --- src/api.rs | 29 +---------------------------- src/ast.rs | 7 ------- 2 files changed, 1 insertion(+), 35 deletions(-) diff --git a/src/api.rs b/src/api.rs index 7aaac5b7..bb2f0a51 100644 --- a/src/api.rs +++ b/src/api.rs @@ -1,4 +1,4 @@ -use std::{borrow::Cow, fmt, iter, marker::PhantomData, ops::Range}; +use std::{borrow::Cow, fmt, iter, marker::PhantomData}; use crate::{ cursor, green::GreenTokenData, Direction, GreenNode, GreenNodeData, GreenToken, NodeOrToken, @@ -243,23 +243,6 @@ impl SyntaxNode { pub fn clone_subtree(&self) -> SyntaxNode { SyntaxNode::from(self.raw.clone_subtree()) } - - pub fn clone_for_update(&self) -> 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::>(); - self.raw.splice_children(to_delete, to_insert) - } } impl SyntaxToken { @@ -331,10 +314,6 @@ impl SyntaxToken { pub fn prev_token(&self) -> Option> { self.raw.prev_token().map(SyntaxToken::from) } - - pub fn detach(&self) { - self.raw.detach() - } } impl SyntaxElement { @@ -393,12 +372,6 @@ impl SyntaxElement { NodeOrToken::Token(it) => it.prev_sibling_or_token(), } } - pub fn detach(&self) { - match self { - NodeOrToken::Node(it) => it.detach(), - NodeOrToken::Token(it) => it.detach(), - } - } } #[derive(Debug, Clone)] diff --git a/src/ast.rs b/src/ast.rs index 856a9f63..01f05915 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -39,13 +39,6 @@ pub trait AstNode { fn syntax(&self) -> &SyntaxNode; - fn clone_for_update(&self) -> Self - where - Self: Sized, - { - Self::cast(self.syntax().clone_for_update()).unwrap() - } - fn clone_subtree(&self) -> Self where Self: Sized, From 26be638ce96b9d70ae8bac33e2c9fbcda69a9d32 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Fri, 31 Jul 2026 11:21:25 +0530 Subject: [PATCH 2/5] Remove the cursor mutation engine --- src/cursor.rs | 356 ++++--------------------------------------- src/green/token.rs | 7 - src/lib.rs | 2 - src/sll.rs | 129 ---------------- src/utility_types.rs | 28 +--- 5 files changed, 27 insertions(+), 495 deletions(-) delete mode 100644 src/sll.rs diff --git a/src/cursor.rs b/src/cursor.rs index cb210eca..b22e5e5d 100644 --- a/src/cursor.rs +++ b/src/cursor.rs @@ -7,12 +7,6 @@ //! `SyntaxNode`. This allows cursor to provide iteration over both ancestors //! and descendants, as well as a cheep access to absolute offset of the node in //! file. -//! -//! By default `SyntaxNode`s are immutable, but you can get a mutable copy of -//! the tree by calling `clone_for_update`. Mutation is based on interior -//! mutability and doesn't need `&mut`. You can have two `SyntaxNode`s pointing -//! at different parts of the same tree; mutations via the first node will be -//! reflected in the other. // Implementation notes: // @@ -35,51 +29,7 @@ // pointing somewhere in the middle of the tree, then all `NodeData` on the path // from that point towards the root have ref count equal to one. // -// `NodeData` which doesn't have a parent (is a root) owns the corresponding -// green node or token, and is responsible for freeing it. -// -// That's mostly it for the immutable subset of the API. Mutation is fun though, -// you'll like it! -// -// Mutability is a run-time property of a tree of `NodeData`. The whole tree is -// either mutable or immutable. `clone_for_update` clones the whole tree of -// `NodeData`s, making it mutable (note that the green tree is re-used). -// -// If the tree is mutable, then all live `NodeData` are additionally liked to -// each other via intrusive liked lists. Specifically, there are two pointers to -// siblings, as well as a pointer to the first child. Note that only live nodes -// are considered. If the user only has `SyntaxNode`s for the first and last -// children of some particular node, then their `NodeData` will point at each -// other. -// -// The links are used to propagate mutations across the tree. Specifically, each -// `NodeData` remembers it's index in parent. When the node is detached from or -// attached to the tree, we need to adjust the indices of all subsequent -// siblings. That's what makes the `for c in node.children() { c.detach() }` -// pattern work despite the apparent iterator invalidation. -// -// This code is encapsulated into the sorted linked list (`sll`) module. -// -// The actual mutation consist of functionally "mutating" (creating a -// structurally shared copy) the green node, and then re-spinning the tree. This -// is a delicate process: `NodeData` point directly to the green nodes, so we -// must make sure that those nodes don't move. Additionally, during mutation a -// node might become or might stop being a root, so we must take care to not -// double free / leak its green node. -// -// Because we can change green nodes using only shared references, handing out -// references into green nodes in the public API would be unsound. We don't do -// that, but we do use such references internally a lot. Additionally, for -// tokens the underlying green token actually is immutable, so we can, and do -// return `&str`. -// -// Invariants [must not leak outside of the module]: -// - Mutability is the property of the whole tree. Intermixing elements that -// differ in mutability is not allowed. -// - Mutability property is persistent. -// - References to the green elements' data are not exposed into public API -// when the tree is mutable. -// - TBD +// A root `NodeData` owns its green node and is responsible for freeing it. use std::{ borrow::Cow, @@ -87,8 +37,7 @@ use std::{ fmt, hash::{Hash, Hasher}, iter, - mem::{self, ManuallyDrop}, - ops::Range, + mem::ManuallyDrop, ptr, slice, }; @@ -96,14 +45,12 @@ use countme::Count; use crate::{ green::{GreenChild, GreenElementRef, GreenNodeData, GreenTokenData, SyntaxKind}, - sll, - utility_types::Delta, Direction, GreenNode, GreenToken, NodeOrToken, SyntaxText, TextRange, TextSize, TokenAtOffset, WalkEvent, }; enum Green { - Node { ptr: Cell> }, + Node { ptr: ptr::NonNull }, Token { ptr: ptr::NonNull }, } @@ -113,32 +60,10 @@ struct NodeData { _c: Count<_SyntaxElement>, rc: Cell, - parent: Cell>>, - index: Cell, + parent: Option>, + index: u32, green: Green, - - /// Invariant: never changes after NodeData is created. - mutable: bool, - /// Absolute offset for immutable nodes, unused for mutable nodes. offset: TextSize, - // The following links only have meaning when `mutable` is true. - first: Cell<*const NodeData>, - /// Invariant: never null if mutable. - next: Cell<*const NodeData>, - /// Invariant: never null if mutable. - prev: Cell<*const NodeData>, -} - -unsafe impl sll::Elem for NodeData { - fn prev(&self) -> &Cell<*const Self> { - &self.prev - } - fn next(&self) -> &Cell<*const Self> { - &self.next - } - fn key(&self) -> &Cell { - &self.index - } } pub type SyntaxElement = NodeOrToken; @@ -190,14 +115,10 @@ impl Drop for SyntaxToken { 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() { + match node.parent { 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 { @@ -205,13 +126,10 @@ unsafe fn free(mut data: ptr::NonNull) { } } None => { - match &node.green { - Green::Node { ptr } => { - let _ = GreenNode::from_raw(ptr.get()); - } - Green::Token { ptr } => { - let _ = GreenToken::from_raw(*ptr); - } + if let Green::Node { ptr } = &node.green { + let _ = GreenNode::from_raw(*ptr); + } else { + unreachable!("a token cannot be a root"); } break; } @@ -226,57 +144,17 @@ impl NodeData { index: u32, offset: TextSize, green: Green, - mutable: bool, ) -> ptr::NonNull { let parent = ManuallyDrop::new(parent); let res = NodeData { _c: Count::new(), rc: Cell::new(1), - parent: Cell::new(parent.as_ref().map(|it| it.ptr)), - index: Cell::new(index), + parent: parent.as_ref().map(|it| it.ptr), + index, green, - - mutable, offset, - first: Cell::new(ptr::null()), - next: Cell::new(ptr::null()), - prev: Cell::new(ptr::null()), }; - unsafe { - if mutable { - let res_ptr: *const NodeData = &res; - match sll::init((*res_ptr).parent().map(|it| &it.first), res_ptr.as_ref().unwrap()) - { - sll::AddToSllResult::AlreadyInSll(node) => { - if cfg!(debug_assertions) { - assert_eq!((*node).index(), (*res_ptr).index()); - match ((*node).green(), (*res_ptr).green()) { - (NodeOrToken::Node(lhs), NodeOrToken::Node(rhs)) => { - assert!(ptr::eq(lhs, rhs)) - } - (NodeOrToken::Token(lhs), NodeOrToken::Token(rhs)) => { - assert!(ptr::eq(lhs, rhs)) - } - it => { - panic!("node/token confusion: {:?}", it) - } - } - } - - ManuallyDrop::into_inner(parent); - let res = node as *mut NodeData; - (*res).inc_rc(); - return ptr::NonNull::new_unchecked(res); - } - it => { - let res = Box::into_raw(Box::new(res)); - it.add_to_sll(res); - return ptr::NonNull::new_unchecked(res); - } - } - } - ptr::NonNull::new_unchecked(Box::into_raw(Box::new(res))) - } + ptr::NonNull::from(Box::leak(Box::new(res))) } #[inline] @@ -298,7 +176,7 @@ impl NodeData { #[inline] fn key(&self) -> (ptr::NonNull<()>, TextSize) { let ptr = match &self.green { - Green::Node { ptr } => ptr.get().cast(), + Green::Node { ptr } => ptr.cast(), Green::Token { ptr } => ptr.cast(), }; (ptr, self.offset()) @@ -314,20 +192,20 @@ impl NodeData { #[inline] fn parent(&self) -> Option<&NodeData> { - self.parent.get().map(|it| unsafe { &*it.as_ptr() }) + self.parent.map(|it| unsafe { &*it.as_ptr() }) } #[inline] 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::Node { ptr } => GreenElementRef::Node(unsafe { &*ptr.as_ptr() }), + Green::Token { ptr } => GreenElementRef::Token(unsafe { ptr.as_ref() }), } } #[inline] fn green_siblings(&self) -> slice::Iter<'_, GreenChild> { match &self.parent().map(|it| &it.green) { - Some(Green::Node { ptr }) => unsafe { &*ptr.get().as_ptr() }.children().raw, + Some(Green::Node { ptr }) => unsafe { &*ptr.as_ptr() }.children().raw, Some(Green::Token { .. }) => { debug_assert!(false); [].iter() @@ -337,30 +215,12 @@ impl NodeData { } #[inline] fn index(&self) -> u32 { - self.index.get() + self.index } #[inline] fn offset(&self) -> TextSize { - if self.mutable { - self.offset_mut() - } else { - self.offset - } - } - - #[cold] - fn offset_mut(&self) -> TextSize { - let mut res = TextSize::from(0); - - let mut node = self; - while let Some(parent) = node.parent() { - let green = parent.green().into_node().unwrap(); - res += green.children().raw.nth(node.index() as usize).unwrap().rel_offset(); - node = parent; - } - - res + self.offset } #[inline] @@ -422,115 +282,13 @@ impl NodeData { Some(SyntaxElement::new(child.as_ref(), parent, index as u32, offset)) }) } - - fn detach(&self) { - assert!(self.mutable); - assert!(self.rc.get() > 0); - let parent_ptr = match self.parent.take() { - Some(parent) => parent, - 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); - } - } - - match parent.green() { - NodeOrToken::Node(green) => { - let green = green.remove_child(self.index() as usize); - parent.respine(green) - } - NodeOrToken::Token(_) => unreachable!(), - } - - if parent.dec_rc() { - 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(); - - if !self.first.get().is_null() { - sll::adjust(&*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 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(), - }; - - let green = green.insert_child(index, child_green); - 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; - } - _ => unreachable!(), - }, - None => { - mem::forget(new_green); - let _ = GreenNode::from_raw(old_green); - break; - } - } - } - } } impl SyntaxNode { pub fn new_root(green: GreenNode) -> SyntaxNode { let green = GreenNode::into_raw(green); - let green = Green::Node { ptr: Cell::new(green) }; - SyntaxNode { ptr: NodeData::new(None, 0, 0.into(), green, false) } - } - - pub fn new_root_mut(green: GreenNode) -> SyntaxNode { - let green = GreenNode::into_raw(green); - let green = Green::Node { ptr: Cell::new(green) }; - SyntaxNode { ptr: NodeData::new(None, 0, 0.into(), green, true) } + let green = Green::Node { ptr: green }; + SyntaxNode { ptr: NodeData::new(None, 0, 0.into(), green) } } fn new_child( @@ -539,24 +297,8 @@ impl SyntaxNode { index: u32, offset: TextSize, ) -> SyntaxNode { - let mutable = parent.data().mutable; - let green = Green::Node { ptr: Cell::new(green.into()) }; - 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() { - Some(parent) => { - let parent = parent.clone_for_update(); - SyntaxNode::new_child(self.green_ref(), parent, self.data().index(), self.offset()) - } - None => SyntaxNode::new_root_mut(self.green_ref().to_owned()), - } + let green = Green::Node { ptr: green.into() }; + SyntaxNode { ptr: NodeData::new(Some(parent), index, offset, green) } } pub fn clone_subtree(&self) -> SyntaxNode { @@ -608,11 +350,7 @@ impl SyntaxNode { #[inline] pub fn green(&self) -> Cow<'_, GreenNodeData> { - let green_ref = self.green_ref(); - match self.data().mutable { - false => Cow::Borrowed(green_ref), - true => Cow::Owned(green_ref.to_owned()), - } + Cow::Borrowed(self.green_ref()) } #[inline] fn green_ref(&self) -> &GreenNodeData { @@ -814,35 +552,6 @@ impl SyntaxNode { SyntaxElement::new(green, self.clone(), index as u32, self.offset() + rel_offset) }) } - - pub fn splice_children(&self, to_delete: Range, to_insert: Vec) { - assert!(self.data().mutable, "immutable tree: {}", self); - for (i, child) in self.children_with_tokens().enumerate() { - if to_delete.contains(&i) { - child.detach(); - } - } - let mut index = to_delete.start; - for child in to_insert { - self.attach_child(index, child); - index += 1; - } - } - - pub fn detach(&self) { - assert!(self.data().mutable, "immutable tree: {}", self); - self.data().detach() - } - - fn attach_child(&self, index: usize, child: SyntaxElement) { - assert!(self.data().mutable, "immutable tree: {}", self); - child.detach(); - let data = match &child { - NodeOrToken::Node(it) => it.data(), - NodeOrToken::Token(it) => it.data(), - }; - self.data().attach_child(index, data) - } } impl SyntaxToken { @@ -852,9 +561,8 @@ impl SyntaxToken { index: u32, offset: TextSize, ) -> SyntaxToken { - let mutable = parent.data().mutable; let green = Green::Token { ptr: green.into() }; - SyntaxToken { ptr: NodeData::new(Some(parent), index, offset, green, mutable) } + SyntaxToken { ptr: NodeData::new(Some(parent), index, offset, green) } } #[inline] @@ -958,11 +666,6 @@ impl SyntaxToken { .and_then(|element| element.last_token()), } } - - pub fn detach(&self) { - assert!(self.data().mutable, "immutable tree: {}", self); - self.data().detach() - } } impl SyntaxElement { @@ -1064,13 +767,6 @@ impl SyntaxElement { NodeOrToken::Node(node) => node.token_at_offset(offset), } } - - pub fn detach(&self) { - match self { - NodeOrToken::Node(it) => it.detach(), - NodeOrToken::Token(it) => it.detach(), - } - } } // region: impls diff --git a/src/green/token.rs b/src/green/token.rs index 1a4548a4..c8003fea 100644 --- a/src/green/token.rs +++ b/src/green/token.rs @@ -116,13 +116,6 @@ impl GreenToken { let ptr = ThinArc::from_header_and_iter(head, text.bytes()); GreenToken { ptr } } - #[inline] - pub(crate) fn into_raw(this: GreenToken) -> ptr::NonNull { - let green = ManuallyDrop::new(this); - let green: &GreenTokenData = &*green; - ptr::NonNull::from(&*green) - } - #[inline] pub(crate) unsafe fn from_raw(ptr: ptr::NonNull) -> GreenToken { let arc = Arc::from_raw(&ptr.as_ref().data as *const ReprThin); diff --git a/src/lib.rs b/src/lib.rs index bb8f30d0..70e38aaa 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,8 +19,6 @@ mod utility_types; mod cow_mut; #[allow(unsafe_code)] -mod sll; -#[allow(unsafe_code)] mod arc; #[cfg(feature = "serde1")] mod serde_impls; diff --git a/src/sll.rs b/src/sll.rs deleted file mode 100644 index 87d2f1f3..00000000 --- a/src/sll.rs +++ /dev/null @@ -1,129 +0,0 @@ -//! Sorted Linked List - -use std::{cell::Cell, cmp::Ordering, ptr}; - -use crate::utility_types::Delta; -pub(crate) unsafe trait Elem { - fn prev(&self) -> &Cell<*const Self>; - fn next(&self) -> &Cell<*const Self>; - fn key(&self) -> &Cell; -} - -pub(crate) enum AddToSllResult<'a, E: Elem> { - NoHead, - EmptyHead(&'a Cell<*const E>), - SmallerThanHead(&'a Cell<*const E>), - SmallerThanNotHead(*const E), - AlreadyInSll(*const E), -} - -impl<'a, E: Elem> AddToSllResult<'a, E> { - pub(crate) fn add_to_sll(&self, elem_ptr: *const E) { - unsafe { - (*elem_ptr).prev().set(elem_ptr); - (*elem_ptr).next().set(elem_ptr); - - match self { - // Case 1: empty head, replace it. - AddToSllResult::EmptyHead(head) => head.set(elem_ptr), - - // Case 2: we are smaller than the head, replace it. - AddToSllResult::SmallerThanHead(head) => { - let old_head = head.get(); - let prev = (*old_head).prev().replace(elem_ptr); - (*prev).next().set(elem_ptr); - (*elem_ptr).next().set(old_head); - (*elem_ptr).prev().set(prev); - head.set(elem_ptr); - } - - // Case 3: insert in place found by looping - AddToSllResult::SmallerThanNotHead(curr) => { - let next = (**curr).next().replace(elem_ptr); - (*next).prev().set(elem_ptr); - (*elem_ptr).prev().set(*curr); - (*elem_ptr).next().set(next); - } - AddToSllResult::NoHead | AddToSllResult::AlreadyInSll(_) => (), - } - } - } -} - -#[cold] -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 - } -} - -#[cold] -pub(crate) fn unlink(head: &Cell<*const E>, elem: &E) { - debug_assert!(!head.get().is_null(), "invalid linked list head"); - - let elem_ptr: *const E = elem; - - let prev = elem.prev().replace(elem_ptr); - let next = elem.next().replace(elem_ptr); - unsafe { - debug_assert_eq!((*prev).next().get(), elem_ptr, "invalid linked list links"); - debug_assert_eq!((*next).prev().get(), elem_ptr, "invalid linked list links"); - (*prev).next().set(next); - (*next).prev().set(prev); - } - - if head.get() == elem_ptr { - head.set(if next == elem_ptr { ptr::null() } else { next }) - } -} - -#[cold] -pub(crate) fn link<'a, E: Elem>(head: &'a Cell<*const E>, elem: &E) -> AddToSllResult<'a, E> { - 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); - } - - // Case 3: loop *backward* until we find insertion place. Because of - // Case 2, we can't loop beyond the head. - let mut curr = (*old_head).prev().get(); - loop { - match (*curr).key().cmp(elem.key()) { - Ordering::Less => return AddToSllResult::SmallerThanNotHead(curr), - Ordering::Equal => return AddToSllResult::AlreadyInSll(curr), - Ordering::Greater => curr = (*curr).prev().get(), - } - } - } -} - -pub(crate) fn adjust(elem: &E, from: u32, by: Delta) { - let elem_ptr: *const E = elem; - - unsafe { - let mut curr = elem_ptr; - loop { - let mut key = (*curr).key().get(); - if key >= from { - key += by; - (*curr).key().set(key); - } - curr = (*curr).next().get(); - if curr == elem_ptr { - break; - } - } - } -} diff --git a/src/utility_types.rs b/src/utility_types.rs index 817add72..c923e618 100644 --- a/src/utility_types.rs +++ b/src/utility_types.rs @@ -1,8 +1,4 @@ -use std::{ - fmt, - ops::{AddAssign, Deref}, -}; -use text_size::TextSize; +use std::{fmt, ops::Deref}; #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum NodeOrToken { @@ -156,25 +152,3 @@ macro_rules! _static_assert { } pub(crate) use _static_assert as static_assert; - -#[derive(Copy, Clone, Debug)] -pub(crate) enum Delta { - Add(T), - Sub(T), -} - -// This won't be coherent :-( -// impl AddAssign> for T -macro_rules! impls { - ($($ty:ident)*) => {$( - impl AddAssign> for $ty { - fn add_assign(&mut self, rhs: Delta<$ty>) { - match rhs { - Delta::Add(amt) => *self += amt, - Delta::Sub(amt) => *self -= amt, - } - } - } - )*}; -} -impls!(u32 TextSize); From 2724ff923b6b3d69f06e92c4a2a3a695b70aed57 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Fri, 31 Jul 2026 11:21:45 +0530 Subject: [PATCH 3/5] Borrow green nodes from syntax nodes --- src/api.rs | 4 ++-- src/cursor.rs | 7 +++---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/api.rs b/src/api.rs index bb2f0a51..9b7ca5a1 100644 --- a/src/api.rs +++ b/src/api.rs @@ -1,4 +1,4 @@ -use std::{borrow::Cow, fmt, iter, marker::PhantomData}; +use std::{fmt, iter, marker::PhantomData}; use crate::{ cursor, green::GreenTokenData, Direction, GreenNode, GreenNodeData, GreenToken, NodeOrToken, @@ -121,7 +121,7 @@ impl SyntaxNode { self.raw.text() } - pub fn green(&self) -> Cow<'_, GreenNodeData> { + pub fn green(&self) -> &GreenNodeData { self.raw.green() } diff --git a/src/cursor.rs b/src/cursor.rs index b22e5e5d..a31a7668 100644 --- a/src/cursor.rs +++ b/src/cursor.rs @@ -32,7 +32,6 @@ // A root `NodeData` owns its green node and is responsible for freeing it. use std::{ - borrow::Cow, cell::Cell, fmt, hash::{Hash, Hasher}, @@ -302,7 +301,7 @@ impl SyntaxNode { } pub fn clone_subtree(&self) -> SyntaxNode { - SyntaxNode::new_root(self.green().into()) + SyntaxNode::new_root(self.green().to_owned()) } #[inline] @@ -349,8 +348,8 @@ impl SyntaxNode { } #[inline] - pub fn green(&self) -> Cow<'_, GreenNodeData> { - Cow::Borrowed(self.green_ref()) + pub fn green(&self) -> &GreenNodeData { + self.green_ref() } #[inline] fn green_ref(&self) -> &GreenNodeData { From 52174e7d71dd4845a23061e7d59799b0bb1b3273 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Fri, 31 Jul 2026 11:22:12 +0530 Subject: [PATCH 4/5] Remove obsolete green node Cow conversions --- src/green/element.rs | 9 --------- src/green/node.rs | 9 +-------- src/green/token.rs | 2 +- 3 files changed, 2 insertions(+), 18 deletions(-) diff --git a/src/green/element.rs b/src/green/element.rs index 2d1ce1f6..d2a2dcfb 100644 --- a/src/green/element.rs +++ b/src/green/element.rs @@ -1,5 +1,3 @@ -use std::borrow::Cow; - use crate::{ green::{GreenNode, GreenToken, SyntaxKind}, GreenNodeData, NodeOrToken, TextSize, @@ -31,13 +29,6 @@ impl From for GreenElement { } } -impl From> for GreenElement { - #[inline] - fn from(cow: Cow<'_, GreenNodeData>) -> Self { - NodeOrToken::Node(cow.into_owned()) - } -} - impl<'a> From<&'a GreenToken> for GreenElementRef<'a> { #[inline] fn from(token: &'a GreenToken) -> GreenElementRef<'a> { diff --git a/src/green/node.rs b/src/green/node.rs index 7c9e68f9..c2eddf0b 100644 --- a/src/green/node.rs +++ b/src/green/node.rs @@ -1,5 +1,5 @@ use std::{ - borrow::{Borrow, Cow}, + borrow::Borrow, fmt, iter::{self, FusedIterator}, mem::{self, ManuallyDrop}, @@ -71,13 +71,6 @@ impl Borrow for GreenNode { } } -impl From> for GreenNode { - #[inline] - fn from(cow: Cow<'_, GreenNodeData>) -> Self { - cow.into_owned() - } -} - impl fmt::Debug for GreenNodeData { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("GreenNode") diff --git a/src/green/token.rs b/src/green/token.rs index c8003fea..fcc5a112 100644 --- a/src/green/token.rs +++ b/src/green/token.rs @@ -117,7 +117,7 @@ impl GreenToken { GreenToken { ptr } } #[inline] - pub(crate) unsafe fn from_raw(ptr: ptr::NonNull) -> GreenToken { + 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); GreenToken { ptr: arc } From cfe7ef4ebc55a5be94b15f06c673f9e5fa123730 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Fri, 31 Jul 2026 11:28:59 +0530 Subject: [PATCH 5/5] bump to 0.17.0 --- Cargo.toml | 2 +- src/cursor.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 8ecfcfcf..097c7432 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rowan" -version = "0.15.19" +version = "0.17.0" authors = ["Aleksey Kladov "] repository = "https://github.com/rust-analyzer/rowan" license = "MIT OR Apache-2.0" diff --git a/src/cursor.rs b/src/cursor.rs index a31a7668..3ebe023a 100644 --- a/src/cursor.rs +++ b/src/cursor.rs @@ -153,7 +153,7 @@ impl NodeData { green, offset, }; - ptr::NonNull::from(Box::leak(Box::new(res))) + unsafe { ptr::NonNull::new_unchecked(Box::into_raw(Box::new(res))) } } #[inline]