Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 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
b0a922b
Allow passing an iterable to `splice_children`
michaelvanstraten Jun 21, 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
dc1eb13
Bump crate version number
michaelvanstraten Aug 18, 2024
de36433
Merge pull request #165 from michaelvanstraten/allow-passing-an-itera…
lnicola Nov 11, 2024
84b1c8f
Fix prev_sibling indexing off-by-one
milianw Nov 22, 2024
e25dbdf
Merge pull request #176 from milianw/fix-prev_sibling
lnicola Nov 22, 2024
2caf851
Fix CI
kornelski Feb 14, 2025
da4dc39
Make tests pass with -Zrandomize-layout
kornelski Feb 14, 2025
080b942
Update link in README (#185)
IoannisNezis Feb 23, 2025
42cb260
rust 2024 + clippy
BenjaminBrienen-CanonPP Mar 5, 2025
4e97c36
fix link
BenjaminBrienen-CanonPP Mar 5, 2025
d725a34
fix link in doc
BenjaminBrienen-CanonPP Mar 5, 2025
42b3fcd
Merge pull request #186 from BenjaminBrienen/update-all
Veykril Mar 5, 2025
fe2e4a7
README.md: add link to docs.rs (#193)
chris-laplante May 5, 2025
30d686c
move size assert from const to tests
the8472 Jun 14, 2025
e9813a6
remove now-unusued macro
the8472 Jun 14, 2025
b477a12
Merge pull request #196 from the8472/fix-layout-randomization
Veykril Jun 15, 2025
d80594d
Slap `derive(Debug, Clone)` on preorder iterators
ChayimFriedman2 Jul 27, 2025
0b9a8d5
Merge pull request #198 from ChayimFriedman2/clone-preorder
ChayimFriedman2 Jul 27, 2025
8cb4e0d
Update version to 1.16.2
ChayimFriedman2 Jul 27, 2025
5181988
Merge pull request #199 from ChayimFriedman2/update-version
ChayimFriedman2 Jul 27, 2025
9dd084f
Don't capture unneeded lifetimes in `impl Iterator` return types
ChayimFriedman2 Jul 27, 2025
0c1077e
Merge pull request #200 from ChayimFriedman2/fix-ra
ChayimFriedman2 Jul 27, 2025
3a444fc
Derive Default for Syntax{Node,Element}Children
antonylsg Sep 4, 2025
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
20 changes: 10 additions & 10 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,27 +1,27 @@
[package]
name = "rowan"
version = "0.15.15"
version = "0.16.2"
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"

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"
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -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.

Expand Down
3 changes: 2 additions & 1 deletion examples/math.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ enum SyntaxKind {
OPERATION,
ROOT,
}

use SyntaxKind::*;

impl From<SyntaxKind> for rowan::SyntaxKind {
Expand Down Expand Up @@ -111,7 +112,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
10 changes: 4 additions & 6 deletions examples/s_expressions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -194,8 +194,10 @@ fn parse(text: &str) -> Parse {
/// has identity semantics.

type SyntaxNode = rowan::SyntaxNode<Lang>;

#[allow(unused)]
type SyntaxToken = rowan::SyntaxToken<Lang>;

#[allow(unused)]
type SyntaxElement = rowan::NodeOrToken<SyntaxNode, SyntaxToken>;

Expand Down Expand Up @@ -255,11 +257,7 @@ macro_rules! ast_node {
impl $ast {
#[allow(unused)]
fn cast(node: SyntaxNode) -> Option<Self> {
if node.kind() == $kind {
Some(Self(node))
} else {
None
}
if node.kind() == $kind { Some(Self(node)) } else { None }
}
}
};
Expand Down
114 changes: 96 additions & 18 deletions src/api.rs
Original file line number Diff line number Diff line change
@@ -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 {
Expand Down 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 @@ -129,7 +133,7 @@ impl<L: Language> SyntaxNode<L> {
self.raw.parent().map(Self::from)
}

pub fn ancestors(&self) -> impl Iterator<Item = SyntaxNode<L>> {
pub fn ancestors(&self) -> impl Iterator<Item = SyntaxNode<L>> + use<L> {
self.raw.ancestors().map(SyntaxNode::from)
}

Expand All @@ -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 All @@ -178,7 +219,7 @@ impl<L: Language> SyntaxNode<L> {
self.raw.last_token().map(SyntaxToken::from)
}

pub fn siblings(&self, direction: Direction) -> impl Iterator<Item = SyntaxNode<L>> {
pub fn siblings(&self, direction: Direction) -> impl Iterator<Item = SyntaxNode<L>> + use<L> {
self.raw.siblings(direction).map(SyntaxNode::from)
}

Expand All @@ -189,11 +230,11 @@ impl<L: Language> SyntaxNode<L> {
self.raw.siblings_with_tokens(direction).map(SyntaxElement::from)
}

pub fn descendants(&self) -> impl Iterator<Item = SyntaxNode<L>> {
pub fn descendants(&self) -> impl Iterator<Item = SyntaxNode<L>> + use<L> {
self.raw.descendants().map(SyntaxNode::from)
}

pub fn descendants_with_tokens(&self) -> impl Iterator<Item = SyntaxElement<L>> {
pub fn descendants_with_tokens(&self) -> impl Iterator<Item = SyntaxElement<L>> + use<L> {
self.raw.descendants_with_tokens().map(NodeOrToken::from)
}

Expand All @@ -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,20 +285,28 @@ 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()
}

pub fn splice_children(&self, to_delete: Range<usize>, to_insert: Vec<SyntaxElement<L>>) {
let to_insert = to_insert.into_iter().map(cursor::SyntaxElement::from).collect::<Vec<_>>();
pub fn splice_children<I: IntoIterator<Item = SyntaxElement<L>>>(
&self,
to_delete: Range<usize>,
to_insert: I,
) {
let to_insert = to_insert.into_iter().map(cursor::SyntaxElement::from);
self.raw.splice_children(to_delete, to_insert)
}
}

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 @@ -288,12 +337,12 @@ impl<L: Language> SyntaxToken<L> {

/// Iterator over all the ancestors of this token excluding itself.
#[deprecated = "use `SyntaxToken::parent_ancestors` instead"]
pub fn ancestors(&self) -> impl Iterator<Item = SyntaxNode<L>> {
pub fn ancestors(&self) -> impl Iterator<Item = SyntaxNode<L>> + use<L> {
self.parent_ancestors()
}

/// Iterator over all the ancestors of this token excluding itself.
pub fn parent_ancestors(&self) -> impl Iterator<Item = SyntaxNode<L>> {
pub fn parent_ancestors(&self) -> impl Iterator<Item = SyntaxNode<L>> + use<L> {
self.raw.ancestors().map(SyntaxNode::from)
}

Expand All @@ -307,7 +356,7 @@ impl<L: Language> SyntaxToken<L> {
pub fn siblings_with_tokens(
&self,
direction: Direction,
) -> impl Iterator<Item = SyntaxElement<L>> {
) -> impl Iterator<Item = SyntaxElement<L>> + use<L> {
self.raw.siblings_with_tokens(direction).map(SyntaxElement::from)
}

Expand Down Expand Up @@ -354,7 +403,7 @@ impl<L: Language> SyntaxElement<L> {
}
}

pub fn ancestors(&self) -> impl Iterator<Item = SyntaxNode<L>> {
pub fn ancestors(&self) -> impl Iterator<Item = SyntaxNode<L>> + use<L> {
let first = match self {
NodeOrToken::Node(it) => Some(it.clone()),
NodeOrToken::Token(it) => it.parent(),
Expand Down Expand Up @@ -388,26 +437,54 @@ pub struct SyntaxNodeChildren<L: Language> {
_p: PhantomData<L>,
}

impl<L: Language> Default for SyntaxNodeChildren<L> {
fn default() -> Self {
Self { raw: Default::default(), _p: PhantomData }
}
}

impl<L: Language> Iterator for SyntaxNodeChildren<L> {
type Item = SyntaxNode<L>;
fn next(&mut self) -> Option<Self::Item> {
self.raw.next().map(SyntaxNode::from)
}
}

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))).map(SyntaxNode::from)
}
}

#[derive(Debug, Clone)]
pub struct SyntaxElementChildren<L: Language> {
raw: cursor::SyntaxElementChildren,
_p: PhantomData<L>,
}

impl<L: Language> Default for SyntaxElementChildren<L> {
fn default() -> Self {
Self { raw: Default::default(), _p: PhantomData }
}
}

impl<L: Language> Iterator for SyntaxElementChildren<L> {
type Item = SyntaxElement<L>;
fn next(&mut self) -> Option<Self::Item> {
self.raw.next().map(NodeOrToken::from)
}
}

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)
}
}

#[derive(Debug, Clone)]
pub struct Preorder<L: Language> {
raw: cursor::Preorder,
_p: PhantomData<L>,
Expand All @@ -426,6 +503,7 @@ impl<L: Language> Iterator for Preorder<L> {
}
}

#[derive(Debug, Clone)]
pub struct PreorderWithTokens<L: Language> {
raw: cursor::PreorderWithTokens,
_p: PhantomData<L>,
Expand Down
Loading
Loading