feat(export): update html render
This commit is contained in:
parent
33f78ee207
commit
0a876e2f2b
|
@ -1,72 +1,72 @@
|
||||||
use orgize::export::*;
|
|
||||||
use orgize::headline::Headline;
|
|
||||||
use slugify::slugify;
|
|
||||||
use std::convert::From;
|
use std::convert::From;
|
||||||
use std::env::args;
|
use std::env::args;
|
||||||
use std::fs::File;
|
use std::fs;
|
||||||
use std::io::{Cursor, Error as IOError, Read, Write};
|
use std::io::{Error as IOError, Write};
|
||||||
|
use std::result::Result;
|
||||||
use std::string::FromUtf8Error;
|
use std::string::FromUtf8Error;
|
||||||
|
|
||||||
struct CustomHtmlHandler;
|
use orgize::export::*;
|
||||||
|
use orgize::{Container, Org};
|
||||||
|
use slugify::slugify;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum Error {
|
enum MyError {
|
||||||
IO(IOError),
|
IO(IOError),
|
||||||
Heading,
|
Heading,
|
||||||
Utf8(FromUtf8Error),
|
Utf8(FromUtf8Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
// From<std::io::Error> trait is required
|
// From<std::io::Error> trait is required for custom error type
|
||||||
impl From<IOError> for Error {
|
impl From<IOError> for MyError {
|
||||||
fn from(err: IOError) -> Error {
|
fn from(err: IOError) -> Self {
|
||||||
Error::IO(err)
|
MyError::IO(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<FromUtf8Error> for Error {
|
impl From<FromUtf8Error> for MyError {
|
||||||
fn from(err: FromUtf8Error) -> Error {
|
fn from(err: FromUtf8Error) -> Self {
|
||||||
Error::Utf8(err)
|
MyError::Utf8(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type Result = std::result::Result<(), Error>;
|
struct CustomHtmlHandler;
|
||||||
|
|
||||||
impl<W: Write> HtmlHandler<W, Error> for CustomHtmlHandler {
|
impl HtmlHandler<MyError> for CustomHtmlHandler {
|
||||||
fn headline_beg(&mut self, w: &mut W, hdl: Headline) -> Result {
|
fn start<W: Write>(&mut self, mut w: W, container: Container<'_>) -> Result<(), MyError> {
|
||||||
|
let mut default_handler = DefaultHtmlHandler;
|
||||||
|
match container {
|
||||||
|
Container::Headline(hdl) => {
|
||||||
if hdl.level > 6 {
|
if hdl.level > 6 {
|
||||||
Err(Error::Heading)
|
return Err(MyError::Heading);
|
||||||
} else {
|
} else {
|
||||||
Ok(write!(
|
let slugify = slugify!(hdl.title);
|
||||||
|
write!(
|
||||||
w,
|
w,
|
||||||
r##"<h{0}><a class="anchor" href="#{1}">{2}</a></h{0}>"##,
|
"<h{0}><a id=\"{1}\" href=\"#{1}\">{2}</a></h{0}>",
|
||||||
hdl.level,
|
hdl.level, slugify, hdl.title,
|
||||||
slugify!(hdl.title),
|
)?;
|
||||||
hdl.title,
|
|
||||||
)?)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
_ => default_handler.start(w, container)?,
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() -> Result {
|
fn main() -> Result<(), MyError> {
|
||||||
let args: Vec<_> = args().collect();
|
let args: Vec<_> = args().collect();
|
||||||
|
|
||||||
if args.len() < 2 {
|
if args.len() < 2 {
|
||||||
println!("Usage: {} <org-file>", args[0]);
|
eprintln!("Usage: {} <org-file>", args[0]);
|
||||||
} else {
|
} else {
|
||||||
let mut file = File::open(&args[1])?;
|
let contents = String::from_utf8(fs::read(&args[1])?)?;
|
||||||
|
let mut org = Org::new(&contents);
|
||||||
|
let mut writer = Vec::new();
|
||||||
|
|
||||||
let mut contents = String::new();
|
org.parse();
|
||||||
file.read_to_string(&mut contents)?;
|
org.html(&mut writer, CustomHtmlHandler)?;
|
||||||
|
|
||||||
let mut cursor = Cursor::new(Vec::new());
|
println!("{}", String::from_utf8(writer)?);
|
||||||
|
|
||||||
//let mut render = DefaultHtmlRender::new(cursor, &contents);
|
|
||||||
// comment the following line and uncomment the line above to use the default handler
|
|
||||||
let mut render = HtmlRender::new(CustomHtmlHandler, &mut cursor, &contents);
|
|
||||||
|
|
||||||
render.render()?;
|
|
||||||
|
|
||||||
println!("{}", String::from_utf8(cursor.into_inner())?);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -8,6 +8,12 @@ pub struct Keyword<'a> {
|
||||||
pub value: &'a str,
|
pub value: &'a str,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct BabelCall<'a> {
|
||||||
|
pub key: &'a str,
|
||||||
|
pub value: &'a str,
|
||||||
|
}
|
||||||
|
|
||||||
impl Keyword<'_> {
|
impl Keyword<'_> {
|
||||||
#[inline]
|
#[inline]
|
||||||
// return (key, option, value, offset)
|
// return (key, option, value, offset)
|
||||||
|
|
|
@ -33,7 +33,7 @@ pub use self::{
|
||||||
headline::Headline,
|
headline::Headline,
|
||||||
inline_call::InlineCall,
|
inline_call::InlineCall,
|
||||||
inline_src::InlineSrc,
|
inline_src::InlineSrc,
|
||||||
keyword::Keyword,
|
keyword::{BabelCall, Keyword},
|
||||||
link::Link,
|
link::Link,
|
||||||
list::{List, ListItem},
|
list::{List, ListItem},
|
||||||
macros::Macros,
|
macros::Macros,
|
||||||
|
@ -56,7 +56,7 @@ pub enum Element<'a> {
|
||||||
contents_end: usize,
|
contents_end: usize,
|
||||||
},
|
},
|
||||||
BabelCall {
|
BabelCall {
|
||||||
value: &'a str,
|
call: BabelCall<'a>,
|
||||||
begin: usize,
|
begin: usize,
|
||||||
end: usize,
|
end: usize,
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,15 +1,13 @@
|
||||||
#![allow(unused_variables)]
|
#![allow(unused_variables)]
|
||||||
|
#![allow(unused_mut)]
|
||||||
|
|
||||||
use crate::{elements::*, headline::Headline, objects::*, Event, Parser};
|
use crate::elements::*;
|
||||||
|
use crate::iter::Container;
|
||||||
use jetscii::bytes;
|
use jetscii::bytes;
|
||||||
use std::{
|
use std::io::{Error, Write};
|
||||||
convert::From,
|
|
||||||
io::{Error, Write},
|
|
||||||
marker::PhantomData,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub trait HtmlHandler<W: Write, E: From<Error>> {
|
pub trait HtmlHandler<E: From<Error>> {
|
||||||
fn escape(&mut self, w: &mut W, text: &str) -> Result<(), E> {
|
fn escape<W: Write>(&mut self, mut w: W, text: &str) -> Result<(), E> {
|
||||||
let mut pos = 0;
|
let mut pos = 0;
|
||||||
let bytes = text.as_bytes();
|
let bytes = text.as_bytes();
|
||||||
while let Some(off) = bytes!(b'<', b'>', b'&', b'\'', b'"').find(&bytes[pos..]) {
|
while let Some(off) = bytes!(b'<', b'>', b'&', b'\'', b'"').find(&bytes[pos..]) {
|
||||||
|
@ -29,261 +27,138 @@ pub trait HtmlHandler<W: Write, E: From<Error>> {
|
||||||
|
|
||||||
Ok(w.write_all(&bytes[pos..])?)
|
Ok(w.write_all(&bytes[pos..])?)
|
||||||
}
|
}
|
||||||
fn event(&mut self, w: &mut W, event: Event) -> Result<(), E> {
|
fn start<W: Write>(&mut self, mut w: W, container: Container) -> Result<(), E> {
|
||||||
handle_event!(event, self, w);
|
match container {
|
||||||
Ok(())
|
Container::Block(block) => write!(w, "<div>")?,
|
||||||
}
|
Container::Bold => write!(w, "<b>")?,
|
||||||
fn headline_beg(&mut self, w: &mut W, hdl: Headline) -> Result<(), E> {
|
Container::Document => write!(w, "<main>")?,
|
||||||
|
Container::DynBlock(_) => (),
|
||||||
|
Container::Headline(hdl) => {
|
||||||
let level = if hdl.level <= 6 { hdl.level } else { 6 };
|
let level = if hdl.level <= 6 { hdl.level } else { 6 };
|
||||||
write!(w, "<h{}>", level)?;
|
write!(&mut w, "<h{}>", level)?;
|
||||||
self.text(w, hdl.title)?;
|
self.text(&mut w, hdl.title)?;
|
||||||
write!(w, "</h{}>", level)?;
|
write!(&mut w, "</h{}>", level)?;
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
fn headline_end(&mut self, w: &mut W) -> Result<(), E> {
|
Container::Italic => write!(w, "<i>")?,
|
||||||
Ok(())
|
Container::List(list) => {
|
||||||
}
|
if list.ordered {
|
||||||
fn section_beg(&mut self, w: &mut W) -> Result<(), E> {
|
write!(w, "<ol>")?;
|
||||||
Ok(write!(w, "<section>")?)
|
|
||||||
}
|
|
||||||
fn section_end(&mut self, w: &mut W) -> Result<(), E> {
|
|
||||||
Ok(write!(w, "</section>")?)
|
|
||||||
}
|
|
||||||
fn drawer_beg(&mut self, w: &mut W, name: &str) -> Result<(), E> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
fn drawer_end(&mut self, w: &mut W) -> Result<(), E> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
fn paragraph_beg(&mut self, w: &mut W) -> Result<(), E> {
|
|
||||||
Ok(write!(w, "<p>")?)
|
|
||||||
}
|
|
||||||
fn paragraph_end(&mut self, w: &mut W) -> Result<(), E> {
|
|
||||||
Ok(write!(w, "</p>")?)
|
|
||||||
}
|
|
||||||
fn ctr_block_beg(&mut self, w: &mut W) -> Result<(), E> {
|
|
||||||
Ok(write!(w, r#"<div style="text-align: center">"#)?)
|
|
||||||
}
|
|
||||||
fn ctr_block_end(&mut self, w: &mut W) -> Result<(), E> {
|
|
||||||
Ok(write!(w, "</div>")?)
|
|
||||||
}
|
|
||||||
fn qte_block_beg(&mut self, w: &mut W) -> Result<(), E> {
|
|
||||||
Ok(write!(w, "<blockquote>")?)
|
|
||||||
}
|
|
||||||
fn qte_block_end(&mut self, w: &mut W) -> Result<(), E> {
|
|
||||||
Ok(write!(w, "</blockquote>")?)
|
|
||||||
}
|
|
||||||
fn spl_block_beg(&mut self, w: &mut W, name: &str, args: Option<&str>) -> Result<(), E> {
|
|
||||||
Ok(write!(w, "<div>")?)
|
|
||||||
}
|
|
||||||
fn spl_block_end(&mut self, w: &mut W) -> Result<(), E> {
|
|
||||||
Ok(write!(w, "</div>")?)
|
|
||||||
}
|
|
||||||
fn comment_block(&mut self, w: &mut W, cont: &str, args: Option<&str>) -> Result<(), E> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
fn example_block(&mut self, w: &mut W, cont: &str, args: Option<&str>) -> Result<(), E> {
|
|
||||||
write!(w, "<pre><code>")?;
|
|
||||||
self.escape(w, cont)?;
|
|
||||||
write!(w, "</pre></code>")?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
fn export_block(&mut self, w: &mut W, cont: &str, args: Option<&str>) -> Result<(), E> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
fn src_block(&mut self, w: &mut W, cont: &str, args: Option<&str>) -> Result<(), E> {
|
|
||||||
write!(w, "<pre><code>")?;
|
|
||||||
self.escape(w, cont)?;
|
|
||||||
write!(w, "</pre></code>")?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
fn verse_block(&mut self, w: &mut W, cont: &str, args: Option<&str>) -> Result<(), E> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
fn dyn_block_beg(&mut self, w: &mut W, name: &str, args: Option<&str>) -> Result<(), E> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
fn dyn_block_end(&mut self, w: &mut W) -> Result<(), E> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
fn list_beg(&mut self, w: &mut W, _indent: usize, ordered: bool) -> Result<(), E> {
|
|
||||||
if ordered {
|
|
||||||
Ok(write!(w, "<ol>")?)
|
|
||||||
} else {
|
} else {
|
||||||
Ok(write!(w, "<ul>")?)
|
write!(w, "<ul>")?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn list_end(&mut self, w: &mut W, _indent: usize, ordered: bool) -> Result<(), E> {
|
Container::ListItem(_) => write!(w, "<li>")?,
|
||||||
if ordered {
|
Container::Paragraph => write!(w, "<p>")?,
|
||||||
Ok(write!(w, "</ol>")?)
|
Container::Section => write!(w, "<section>")?,
|
||||||
|
Container::Strike => write!(w, "<s>")?,
|
||||||
|
Container::Underline => write!(w, "<u>")?,
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
fn end<W: Write>(&mut self, mut w: W, container: Container) -> Result<(), E> {
|
||||||
|
match container {
|
||||||
|
Container::Block(block) => write!(w, "</div>")?,
|
||||||
|
Container::Bold => write!(w, "</b>")?,
|
||||||
|
Container::Document => write!(w, "</main>")?,
|
||||||
|
Container::DynBlock(_) => (),
|
||||||
|
Container::Headline(_) => (),
|
||||||
|
Container::Italic => write!(w, "</i>")?,
|
||||||
|
Container::List(list) => {
|
||||||
|
if list.ordered {
|
||||||
|
write!(w, "</ol>")?;
|
||||||
} else {
|
} else {
|
||||||
Ok(write!(w, "</ul>")?)
|
write!(w, "</ul>")?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn list_beg_item(&mut self, w: &mut W, bullet: &str) -> Result<(), E> {
|
Container::ListItem(_) => write!(w, "</li>")?,
|
||||||
Ok(write!(w, "<li>")?)
|
Container::Paragraph => write!(w, "</p>")?,
|
||||||
}
|
Container::Section => write!(w, "</section>")?,
|
||||||
fn list_end_item(&mut self, w: &mut W) -> Result<(), E> {
|
Container::Strike => write!(w, "</s>")?,
|
||||||
Ok(write!(w, "</li>")?)
|
Container::Underline => write!(w, "</u>")?,
|
||||||
}
|
|
||||||
fn call(&mut self, w: &mut W, value: &str) -> Result<(), E> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
fn clock(&mut self, w: &mut W, clock: Clock<'_>) -> Result<(), E> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
fn comment(&mut self, w: &mut W, cont: &str) -> Result<(), E> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
fn fixed_width(&mut self, w: &mut W, cont: &str) -> Result<(), E> {
|
|
||||||
for line in cont.lines() {
|
|
||||||
// remove leading colon
|
|
||||||
write!(w, "<pre>")?;
|
|
||||||
self.escape(w, &line[1..])?;
|
|
||||||
write!(w, "</pre>")?;
|
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
fn table_start(&mut self, w: &mut W) -> Result<(), E> {
|
fn keyword<W: Write>(&mut self, mut w: W, keyword: &Keyword<'_>) -> Result<(), E> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
fn table_end(&mut self, w: &mut W) -> Result<(), E> {
|
fn drawer<W: Write>(&mut self, mut w: W, drawer: &Drawer<'_>) -> Result<(), E> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
fn table_cell(&mut self, w: &mut W) -> Result<(), E> {
|
fn rule<W: Write>(&mut self, mut w: W) -> Result<(), E> {
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
fn latex_env(&mut self, w: &mut W) -> Result<(), E> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
fn fn_def(&mut self, w: &mut W, label: &str, cont: &str) -> Result<(), E> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
fn keyword(&mut self, w: &mut W, keyword: Keyword<'_>) -> Result<(), E> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
fn rule(&mut self, w: &mut W) -> Result<(), E> {
|
|
||||||
Ok(write!(w, "<hr>")?)
|
Ok(write!(w, "<hr>")?)
|
||||||
}
|
}
|
||||||
fn cookie(&mut self, w: &mut W, cookie: Cookie) -> Result<(), E> {
|
fn cookie<W: Write>(&mut self, mut w: W, cookie: &Cookie) -> Result<(), E> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
fn fn_ref(&mut self, w: &mut W, fn_ref: FnRef<'_>) -> Result<(), E> {
|
fn fn_ref<W: Write>(&mut self, mut w: W, fn_ref: &FnRef<'_>) -> Result<(), E> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
fn inline_call(&mut self, w: &mut W, call: InlineCall<'_>) -> Result<(), E> {
|
fn babel_call<W: Write>(&mut self, mut w: W, call: &BabelCall<'_>) -> Result<(), E> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
fn inline_src(&mut self, w: &mut W, src: InlineSrc<'_>) -> Result<(), E> {
|
fn inline_call<W: Write>(&mut self, mut w: W, call: &InlineCall<'_>) -> Result<(), E> {
|
||||||
write!(w, "<code>")?;
|
|
||||||
self.text(w, src.body)?;
|
|
||||||
write!(w, "</code>")?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
fn link(&mut self, w: &mut W, link: Link<'_>) -> Result<(), E> {
|
fn inline_src<W: Write>(&mut self, mut w: W, src: &InlineSrc<'_>) -> Result<(), E> {
|
||||||
write!(w, r#"<a href=""#)?;
|
write!(&mut w, "<code>")?;
|
||||||
self.text(w, link.path)?;
|
self.text(&mut w, src.body)?;
|
||||||
write!(w, r#"">"#)?;
|
write!(&mut w, "</code>")?;
|
||||||
self.text(w, link.desc.unwrap_or(link.path))?;
|
|
||||||
write!(w, "</a>")?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
fn macros(&mut self, w: &mut W, macros: Macros<'_>) -> Result<(), E> {
|
fn link<W: Write>(&mut self, mut w: W, link: &Link<'_>) -> Result<(), E> {
|
||||||
|
write!(&mut w, r#"<a href=""#)?;
|
||||||
|
self.text(&mut w, link.path)?;
|
||||||
|
write!(&mut w, r#"">"#)?;
|
||||||
|
self.text(&mut w, link.desc.unwrap_or(link.path))?;
|
||||||
|
write!(&mut w, "</a>")?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
fn radio_target(&mut self, w: &mut W, target: &str) -> Result<(), E> {
|
fn macros<W: Write>(&mut self, mut w: W, macros: &Macros<'_>) -> Result<(), E> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
fn snippet(&mut self, w: &mut W, snippet: Snippet<'_>) -> Result<(), E> {
|
fn radio_target<W: Write>(&mut self, mut w: W, target: &RadioTarget<'_>) -> Result<(), E> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
fn snippet<W: Write>(&mut self, mut w: W, snippet: &Snippet<'_>) -> Result<(), E> {
|
||||||
if snippet.name.eq_ignore_ascii_case("HTML") {
|
if snippet.name.eq_ignore_ascii_case("HTML") {
|
||||||
Ok(write!(w, "{}", snippet.value)?)
|
Ok(write!(w, "{}", snippet.value)?)
|
||||||
} else {
|
} else {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn target(&mut self, w: &mut W, target: &str) -> Result<(), E> {
|
fn target<W: Write>(&mut self, mut w: W, target: &Target<'_>) -> Result<(), E> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
fn timestamp(&mut self, w: &mut W, timestamp: Timestamp) -> Result<(), E> {
|
fn timestamp<W: Write>(&mut self, mut w: W, timestamp: &Timestamp) -> Result<(), E> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
fn bold_beg(&mut self, w: &mut W) -> Result<(), E> {
|
fn verbatim<W: Write>(&mut self, mut w: W, cont: &str) -> Result<(), E> {
|
||||||
Ok(write!(w, "<b>")?)
|
write!(&mut w, "<code>")?;
|
||||||
}
|
self.text(&mut w, cont)?;
|
||||||
fn bold_end(&mut self, w: &mut W) -> Result<(), E> {
|
write!(&mut w, "</code>")?;
|
||||||
Ok(write!(w, "</b>")?)
|
|
||||||
}
|
|
||||||
fn italic_beg(&mut self, w: &mut W) -> Result<(), E> {
|
|
||||||
Ok(write!(w, "<i>")?)
|
|
||||||
}
|
|
||||||
fn italic_end(&mut self, w: &mut W) -> Result<(), E> {
|
|
||||||
Ok(write!(w, "</i>")?)
|
|
||||||
}
|
|
||||||
fn strike_beg(&mut self, w: &mut W) -> Result<(), E> {
|
|
||||||
Ok(write!(w, "<s>")?)
|
|
||||||
}
|
|
||||||
fn strike_end(&mut self, w: &mut W) -> Result<(), E> {
|
|
||||||
Ok(write!(w, "</s>")?)
|
|
||||||
}
|
|
||||||
fn underline_beg(&mut self, w: &mut W) -> Result<(), E> {
|
|
||||||
Ok(write!(w, "<u>")?)
|
|
||||||
}
|
|
||||||
fn underline_end(&mut self, w: &mut W) -> Result<(), E> {
|
|
||||||
Ok(write!(w, "</u>")?)
|
|
||||||
}
|
|
||||||
fn verbatim(&mut self, w: &mut W, cont: &str) -> Result<(), E> {
|
|
||||||
write!(w, "<code>")?;
|
|
||||||
self.text(w, cont)?;
|
|
||||||
write!(w, "</code>")?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
fn code(&mut self, w: &mut W, cont: &str) -> Result<(), E> {
|
fn code<W: Write>(&mut self, mut w: W, cont: &str) -> Result<(), E> {
|
||||||
write!(w, "<code>")?;
|
write!(&mut w, "<code>")?;
|
||||||
self.text(w, cont)?;
|
self.text(&mut w, cont)?;
|
||||||
write!(w, "</code>")?;
|
write!(&mut w, "</code>")?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
fn text(&mut self, w: &mut W, cont: &str) -> Result<(), E> {
|
fn text<W: Write>(&mut self, mut w: W, cont: &str) -> Result<(), E> {
|
||||||
self.escape(w, cont)?;
|
self.escape(w, cont)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
fn planning(&mut self, w: &mut W, planning: Planning) -> Result<(), E> {
|
fn planning<W: Write>(&mut self, mut w: W, planning: &Planning) -> Result<(), E> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
fn clock<W: Write>(&mut self, mut w: W, clock: &Clock<'_>) -> Result<(), E> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
fn fn_def<W: Write>(&mut self, mut w: W, fn_def: &FnDef<'_>) -> Result<(), E> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct DefaultHtmlHandler;
|
pub struct DefaultHtmlHandler;
|
||||||
|
|
||||||
impl<W: Write> HtmlHandler<W, Error> for DefaultHtmlHandler {}
|
impl HtmlHandler<Error> for DefaultHtmlHandler {}
|
||||||
|
|
||||||
pub struct HtmlRender<'a, W: Write, E: From<Error>, H: HtmlHandler<W, E>> {
|
|
||||||
pub parser: Parser<'a>,
|
|
||||||
pub writer: &'a mut W,
|
|
||||||
handler: H,
|
|
||||||
error_type: PhantomData<E>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, W: Write> HtmlRender<'a, W, Error, DefaultHtmlHandler> {
|
|
||||||
pub fn default(writer: &'a mut W, text: &'a str) -> Self {
|
|
||||||
HtmlRender::new(DefaultHtmlHandler, writer, text)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, W: Write, E: From<Error>, H: HtmlHandler<W, E>> HtmlRender<'a, W, E, H> {
|
|
||||||
pub fn new(handler: H, writer: &'a mut W, text: &'a str) -> Self {
|
|
||||||
HtmlRender {
|
|
||||||
parser: Parser::new(text),
|
|
||||||
handler,
|
|
||||||
writer,
|
|
||||||
error_type: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn render(&mut self) -> Result<(), E> {
|
|
||||||
for event in &mut self.parser {
|
|
||||||
self.handler.event(self.writer, event)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,71 +1,3 @@
|
||||||
#[macro_use]
|
|
||||||
macro_rules! handle_event {
|
|
||||||
($event:expr, $handler:expr, $writer:expr) => {
|
|
||||||
use crate::parser::Event::*;
|
|
||||||
|
|
||||||
match $event {
|
|
||||||
HeadlineBeg(hdl) => $handler.headline_beg($writer, hdl)?,
|
|
||||||
HeadlineEnd => $handler.headline_end($writer)?,
|
|
||||||
SectionBeg => $handler.section_beg($writer)?,
|
|
||||||
SectionEnd => $handler.section_end($writer)?,
|
|
||||||
ParagraphBeg => $handler.paragraph_beg($writer)?,
|
|
||||||
ParagraphEnd => $handler.paragraph_end($writer)?,
|
|
||||||
DrawerBeg(n) => $handler.drawer_beg($writer, n)?,
|
|
||||||
DrawerEnd => $handler.drawer_end($writer)?,
|
|
||||||
CtrBlockBeg => $handler.ctr_block_beg($writer)?,
|
|
||||||
CtrBlockEnd => $handler.ctr_block_end($writer)?,
|
|
||||||
QteBlockBeg => $handler.qte_block_beg($writer)?,
|
|
||||||
QteBlockEnd => $handler.qte_block_end($writer)?,
|
|
||||||
SplBlockBeg { name, args } => $handler.spl_block_beg($writer, name, args)?,
|
|
||||||
SplBlockEnd => $handler.spl_block_end($writer)?,
|
|
||||||
CommentBlock { cont, args } => $handler.comment_block($writer, cont, args)?,
|
|
||||||
ExampleBlock { cont, args } => $handler.example_block($writer, cont, args)?,
|
|
||||||
ExportBlock { cont, args } => $handler.export_block($writer, cont, args)?,
|
|
||||||
SrcBlock { cont, args } => $handler.src_block($writer, cont, args)?,
|
|
||||||
VerseBlock { cont, args } => $handler.verse_block($writer, cont, args)?,
|
|
||||||
DynBlockBeg { name, args } => $handler.dyn_block_beg($writer, name, args)?,
|
|
||||||
DynBlockEnd => $handler.dyn_block_end($writer)?,
|
|
||||||
ListBeg { indent, ordered } => $handler.list_beg($writer, indent, ordered)?,
|
|
||||||
ListEnd { indent, ordered } => $handler.list_end($writer, indent, ordered)?,
|
|
||||||
ListItemBeg { bullet } => $handler.list_beg_item($writer, bullet)?,
|
|
||||||
ListItemEnd => $handler.list_end_item($writer)?,
|
|
||||||
Call { value } => $handler.call($writer, value)?,
|
|
||||||
Planning(p) => $handler.planning($writer, p)?,
|
|
||||||
Clock(c) => $handler.clock($writer, c)?,
|
|
||||||
Timestamp(t) => $handler.timestamp($writer, t)?,
|
|
||||||
Comment(c) => $handler.comment($writer, c)?,
|
|
||||||
FixedWidth(f) => $handler.fixed_width($writer, f)?,
|
|
||||||
TableStart => $handler.table_start($writer)?,
|
|
||||||
TableEnd => $handler.table_end($writer)?,
|
|
||||||
TableCell => $handler.table_cell($writer)?,
|
|
||||||
LatexEnv => $handler.latex_env($writer)?,
|
|
||||||
FnDef { label, cont } => $handler.fn_def($writer, label, cont)?,
|
|
||||||
Keyword(keyword) => $handler.keyword($writer, keyword)?,
|
|
||||||
Rule => $handler.rule($writer)?,
|
|
||||||
Cookie(cookie) => $handler.cookie($writer, cookie)?,
|
|
||||||
FnRef(fn_ref) => $handler.fn_ref($writer, fn_ref)?,
|
|
||||||
InlineSrc(src) => $handler.inline_src($writer, src)?,
|
|
||||||
InlineCall(call) => $handler.inline_call($writer, call)?,
|
|
||||||
Link(link) => $handler.link($writer, link)?,
|
|
||||||
Macros(macros) => $handler.macros($writer, macros)?,
|
|
||||||
RadioTarget { target } => $handler.radio_target($writer, target)?,
|
|
||||||
Snippet(snippet) => $handler.snippet($writer, snippet)?,
|
|
||||||
Target { target } => $handler.target($writer, target)?,
|
|
||||||
BoldBeg => $handler.bold_beg($writer)?,
|
|
||||||
BoldEnd => $handler.bold_end($writer)?,
|
|
||||||
ItalicBeg => $handler.italic_beg($writer)?,
|
|
||||||
ItalicEnd => $handler.italic_end($writer)?,
|
|
||||||
StrikeBeg => $handler.strike_beg($writer)?,
|
|
||||||
StrikeEnd => $handler.strike_end($writer)?,
|
|
||||||
UnderlineBeg => $handler.underline_beg($writer)?,
|
|
||||||
UnderlineEnd => $handler.underline_end($writer)?,
|
|
||||||
Verbatim(cont) => $handler.verbatim($writer, cont)?,
|
|
||||||
Code(cont) => $handler.code($writer, cont)?,
|
|
||||||
Text(cont) => $handler.text($writer, cont)?,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
mod html;
|
mod html;
|
||||||
|
|
||||||
pub use html::*;
|
pub use html::*;
|
||||||
|
|
10
src/iter.rs
10
src/iter.rs
|
@ -41,7 +41,7 @@ pub enum Event<'a> {
|
||||||
Text(&'a str),
|
Text(&'a str),
|
||||||
Code(&'a str),
|
Code(&'a str),
|
||||||
Verbatim(&'a str),
|
Verbatim(&'a str),
|
||||||
BabelCall(&'a str),
|
BabelCall(&'a BabelCall<'a>),
|
||||||
}
|
}
|
||||||
|
|
||||||
enum State {
|
enum State {
|
||||||
|
@ -73,9 +73,9 @@ impl<'a> Iter<'a> {
|
||||||
self.state = State::Finished;
|
self.state = State::Finished;
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
Element::BabelCall { value, .. } => {
|
Element::BabelCall { call, .. } => {
|
||||||
self.state = State::Start;
|
self.state = State::Start;
|
||||||
Some(Event::BabelCall(value))
|
Some(Event::BabelCall(call))
|
||||||
}
|
}
|
||||||
Element::Verbatim { value, .. } => {
|
Element::Verbatim { value, .. } => {
|
||||||
self.state = State::Start;
|
self.state = State::Start;
|
||||||
|
@ -260,9 +260,9 @@ impl<'a> Iter<'a> {
|
||||||
self.state = State::Finished;
|
self.state = State::Finished;
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
Element::BabelCall { value, .. } => {
|
Element::BabelCall { call, .. } => {
|
||||||
self.state = State::End;
|
self.state = State::End;
|
||||||
Some(Event::BabelCall(value))
|
Some(Event::BabelCall(call))
|
||||||
}
|
}
|
||||||
Element::Verbatim { value, .. } => {
|
Element::Verbatim { value, .. } => {
|
||||||
self.state = State::End;
|
self.state = State::End;
|
||||||
|
|
|
@ -140,9 +140,11 @@
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
pub mod elements;
|
pub mod elements;
|
||||||
|
pub mod export;
|
||||||
pub mod iter;
|
pub mod iter;
|
||||||
pub mod org;
|
pub mod org;
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
mod serde;
|
mod serde;
|
||||||
|
|
||||||
|
pub use iter::{Container, Event};
|
||||||
pub use org::Org;
|
pub use org::Org;
|
||||||
|
|
60
src/org.rs
60
src/org.rs
|
@ -1,28 +1,32 @@
|
||||||
use crate::elements::*;
|
use crate::elements::*;
|
||||||
|
use crate::export::{DefaultHtmlHandler, HtmlHandler};
|
||||||
use crate::iter::Iter;
|
use crate::iter::Iter;
|
||||||
|
|
||||||
use indextree::{Arena, NodeId};
|
use indextree::{Arena, NodeId};
|
||||||
use jetscii::bytes;
|
use jetscii::bytes;
|
||||||
use memchr::{memchr_iter, memrchr_iter};
|
use memchr::{memchr_iter, memrchr_iter};
|
||||||
|
use std::io::{Error, Write};
|
||||||
|
|
||||||
pub struct Org<'a> {
|
pub struct Org<'a> {
|
||||||
pub(crate) arena: Arena<Element<'a>>,
|
pub(crate) arena: Arena<Element<'a>>,
|
||||||
pub(crate) document: NodeId,
|
pub(crate) document: NodeId,
|
||||||
root: Option<NodeId>,
|
root: NodeId,
|
||||||
text: &'a str,
|
text: &'a str,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Org<'a> {
|
impl<'a> Org<'a> {
|
||||||
pub fn new(text: &'a str) -> Self {
|
pub fn new(text: &'a str) -> Self {
|
||||||
let mut arena = Arena::new();
|
let mut arena = Arena::new();
|
||||||
|
let root = arena.new_node(Element::Root);
|
||||||
let document = arena.new_node(Element::Document {
|
let document = arena.new_node(Element::Document {
|
||||||
begin: 0,
|
begin: 0,
|
||||||
end: text.len(),
|
end: text.len(),
|
||||||
});
|
});
|
||||||
|
root.append(document, &mut arena).unwrap();
|
||||||
|
|
||||||
Org {
|
Org {
|
||||||
arena,
|
arena,
|
||||||
root: None,
|
root,
|
||||||
document,
|
document,
|
||||||
text,
|
text,
|
||||||
}
|
}
|
||||||
|
@ -32,14 +36,50 @@ impl<'a> Org<'a> {
|
||||||
self.arena[self.document].first_child().is_some()
|
self.arena[self.document].first_child().is_some()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn iter(&'a mut self) -> Iter<'a> {
|
pub fn iter(&'a self) -> Iter<'a> {
|
||||||
if let Some(root) = self.root {
|
Iter::new(&self.arena, self.root)
|
||||||
Iter::new(&self.arena, root)
|
|
||||||
} else {
|
|
||||||
let root = self.arena.new_node(Element::Root);
|
|
||||||
root.append(self.document, &mut self.arena).unwrap();
|
|
||||||
Iter::new(&self.arena, root)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn html<W, H, E>(&self, mut writer: W, mut handler: H) -> Result<(), E>
|
||||||
|
where
|
||||||
|
W: Write,
|
||||||
|
E: From<Error>,
|
||||||
|
H: HtmlHandler<E>,
|
||||||
|
{
|
||||||
|
use crate::iter::Event::*;
|
||||||
|
|
||||||
|
for event in self.iter() {
|
||||||
|
match event {
|
||||||
|
Start(e) => handler.start(&mut writer, e)?,
|
||||||
|
End(e) => handler.end(&mut writer, e)?,
|
||||||
|
Clock(e) => handler.clock(&mut writer, e)?,
|
||||||
|
Cookie(e) => handler.cookie(&mut writer, e)?,
|
||||||
|
Drawer(e) => handler.drawer(&mut writer, e)?,
|
||||||
|
FnDef(e) => handler.fn_def(&mut writer, e)?,
|
||||||
|
FnRef(e) => handler.fn_ref(&mut writer, e)?,
|
||||||
|
InlineCall(e) => handler.inline_call(&mut writer, e)?,
|
||||||
|
InlineSrc(e) => handler.inline_src(&mut writer, e)?,
|
||||||
|
Keyword(e) => handler.keyword(&mut writer, e)?,
|
||||||
|
Link(e) => handler.link(&mut writer, e)?,
|
||||||
|
Macros(e) => handler.macros(&mut writer, e)?,
|
||||||
|
Planning(e) => handler.planning(&mut writer, e)?,
|
||||||
|
RadioTarget(e) => handler.radio_target(&mut writer, e)?,
|
||||||
|
Snippet(e) => handler.snippet(&mut writer, e)?,
|
||||||
|
Target(e) => handler.target(&mut writer, e)?,
|
||||||
|
Timestamp(e) => handler.timestamp(&mut writer, e)?,
|
||||||
|
Text(e) => handler.text(&mut writer, e)?,
|
||||||
|
Code(e) => handler.code(&mut writer, e)?,
|
||||||
|
Verbatim(e) => handler.verbatim(&mut writer, e)?,
|
||||||
|
BabelCall(e) => handler.babel_call(&mut writer, e)?,
|
||||||
|
Rule => handler.rule(&mut writer)?,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn html_default<W: Write>(&self, wrtier: W) -> Result<(), Error> {
|
||||||
|
self.html(wrtier, DefaultHtmlHandler)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse(&mut self) {
|
pub fn parse(&mut self) {
|
||||||
|
@ -327,7 +367,7 @@ impl<'a> Org<'a> {
|
||||||
} else if let Some((key, option, value, end)) = Keyword::parse(tail) {
|
} else if let Some((key, option, value, end)) = Keyword::parse(tail) {
|
||||||
if key.eq_ignore_ascii_case("CALL") {
|
if key.eq_ignore_ascii_case("CALL") {
|
||||||
let call = Element::BabelCall {
|
let call = Element::BabelCall {
|
||||||
value,
|
call: BabelCall { key, value },
|
||||||
begin,
|
begin,
|
||||||
end: begin + line_begin + end,
|
end: begin + line_begin + end,
|
||||||
};
|
};
|
||||||
|
|
|
@ -201,7 +201,7 @@ impl Serialize for ElementNode<'_> {
|
||||||
state.serialize_field("end", end)?;
|
state.serialize_field("end", end)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Element::BabelCall { value, begin, end } => {
|
Element::BabelCall { call, begin, end } => {
|
||||||
state = serializer.serialize_struct("Element::BabelCall", 2)?;
|
state = serializer.serialize_struct("Element::BabelCall", 2)?;
|
||||||
state.serialize_field("type", "babel_call")?;
|
state.serialize_field("type", "babel_call")?;
|
||||||
if cfg!(feature = "extra-serde-info") {
|
if cfg!(feature = "extra-serde-info") {
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
extern crate orgize;
|
extern crate orgize;
|
||||||
|
|
||||||
use orgize::export::HtmlRender;
|
use orgize::Org;
|
||||||
use std::io::Cursor;
|
|
||||||
|
|
||||||
macro_rules! html_test {
|
macro_rules! html_test {
|
||||||
($name:ident, $content:expr, $expected:expr) => {
|
($name:ident, $content:expr, $expected:expr) => {
|
||||||
#[test]
|
#[test]
|
||||||
fn $name() {
|
fn $name() {
|
||||||
let mut cursor = Cursor::new(Vec::new());
|
let mut writer = Vec::new();
|
||||||
let mut render = HtmlRender::default(&mut cursor, $content);
|
let mut org = Org::new($content);
|
||||||
render.render().expect("render error");
|
org.parse();
|
||||||
let s = String::from_utf8(cursor.into_inner()).expect("invalid utf-8");
|
org.html_default(&mut writer).unwrap();
|
||||||
assert_eq!(s, $expected);
|
let string = String::from_utf8(writer).unwrap();
|
||||||
|
assert_eq!(string, $expected);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@ macro_rules! html_test {
|
||||||
html_test!(
|
html_test!(
|
||||||
emphasis,
|
emphasis,
|
||||||
"*bold*, /italic/,_underlined_, =verbatim= and ~code~",
|
"*bold*, /italic/,_underlined_, =verbatim= and ~code~",
|
||||||
"<section><p><b>bold</b>, <i>italic</i>,<u>underlined</u>, <code>verbatim</code> and <code>code</code></p></section>"
|
"<main><section><p><b>bold</b>, <i>italic</i>,<u>underlined</u>, <code>verbatim</code> and <code>code</code></p></section></main>"
|
||||||
);
|
);
|
||||||
|
|
||||||
html_test!(
|
html_test!(
|
||||||
|
@ -32,14 +32,14 @@ _Section 2_
|
||||||
/Section 3/
|
/Section 3/
|
||||||
* Title 4
|
* Title 4
|
||||||
=Section 4="#,
|
=Section 4="#,
|
||||||
"<h1>Title 1</h1>\
|
"<main><h1>Title 1</h1>\
|
||||||
<section><p><b>Section 1</b></p></section>\
|
<section><p><b>Section 1</b></p></section>\
|
||||||
<h2>Title 2</h2>\
|
<h2>Title 2</h2>\
|
||||||
<section><p><u>Section 2</u></p></section>\
|
<section><p><u>Section 2</u></p></section>\
|
||||||
<h1>Title 3</h1>\
|
<h1>Title 3</h1>\
|
||||||
<section><p><i>Section 3</i></p></section>\
|
<section><p><i>Section 3</i></p></section>\
|
||||||
<h1>Title 4</h1>\
|
<h1>Title 4</h1>\
|
||||||
<section><p><code>Section 4</code></p></section>"
|
<section><p><code>Section 4</code></p></section></main>"
|
||||||
);
|
);
|
||||||
|
|
||||||
html_test!(
|
html_test!(
|
||||||
|
@ -53,15 +53,15 @@ html_test!(
|
||||||
- 4
|
- 4
|
||||||
|
|
||||||
+ 5"#,
|
+ 5"#,
|
||||||
"<section><ul>\
|
"<main><section><ul>\
|
||||||
<li><p>1</p></li>\
|
<li><p>1</p></li>\
|
||||||
<li><p>2</p><ul><li><p>3</p></li><li><p>4</p></li></ul></li>\
|
<li><p>2</p><ul><li><p>3</p></li><li><p>4</p></li></ul></li>\
|
||||||
<li><p>5</p></li>\
|
<li><p>5</p></li>\
|
||||||
</ul></section>"
|
</ul></section></main>"
|
||||||
);
|
);
|
||||||
|
|
||||||
html_test!(
|
html_test!(
|
||||||
snippet,
|
snippet,
|
||||||
"@@html:<del>@@delete this@@html:</del>@@",
|
"@@html:<del>@@delete this@@html:</del>@@",
|
||||||
"<section><p><del>delete this</del></p></section>"
|
"<main><section><p><del>delete this</del></p></section></main>"
|
||||||
);
|
);
|
||||||
|
|
Loading…
Reference in a new issue