use std::ops::ControlFlow;
use super::red::{SyntaxElement, SyntaxNode, SyntaxToken};
#[derive(Clone, Debug)]
pub struct WalkOptions {
pub visit_tokens: bool,
pub visit_trivia: bool,
}
impl Default for WalkOptions {
fn default() -> Self {
Self {
visit_tokens: true,
visit_trivia: false,
}
}
}
impl WalkOptions {
#[must_use]
pub const fn nodes_only() -> Self {
Self {
visit_tokens: false,
visit_trivia: false,
}
}
#[must_use]
pub fn semantic_only() -> Self {
Self::default()
}
#[must_use]
pub const fn full() -> Self {
Self {
visit_tokens: true,
visit_trivia: true,
}
}
}
pub type WalkResult = ControlFlow<(), ()>;
pub trait Visitor {
fn enter_node(&mut self, _node: &SyntaxNode) -> WalkResult {
WalkResult::Continue(())
}
fn leave_node(&mut self, _node: &SyntaxNode) -> WalkResult {
WalkResult::Continue(())
}
fn visit_token(&mut self, _token: &SyntaxToken) -> WalkResult {
WalkResult::Continue(())
}
}
fn walk_node(node: &SyntaxNode, visitor: &mut impl Visitor, options: &WalkOptions) -> WalkResult {
if visitor.enter_node(node) == WalkResult::Break(()) {
return WalkResult::Break(());
}
if options.visit_tokens {
for elem in node.children() {
match elem {
SyntaxElement::Node(child) => {
if walk_node(&child, visitor, options) == WalkResult::Break(()) {
let _ = visitor.leave_node(node);
return WalkResult::Break(());
}
}
SyntaxElement::Token(token) => {
if token.is_trivia() && !options.visit_trivia {
continue;
}
if visitor.visit_token(&token) == WalkResult::Break(()) {
let _ = visitor.leave_node(node);
return WalkResult::Break(());
}
}
}
}
} else {
for child in node.child_nodes() {
if walk_node(&child, visitor, options) == WalkResult::Break(()) {
let _ = visitor.leave_node(node);
return WalkResult::Break(());
}
}
}
if visitor.leave_node(node) == WalkResult::Break(()) {
return WalkResult::Break(());
}
WalkResult::Continue(())
}
impl SyntaxNode {
pub fn walk(&self, visitor: &mut impl Visitor, options: &WalkOptions) -> WalkResult {
walk_node(self, visitor, options)
}
}
#[inline]
pub fn walk(root: &SyntaxNode, visitor: &mut impl Visitor, options: &WalkOptions) -> WalkResult {
root.walk(visitor, options)
}