diff --git a/src/api.rs b/src/api.rs index 6e2cd61..8fb8687 100644 --- a/src/api.rs +++ b/src/api.rs @@ -388,6 +388,34 @@ impl Iterator for SyntaxNodeChildren { } } +impl SyntaxNodeChildren { + 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, +} + +impl<'a, L: Language> Iterator for SyntaxNodeChildrenByKind<'a, L> { + type Item = SyntaxNode; + fn next(&mut self) -> Option { + 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 { raw: cursor::SyntaxElementChildren, @@ -401,6 +429,34 @@ impl Iterator for SyntaxElementChildren { } } +impl SyntaxElementChildren { + 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, +} + +impl<'a, L: Language> Iterator for SyntaxElementChildrenByKind<'a, L> { + type Item = SyntaxElement; + fn next(&mut self) -> Option { + 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 { raw: cursor::Preorder, _p: PhantomData, diff --git a/src/cursor.rs b/src/cursor.rs index 39b0c96..be028f8 100644 --- a/src/cursor.rs +++ b/src/cursor.rs @@ -376,11 +376,26 @@ impl NodeData { } fn next_sibling(&self) -> Option { - let mut siblings = self.green_siblings().enumerate(); + let siblings = self.green_siblings().enumerate(); + let index = self.index() as usize; + + siblings.skip(index + 1).find_map(|(index, child)| { + child.as_ref().into_node().and_then(|green| { + let parent = self.parent_node()?; + let offset = parent.offset() + child.rel_offset(); + Some(SyntaxNode::new_child(green, parent, index as u32, offset)) + }) + }) + } + + fn next_sibling_by_kind(&self, matcher: &impl Fn(SyntaxKind) -> bool) -> Option { + let siblings = self.green_siblings().enumerate(); let index = self.index() as usize; - siblings.nth(index); - siblings.find_map(|(index, child)| { + siblings.skip(index + 1).find_map(|(index, child)| { + if !matcher(child.as_ref().kind()) { + return None; + } child.as_ref().into_node().and_then(|green| { let parent = self.parent_node()?; let offset = parent.offset() + child.rel_offset(); @@ -388,12 +403,12 @@ impl NodeData { }) }) } + fn prev_sibling(&self) -> Option { - let mut rev_siblings = self.green_siblings().enumerate().rev(); + let rev_siblings = self.green_siblings().enumerate().rev(); let index = rev_siblings.len() - (self.index() as usize); - rev_siblings.nth(index); - rev_siblings.find_map(|(index, child)| { + rev_siblings.skip(index + 1).find_map(|(index, child)| { child.as_ref().into_node().and_then(|green| { let parent = self.parent_node()?; let offset = parent.offset() + child.rel_offset(); @@ -412,6 +427,24 @@ impl NodeData { Some(SyntaxElement::new(child.as_ref(), parent, index as u32, offset)) }) } + + fn next_sibling_or_token_by_kind( + &self, + matcher: &impl Fn(SyntaxKind) -> bool, + ) -> Option { + let siblings = self.green_siblings().enumerate(); + let index = self.index() as usize; + + siblings.skip(index + 1).find_map(|(index, child)| { + if !matcher(child.as_ref().kind()) { + return None; + } + let parent = self.parent_node()?; + let offset = parent.offset() + child.rel_offset(); + Some(SyntaxElement::new(child.as_ref(), parent, index as u32, offset)) + }) + } + fn prev_sibling_or_token(&self) -> Option { let mut siblings = self.green_siblings().enumerate(); let index = self.index().checked_sub(1)? as usize; @@ -647,6 +680,23 @@ impl SyntaxNode { }) }) } + + pub fn first_child_by_kind(&self, matcher: &impl Fn(SyntaxKind) -> bool) -> Option { + self.green_ref().children().raw.enumerate().find_map(|(index, child)| { + if !matcher(child.as_ref().kind()) { + return None; + } + child.as_ref().into_node().map(|green| { + SyntaxNode::new_child( + green, + self.clone(), + index as u32, + self.offset() + child.rel_offset(), + ) + }) + }) + } + pub fn last_child(&self) -> Option { self.green_ref().children().raw.enumerate().rev().find_map(|(index, child)| { child.as_ref().into_node().map(|green| { @@ -665,6 +715,24 @@ impl SyntaxNode { SyntaxElement::new(child.as_ref(), self.clone(), 0, self.offset() + child.rel_offset()) }) } + + pub fn first_child_or_token_by_kind( + &self, + matcher: &impl Fn(SyntaxKind) -> bool, + ) -> Option { + self.green_ref().children().raw.enumerate().find_map(|(index, child)| { + if !matcher(child.as_ref().kind()) { + return None; + } + Some(SyntaxElement::new( + child.as_ref(), + self.clone(), + index as u32, + self.offset() + child.rel_offset(), + )) + }) + } + pub fn last_child_or_token(&self) -> Option { self.green_ref().children().raw.enumerate().next_back().map(|(index, child)| { SyntaxElement::new( @@ -679,6 +747,14 @@ impl SyntaxNode { pub fn next_sibling(&self) -> Option { self.data().next_sibling() } + + pub fn next_sibling_by_kind( + &self, + matcher: &impl Fn(SyntaxKind) -> bool, + ) -> Option { + self.data().next_sibling_by_kind(matcher) + } + pub fn prev_sibling(&self) -> Option { self.data().prev_sibling() } @@ -686,6 +762,14 @@ impl SyntaxNode { pub fn next_sibling_or_token(&self) -> Option { self.data().next_sibling_or_token() } + + pub fn next_sibling_or_token_by_kind( + &self, + matcher: &impl Fn(SyntaxKind) -> bool, + ) -> Option { + self.data().next_sibling_or_token_by_kind(matcher) + } + pub fn prev_sibling_or_token(&self) -> Option { self.data().prev_sibling_or_token() } @@ -910,6 +994,14 @@ impl SyntaxToken { pub fn next_sibling_or_token(&self) -> Option { self.data().next_sibling_or_token() } + + pub fn next_sibling_or_token_by_kind( + &self, + matcher: &impl Fn(SyntaxKind) -> bool, + ) -> Option { + self.data().next_sibling_or_token_by_kind(matcher) + } + pub fn prev_sibling_or_token(&self) -> Option { self.data().prev_sibling_or_token() } @@ -1028,6 +1120,17 @@ impl SyntaxElement { NodeOrToken::Token(it) => it.next_sibling_or_token(), } } + + pub fn next_sibling_or_token_by_kind( + &self, + matcher: &impl Fn(SyntaxKind) -> bool, + ) -> Option { + match self { + NodeOrToken::Node(it) => it.next_sibling_or_token_by_kind(matcher), + NodeOrToken::Token(it) => it.next_sibling_or_token_by_kind(matcher), + } + } + pub fn prev_sibling_or_token(&self) -> Option { match self { NodeOrToken::Node(it) => it.prev_sibling_or_token(), @@ -1133,18 +1236,41 @@ impl From for SyntaxElement { #[derive(Clone, Debug)] pub struct SyntaxNodeChildren { + parent: SyntaxNode, next: Option, + next_initialized: bool, } impl SyntaxNodeChildren { fn new(parent: SyntaxNode) -> SyntaxNodeChildren { - SyntaxNodeChildren { next: parent.first_child() } + SyntaxNodeChildren { parent, next: None, next_initialized: false } + } + + pub fn by_kind bool>(self, matcher: F) -> SyntaxNodeChildrenByKind { + if !self.next_initialized { + SyntaxNodeChildrenByKind { next: self.parent.first_child_by_kind(&matcher), matcher } + } else { + SyntaxNodeChildrenByKind { + next: self.next.and_then(|node| { + if matcher(node.kind()) { + Some(node) + } else { + node.next_sibling_by_kind(&matcher) + } + }), + matcher, + } + } } } impl Iterator for SyntaxNodeChildren { type Item = SyntaxNode; fn next(&mut self) -> Option { + if !self.next_initialized { + self.next = self.parent.first_child(); + self.next_initialized = true; + } self.next.take().map(|next| { self.next = next.next_sibling(); next @@ -1152,20 +1278,62 @@ impl Iterator for SyntaxNodeChildren { } } +#[derive(Clone, Debug)] +pub struct SyntaxNodeChildrenByKind bool> { + next: Option, + matcher: F, +} + +impl bool> Iterator for SyntaxNodeChildrenByKind { + type Item = SyntaxNode; + fn next(&mut self) -> Option { + self.next.take().map(|next| { + self.next = next.next_sibling_by_kind(&self.matcher); + next + }) + } +} + #[derive(Clone, Debug)] pub struct SyntaxElementChildren { + parent: SyntaxNode, next: Option, + next_initialized: bool, } impl SyntaxElementChildren { fn new(parent: SyntaxNode) -> SyntaxElementChildren { - SyntaxElementChildren { next: parent.first_child_or_token() } + SyntaxElementChildren { parent, next: None, next_initialized: false } + } + + pub fn by_kind bool>(self, matcher: F) -> SyntaxElementChildrenByKind { + if !self.next_initialized { + SyntaxElementChildrenByKind { + next: self.parent.first_child_or_token_by_kind(&matcher), + matcher, + } + } else { + SyntaxElementChildrenByKind { + next: self.next.and_then(|node| { + if matcher(node.kind()) { + Some(node) + } else { + node.next_sibling_or_token_by_kind(&matcher) + } + }), + matcher, + } + } } } impl Iterator for SyntaxElementChildren { type Item = SyntaxElement; fn next(&mut self) -> Option { + if !self.next_initialized { + self.next = self.parent.first_child_or_token(); + self.next_initialized = true; + } self.next.take().map(|next| { self.next = next.next_sibling_or_token(); next @@ -1173,6 +1341,22 @@ impl Iterator for SyntaxElementChildren { } } +#[derive(Clone, Debug)] +pub struct SyntaxElementChildrenByKind bool> { + next: Option, + matcher: F, +} + +impl bool> Iterator for SyntaxElementChildrenByKind { + type Item = SyntaxElement; + fn next(&mut self) -> Option { + self.next.take().map(|next| { + self.next = next.next_sibling_or_token_by_kind(&self.matcher); + next + }) + } +} + pub struct Preorder { start: SyntaxNode, next: Option>, diff --git a/src/lib.rs b/src/lib.rs index bab6cd9..c398811 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -29,7 +29,8 @@ pub use text_size::{TextLen, TextRange, TextSize}; pub use crate::{ api::{ - Language, SyntaxElement, SyntaxElementChildren, SyntaxNode, SyntaxNodeChildren, SyntaxToken, + Language, SyntaxElement, SyntaxElementChildren, SyntaxElementChildrenByKind, SyntaxNode, + SyntaxNodeChildren, SyntaxNodeChildrenByKind, SyntaxToken, }, green::{ Checkpoint, Children, GreenNode, GreenNodeBuilder, GreenNodeData, GreenToken,