Skip to content

Commit

Permalink
Reduce allocations when iterating over Syntax{Node,Element} children
Browse files Browse the repository at this point in the history
This allows users of the API to apply a filter on the SyntaxKind
before materializing concrete SyntaxNode/Token objects, which require
a memory allocation for the NodeData.

For slint, this removes ~400k allocations when parsing a largish
project, about 50% of all rowan allocations (850k down to 450k).
  • Loading branch information
theo-lw authored and milianw committed Oct 27, 2024
1 parent ee37be7 commit f06a2c9
Show file tree
Hide file tree
Showing 3 changed files with 288 additions and 10 deletions.
93 changes: 93 additions & 0 deletions src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,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 @@ -403,6 +440,34 @@ impl<L: Language> Iterator for SyntaxNodeChildren<L> {
}
}

impl<L: Language> SyntaxNodeChildren<L> {
pub fn by_kind<'a>(
self,
matcher: &'a dyn Fn(SyntaxKind) -> bool,
) -> SyntaxNodeChildrenByKind<'a, L> {
SyntaxNodeChildrenByKind { raw: self.raw.by_kind(matcher), _p: PhantomData }
}
}

#[derive(Clone)]
pub struct SyntaxNodeChildrenByKind<'a, L: Language> {
raw: cursor::SyntaxNodeChildrenByKind<&'a dyn Fn(SyntaxKind) -> bool>,
_p: PhantomData<L>,
}

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

impl<'a, L: Language> fmt::Debug for SyntaxNodeChildrenByKind<'a, L> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("SyntaxNodeChildrenByKind").finish()
}
}

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

impl<L: Language> SyntaxElementChildren<L> {
pub fn by_kind<'a>(
self,
matcher: &'a dyn Fn(SyntaxKind) -> bool,
) -> SyntaxElementChildrenByKind<'a, L> {
SyntaxElementChildrenByKind { raw: self.raw.by_kind(matcher), _p: PhantomData }
}
}

#[derive(Clone)]
pub struct SyntaxElementChildrenByKind<'a, L: Language> {
raw: cursor::SyntaxElementChildrenByKind<&'a dyn Fn(SyntaxKind) -> bool>,
_p: PhantomData<L>,
}

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

impl<'a, L: Language> fmt::Debug for SyntaxElementChildrenByKind<'a, L> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("SyntaxElementChildrenByKind").finish()
}
}

pub struct Preorder<L: Language> {
raw: cursor::Preorder,
_p: PhantomData<L>,
Expand Down
Loading

0 comments on commit f06a2c9

Please sign in to comment.