Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
bfd10e4
Use niching for Checkpoint
mattfbacon Jan 21, 2024
2e75f11
Merge pull request #161 from mattfbacon/main
Veykril Jan 24, 2024
62bd09b
Add rowan::api::SyntaxNode::new_root_mut
jelmer Oct 4, 2023
20f3001
Fix pointer comparison warning on newer rustc
jelmer May 13, 2024
9f64e50
Prevent use of SyntaxNodePtr and AstPtr on mutable trees
Technohacker Feb 18, 2023
54c9c96
0503
peamaeq May 4, 2022
c082936
'0503'
peamaeq Jun 16, 2022
ba6b8e4
Remove `memoffset` dependency.
aDotInTheVoid May 31, 2024
e2d2e93
v0.15.16
aDotInTheVoid Jun 3, 2024
a87bcd2
Fix typo in docstring: two => to
jelmer Aug 16, 2024
73ed5e7
Merge pull request #167 from jelmer/typo
lnicola Aug 16, 2024
f45924c
Fix typos
jayvdb Sep 27, 2024
448d355
Merge pull request #169 from jayvdb/typos
lnicola Sep 28, 2024
2f4371a
Remove needless .into()
hurryabit Oct 7, 2024
ee37be7
Merge pull request #170 from hurryabit/needless-into
lnicola Oct 7, 2024
f06a2c9
Reduce allocations when iterating over Syntax{Node,Element} children
theo-lw Oct 3, 2021
0b1a6ec
Optimize iteration over sibling nodes by reusing NodeData if possible
milianw Oct 27, 2024
ab5463e
Use iterators for by_kind instead of introducing separate structs
milianw Oct 27, 2024
4bc8ba0
Merge pull request #171 from milianw/optimize-children-iteration
Veykril Oct 28, 2024
3294973
Decrement refcount before calling free in to_next_sibling
milianw Oct 31, 2024
c4cb18a
Merge pull request #173 from milianw/fix-to_next_sibling-rc
Veykril Oct 31, 2024
ac472cf
Implement Eq and Hash for GreenTokenData
hurryabit Nov 7, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
[package]
name = "rowan"
version = "0.15.15"
version = "0.15.16"
authors = ["Aleksey Kladov <aleksey.kladov@gmail.com>"]
repository = "https://github.com/rust-analyzer/rowan"
license = "MIT OR Apache-2.0"
description = "Library for generic lossless syntax trees"
edition = "2021"

rust-version = "1.77.0"
exclude = [".github/", "bors.toml", "rustfmt.toml"]

[workspace]
Expand All @@ -18,7 +18,6 @@ hashbrown = { version = "0.14.3", features = [
"inline-more",
], default-features = false }
text-size = "1.1.0"
memoffset = "0.9"
countme = "3.0.0"

