feat: simpify Traverser trait
This commit is contained in:
parent
fd098f0cf1
commit
19c62979f0
7 changed files with 439 additions and 885 deletions
70
src/export/event.rs
Normal file
70
src/export/event.rs
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
use crate::{ast::*, SyntaxToken};
|
||||
|
||||
#[non_exhaustive]
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub enum Container {
|
||||
Document(Document),
|
||||
Section(Section),
|
||||
Paragraph(Paragraph),
|
||||
Headline(Headline),
|
||||
|
||||
OrgTable(OrgTable),
|
||||
OrgTableRow(OrgTableRow),
|
||||
OrgTableCell(OrgTableCell),
|
||||
TableEl(TableEl),
|
||||
|
||||
List(List),
|
||||
ListItem(ListItem),
|
||||
Drawer(Drawer),
|
||||
DynBlock(DynBlock),
|
||||
|
||||
FnDef(FnDef),
|
||||
Comment(Comment),
|
||||
FixedWidth(FixedWidth),
|
||||
SpecialBlock(SpecialBlock),
|
||||
QuoteBlock(QuoteBlock),
|
||||
CenterBlock(CenterBlock),
|
||||
VerseBlock(VerseBlock),
|
||||
CommentBlock(CommentBlock),
|
||||
ExampleBlock(ExampleBlock),
|
||||
ExportBlock(ExportBlock),
|
||||
SourceBlock(SourceBlock),
|
||||
|
||||
Link(Link),
|
||||
RadioTarget(RadioTarget),
|
||||
FnRef(FnRef),
|
||||
Target(Target),
|
||||
Bold(Bold),
|
||||
Strike(Strike),
|
||||
Italic(Italic),
|
||||
Underline(Underline),
|
||||
Verbatim(Verbatim),
|
||||
Code(Code),
|
||||
Superscript(Superscript),
|
||||
Subscript(Subscript),
|
||||
BabelCall(BabelCall),
|
||||
PropertyDrawer(PropertyDrawer),
|
||||
AffiliatedKeyword(AffiliatedKeyword),
|
||||
Keyword(Keyword),
|
||||
}
|
||||
|
||||
#[non_exhaustive]
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub enum Event {
|
||||
Enter(Container),
|
||||
Leave(Container),
|
||||
|
||||
Text(SyntaxToken),
|
||||
Macros(Macros),
|
||||
Cookie(Cookie),
|
||||
InlineCall(InlineCall),
|
||||
InlineSrc(InlineSrc),
|
||||
Clock(Clock),
|
||||
LineBreak(LineBreak),
|
||||
Snippet(Snippet),
|
||||
Rule(Rule),
|
||||
Timestamp(Timestamp),
|
||||
LatexFragment(LatexFragment),
|
||||
LatexEnvironment(LatexEnvironment),
|
||||
Entity(Entity),
|
||||
}
|
||||
|
|
@ -1,224 +0,0 @@
|
|||
/// Forward traverser method implement to other
|
||||
///
|
||||
/// Used to "extend" some builtin traverser like `HtmlExport`.
|
||||
///
|
||||
/// ```rust
|
||||
/// use orgize::{
|
||||
/// ast::Headline,
|
||||
/// export::{HtmlExport, TraversalContext, Traverser},
|
||||
/// forward_handler,
|
||||
/// rowan::{ast::AstNode, WalkEvent},
|
||||
/// Org,
|
||||
/// };
|
||||
/// use slugify::slugify;
|
||||
/// use std::cmp::min;
|
||||
///
|
||||
/// #[derive(Default)]
|
||||
/// struct SlugifyTitleHandler(pub HtmlExport);
|
||||
///
|
||||
/// // AsMut trait is required
|
||||
/// impl AsMut<HtmlExport> for SlugifyTitleHandler {
|
||||
/// fn as_mut(&mut self) -> &mut HtmlExport {
|
||||
/// &mut self.0
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// impl Traverser for SlugifyTitleHandler {
|
||||
/// fn headline(&mut self, event: WalkEvent<&Headline>, ctx: &mut TraversalContext) {
|
||||
/// if let WalkEvent::Enter(headline) = event {
|
||||
/// let level = min(headline.level(), 6);
|
||||
/// let title = headline.title().map(|e| e.to_string()).collect::<String>();
|
||||
/// self.0.push_str(format!(
|
||||
/// "<h{level}><a id=\"{0}\" href=\"#{0}\">",
|
||||
/// slugify!(&title)
|
||||
/// ));
|
||||
/// for elem in headline.title() {
|
||||
/// self.element(elem, ctx);
|
||||
/// }
|
||||
/// self.0.push_str(format!("</a></h{level}>"));
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// forward_handler! {
|
||||
/// HtmlExport,
|
||||
/// link text document paragraph section rule comment
|
||||
/// inline_src inline_call code bold verbatim italic strike underline list list_item
|
||||
/// special_block quote_block center_block verse_block comment_block example_block export_block
|
||||
/// source_block babel_call clock cookie radio_target drawer dyn_block fn_def fn_ref macros
|
||||
/// snippet timestamp target fixed_width org_table org_table_row org_table_cell latex_fragment
|
||||
/// latex_environment entity line_break superscript subscript keyword property_drawer
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// let mut handler = SlugifyTitleHandler::default();
|
||||
/// Org::parse("* hello world!").traverse(&mut handler);
|
||||
/// assert_eq!(handler.0.finish(), r##"<main><h1><a id="hello-world" href="#hello-world">hello world!</a></h1></main>"##);
|
||||
/// ```
|
||||
#[macro_export(local_inner_macros)]
|
||||
macro_rules! forward_handler {
|
||||
($handler:ty, $($func:ident)*) => {
|
||||
$(
|
||||
forward_handler!(@method $handler, $func);
|
||||
)*
|
||||
};
|
||||
|
||||
(@method $handler:ty, text) => {
|
||||
forward_handler!(@method $handler, text, $crate::SyntaxToken);
|
||||
};
|
||||
(@method $handler:ty, document) => {
|
||||
forward_handler!(@method $handler, document, WalkEvent<&$crate::ast::Document>);
|
||||
};
|
||||
(@method $handler:ty, headline) => {
|
||||
forward_handler!(@method $handler, headline, WalkEvent<&$crate::ast::Headline>);
|
||||
};
|
||||
(@method $handler:ty, paragraph) => {
|
||||
forward_handler!(@method $handler, paragraph, WalkEvent<&$crate::ast::Paragraph>);
|
||||
};
|
||||
(@method $handler:ty, section) => {
|
||||
forward_handler!(@method $handler, section, WalkEvent<&$crate::ast::Section>);
|
||||
};
|
||||
(@method $handler:ty, rule) => {
|
||||
forward_handler!(@method $handler, rule, WalkEvent<&$crate::ast::Rule>);
|
||||
};
|
||||
(@method $handler:ty, comment) => {
|
||||
forward_handler!(@method $handler, comment, WalkEvent<&$crate::ast::Comment>);
|
||||
};
|
||||
(@method $handler:ty, inline_src) => {
|
||||
forward_handler!(@method $handler, inline_src, WalkEvent<&$crate::ast::InlineSrc>);
|
||||
};
|
||||
(@method $handler:ty, inline_call) => {
|
||||
forward_handler!(@method $handler, inline_call, WalkEvent<&$crate::ast::InlineCall>);
|
||||
};
|
||||
(@method $handler:ty, code) => {
|
||||
forward_handler!(@method $handler, code, WalkEvent<&$crate::ast::Code>);
|
||||
};
|
||||
(@method $handler:ty, bold) => {
|
||||
forward_handler!(@method $handler, bold, WalkEvent<&$crate::ast::Bold>);
|
||||
};
|
||||
(@method $handler:ty, verbatim) => {
|
||||
forward_handler!(@method $handler, verbatim, WalkEvent<&$crate::ast::Verbatim>);
|
||||
};
|
||||
(@method $handler:ty, italic) => {
|
||||
forward_handler!(@method $handler, italic, WalkEvent<&$crate::ast::Italic>);
|
||||
};
|
||||
(@method $handler:ty, strike) => {
|
||||
forward_handler!(@method $handler, strike, WalkEvent<&$crate::ast::Strike>);
|
||||
};
|
||||
(@method $handler:ty, underline) => {
|
||||
forward_handler!(@method $handler, underline, WalkEvent<&$crate::ast::Underline>);
|
||||
};
|
||||
(@method $handler:ty, list) => {
|
||||
forward_handler!(@method $handler, list, WalkEvent<&$crate::ast::List>);
|
||||
};
|
||||
(@method $handler:ty, list_item) => {
|
||||
forward_handler!(@method $handler, list_item, WalkEvent<&$crate::ast::ListItem>);
|
||||
};
|
||||
(@method $handler:ty, special_block) => {
|
||||
forward_handler!(@method $handler, special_block, WalkEvent<&$crate::ast::SpecialBlock>);
|
||||
};
|
||||
(@method $handler:ty, quote_block) => {
|
||||
forward_handler!(@method $handler, quote_block, WalkEvent<&$crate::ast::QuoteBlock>);
|
||||
};
|
||||
(@method $handler:ty, center_block) => {
|
||||
forward_handler!(@method $handler, center_block, WalkEvent<&$crate::ast::CenterBlock>);
|
||||
};
|
||||
(@method $handler:ty, verse_block) => {
|
||||
forward_handler!(@method $handler, verse_block, WalkEvent<&$crate::ast::VerseBlock>);
|
||||
};
|
||||
(@method $handler:ty, comment_block) => {
|
||||
forward_handler!(@method $handler, comment_block, WalkEvent<&$crate::ast::CommentBlock>);
|
||||
};
|
||||
(@method $handler:ty, example_block) => {
|
||||
forward_handler!(@method $handler, example_block, WalkEvent<&$crate::ast::ExampleBlock>);
|
||||
};
|
||||
(@method $handler:ty, export_block) => {
|
||||
forward_handler!(@method $handler, export_block, WalkEvent<&$crate::ast::ExportBlock>);
|
||||
};
|
||||
(@method $handler:ty, source_block) => {
|
||||
forward_handler!(@method $handler, source_block, WalkEvent<&$crate::ast::SourceBlock>);
|
||||
};
|
||||
(@method $handler:ty, babel_call) => {
|
||||
forward_handler!(@method $handler, babel_call, WalkEvent<&$crate::ast::BabelCall>);
|
||||
};
|
||||
(@method $handler:ty, clock) => {
|
||||
forward_handler!(@method $handler, clock, WalkEvent<&$crate::ast::Clock>);
|
||||
};
|
||||
(@method $handler:ty, cookie) => {
|
||||
forward_handler!(@method $handler, cookie, WalkEvent<&$crate::ast::Cookie>);
|
||||
};
|
||||
(@method $handler:ty, radio_target) => {
|
||||
forward_handler!(@method $handler, radio_target, WalkEvent<&$crate::ast::RadioTarget>);
|
||||
};
|
||||
(@method $handler:ty, drawer) => {
|
||||
forward_handler!(@method $handler, drawer, WalkEvent<&$crate::ast::Drawer>);
|
||||
};
|
||||
(@method $handler:ty, dyn_block) => {
|
||||
forward_handler!(@method $handler, dyn_block, WalkEvent<&$crate::ast::DynBlock>);
|
||||
};
|
||||
(@method $handler:ty, fn_def) => {
|
||||
forward_handler!(@method $handler, fn_def, WalkEvent<&$crate::ast::FnDef>);
|
||||
};
|
||||
(@method $handler:ty, fn_ref) => {
|
||||
forward_handler!(@method $handler, fn_ref, WalkEvent<&$crate::ast::FnRef>);
|
||||
};
|
||||
(@method $handler:ty, macros) => {
|
||||
forward_handler!(@method $handler, macros, WalkEvent<&$crate::ast::Macros>);
|
||||
};
|
||||
(@method $handler:ty, snippet) => {
|
||||
forward_handler!(@method $handler, snippet, WalkEvent<&$crate::ast::Snippet>);
|
||||
};
|
||||
(@method $handler:ty, timestamp) => {
|
||||
forward_handler!(@method $handler, timestamp, WalkEvent<&$crate::ast::Timestamp>);
|
||||
};
|
||||
(@method $handler:ty, target) => {
|
||||
forward_handler!(@method $handler, target, WalkEvent<&$crate::ast::Target>);
|
||||
};
|
||||
(@method $handler:ty, fixed_width) => {
|
||||
forward_handler!(@method $handler, fixed_width, WalkEvent<&$crate::ast::FixedWidth>);
|
||||
};
|
||||
(@method $handler:ty, org_table) => {
|
||||
forward_handler!(@method $handler, org_table, WalkEvent<&$crate::ast::OrgTable>);
|
||||
};
|
||||
(@method $handler:ty, org_table_row) => {
|
||||
forward_handler!(@method $handler, org_table_row, WalkEvent<&$crate::ast::OrgTableRow>);
|
||||
};
|
||||
(@method $handler:ty, org_table_cell) => {
|
||||
forward_handler!(@method $handler, org_table_cell, WalkEvent<&$crate::ast::OrgTableCell>);
|
||||
};
|
||||
(@method $handler:ty, link) => {
|
||||
forward_handler!(@method $handler, link, WalkEvent<&$crate::ast::Link>);
|
||||
};
|
||||
(@method $handler:ty, latex_fragment) => {
|
||||
forward_handler!(@method $handler, latex_fragment, WalkEvent<&$crate::ast::LatexFragment>);
|
||||
};
|
||||
(@method $handler:ty, latex_environment) => {
|
||||
forward_handler!(@method $handler, latex_environment, WalkEvent<&$crate::ast::LatexEnvironment>);
|
||||
};
|
||||
(@method $handler:ty, entity) => {
|
||||
forward_handler!(@method $handler, entity, WalkEvent<&$crate::ast::Entity>);
|
||||
};
|
||||
(@method $handler:ty, line_break) => {
|
||||
forward_handler!(@method $handler, line_break, WalkEvent<&$crate::ast::LineBreak>);
|
||||
};
|
||||
(@method $handler:ty, superscript) => {
|
||||
forward_handler!(@method $handler, superscript, WalkEvent<&$crate::ast::Superscript>);
|
||||
};
|
||||
(@method $handler:ty, subscript) => {
|
||||
forward_handler!(@method $handler, subscript, WalkEvent<&$crate::ast::Subscript>);
|
||||
};
|
||||
(@method $handler:ty, keyword) => {
|
||||
forward_handler!(@method $handler, keyword, WalkEvent<&$crate::ast::Keyword>);
|
||||
};
|
||||
(@method $handler:ty, property_drawer) => {
|
||||
forward_handler!(@method $handler, property_drawer, WalkEvent<&$crate::ast::PropertyDrawer>);
|
||||
};
|
||||
(@method $handler:ty, $x:ident) => {
|
||||
std::compile_error!(std::concat!(std::stringify!($x), " is not a method"));
|
||||
};
|
||||
|
||||
(@method $handler:ty, $name:ident, $type:ty) => {
|
||||
fn $name(&mut self, item: $type, ctx: &mut $crate::export::TraversalContext) {
|
||||
<Self as AsMut<$handler>>::as_mut(self).$name(item, ctx)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
@ -1,12 +1,11 @@
|
|||
use rowan::{NodeOrToken, WalkEvent};
|
||||
use rowan::NodeOrToken;
|
||||
use std::cmp::min;
|
||||
use std::fmt;
|
||||
use std::fmt::Write as _;
|
||||
|
||||
use super::event::{Container, Event};
|
||||
use super::TraversalContext;
|
||||
use super::Traverser;
|
||||
use crate::ast::*;
|
||||
use crate::syntax::SyntaxToken;
|
||||
use crate::SyntaxKind;
|
||||
|
||||
/// A wrapper for escaping sensitive characters in html.
|
||||
|
|
@ -77,23 +76,70 @@ impl HtmlExport {
|
|||
}
|
||||
|
||||
impl Traverser for HtmlExport {
|
||||
#[tracing::instrument(skip(self, _ctx))]
|
||||
fn text(&mut self, token: SyntaxToken, _ctx: &mut TraversalContext) {
|
||||
self.output += &HtmlEscape(token.text()).to_string();
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self, _ctx))]
|
||||
fn document(&mut self, event: WalkEvent<&Document>, _ctx: &mut TraversalContext) {
|
||||
self.output += match event {
|
||||
WalkEvent::Enter(_) => "<main>",
|
||||
WalkEvent::Leave(_) => "</main>",
|
||||
};
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self, _ctx))]
|
||||
fn list(&mut self, event: WalkEvent<&List>, _ctx: &mut TraversalContext) {
|
||||
fn event(&mut self, event: Event, ctx: &mut TraversalContext) {
|
||||
match event {
|
||||
WalkEvent::Enter(list) => {
|
||||
Event::Enter(Container::Document(_)) => self.output += "<main>",
|
||||
Event::Leave(Container::Document(_)) => self.output += "</main>",
|
||||
|
||||
Event::Enter(Container::Headline(headline)) => {
|
||||
let level = min(headline.level(), 6);
|
||||
let _ = write!(&mut self.output, "<h{level}>");
|
||||
for elem in headline.title() {
|
||||
self.element(elem, ctx);
|
||||
}
|
||||
let _ = write!(&mut self.output, "</h{level}>");
|
||||
}
|
||||
Event::Leave(Container::Headline(_)) => {}
|
||||
|
||||
Event::Enter(Container::Paragraph(_)) => self.output += "<p>",
|
||||
Event::Leave(Container::Paragraph(_)) => self.output += "</p>",
|
||||
|
||||
Event::Enter(Container::Section(_)) => self.output += "<section>",
|
||||
Event::Leave(Container::Section(_)) => self.output += "</section>",
|
||||
|
||||
Event::Enter(Container::Italic(_)) => self.output += "<i>",
|
||||
Event::Leave(Container::Italic(_)) => self.output += "</i>",
|
||||
|
||||
Event::Enter(Container::Bold(_)) => self.output += "<b>",
|
||||
Event::Leave(Container::Bold(_)) => self.output += "</b>",
|
||||
|
||||
Event::Enter(Container::Strike(_)) => self.output += "<s>",
|
||||
Event::Leave(Container::Strike(_)) => self.output += "</s>",
|
||||
|
||||
Event::Enter(Container::Underline(_)) => self.output += "<u>",
|
||||
Event::Leave(Container::Underline(_)) => self.output += "</u>",
|
||||
|
||||
Event::Enter(Container::Verbatim(_)) => self.output += "<code>",
|
||||
Event::Leave(Container::Verbatim(_)) => self.output += "</code>",
|
||||
|
||||
Event::Enter(Container::Code(_)) => self.output += "<code>",
|
||||
Event::Leave(Container::Code(_)) => self.output += "</code>",
|
||||
|
||||
Event::Enter(Container::QuoteBlock(_)) => self.output += "<blockquote>",
|
||||
Event::Leave(Container::QuoteBlock(_)) => self.output += "</blockquote>",
|
||||
|
||||
Event::Enter(Container::VerseBlock(_)) => self.output += "<p class=\"verse\">",
|
||||
Event::Leave(Container::VerseBlock(_)) => self.output += "</p>",
|
||||
|
||||
Event::Enter(Container::ExampleBlock(_)) => self.output += "<pre class=\"example\">",
|
||||
Event::Leave(Container::ExampleBlock(_)) => self.output += "</pre>",
|
||||
|
||||
Event::Enter(Container::CenterBlock(_)) => self.output += "<div class=\"center\">",
|
||||
Event::Leave(Container::CenterBlock(_)) => self.output += "</div>",
|
||||
|
||||
Event::Enter(Container::CommentBlock(_)) => self.output += "<!--",
|
||||
Event::Leave(Container::CommentBlock(_)) => self.output += "-->",
|
||||
|
||||
Event::Enter(Container::Comment(_)) => self.output += "<!--",
|
||||
Event::Leave(Container::Comment(_)) => self.output += "-->",
|
||||
|
||||
Event::Enter(Container::Subscript(_)) => self.output += "<sub>",
|
||||
Event::Leave(Container::Subscript(_)) => self.output += "</sub>",
|
||||
|
||||
Event::Enter(Container::Superscript(_)) => self.output += "<sup>",
|
||||
Event::Leave(Container::Superscript(_)) => self.output += "</sup>",
|
||||
|
||||
Event::Enter(Container::List(list)) => {
|
||||
self.output += if list.is_ordered() {
|
||||
self.in_descriptive_list.push(false);
|
||||
"<ol>"
|
||||
|
|
@ -105,7 +151,7 @@ impl Traverser for HtmlExport {
|
|||
"<ul>"
|
||||
};
|
||||
}
|
||||
WalkEvent::Leave(list) => {
|
||||
Event::Leave(Container::List(list)) => {
|
||||
self.output += if list.is_ordered() {
|
||||
"</ol>"
|
||||
} else if let Some(true) = self.in_descriptive_list.last() {
|
||||
|
|
@ -115,123 +161,92 @@ impl Traverser for HtmlExport {
|
|||
};
|
||||
self.in_descriptive_list.pop();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self, ctx))]
|
||||
fn list_item(&mut self, event: WalkEvent<&ListItem>, ctx: &mut TraversalContext) {
|
||||
if self.in_descriptive_list.last().copied().unwrap_or_default() {
|
||||
match event {
|
||||
WalkEvent::Enter(item) => {
|
||||
Event::Enter(Container::ListItem(list_item)) => {
|
||||
if let Some(&true) = self.in_descriptive_list.last() {
|
||||
self.output += "<dt>";
|
||||
for elem in item.tag() {
|
||||
for elem in list_item.tag() {
|
||||
self.element(elem, ctx);
|
||||
}
|
||||
self.output += "</dt><dd>";
|
||||
} else {
|
||||
self.output += "<li>";
|
||||
}
|
||||
}
|
||||
Event::Leave(Container::ListItem(_)) => {
|
||||
if let Some(&true) = self.in_descriptive_list.last() {
|
||||
self.output += "</dd>";
|
||||
} else {
|
||||
self.output += "</li>";
|
||||
}
|
||||
WalkEvent::Leave(_) => self.output += "</dd>",
|
||||
};
|
||||
} else {
|
||||
match event {
|
||||
WalkEvent::Enter(_) => self.output += "<li>",
|
||||
WalkEvent::Leave(_) => self.output += "</li>",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self, _ctx))]
|
||||
fn paragraph(&mut self, event: WalkEvent<&Paragraph>, _ctx: &mut TraversalContext) {
|
||||
self.output += match event {
|
||||
WalkEvent::Enter(_) => "<p>",
|
||||
WalkEvent::Leave(_) => "</p>",
|
||||
};
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self, _ctx))]
|
||||
fn section(&mut self, event: WalkEvent<&Section>, _ctx: &mut TraversalContext) {
|
||||
self.output += match event {
|
||||
WalkEvent::Enter(_) => "<section>",
|
||||
WalkEvent::Leave(_) => "</section>",
|
||||
};
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self, _ctx))]
|
||||
fn fixed_width(&mut self, event: WalkEvent<&FixedWidth>, _ctx: &mut TraversalContext) {
|
||||
if let WalkEvent::Enter(_f) = event {
|
||||
// self.output += f.text();
|
||||
};
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self, ctx))]
|
||||
fn snippet(&mut self, event: WalkEvent<&Snippet>, ctx: &mut TraversalContext) {
|
||||
if let WalkEvent::Enter(snippet) = event {
|
||||
if snippet.backend().eq_ignore_ascii_case("html") {
|
||||
self.output += &snippet.value();
|
||||
}
|
||||
return ctx.skip();
|
||||
};
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self, _ctx))]
|
||||
fn italic(&mut self, event: WalkEvent<&Italic>, _ctx: &mut TraversalContext) {
|
||||
self.output += match event {
|
||||
WalkEvent::Enter(_) => "<i>",
|
||||
WalkEvent::Leave(_) => "</i>",
|
||||
};
|
||||
}
|
||||
Event::Enter(Container::OrgTable(table)) => {
|
||||
self.output += "<table>";
|
||||
self.table_row = if table.has_header() {
|
||||
TableRow::HeaderRule
|
||||
} else {
|
||||
TableRow::BodyRule
|
||||
}
|
||||
}
|
||||
Event::Leave(Container::OrgTable(_)) => {
|
||||
match self.table_row {
|
||||
TableRow::Body => self.output += "</tbody>",
|
||||
TableRow::Header => self.output += "</thead>",
|
||||
_ => {}
|
||||
}
|
||||
self.output += "</table>";
|
||||
}
|
||||
Event::Enter(Container::OrgTableRow(row)) => {
|
||||
if row.is_rule() {
|
||||
match self.table_row {
|
||||
TableRow::Body => {
|
||||
self.output += "</tbody>";
|
||||
self.table_row = TableRow::BodyRule;
|
||||
}
|
||||
TableRow::Header => {
|
||||
self.output += "</thead>";
|
||||
self.table_row = TableRow::BodyRule;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
ctx.skip();
|
||||
} else {
|
||||
match self.table_row {
|
||||
TableRow::HeaderRule => {
|
||||
self.table_row = TableRow::Header;
|
||||
self.output += "<thead>";
|
||||
}
|
||||
TableRow::BodyRule => {
|
||||
self.table_row = TableRow::Body;
|
||||
self.output += "<tbody>";
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
self.output += "<tr>";
|
||||
}
|
||||
}
|
||||
Event::Leave(Container::OrgTableRow(row)) => {
|
||||
if row.is_rule() {
|
||||
match self.table_row {
|
||||
TableRow::Body => {
|
||||
self.output += "</tbody>";
|
||||
self.table_row = TableRow::BodyRule;
|
||||
}
|
||||
TableRow::Header => {
|
||||
self.output += "</thead>";
|
||||
self.table_row = TableRow::BodyRule;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
ctx.skip();
|
||||
} else {
|
||||
self.output += "</tr>";
|
||||
}
|
||||
}
|
||||
Event::Enter(Container::OrgTableCell(_)) => self.output += "<td>",
|
||||
Event::Leave(Container::OrgTableCell(_)) => self.output += "</td>",
|
||||
|
||||
#[tracing::instrument(skip(self, _ctx))]
|
||||
fn bold(&mut self, event: WalkEvent<&Bold>, _ctx: &mut TraversalContext) {
|
||||
self.output += match event {
|
||||
WalkEvent::Enter(_) => "<b>",
|
||||
WalkEvent::Leave(_) => "</b>",
|
||||
};
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self, _ctx))]
|
||||
fn strike(&mut self, event: WalkEvent<&Strike>, _ctx: &mut TraversalContext) {
|
||||
self.output += match event {
|
||||
WalkEvent::Enter(_) => "<s>",
|
||||
WalkEvent::Leave(_) => "</s>",
|
||||
};
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self, _ctx))]
|
||||
fn underline(&mut self, event: WalkEvent<&Underline>, _ctx: &mut TraversalContext) {
|
||||
self.output += match event {
|
||||
WalkEvent::Enter(_) => "<u>",
|
||||
WalkEvent::Leave(_) => "</u>",
|
||||
};
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self, _ctx))]
|
||||
fn verbatim(&mut self, event: WalkEvent<&Verbatim>, _ctx: &mut TraversalContext) {
|
||||
self.output += match event {
|
||||
WalkEvent::Enter(_) => "<code>",
|
||||
WalkEvent::Leave(_) => "</code>",
|
||||
};
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self, _ctx))]
|
||||
fn code(&mut self, event: WalkEvent<&Code>, _ctx: &mut TraversalContext) {
|
||||
self.output += match event {
|
||||
WalkEvent::Enter(_) => "<code>",
|
||||
WalkEvent::Leave(_) => "</code>",
|
||||
};
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self, ctx))]
|
||||
fn rule(&mut self, event: WalkEvent<&Rule>, ctx: &mut TraversalContext) {
|
||||
if let WalkEvent::Enter(_) = event {
|
||||
self.output += "<hr/>"
|
||||
};
|
||||
ctx.skip()
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self, ctx))]
|
||||
fn link(&mut self, event: WalkEvent<&Link>, ctx: &mut TraversalContext) {
|
||||
match event {
|
||||
WalkEvent::Enter(link) => {
|
||||
Event::Enter(Container::Link(link)) => {
|
||||
let path = link.path();
|
||||
|
||||
if link.is_image() {
|
||||
|
|
@ -243,299 +258,51 @@ impl Traverser for HtmlExport {
|
|||
|
||||
if !link.has_description() {
|
||||
let _ = write!(&mut self.output, "{}</a>", HtmlEscape(&path));
|
||||
return ctx.skip();
|
||||
ctx.skip();
|
||||
}
|
||||
}
|
||||
WalkEvent::Leave(_) => {
|
||||
self.output += "</a>";
|
||||
Event::Leave(Container::Link(_)) => self.output += "</a>",
|
||||
|
||||
Event::Text(text) => {
|
||||
let _ = write!(&mut self.output, "{}", HtmlEscape(text.text()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self, _ctx))]
|
||||
fn quote_block(&mut self, event: WalkEvent<&QuoteBlock>, _ctx: &mut TraversalContext) {
|
||||
self.output += match event {
|
||||
WalkEvent::Enter(_) => "<blockquote>",
|
||||
WalkEvent::Leave(_) => "</blockquote>",
|
||||
};
|
||||
}
|
||||
Event::LineBreak(_) => self.output += "<br/>",
|
||||
|
||||
#[tracing::instrument(skip(self, _ctx))]
|
||||
fn verse_block(&mut self, event: WalkEvent<&VerseBlock>, _ctx: &mut TraversalContext) {
|
||||
self.output += match event {
|
||||
WalkEvent::Enter(_) => "<p class=\"verse\">",
|
||||
WalkEvent::Leave(_) => "</p>",
|
||||
};
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self, _ctx))]
|
||||
fn example_block(&mut self, event: WalkEvent<&ExampleBlock>, _ctx: &mut TraversalContext) {
|
||||
self.output += match event {
|
||||
WalkEvent::Enter(_) => "<pre class=\"example\">",
|
||||
WalkEvent::Leave(_) => "</pre>",
|
||||
};
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self, _ctx))]
|
||||
fn center_block(&mut self, event: WalkEvent<&CenterBlock>, _ctx: &mut TraversalContext) {
|
||||
self.output += match event {
|
||||
WalkEvent::Enter(_) => "<div class=\"center\">",
|
||||
WalkEvent::Leave(_) => "</div>",
|
||||
};
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self, _ctx))]
|
||||
fn org_table(&mut self, event: WalkEvent<&OrgTable>, _ctx: &mut TraversalContext) {
|
||||
match event {
|
||||
WalkEvent::Enter(table) => {
|
||||
self.output += "<table>";
|
||||
self.table_row = if table.has_header() {
|
||||
TableRow::HeaderRule
|
||||
} else {
|
||||
TableRow::BodyRule
|
||||
Event::Snippet(snippet) => {
|
||||
if snippet.backend().eq_ignore_ascii_case("html") {
|
||||
self.output += &snippet.value();
|
||||
}
|
||||
}
|
||||
WalkEvent::Leave(_) => {
|
||||
match self.table_row {
|
||||
TableRow::Body => self.output += "</tbody>",
|
||||
TableRow::Header => self.output += "</thead>",
|
||||
_ => {}
|
||||
}
|
||||
self.output += "</table>";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self, ctx))]
|
||||
fn org_table_row(&mut self, event: WalkEvent<&OrgTableRow>, ctx: &mut TraversalContext) {
|
||||
if match event {
|
||||
WalkEvent::Enter(n) | WalkEvent::Leave(n) => n.is_rule(),
|
||||
} {
|
||||
match self.table_row {
|
||||
TableRow::Body => {
|
||||
self.output += "</tbody>";
|
||||
self.table_row = TableRow::BodyRule;
|
||||
}
|
||||
TableRow::Header => {
|
||||
self.output += "</thead>";
|
||||
self.table_row = TableRow::BodyRule;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
return ctx.skip();
|
||||
}
|
||||
Event::Rule(_) => self.output += "<hr/>",
|
||||
|
||||
match event {
|
||||
WalkEvent::Enter(_) => {
|
||||
match self.table_row {
|
||||
TableRow::HeaderRule => {
|
||||
self.table_row = TableRow::Header;
|
||||
self.output += "<thead>";
|
||||
Event::Timestamp(timestamp) => {
|
||||
self.output += r#"<span class="timestamp-wrapper"><span class="timestamp">"#;
|
||||
for e in timestamp.syntax.children_with_tokens() {
|
||||
match e {
|
||||
NodeOrToken::Token(t) if t.kind() == SyntaxKind::MINUS2 => {
|
||||
self.output += "–";
|
||||
}
|
||||
NodeOrToken::Token(t) => {
|
||||
self.output += t.text();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
TableRow::BodyRule => {
|
||||
self.table_row = TableRow::Body;
|
||||
self.output += "<tbody>";
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
self.output += "<tr>";
|
||||
self.output += r#"</span></span>"#;
|
||||
}
|
||||
WalkEvent::Leave(_) => {
|
||||
self.output += "</tr>";
|
||||
|
||||
Event::LatexFragment(latex) => {
|
||||
let _ = write!(&mut self.output, "{}", &latex.syntax);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self, _ctx))]
|
||||
fn org_table_cell(&mut self, event: WalkEvent<&OrgTableCell>, _ctx: &mut TraversalContext) {
|
||||
self.output += match event {
|
||||
WalkEvent::Enter(_) => "<td>",
|
||||
WalkEvent::Leave(_) => "</td>",
|
||||
};
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self, _ctx))]
|
||||
fn comment(&mut self, event: WalkEvent<&Comment>, _ctx: &mut TraversalContext) {
|
||||
self.output += match event {
|
||||
WalkEvent::Enter(_) => "<!--",
|
||||
WalkEvent::Leave(_) => "-->",
|
||||
};
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self, _ctx))]
|
||||
fn comment_block(&mut self, event: WalkEvent<&CommentBlock>, _ctx: &mut TraversalContext) {
|
||||
self.output += match event {
|
||||
WalkEvent::Enter(_) => "<!--",
|
||||
WalkEvent::Leave(_) => "-->",
|
||||
};
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self, ctx))]
|
||||
fn headline(&mut self, event: WalkEvent<&Headline>, ctx: &mut TraversalContext) {
|
||||
if let WalkEvent::Enter(headline) = event {
|
||||
let level = min(headline.level(), 6);
|
||||
let _ = write!(&mut self.output, "<h{level}>");
|
||||
for elem in headline.title() {
|
||||
self.element(elem, ctx);
|
||||
Event::LatexEnvironment(latex) => {
|
||||
let _ = write!(&mut self.output, "{}", &latex.syntax);
|
||||
}
|
||||
let _ = write!(&mut self.output, "</h{level}>");
|
||||
|
||||
Event::Entity(entity) => self.output += entity.html(),
|
||||
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self, ctx))]
|
||||
fn inline_src(&mut self, _event: WalkEvent<&InlineSrc>, ctx: &mut TraversalContext) {
|
||||
ctx.skip();
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self, ctx))]
|
||||
fn inline_call(&mut self, _event: WalkEvent<&InlineCall>, ctx: &mut TraversalContext) {
|
||||
ctx.skip();
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self, ctx))]
|
||||
fn special_block(&mut self, _event: WalkEvent<&SpecialBlock>, ctx: &mut TraversalContext) {
|
||||
ctx.skip();
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self, ctx))]
|
||||
fn export_block(&mut self, _event: WalkEvent<&ExportBlock>, ctx: &mut TraversalContext) {
|
||||
ctx.skip();
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self, ctx))]
|
||||
fn source_block(&mut self, _event: WalkEvent<&SourceBlock>, ctx: &mut TraversalContext) {
|
||||
ctx.skip();
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self, ctx))]
|
||||
fn babel_call(&mut self, _event: WalkEvent<&BabelCall>, ctx: &mut TraversalContext) {
|
||||
ctx.skip();
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self, ctx))]
|
||||
fn clock(&mut self, _event: WalkEvent<&Clock>, ctx: &mut TraversalContext) {
|
||||
ctx.skip();
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self, ctx))]
|
||||
fn cookie(&mut self, _event: WalkEvent<&Cookie>, ctx: &mut TraversalContext) {
|
||||
ctx.skip();
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self, ctx))]
|
||||
fn radio_target(&mut self, _event: WalkEvent<&RadioTarget>, ctx: &mut TraversalContext) {
|
||||
ctx.skip();
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self, ctx))]
|
||||
fn drawer(&mut self, _event: WalkEvent<&Drawer>, ctx: &mut TraversalContext) {
|
||||
ctx.skip();
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self, ctx))]
|
||||
fn dyn_block(&mut self, _event: WalkEvent<&DynBlock>, ctx: &mut TraversalContext) {
|
||||
ctx.skip();
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self, ctx))]
|
||||
fn fn_def(&mut self, _event: WalkEvent<&FnDef>, ctx: &mut TraversalContext) {
|
||||
ctx.skip();
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self, ctx))]
|
||||
fn fn_ref(&mut self, _event: WalkEvent<&FnRef>, ctx: &mut TraversalContext) {
|
||||
ctx.skip();
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self, ctx))]
|
||||
fn macros(&mut self, _event: WalkEvent<&Macros>, ctx: &mut TraversalContext) {
|
||||
ctx.skip();
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self, ctx))]
|
||||
fn timestamp(&mut self, event: WalkEvent<&Timestamp>, ctx: &mut TraversalContext) {
|
||||
if let WalkEvent::Enter(t) = event {
|
||||
self.output += r#"<span class="timestamp-wrapper"><span class="timestamp">"#;
|
||||
for e in t.syntax.children_with_tokens() {
|
||||
match e {
|
||||
NodeOrToken::Token(t) if t.kind() == SyntaxKind::MINUS2 => {
|
||||
self.output += "–";
|
||||
}
|
||||
NodeOrToken::Token(t) => {
|
||||
self.output += t.text();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
self.output += r#"</span></span>"#;
|
||||
ctx.skip();
|
||||
}
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self, ctx))]
|
||||
fn target(&mut self, _event: WalkEvent<&Target>, ctx: &mut TraversalContext) {
|
||||
ctx.skip();
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self, ctx))]
|
||||
fn latex_fragment(&mut self, event: WalkEvent<&LatexFragment>, ctx: &mut TraversalContext) {
|
||||
if let WalkEvent::Enter(l) = event {
|
||||
self.output += &l.syntax.to_string();
|
||||
ctx.skip();
|
||||
}
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self, ctx))]
|
||||
fn latex_environment(
|
||||
&mut self,
|
||||
event: WalkEvent<&LatexEnvironment>,
|
||||
ctx: &mut TraversalContext,
|
||||
) {
|
||||
if let WalkEvent::Enter(l) = event {
|
||||
self.output += &l.syntax.to_string();
|
||||
ctx.skip();
|
||||
}
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self, ctx))]
|
||||
fn entity(&mut self, event: WalkEvent<&Entity>, ctx: &mut TraversalContext) {
|
||||
if let WalkEvent::Enter(e) = event {
|
||||
self.output += e.html();
|
||||
ctx.skip();
|
||||
}
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self, ctx))]
|
||||
fn line_break(&mut self, event: WalkEvent<&LineBreak>, ctx: &mut TraversalContext) {
|
||||
if let WalkEvent::Enter(_) = event {
|
||||
self.output += "<br/>";
|
||||
ctx.skip();
|
||||
}
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self, _ctx))]
|
||||
fn subscript(&mut self, event: WalkEvent<&Subscript>, _ctx: &mut TraversalContext) {
|
||||
match event {
|
||||
WalkEvent::Enter(_) => self.output += "<sub>",
|
||||
WalkEvent::Leave(_) => self.output += "</sub>",
|
||||
}
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self, _ctx))]
|
||||
fn superscript(&mut self, event: WalkEvent<&Superscript>, _ctx: &mut TraversalContext) {
|
||||
match event {
|
||||
WalkEvent::Enter(_) => self.output += "<sup>",
|
||||
WalkEvent::Leave(_) => self.output += "</sup>",
|
||||
}
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self, ctx))]
|
||||
fn keyword(&mut self, _event: WalkEvent<&Keyword>, ctx: &mut TraversalContext) {
|
||||
ctx.skip();
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self, ctx))]
|
||||
fn property_drawer(&mut self, _event: WalkEvent<&PropertyDrawer>, ctx: &mut TraversalContext) {
|
||||
ctx.skip()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
//! Export `Org` struct to various formats.
|
||||
|
||||
mod forward;
|
||||
mod event;
|
||||
mod html;
|
||||
mod traverse;
|
||||
|
||||
pub use event::{Container, Event};
|
||||
pub use html::{HtmlEscape, HtmlExport};
|
||||
pub use traverse::{TraversalContext, Traverser};
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
use crate::ast::*;
|
||||
use crate::syntax::{SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken};
|
||||
use rowan::{ast::AstNode, WalkEvent};
|
||||
use crate::syntax::{SyntaxElement, SyntaxKind};
|
||||
use rowan::ast::AstNode;
|
||||
use SyntaxKind::*;
|
||||
|
||||
use super::event::{Container, Event};
|
||||
|
||||
#[derive(Default, Debug, PartialEq, Eq, Clone, Copy)]
|
||||
enum TraversalControl {
|
||||
Up,
|
||||
|
|
@ -12,26 +14,6 @@ enum TraversalControl {
|
|||
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,
|
||||
|
|
@ -56,216 +38,187 @@ impl TraversalContext {
|
|||
}
|
||||
}
|
||||
|
||||
/// Enumerates org syntax tree
|
||||
/// A trait for enumerating org syntax tree
|
||||
///
|
||||
/// Traverser enumerates org syntax tree and calls handle method on each
|
||||
/// enumerated node and token.
|
||||
/// ### `TraversalContext`
|
||||
///
|
||||
/// Each handle method can returns a `TraversalControl` to control the traversal.
|
||||
/// `TraversalContext` can be used to control the traversal.
|
||||
///
|
||||
/// For example, `ctx.skip()` will skips the traversal for current
|
||||
/// element and its descendants and improve the traversal performance.
|
||||
///
|
||||
/// ```rust
|
||||
/// use orgize::{
|
||||
/// export::{Container, Event, HtmlExport, TraversalContext, Traverser},
|
||||
/// Org,
|
||||
/// };
|
||||
/// use slugify::slugify;
|
||||
///
|
||||
/// #[derive(Default)]
|
||||
/// struct Toc(HtmlExport);
|
||||
///
|
||||
/// impl Traverser for Toc {
|
||||
/// fn event(&mut self, event: Event, ctx: &mut TraversalContext) {
|
||||
/// match event {
|
||||
/// Event::Enter(Container::Headline(headline)) => {
|
||||
/// let title = headline.title().map(|e| e.to_string()).collect::<String>();
|
||||
/// self.0.push_str(&format!("<a href='#{}'>", slugify!(&title)));
|
||||
/// for elem in headline.title() {
|
||||
/// self.element(elem, ctx);
|
||||
/// }
|
||||
/// self.0.push_str("</a>");
|
||||
/// if headline.headlines().count() > 0 {
|
||||
/// self.0.push_str("<ul>");
|
||||
/// }
|
||||
/// }
|
||||
/// Event::Leave(Container::Headline(headline)) => {
|
||||
/// if headline.headlines().count() > 0 {
|
||||
/// self.0.push_str("</ul>");
|
||||
/// }
|
||||
/// }
|
||||
/// Event::Enter(Container::Section(_)) | Event::Leave(Container::Section(_)) => ctx.skip(),
|
||||
/// Event::Enter(Container::Document(_)) | Event::Leave(Container::Document(_)) => {}
|
||||
/// _ => self.0.event(event, ctx),
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// let org = Org::parse(r#"
|
||||
/// * heading 1
|
||||
/// section 1
|
||||
/// ** heading 1.1
|
||||
/// ** heading 1.2
|
||||
/// * heading 2
|
||||
/// section 2
|
||||
/// * heading 3
|
||||
/// **** heading 3.1"#);
|
||||
/// let mut toc = Toc::default();
|
||||
/// org.traverse(&mut toc);
|
||||
/// assert_eq!(toc.0.finish(), "\
|
||||
/// <a href='#heading-1'>heading 1</a>\
|
||||
/// <ul><a href='#heading-1-1'>heading 1.1</a><a href='#heading-1-2'>heading 1.2</a></ul>\
|
||||
/// <a href='#heading-2'>heading 2</a>\
|
||||
/// <a href='#heading-3'>heading 3</a>\
|
||||
/// <ul><a href='#heading-3-1'>heading 3.1</a></ul>");
|
||||
/// ```
|
||||
pub trait Traverser {
|
||||
/// Handles traversal event
|
||||
fn event(&mut self, event: Event, ctx: &mut TraversalContext);
|
||||
|
||||
fn element(&mut self, element: SyntaxElement, ctx: &mut TraversalContext) {
|
||||
macro_rules! take_control {
|
||||
() => {
|
||||
match ctx.control {
|
||||
TraversalControl::Stop => {
|
||||
ctx.control = TraversalControl::Stop;
|
||||
return;
|
||||
}
|
||||
TraversalControl::Up => {
|
||||
ctx.control = TraversalControl::Skip;
|
||||
return;
|
||||
}
|
||||
TraversalControl::Skip => {
|
||||
ctx.control = TraversalControl::Continue;
|
||||
return;
|
||||
}
|
||||
TraversalControl::Continue => {}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
match element {
|
||||
SyntaxElement::Node(node) => self.node(node, ctx),
|
||||
SyntaxElement::Token(token) => self.token(token, ctx),
|
||||
SyntaxElement::Node(node) => {
|
||||
macro_rules! walk {
|
||||
($ast:ident) => {{
|
||||
debug_assert!($ast::can_cast(node.kind()));
|
||||
let node = $ast { syntax: node };
|
||||
self.event(Event::Enter(Container::$ast(node.clone())), ctx);
|
||||
take_control!();
|
||||
for child in node.syntax.children_with_tokens() {
|
||||
self.element(child, ctx);
|
||||
take_control!();
|
||||
}
|
||||
self.event(Event::Leave(Container::$ast(node.clone())), ctx);
|
||||
take_control!();
|
||||
}};
|
||||
(@$ast:ident) => {{
|
||||
debug_assert!($ast::can_cast(node.kind()));
|
||||
let node = $ast { syntax: node };
|
||||
self.event(Event::$ast(node), ctx);
|
||||
take_control!();
|
||||
}};
|
||||
}
|
||||
|
||||
match node.kind() {
|
||||
DOCUMENT => walk!(Document),
|
||||
HEADLINE => walk!(Headline),
|
||||
SECTION => walk!(Section),
|
||||
PARAGRAPH => walk!(Paragraph),
|
||||
BOLD => walk!(Bold),
|
||||
ITALIC => walk!(Italic),
|
||||
STRIKE => walk!(Strike),
|
||||
UNDERLINE => walk!(Underline),
|
||||
LIST => walk!(List),
|
||||
LIST_ITEM => walk!(ListItem),
|
||||
CODE => walk!(Code),
|
||||
INLINE_CALL => walk!(@InlineCall),
|
||||
INLINE_SRC => walk!(@InlineSrc),
|
||||
RULE => walk!(@Rule),
|
||||
VERBATIM => walk!(Verbatim),
|
||||
SPECIAL_BLOCK => walk!(SpecialBlock),
|
||||
QUOTE_BLOCK => walk!(QuoteBlock),
|
||||
CENTER_BLOCK => walk!(CenterBlock),
|
||||
VERSE_BLOCK => walk!(VerseBlock),
|
||||
COMMENT_BLOCK => walk!(CommentBlock),
|
||||
EXAMPLE_BLOCK => walk!(ExampleBlock),
|
||||
EXPORT_BLOCK => walk!(ExportBlock),
|
||||
SOURCE_BLOCK => walk!(SourceBlock),
|
||||
BABEL_CALL => walk!(BabelCall),
|
||||
CLOCK => walk!(@Clock),
|
||||
COOKIE => walk!(@Cookie),
|
||||
RADIO_TARGET => walk!(RadioTarget),
|
||||
DRAWER => walk!(Drawer),
|
||||
DYN_BLOCK => walk!(DynBlock),
|
||||
FN_DEF => walk!(FnDef),
|
||||
FN_REF => walk!(FnRef),
|
||||
MACROS => walk!(@Macros),
|
||||
SNIPPET => walk!(@Snippet),
|
||||
TIMESTAMP_ACTIVE | TIMESTAMP_INACTIVE | TIMESTAMP_DIARY => walk!(@Timestamp),
|
||||
TARGET => walk!(Target),
|
||||
COMMENT => walk!(Comment),
|
||||
FIXED_WIDTH => walk!(FixedWidth),
|
||||
ORG_TABLE => walk!(OrgTable),
|
||||
ORG_TABLE_RULE_ROW | ORG_TABLE_STANDARD_ROW => walk!(OrgTableRow),
|
||||
ORG_TABLE_CELL => walk!(OrgTableCell),
|
||||
LINK => walk!(Link),
|
||||
LATEX_FRAGMENT => walk!(@LatexFragment),
|
||||
LATEX_ENVIRONMENT => walk!(@LatexEnvironment),
|
||||
ENTITY => walk!(@Entity),
|
||||
LINE_BREAK => walk!(@LineBreak),
|
||||
SUPERSCRIPT => walk!(Superscript),
|
||||
SUBSCRIPT => walk!(Subscript),
|
||||
KEYWORD => walk!(Keyword),
|
||||
PROPERTY_DRAWER => walk!(PropertyDrawer),
|
||||
BLOCK_CONTENT | LIST_ITEM_CONTENT => {
|
||||
for child in node.children_with_tokens() {
|
||||
self.element(child, ctx);
|
||||
take_control!();
|
||||
}
|
||||
}
|
||||
|
||||
kind => debug_assert!(
|
||||
!kind.is_element() && !kind.is_object(),
|
||||
"{:?} is not handled",
|
||||
kind
|
||||
),
|
||||
}
|
||||
}
|
||||
SyntaxElement::Token(token) => {
|
||||
if token.kind() == TEXT {
|
||||
self.event(Event::Text(token), ctx);
|
||||
take_control!();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// 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() {
|
||||
self.element(child, 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),
|
||||
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),
|
||||
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),
|
||||
LATEX_FRAGMENT => traverse!(LatexFragment, latex_fragment),
|
||||
LATEX_ENVIRONMENT => traverse!(LatexEnvironment, latex_environment),
|
||||
ENTITY => traverse!(Entity, entity),
|
||||
LINE_BREAK => traverse!(LineBreak, line_break),
|
||||
SUPERSCRIPT => traverse!(Superscript, superscript),
|
||||
SUBSCRIPT => traverse!(Subscript, subscript),
|
||||
KEYWORD => traverse!(Keyword, keyword),
|
||||
PROPERTY_DRAWER => traverse!(PropertyDrawer, property_drawer),
|
||||
|
||||
BLOCK_CONTENT | LIST_ITEM_CONTENT => traverse_children!(node),
|
||||
|
||||
kind => debug_assert!(
|
||||
!kind.is_element() && !kind.is_object(),
|
||||
"{:?} is not handled",
|
||||
kind
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
/// 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 `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 `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);
|
||||
/// Called when entering or leaving `LatexFragment` node
|
||||
fn latex_fragment(&mut self, event: WalkEvent<&LatexFragment>, ctx: &mut TraversalContext);
|
||||
/// Called when entering or leaving `LatexEnvironment` node
|
||||
fn latex_environment(
|
||||
&mut self,
|
||||
event: WalkEvent<&LatexEnvironment>,
|
||||
ctx: &mut TraversalContext,
|
||||
);
|
||||
/// Called when entering or leaving `Entity` node
|
||||
fn entity(&mut self, event: WalkEvent<&Entity>, ctx: &mut TraversalContext);
|
||||
/// Called when entering or leaving `LineBreak` node
|
||||
fn line_break(&mut self, event: WalkEvent<&LineBreak>, ctx: &mut TraversalContext);
|
||||
/// Called when entering or leaving `Superscript` node
|
||||
fn superscript(&mut self, event: WalkEvent<&Superscript>, ctx: &mut TraversalContext);
|
||||
/// Called when entering or leaving `Subscript` node
|
||||
fn subscript(&mut self, event: WalkEvent<&Subscript>, ctx: &mut TraversalContext);
|
||||
/// Called when entering or leaving `Keyword` node
|
||||
fn keyword(&mut self, event: WalkEvent<&Keyword>, ctx: &mut TraversalContext);
|
||||
/// Called when entering or leaving `PropertyDrawer` node
|
||||
fn property_drawer(&mut self, event: WalkEvent<&PropertyDrawer>, ctx: &mut TraversalContext);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ use crate::ast::Document;
|
|||
use crate::config::ParseConfig;
|
||||
use crate::export::{HtmlExport, TraversalContext, Traverser};
|
||||
use crate::syntax::{OrgLanguage, SyntaxNode};
|
||||
use crate::SyntaxElement;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Org {
|
||||
|
|
@ -46,9 +47,12 @@ impl Org {
|
|||
}
|
||||
|
||||
/// Walk through org element tree using given traverser
|
||||
pub fn traverse<T: Traverser>(&self, h: &mut T) {
|
||||
pub fn traverse<T: Traverser>(&self, t: &mut T) {
|
||||
let mut ctx = TraversalContext::default();
|
||||
h.node(SyntaxNode::new_root(self.green.clone()), &mut ctx);
|
||||
t.element(
|
||||
SyntaxElement::Node(SyntaxNode::new_root(self.green.clone())),
|
||||
&mut ctx,
|
||||
);
|
||||
}
|
||||
|
||||
/// Returns the first node in org element tree in depth first order
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue