refactor(rome_rowan): change `splice_slots` and `replace_child` to return a detached node
MODIFY
crates/rome_rowan/src/ast/batch.rs
crates/rome_rowan/src/ast/batch.rs
@@ -176,6 +176,18 @@ where
self.push_change(prev_element, Some(next_element))
}
+ /// Push a change to replace the "prev_node" with "next_node".
+ ///
+ /// Changes to take effect must be commited.
+ pub fn replace_node_discard_trivia<T>(&mut self, prev_node: T, next_node: T)
+ where
+ T: AstNode<Language = L>,
+ {
+ self.replace_element_discard_trivia(
+ prev_node.into_syntax().into(),
+ next_node.into_syntax().into(),
+ )
+ }
/// Push a change to replace the "prev_element" with "next_element".
///
/// Changes to take effect must be commited.

MODIFY
crates/rome_rowan/src/ast/mutation.rs
crates/rome_rowan/src/ast/mutation.rs
@@ -50,6 +50,8 @@ pub trait AstNodeExt: AstNode {
) -> Option<Self>
where
Self: Sized;
+
+ fn detach(self) -> Self;
}
impl<T> AstNodeExt for T
@@ -146,6 +148,10 @@ where
),
)
}
+
+ fn detach(self) -> Self {
+ Self::unwrap_cast(self.into_syntax().detach())
+ }
}
pub trait AstNodeListExt: AstNodeList {

MODIFY
crates/rome_rowan/src/cursor.rs
crates/rome_rowan/src/cursor.rs
@@ -317,69 +317,66 @@ impl NodeData {
R: ops::RangeBounds<usize>,
I: Iterator<Item = Option<green::GreenElement>>,
{
- let new_green = match self.green() {
- NodeOrToken::Node(green) => green.splice_slots(range, replace_with),
+ let green = match self.green() {
+ NodeOrToken::Node(green) => green.splice_slots(range, replace_with).into(),
NodeOrToken::Token(_) => panic!("called splice_slots on a token node"),
};
- // SAFETY: This conversion can only fail on 16-bits systems for nodes with more than 65 535 children
- let index = usize::try_from(self.slot).expect("integer overflow");
-
// Try to reuse the underlying memory allocation if self is the only
// outstanding reference to this NodeData
match Rc::get_mut(&mut self) {
Some(node) => {
- // We can't just do `*parent = parent.splice_slots()` because `splice_slots` may panic and `parent`
- // would remain in an inconsistent state (the old value is moved out but the new value isn't yet
- // moved in), instead this has to be done in multiple steps:
+ node.kind = NodeKind::Root { green };
+ node.slot = 0;
+ node.offset = TextSize::from(0);
+ self
+ }
+ None => Self::new(NodeKind::Root { green }, 0, 0.into()),
+ }
+ }
- // 1. Clone the reference to the previous parent if it exists
- let parent = match &node.kind {
- NodeKind::Child { parent, .. } => Some(parent.clone()),
- NodeKind::Root { .. } => None,
- };
+ /// Return a new version of this node with the element `prev_elem` replaced with `next_elem`
+ ///
+ /// `prev_elem` can be a direct child of this node, or an indirect child through any descendant node
+ ///
+ /// Returns `None` if `prev_elem` is not a descendant of this node
+ #[must_use = "syntax elements are immutable, the result of update methods must be propagated to have any effect"]
+ fn replace_child(
+ mut self: Rc<Self>,
+ prev_elem: SyntaxElement,
+ next_elem: SyntaxElement,
+ ) -> Option<Rc<Self>> {
+ let mut green = next_elem.into_green();
+ let mut elem = prev_elem;
- // 2. Unconditionally set the kind to `Root` (to ensure parent is the only outstanding
- // reference to the parent node)
- // This also bumps the reference count of new_green temporarily to be able to construct
- // the (optional) parent tree, in both case the reference will be decremented again (either
- // the node is a root and the second reference gets dropped, or the node is a child and
- // node.kind.green gets downgraded to weak)
- node.kind = NodeKind::Root {
- green: GreenElement::Node(new_green.clone()),
- };
+ loop {
+ let node = elem.parent()?;
+ let is_self = node.key() == self.key();
- // 3. Actually call `splice_slots` on the previous parent if it exists and overwrite the
- // kind to `Child`
- if let Some(parent) = parent {
- node.kind = NodeKind::Child {
- green: WeakGreenElement::new(GreenElementRef::Node(&new_green)),
- parent: parent.splice_slots(
- index..=index,
- iter::once(Some(GreenElement::Node(new_green))),
- ),
- };
+ let index = elem.index();
+ let range = index..=index;
+
+ let replace_with = iter::once(Some(green));
+ green = node.green().splice_slots(range, replace_with).into();
+ elem = node.into();
+
+ if is_self {
+ break;
+ }
}
+ // Try to reuse the underlying memory allocation if self is the only
+ // outstanding reference to this NodeData
+ let result = match Rc::get_mut(&mut self) {
+ Some(node) => {
+ node.kind = NodeKind::Root { green };
+ node.slot = 0;
+ node.offset = TextSize::from(0);
self
}
- None => Rc::new(NodeData {
- _c: Count::new(),
- kind: match &self.kind {
- NodeKind::Root { .. } => NodeKind::Root {
- green: GreenElement::Node(new_green),
- },
- NodeKind::Child { parent, .. } => NodeKind::Child {
- green: WeakGreenElement::new(GreenElementRef::Node(&new_green)),
- parent: parent.clone().splice_slots(
- index..=index,
- iter::once(Some(GreenElement::Node(new_green))),
- ),
- },
- },
- offset: self.offset,
- slot: self.slot,
- }),
- }
+ None => Self::new(NodeKind::Root { green }, 0, 0.into()),
+ };
+
+ Some(result)
}
}

MODIFY
crates/rome_rowan/src/cursor/element.rs
crates/rome_rowan/src/cursor/element.rs
use crate::cursor::{SyntaxNode, SyntaxToken};
-use crate::green::GreenElementRef;
+use crate::green::{GreenElement, GreenElementRef};
use crate::{NodeOrToken, RawSyntaxKind, TokenAtOffset};
use std::iter;
use text_size::{TextRange, TextSize};
@@ -105,6 +105,13 @@ impl SyntaxElement {
NodeOrToken::Token(it) => Self::Token(it.detach()),
}
}
+
+ pub(crate) fn into_green(self) -> GreenElement {
+ match self {
+ NodeOrToken::Node(it) => it.ptr.into_green(),
+ NodeOrToken::Token(it) => it.into_green(),
+ }
+ }
}
// region: impls

