use std::iter::FusedIterator;
use rowan::Language;
use rowan::api::PreorderWithTokens;
use crate::SyntaxElement;
use crate::SyntaxNode;
use crate::WorkflowDescriptionLanguage;
#[allow(missing_debug_implementations)]
pub struct DiveIterator<L, I>
where
L: Language,
I: Fn(&rowan::SyntaxNode<L>) -> bool,
{
it: PreorderWithTokens<L>,
ignore_predicate: I,
}
impl<L, I> DiveIterator<L, I>
where
L: Language,
I: Fn(&rowan::SyntaxNode<L>) -> bool,
{
pub fn new(root: rowan::SyntaxNode<L>, ignore_predicate: I) -> Self {
Self {
it: root.preorder_with_tokens(),
ignore_predicate,
}
}
}
impl<L, I> Iterator for DiveIterator<L, I>
where
L: Language,
I: Fn(&rowan::SyntaxNode<L>) -> bool,
{
type Item = rowan::SyntaxElement<L>;
fn next(&mut self) -> Option<Self::Item> {
while let Some(event) = self.it.next() {
let element = match event {
rowan::WalkEvent::Enter(element) => element,
rowan::WalkEvent::Leave(_) => continue,
};
if let rowan::SyntaxElement::Node(node) = &element
&& (self.ignore_predicate)(node)
{
self.it.skip_subtree();
continue;
}
return Some(element);
}
None
}
}
impl<L, I> FusedIterator for DiveIterator<L, I>
where
L: Language,
I: Fn(&rowan::SyntaxNode<L>) -> bool,
{
}
pub trait Divable<L>
where
L: Language,
{
fn dive<M>(&self, match_predicate: M) -> impl Iterator<Item = rowan::SyntaxElement<L>>
where
M: Fn(&rowan::SyntaxElement<L>) -> bool,
{
self.dive_with_ignore(match_predicate, |_| false)
}
fn dive_with_ignore<M, I>(
&self,
match_predicate: M,
ignore_predicate: I,
) -> impl Iterator<Item = rowan::SyntaxElement<L>>
where
M: Fn(&rowan::SyntaxElement<L>) -> bool,
I: Fn(&rowan::SyntaxNode<L>) -> bool;
}
impl<D, L> Divable<L> for &D
where
D: Divable<L>,
L: Language,
{
fn dive_with_ignore<M, I>(
&self,
match_predicate: M,
ignore_predicate: I,
) -> impl Iterator<Item = rowan::SyntaxElement<L>>
where
M: Fn(&rowan::SyntaxElement<L>) -> bool,
I: Fn(&rowan::SyntaxNode<L>) -> bool,
{
D::dive_with_ignore(self, match_predicate, ignore_predicate)
}
}
impl Divable<WorkflowDescriptionLanguage> for SyntaxNode {
fn dive_with_ignore<M, I>(
&self,
match_predicate: M,
ignore_predicate: I,
) -> impl Iterator<Item = SyntaxElement>
where
M: Fn(&SyntaxElement) -> bool,
I: Fn(&SyntaxNode) -> bool,
{
DiveIterator::new(
self.clone(),
ignore_predicate,
)
.filter(match_predicate)
}
}
#[cfg(test)]
mod tests {
use std::sync::OnceLock;
use rowan::GreenNode;
use crate::SyntaxKind;
use crate::SyntaxNode;
use crate::SyntaxTree;
use crate::dive::Divable;
fn get_syntax_node() -> SyntaxNode {
static GREEN_NODE: OnceLock<GreenNode> = OnceLock::new();
let green_node = GREEN_NODE
.get_or_init(|| {
let (tree, diagnostics) = SyntaxTree::parse(
r#"version 1.2
task hello {
String a_private_declaration = false
}
workflow world {
String another_private_declaration = true
}"#,
None,
);
assert!(diagnostics.is_empty());
tree.green().into()
})
.clone();
SyntaxNode::new_root(green_node)
}
#[test]
fn it_dives_correctly() {
let tree = get_syntax_node();
let mut idents = tree.dive(|element| element.kind() == SyntaxKind::Ident);
assert_eq!(idents.next().unwrap().as_token().unwrap().text(), "hello");
assert_eq!(
idents.next().unwrap().as_token().unwrap().text(),
"a_private_declaration"
);
assert_eq!(idents.next().unwrap().as_token().unwrap().text(), "world");
assert_eq!(
idents.next().unwrap().as_token().unwrap().text(),
"another_private_declaration"
);
assert!(idents.next().is_none());
}
#[test]
fn it_dives_with_ignores_correctly() {
let tree = get_syntax_node();
let mut ignored_idents = tree.dive_with_ignore(
|element| element.kind() == SyntaxKind::Ident,
|node| node.kind() == SyntaxKind::WorkflowDefinitionNode,
);
assert_eq!(
ignored_idents.next().unwrap().as_token().unwrap().text(),
"hello"
);
assert_eq!(
ignored_idents.next().unwrap().as_token().unwrap().text(),
"a_private_declaration"
);
assert!(ignored_idents.next().is_none());
}
}