serde = { version = "1.0.89", optional = true, default-features = false }
Expand Down
2 changes: 1 addition & 1 deletion examples/math.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ impl<I: Iterator<Item = (SyntaxKind, String)>> Parser<I> {
}

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) => {
Expand Down
75 changes: 69 additions & 6 deletions src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,13 @@ impl<L: Language> SyntaxNode<L> {
pub fn new_root(green: GreenNode) -> SyntaxNode<L> {
SyntaxNode::from(cursor::SyntaxNode::new_root(green))
}
pub fn new_root_mut(green: GreenNode) -> SyntaxNode<L> {
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)
}
Expand Down Expand Up @@ -144,27 +148,64 @@ impl<L: Language> SyntaxNode<L> {
pub fn first_child(&self) -> Option<SyntaxNode<L>> {
self.raw.first_child().map(Self::from)
}

pub fn first_child_by_kind(&self, matcher: &impl Fn(L::Kind) -> bool) -> Option<SyntaxNode<L>> {
self.raw
.first_child_by_kind(&|raw_kind| matcher(L::kind_from_raw(raw_kind)))
.map(Self::from)
}

pub fn last_child(&self) -> Option<SyntaxNode<L>> {
self.raw.last_child().map(Self::from)
}

pub fn first_child_or_token(&self) -> Option<SyntaxElement<L>> {
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<SyntaxElement<L>> {
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<SyntaxElement<L>> {
self.raw.last_child_or_token().map(NodeOrToken::from)
}

pub fn next_sibling(&self) -> Option<SyntaxNode<L>> {
self.raw.next_sibling().map(Self::from)
}

pub fn next_sibling_by_kind(
&self,
matcher: &impl Fn(L::Kind) -> bool,
) -> Option<SyntaxNode<L>> {
self.raw
.next_sibling_by_kind(&|raw_kind| matcher(L::kind_from_raw(raw_kind)))
.map(Self::from)
}

pub fn prev_sibling(&self) -> Option<SyntaxNode<L>> {
self.raw.prev_sibling().map(Self::from)
}

pub fn next_sibling_or_token(&self) -> Option<SyntaxElement<L>> {
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<SyntaxElement<L>> {
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<SyntaxElement<L>> {
self.raw.prev_sibling_or_token().map(NodeOrToken::from)
}
Expand Down Expand Up @@ -210,15 +251,15 @@ impl<L: Language> SyntaxNode<L> {
}

/// 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<SyntaxToken<L>> {
self.raw.token_at_offset(offset).map(SyntaxToken::from)
}

/// Return the deepest node or token in the current subtree that fully
/// contains the range. If the range is empty and is contained in two leaf
/// nodes, either one can be returned. Precondition: range must be contained
/// withing the current node
/// within the current node
pub fn covering_element(&self, range: TextRange) -> SyntaxElement<L> {
NodeOrToken::from(self.raw.covering_element(range))
}
Expand All @@ -244,6 +285,10 @@ impl<L: Language> SyntaxNode<L> {
SyntaxNode::from(self.raw.clone_for_update())
}

pub fn is_mutable(&self) -> bool {
self.raw.is_mutable()
}

pub fn detach(&self) {
self.raw.detach()
}
Expand All @@ -256,8 +301,8 @@ impl<L: Language> SyntaxNode<L> {

impl<L: Language> SyntaxToken<L> {
/// 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)
}
Expand Down Expand Up @@ -395,6 +440,15 @@ impl<L: Language> Iterator for SyntaxNodeChildren<L> {
}
}

impl<L: Language> SyntaxNodeChildren<L> {
pub fn by_kind(self, matcher: impl Fn(L::Kind) -> bool) -> impl Iterator<Item = SyntaxNode<L>> {
self.raw
.by_kind(move |raw_kind| matcher(L::kind_from_raw(raw_kind)))
.into_iter()
.map(SyntaxNode::from)
}
}

#[derive(Debug, Clone)]
pub struct SyntaxElementChildren<L: Language> {
raw: cursor::SyntaxElementChildren,
Expand All @@ -408,6 +462,15 @@ impl<L: Language> Iterator for SyntaxElementChildren<L> {
}
}

impl<L: Language> SyntaxElementChildren<L> {
pub fn by_kind(
self,
matcher: impl Fn(L::Kind) -> bool,
) -> impl Iterator<Item = SyntaxElement<L>> {
self.raw.by_kind(move |raw_kind| matcher(L::kind_from_raw(raw_kind))).map(NodeOrToken::from)
}
}

pub struct Preorder<L: Language> {
raw: cursor::Preorder,
_p: PhantomData<L>,
Expand Down
15 changes: 5 additions & 10 deletions src/arc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use std::{
cmp::Ordering,
hash::{Hash, Hasher},
marker::PhantomData,
mem::{self, ManuallyDrop},
mem::{self, offset_of, ManuallyDrop},
ops::Deref,
ptr,
sync::atomic::{
Expand All @@ -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
Expand Down Expand Up @@ -83,7 +81,7 @@ impl<T: ?Sized> Arc<T> {
/// 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<T> {
Expand Down Expand Up @@ -257,12 +255,9 @@ impl<H, T> Deref for HeaderSlice<H, [T; 0]> {
type Target = HeaderSlice<H, [T]>;

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<H, [T]>)
}
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<H, [T]>) }
}
}

Expand Down
72 changes: 71 additions & 1 deletion src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<L: Language> {
kind: L::Kind,
Expand All @@ -63,7 +68,10 @@ pub struct SyntaxNodePtr<L: Language> {

impl<L: Language> SyntaxNodePtr<L> {
/// Returns a [`SyntaxNodePtr`] for the node.
///
/// Panics if the provided node is mutable
pub fn new(node: &SyntaxNode<L>) -> Self {
assert!(!node.is_mutable(), "tree is mutable");
Self { kind: node.kind(), range: node.text_range() }
}

Expand All @@ -82,10 +90,13 @@ impl<L: Language> SyntaxNodePtr<L> {
/// 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<L>) -> Option<SyntaxNode<L>> {
assert!(!root.is_mutable(), "tree is mutable");
if root.parent().is_some() {
return None;
}
Expand Down Expand Up @@ -113,13 +124,21 @@ impl<L: Language> SyntaxNodePtr<L> {
}

/// 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<N: AstNode> {
raw: SyntaxNodePtr<N::Language>,
}

impl<N: AstNode> AstPtr<N> {
/// 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()) }
}

Expand All @@ -129,8 +148,9 @@ impl<N: AstNode> AstPtr<N> {
}

/// 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<N::Language>) -> Option<N> {
// The above mentioned panic is handled by SyntaxNodePtr
N::cast(self.raw.try_to_node(root)?)
}

Expand Down Expand Up @@ -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<TestLanguage> {
// Creates a single-node tree
let mut builder = GreenNodeBuilder::new();
builder.start_node(SyntaxKind(0));
builder.finish_node();

SyntaxNode::<TestLanguage>::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);
}
}
Loading