MODIFY
crates/rome_rowan/src/cursor/node.rs
crates/rome_rowan/src/cursor/node.rs
@@ -415,6 +415,13 @@ impl SyntaxNode {
),
}
}
+
+ #[must_use = "syntax elements are immutable, the result of update methods must be propagated to have any effect"]
+ pub fn replace_child(self, prev_elem: SyntaxElement, next_elem: SyntaxElement) -> Option<Self> {
+ Some(Self {
+ ptr: self.ptr.replace_child(prev_elem, next_elem)?,
+ })
+ }
}
// Identity semantics for hash & eq

MODIFY
crates/rome_rowan/src/syntax/node.rs
crates/rome_rowan/src/syntax/node.rs
@@ -9,7 +9,7 @@ use crate::{
use serde::Serialize;
use std::any::TypeId;
use std::fmt::{Debug, Formatter};
-use std::iter::{self, FusedIterator};
+use std::iter::FusedIterator;
use std::marker::PhantomData;
use std::ptr::NonNull;
use std::{fmt, ops};
@@ -442,28 +442,10 @@ impl<L: Language> SyntaxNode<L> {
prev_elem: SyntaxElement<L>,
next_elem: SyntaxElement<L>,
) -> Option<Self> {
- let (depth, prev_parent, index) = match prev_elem {
- NodeOrToken::Node(prev_node) => (
- prev_node
- .ancestors()
- .position(move |node| node == self)?
- .checked_sub(1)?,
- prev_node.parent()?,
- prev_node.index(),
- ),
- NodeOrToken::Token(prev_token) => (
- prev_token.ancestors().position(move |node| node == self)?,
- prev_token.parent()?,
- prev_token.index(),
- ),
- };
-
- let next_parent = prev_parent.splice_slots(index..=index, iter::once(Some(next_elem)));
-
- match depth {
- 0 => Some(next_parent),
- index => next_parent.ancestors().nth(index),
- }
+ Some(Self {
+ raw: self.raw.replace_child(prev_elem.into(), next_elem.into())?,
+ _p: PhantomData,
+ })
}
pub fn into_list(self) -> SyntaxList<L> {

refactor(rome_rowan): change `splice_slots` and `replace_child` to return a detached node (#2949)