orgize/src/export/traverse.rs
2023-11-15 14:11:18 +08:00

248 lines
12 KiB
Rust

use crate::ast::*;
use crate::syntax::{SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken};
use rowan::{ast::AstNode, WalkEvent};
use SyntaxKind::*;
#[derive(Default, Debug, PartialEq, Eq, Clone, Copy)]
enum TraversalControl {
Up,
Stop,
Skip,
#[default]
Continue,
}
macro_rules! take_control {
($ctrl:expr) => {
match $ctrl.control {
TraversalControl::Stop => {
$ctrl.control = TraversalControl::Stop;
return;
}
TraversalControl::Up => {
$ctrl.control = TraversalControl::Skip;
return;
}
TraversalControl::Skip => {
$ctrl.control = TraversalControl::Continue;
return;
}
TraversalControl::Continue => {}
}
};
}
#[derive(Default)]
pub struct TraversalContext {
control: TraversalControl,
}
impl TraversalContext {
/// Stops traversal completely
pub fn stop(&mut self) {
self.control = TraversalControl::Stop;
}
/// Skips traversal of the current node's siblings
pub fn up(&mut self) {
self.control = TraversalControl::Up;
}
/// Skips traversal of the current node's descendants
pub fn skip(&mut self) {
self.control = TraversalControl::Skip;
}
/// Continues traversal
pub fn r#continue(&mut self) {
self.control = TraversalControl::Continue;
}
}
/// Enumerates org syntax tree
///
/// Traverser enumerates org syntax tree and calls handle method on each
/// enumerated node and token.
///
/// Each handle method can returns a `TraversalControl` to control the traversal.
pub trait Traverser {
/// Called when visiting any node
fn node(&mut self, node: SyntaxNode, ctx: &mut TraversalContext) {
macro_rules! traverse_children {
($node:expr) => {{
for child in $node.children_with_tokens() {
match child {
SyntaxElement::Node(node) => self.node(node, ctx),
SyntaxElement::Token(token) => self.token(token, ctx),
};
take_control!(ctx);
}
}};
}
macro_rules! traverse {
($node:ident, $method:ident) => {{
debug_assert!($node::can_cast(node.kind()));
let node = $node { syntax: node };
self.$method(WalkEvent::Enter(&node), ctx);
take_control!(ctx);
traverse_children!(&node.syntax);
self.$method(WalkEvent::Leave(&node), ctx);
take_control!(ctx);
}};
}
match node.kind() {
DOCUMENT => traverse!(Document, document),
HEADLINE => traverse!(Headline, headline),
SECTION => traverse!(Section, section),
PARAGRAPH => traverse!(Paragraph, paragraph),
BOLD => traverse!(Bold, bold),
ITALIC => traverse!(Italic, italic),
STRIKE => traverse!(Strike, strike),
UNDERLINE => traverse!(Underline, underline),
LIST => traverse!(List, list),
LIST_ITEM => traverse!(ListItem, list_item),
LIST_ITEM_CONTENT => traverse!(ListItemContent, list_item_content),
LIST_ITEM_TAG => traverse!(ListItemTag, list_item_tag),
CODE => traverse!(Code, code),
INLINE_CALL => traverse!(InlineCall, inline_call),
INLINE_SRC => traverse!(InlineSrc, inline_src),
RULE => traverse!(Rule, rule),
VERBATIM => traverse!(Verbatim, verbatim),
SPECIAL_BLOCK => traverse!(SpecialBlock, special_block),
QUOTE_BLOCK => traverse!(QuoteBlock, quote_block),
CENTER_BLOCK => traverse!(CenterBlock, center_block),
VERSE_BLOCK => traverse!(VerseBlock, verse_block),
COMMENT_BLOCK => traverse!(CommentBlock, comment_block),
EXAMPLE_BLOCK => traverse!(ExampleBlock, example_block),
EXPORT_BLOCK => traverse!(ExportBlock, export_block),
SOURCE_BLOCK => traverse!(SourceBlock, source_block),
BABEL_CALL => traverse!(BabelCall, babel_call),
CLOCK => traverse!(Clock, clock),
COOKIE => traverse!(Cookie, cookie),
RADIO_TARGET => traverse!(RadioTarget, radio_target),
DRAWER => traverse!(Drawer, drawer),
DYN_BLOCK => traverse!(DynBlock, dyn_block),
FN_DEF => traverse!(FnDef, fn_def),
FN_REF => traverse!(FnRef, fn_ref),
MACROS => traverse!(Macros, macros),
SNIPPET => traverse!(Snippet, snippet),
TIMESTAMP_ACTIVE | TIMESTAMP_INACTIVE | TIMESTAMP_DIARY => {
traverse!(Timestamp, timestamp)
}
TARGET => traverse!(Target, target),
COMMENT => traverse!(Comment, comment),
FIXED_WIDTH => traverse!(FixedWidth, fixed_width),
HEADLINE_TITLE => traverse!(HeadlineTitle, headline_title),
ORG_TABLE => traverse!(OrgTable, org_table),
ORG_TABLE_RULE_ROW | ORG_TABLE_STANDARD_ROW => traverse!(OrgTableRow, org_table_row),
ORG_TABLE_CELL => traverse!(OrgTableCell, org_table_cell),
LINK => traverse!(Link, link),
BLOCK_CONTENT => traverse_children!(node),
_ => {}
}
}
/// Called when visiting any token
fn token(&mut self, token: SyntaxToken, ctx: &mut TraversalContext) {
if token.kind() == TEXT {
self.text(token, ctx);
}
take_control!(ctx);
}
/// Called when visiting `Text` token
fn text(&mut self, _token: SyntaxToken, _ctx: &mut TraversalContext);
/// Called when entering or leaving `Document` node
fn document(&mut self, _event: WalkEvent<&Document>, _ctx: &mut TraversalContext);
/// Called when entering or leaving `Headline` node
fn headline(&mut self, _event: WalkEvent<&Headline>, _ctx: &mut TraversalContext);
/// Called when entering or leaving `Paragraph` node
fn paragraph(&mut self, _event: WalkEvent<&Paragraph>, _ctx: &mut TraversalContext);
/// Called when entering or leaving `Section` node
fn section(&mut self, _event: WalkEvent<&Section>, _ctx: &mut TraversalContext);
/// Called when entering or leaving `Rule` node
fn rule(&mut self, _event: WalkEvent<&Rule>, _ctx: &mut TraversalContext);
/// Called when entering or leaving `Comment` node
fn comment(&mut self, _event: WalkEvent<&Comment>, _ctx: &mut TraversalContext);
/// Called when entering or leaving `InlineSrc` node
fn inline_src(&mut self, _event: WalkEvent<&InlineSrc>, _ctx: &mut TraversalContext);
/// Called when entering or leaving `InlineCall` node
fn inline_call(&mut self, _event: WalkEvent<&InlineCall>, _ctx: &mut TraversalContext);
/// Called when entering or leaving `Code` node
fn code(&mut self, _event: WalkEvent<&Code>, _ctx: &mut TraversalContext);
/// Called when entering or leaving `Bold` node
fn bold(&mut self, _event: WalkEvent<&Bold>, _ctx: &mut TraversalContext);
/// Called when entering or leaving `Verbatim` node
fn verbatim(&mut self, _event: WalkEvent<&Verbatim>, _ctx: &mut TraversalContext);
/// Called when entering or leaving `Italic` node
fn italic(&mut self, _event: WalkEvent<&Italic>, _ctx: &mut TraversalContext);
/// Called when entering or leaving `Strike` node
fn strike(&mut self, _event: WalkEvent<&Strike>, _ctx: &mut TraversalContext);
/// Called when entering or leaving `Underline` node
fn underline(&mut self, _event: WalkEvent<&Underline>, _ctx: &mut TraversalContext);
/// Called when entering or leaving `List` node
fn list(&mut self, _event: WalkEvent<&List>, _ctx: &mut TraversalContext);
/// Called when entering or leaving `ListItem` node
fn list_item(&mut self, _event: WalkEvent<&ListItem>, _ctx: &mut TraversalContext);
/// Called when entering or leaving `ListItemTag` node
fn list_item_tag(&mut self, _event: WalkEvent<&ListItemTag>, _ctx: &mut TraversalContext);
/// Called when entering or leaving `ListItemContent` node
fn list_item_content(
&mut self,
_event: WalkEvent<&ListItemContent>,
_ctx: &mut TraversalContext,
);
/// Called when entering or leaving `SpecialBlock` node
fn special_block(&mut self, _event: WalkEvent<&SpecialBlock>, _ctx: &mut TraversalContext);
/// Called when entering or leaving `QuoteBlock` node
fn quote_block(&mut self, _event: WalkEvent<&QuoteBlock>, _ctx: &mut TraversalContext);
/// Called when entering or leaving `CenterBlock` node
fn center_block(&mut self, _event: WalkEvent<&CenterBlock>, _ctx: &mut TraversalContext);
/// Called when entering or leaving `VerseBlock` node
fn verse_block(&mut self, _event: WalkEvent<&VerseBlock>, _ctx: &mut TraversalContext);
/// Called when entering or leaving `CommentBlock` node
fn comment_block(&mut self, _event: WalkEvent<&CommentBlock>, _ctx: &mut TraversalContext);
/// Called when entering or leaving `ExampleBlock` node
fn example_block(&mut self, _event: WalkEvent<&ExampleBlock>, _ctx: &mut TraversalContext);
/// Called when entering or leaving `ExportBlock` node
fn export_block(&mut self, _event: WalkEvent<&ExportBlock>, _ctx: &mut TraversalContext);
/// Called when entering or leaving `SourceBlock` node
fn source_block(&mut self, _event: WalkEvent<&SourceBlock>, _ctx: &mut TraversalContext);
/// Called when entering or leaving `BabelCall` node
fn babel_call(&mut self, _event: WalkEvent<&BabelCall>, _ctx: &mut TraversalContext);
/// Called when entering or leaving `Clock` node
fn clock(&mut self, _event: WalkEvent<&Clock>, _ctx: &mut TraversalContext);
/// Called when entering or leaving `Cookie` node
fn cookie(&mut self, _event: WalkEvent<&Cookie>, _ctx: &mut TraversalContext);
/// Called when entering or leaving `RadioTarget` node
fn radio_target(&mut self, _event: WalkEvent<&RadioTarget>, _ctx: &mut TraversalContext);
/// Called when entering or leaving `Drawer` node
fn drawer(&mut self, _event: WalkEvent<&Drawer>, _ctx: &mut TraversalContext);
/// Called when entering or leaving `DynBlock` node
fn dyn_block(&mut self, _event: WalkEvent<&DynBlock>, _ctx: &mut TraversalContext);
/// Called when entering or leaving `FnDef` node
fn fn_def(&mut self, _event: WalkEvent<&FnDef>, _ctx: &mut TraversalContext);
/// Called when entering or leaving `FnRef` node
fn fn_ref(&mut self, _event: WalkEvent<&FnRef>, _ctx: &mut TraversalContext);
/// Called when entering or leaving `Macros` node
fn macros(&mut self, _event: WalkEvent<&Macros>, _ctx: &mut TraversalContext);
/// Called when entering or leaving `Snippet` node
fn snippet(&mut self, _event: WalkEvent<&Snippet>, _ctx: &mut TraversalContext);
/// Called when entering or leaving `Timestamp` node
fn timestamp(&mut self, _event: WalkEvent<&Timestamp>, _ctx: &mut TraversalContext);
/// Called when entering or leaving `Target` node
fn target(&mut self, _event: WalkEvent<&Target>, _ctx: &mut TraversalContext);
/// Called when entering or leaving `FixedWidth` node
fn fixed_width(&mut self, _event: WalkEvent<&FixedWidth>, _ctx: &mut TraversalContext);
/// Called when entering or leaving `HeadlineTitle` node
fn headline_title(&mut self, _event: WalkEvent<&HeadlineTitle>, _ctx: &mut TraversalContext);
/// Called when entering or leaving `OrgTable` node
fn org_table(&mut self, _event: WalkEvent<&OrgTable>, _ctx: &mut TraversalContext);
/// Called when entering or leaving `OrgTableRow` node
fn org_table_row(&mut self, _event: WalkEvent<&OrgTableRow>, _ctx: &mut TraversalContext);
/// Called when entering or leaving `OrgTableCell` node
fn org_table_cell(&mut self, _event: WalkEvent<&OrgTableCell>, _ctx: &mut TraversalContext);
/// Called when entering or leaving `Link` node
fn link(&mut self, _event: WalkEvent<&Link>, _ctx: &mut TraversalContext);
}