PR #29 Allow rendering borrowed events
Merge branch 'render_ref' closes #29
This commit is contained in:
		
				commit
				
					
						8893281310
					
				
			
		
					 8 changed files with 605 additions and 464 deletions
				
			
		| 
						 | 
					@ -51,7 +51,9 @@ fn gen_html(c: &mut criterion::Criterion) {
 | 
				
			||||||
                    || jotdown::Parser::new(input).collect::<Vec<_>>(),
 | 
					                    || jotdown::Parser::new(input).collect::<Vec<_>>(),
 | 
				
			||||||
                    |p| {
 | 
					                    |p| {
 | 
				
			||||||
                        let mut s = String::new();
 | 
					                        let mut s = String::new();
 | 
				
			||||||
                        jotdown::html::Renderer.push(p.into_iter(), &mut s).unwrap();
 | 
					                        jotdown::html::Renderer::default()
 | 
				
			||||||
 | 
					                            .push(p.into_iter(), &mut s)
 | 
				
			||||||
 | 
					                            .unwrap();
 | 
				
			||||||
                        s
 | 
					                        s
 | 
				
			||||||
                    },
 | 
					                    },
 | 
				
			||||||
                    criterion::BatchSize::SmallInput,
 | 
					                    criterion::BatchSize::SmallInput,
 | 
				
			||||||
| 
						 | 
					@ -62,6 +64,60 @@ fn gen_html(c: &mut criterion::Criterion) {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
criterion_group!(html, gen_html);
 | 
					criterion_group!(html, gen_html);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn gen_html_borrow(c: &mut criterion::Criterion) {
 | 
				
			||||||
 | 
					    let mut group = c.benchmark_group("html_borrow");
 | 
				
			||||||
 | 
					    for (name, input) in bench_input::INPUTS {
 | 
				
			||||||
 | 
					        group.throughput(criterion::Throughput::Elements(
 | 
				
			||||||
 | 
					            jotdown::Parser::new(input).count() as u64,
 | 
				
			||||||
 | 
					        ));
 | 
				
			||||||
 | 
					        group.bench_with_input(
 | 
				
			||||||
 | 
					            criterion::BenchmarkId::from_parameter(name),
 | 
				
			||||||
 | 
					            input,
 | 
				
			||||||
 | 
					            |b, &input| {
 | 
				
			||||||
 | 
					                b.iter_batched(
 | 
				
			||||||
 | 
					                    || jotdown::Parser::new(input).collect::<Vec<_>>(),
 | 
				
			||||||
 | 
					                    |p| {
 | 
				
			||||||
 | 
					                        let mut s = String::new();
 | 
				
			||||||
 | 
					                        jotdown::html::Renderer::default()
 | 
				
			||||||
 | 
					                            .push_borrowed(p.as_slice().iter(), &mut s)
 | 
				
			||||||
 | 
					                            .unwrap();
 | 
				
			||||||
 | 
					                        s
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                    criterion::BatchSize::SmallInput,
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					criterion_group!(html_borrow, gen_html_borrow);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn gen_html_clone(c: &mut criterion::Criterion) {
 | 
				
			||||||
 | 
					    let mut group = c.benchmark_group("html_clone");
 | 
				
			||||||
 | 
					    for (name, input) in bench_input::INPUTS {
 | 
				
			||||||
 | 
					        group.throughput(criterion::Throughput::Elements(
 | 
				
			||||||
 | 
					            jotdown::Parser::new(input).count() as u64,
 | 
				
			||||||
 | 
					        ));
 | 
				
			||||||
 | 
					        group.bench_with_input(
 | 
				
			||||||
 | 
					            criterion::BenchmarkId::from_parameter(name),
 | 
				
			||||||
 | 
					            input,
 | 
				
			||||||
 | 
					            |b, &input| {
 | 
				
			||||||
 | 
					                b.iter_batched(
 | 
				
			||||||
 | 
					                    || jotdown::Parser::new(input).collect::<Vec<_>>(),
 | 
				
			||||||
 | 
					                    |p| {
 | 
				
			||||||
 | 
					                        let mut s = String::new();
 | 
				
			||||||
 | 
					                        jotdown::html::Renderer::default()
 | 
				
			||||||
 | 
					                            .push(p.iter().cloned(), &mut s)
 | 
				
			||||||
 | 
					                            .unwrap();
 | 
				
			||||||
 | 
					                        s
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                    criterion::BatchSize::SmallInput,
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					criterion_group!(html_clone, gen_html_clone);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn gen_full(c: &mut criterion::Criterion) {
 | 
					fn gen_full(c: &mut criterion::Criterion) {
 | 
				
			||||||
    let mut group = c.benchmark_group("full");
 | 
					    let mut group = c.benchmark_group("full");
 | 
				
			||||||
    for (name, input) in bench_input::INPUTS {
 | 
					    for (name, input) in bench_input::INPUTS {
 | 
				
			||||||
| 
						 | 
					@ -72,7 +128,7 @@ fn gen_full(c: &mut criterion::Criterion) {
 | 
				
			||||||
            |b, &input| {
 | 
					            |b, &input| {
 | 
				
			||||||
                b.iter_with_large_drop(|| {
 | 
					                b.iter_with_large_drop(|| {
 | 
				
			||||||
                    let mut s = String::new();
 | 
					                    let mut s = String::new();
 | 
				
			||||||
                    jotdown::html::Renderer
 | 
					                    jotdown::html::Renderer::default()
 | 
				
			||||||
                        .push(jotdown::Parser::new(input), &mut s)
 | 
					                        .push(jotdown::Parser::new(input), &mut s)
 | 
				
			||||||
                        .unwrap();
 | 
					                        .unwrap();
 | 
				
			||||||
                    s
 | 
					                    s
 | 
				
			||||||
| 
						 | 
					@ -83,4 +139,4 @@ fn gen_full(c: &mut criterion::Criterion) {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
criterion_group!(full, gen_full);
 | 
					criterion_group!(full, gen_full);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
criterion_main!(block, inline, html, full);
 | 
					criterion_main!(block, inline, html, html_borrow, html_clone, full);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -12,7 +12,7 @@ fn block_inline() -> Option<jotdown::Event<'static>> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn full() -> String {
 | 
					fn full() -> String {
 | 
				
			||||||
    let mut s = String::new();
 | 
					    let mut s = String::new();
 | 
				
			||||||
    jotdown::html::Renderer
 | 
					    jotdown::html::Renderer::default()
 | 
				
			||||||
        .push(jotdown::Parser::new(bench_input::ALL), &mut s)
 | 
					        .push(jotdown::Parser::new(bench_input::ALL), &mut s)
 | 
				
			||||||
        .unwrap();
 | 
					        .unwrap();
 | 
				
			||||||
    s
 | 
					    s
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -7,6 +7,8 @@ use jotdown::Render;
 | 
				
			||||||
pub fn jotdown_render(djot: &str) -> String {
 | 
					pub fn jotdown_render(djot: &str) -> String {
 | 
				
			||||||
    let events = jotdown::Parser::new(djot);
 | 
					    let events = jotdown::Parser::new(djot);
 | 
				
			||||||
    let mut html = String::new();
 | 
					    let mut html = String::new();
 | 
				
			||||||
    jotdown::html::Renderer.push(events, &mut html).unwrap();
 | 
					    jotdown::html::Renderer::default()
 | 
				
			||||||
 | 
					        .push(events, &mut html)
 | 
				
			||||||
 | 
					        .unwrap();
 | 
				
			||||||
    html
 | 
					    html
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										405
									
								
								src/html.rs
									
										
									
									
									
								
							
							
						
						
									
										405
									
								
								src/html.rs
									
										
									
									
									
								
							| 
						 | 
					@ -1,26 +1,4 @@
 | 
				
			||||||
//! An HTML renderer that takes an iterator of [`Event`]s and emits HTML.
 | 
					//! An HTML renderer that takes an iterator of [`Event`]s and emits HTML.
 | 
				
			||||||
//!
 | 
					 | 
				
			||||||
//! The HTML can be written to either a [`std::fmt::Write`] or a [`std::io::Write`] object.
 | 
					 | 
				
			||||||
//!
 | 
					 | 
				
			||||||
//! # Examples
 | 
					 | 
				
			||||||
//!
 | 
					 | 
				
			||||||
//! Push to a [`String`] (implements [`std::fmt::Write`]):
 | 
					 | 
				
			||||||
//!
 | 
					 | 
				
			||||||
//! ```
 | 
					 | 
				
			||||||
//! # use jotdown::Render;
 | 
					 | 
				
			||||||
//! # let events = std::iter::empty();
 | 
					 | 
				
			||||||
//! let mut html = String::new();
 | 
					 | 
				
			||||||
//! jotdown::html::Renderer.push(events, &mut html);
 | 
					 | 
				
			||||||
//! ```
 | 
					 | 
				
			||||||
//!
 | 
					 | 
				
			||||||
//! Write to standard output with buffering ([`std::io::Stdout`] implements [`std::io::Write`]):
 | 
					 | 
				
			||||||
//!
 | 
					 | 
				
			||||||
//! ```
 | 
					 | 
				
			||||||
//! # use jotdown::Render;
 | 
					 | 
				
			||||||
//! # let events = std::iter::empty();
 | 
					 | 
				
			||||||
//! let mut out = std::io::BufWriter::new(std::io::stdout());
 | 
					 | 
				
			||||||
//! jotdown::html::Renderer.write(events, &mut out).unwrap();
 | 
					 | 
				
			||||||
//! ```
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::Alignment;
 | 
					use crate::Alignment;
 | 
				
			||||||
use crate::Container;
 | 
					use crate::Container;
 | 
				
			||||||
| 
						 | 
					@ -31,91 +9,74 @@ use crate::OrderedListNumbering::*;
 | 
				
			||||||
use crate::Render;
 | 
					use crate::Render;
 | 
				
			||||||
use crate::SpanLinkType;
 | 
					use crate::SpanLinkType;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub struct Renderer;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl Render for Renderer {
 | 
					 | 
				
			||||||
    fn push<'s, I: Iterator<Item = Event<'s>>, W: std::fmt::Write>(
 | 
					 | 
				
			||||||
        &self,
 | 
					 | 
				
			||||||
        events: I,
 | 
					 | 
				
			||||||
        out: W,
 | 
					 | 
				
			||||||
    ) -> std::fmt::Result {
 | 
					 | 
				
			||||||
        Writer::new(events, out).write()
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
enum Raw {
 | 
					enum Raw {
 | 
				
			||||||
    None,
 | 
					    None,
 | 
				
			||||||
    Html,
 | 
					    Html,
 | 
				
			||||||
    Other,
 | 
					    Other,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct FilteredEvents<I> {
 | 
					pub struct Renderer {
 | 
				
			||||||
    events: I,
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl<'s, I: Iterator<Item = Event<'s>>> Iterator for FilteredEvents<I> {
 | 
					 | 
				
			||||||
    type Item = Event<'s>;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn next(&mut self) -> Option<Self::Item> {
 | 
					 | 
				
			||||||
        let mut ev = self.events.next();
 | 
					 | 
				
			||||||
        while matches!(ev, Some(Event::Blankline | Event::Escape)) {
 | 
					 | 
				
			||||||
            ev = self.events.next();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        ev
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct Writer<'s, I: Iterator<Item = Event<'s>>, W> {
 | 
					 | 
				
			||||||
    events: std::iter::Peekable<FilteredEvents<I>>,
 | 
					 | 
				
			||||||
    out: W,
 | 
					 | 
				
			||||||
    raw: Raw,
 | 
					    raw: Raw,
 | 
				
			||||||
    img_alt_text: usize,
 | 
					    img_alt_text: usize,
 | 
				
			||||||
    list_tightness: Vec<bool>,
 | 
					    list_tightness: Vec<bool>,
 | 
				
			||||||
    encountered_footnote: bool,
 | 
					    encountered_footnote: bool,
 | 
				
			||||||
    footnote_number: Option<std::num::NonZeroUsize>,
 | 
					    footnote_number: Option<std::num::NonZeroUsize>,
 | 
				
			||||||
    footnote_backlink_written: bool,
 | 
					 | 
				
			||||||
    first_line: bool,
 | 
					    first_line: bool,
 | 
				
			||||||
 | 
					    close_para: bool,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl<'s, I: Iterator<Item = Event<'s>>, W: std::fmt::Write> Writer<'s, I, W> {
 | 
					impl Default for Renderer {
 | 
				
			||||||
    fn new(events: I, out: W) -> Self {
 | 
					    fn default() -> Self {
 | 
				
			||||||
        Self {
 | 
					        Self {
 | 
				
			||||||
            events: FilteredEvents { events }.peekable(),
 | 
					 | 
				
			||||||
            out,
 | 
					 | 
				
			||||||
            raw: Raw::None,
 | 
					            raw: Raw::None,
 | 
				
			||||||
            img_alt_text: 0,
 | 
					            img_alt_text: 0,
 | 
				
			||||||
            list_tightness: Vec::new(),
 | 
					            list_tightness: Vec::new(),
 | 
				
			||||||
            encountered_footnote: false,
 | 
					            encountered_footnote: false,
 | 
				
			||||||
            footnote_number: None,
 | 
					            footnote_number: None,
 | 
				
			||||||
            footnote_backlink_written: false,
 | 
					 | 
				
			||||||
            first_line: true,
 | 
					            first_line: true,
 | 
				
			||||||
 | 
					            close_para: false,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Render for Renderer {
 | 
				
			||||||
 | 
					    fn render_event<'s, W>(&mut self, e: &Event<'s>, mut out: W) -> std::fmt::Result
 | 
				
			||||||
 | 
					    where
 | 
				
			||||||
 | 
					        W: std::fmt::Write,
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if matches!(&e, Event::Blankline | Event::Escape) {
 | 
				
			||||||
 | 
					            return Ok(());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let close_para = self.close_para;
 | 
				
			||||||
 | 
					        if close_para {
 | 
				
			||||||
 | 
					            self.close_para = false;
 | 
				
			||||||
 | 
					            if !matches!(&e, Event::End(Container::Footnote { .. })) {
 | 
				
			||||||
 | 
					                // no need to add href before para close
 | 
				
			||||||
 | 
					                out.write_str("</p>")?;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn write(&mut self) -> std::fmt::Result {
 | 
					 | 
				
			||||||
        while let Some(e) = self.events.next() {
 | 
					 | 
				
			||||||
        match e {
 | 
					        match e {
 | 
				
			||||||
            Event::Start(c, attrs) => {
 | 
					            Event::Start(c, attrs) => {
 | 
				
			||||||
                if c.is_block() && !self.first_line {
 | 
					                if c.is_block() && !self.first_line {
 | 
				
			||||||
                        self.out.write_char('\n')?;
 | 
					                    out.write_char('\n')?;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                if self.img_alt_text > 0 && !matches!(c, Container::Image(..)) {
 | 
					                if self.img_alt_text > 0 && !matches!(c, Container::Image(..)) {
 | 
				
			||||||
                        continue;
 | 
					                    return Ok(());
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                match &c {
 | 
					                match &c {
 | 
				
			||||||
                        Container::Blockquote => self.out.write_str("<blockquote")?,
 | 
					                    Container::Blockquote => out.write_str("<blockquote")?,
 | 
				
			||||||
                    Container::List { kind, tight } => {
 | 
					                    Container::List { kind, tight } => {
 | 
				
			||||||
                        self.list_tightness.push(*tight);
 | 
					                        self.list_tightness.push(*tight);
 | 
				
			||||||
                        match kind {
 | 
					                        match kind {
 | 
				
			||||||
                                ListKind::Unordered | ListKind::Task => {
 | 
					                            ListKind::Unordered | ListKind::Task => out.write_str("<ul")?,
 | 
				
			||||||
                                    self.out.write_str("<ul")?
 | 
					 | 
				
			||||||
                                }
 | 
					 | 
				
			||||||
                            ListKind::Ordered {
 | 
					                            ListKind::Ordered {
 | 
				
			||||||
                                numbering, start, ..
 | 
					                                numbering, start, ..
 | 
				
			||||||
                            } => {
 | 
					                            } => {
 | 
				
			||||||
                                    self.out.write_str("<ol")?;
 | 
					                                out.write_str("<ol")?;
 | 
				
			||||||
                                if *start > 1 {
 | 
					                                if *start > 1 {
 | 
				
			||||||
                                        write!(self.out, r#" start="{}""#, start)?;
 | 
					                                    write!(out, r#" start="{}""#, start)?;
 | 
				
			||||||
                                }
 | 
					                                }
 | 
				
			||||||
                                if let Some(ty) = match numbering {
 | 
					                                if let Some(ty) = match numbering {
 | 
				
			||||||
                                    Decimal => None,
 | 
					                                    Decimal => None,
 | 
				
			||||||
| 
						 | 
					@ -124,87 +85,85 @@ impl<'s, I: Iterator<Item = Event<'s>>, W: std::fmt::Write> Writer<'s, I, W> {
 | 
				
			||||||
                                    RomanLower => Some('i'),
 | 
					                                    RomanLower => Some('i'),
 | 
				
			||||||
                                    RomanUpper => Some('I'),
 | 
					                                    RomanUpper => Some('I'),
 | 
				
			||||||
                                } {
 | 
					                                } {
 | 
				
			||||||
                                        write!(self.out, r#" type="{}""#, ty)?;
 | 
					                                    write!(out, r#" type="{}""#, ty)?;
 | 
				
			||||||
                                }
 | 
					                                }
 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    Container::ListItem | Container::TaskListItem { .. } => {
 | 
					                    Container::ListItem | Container::TaskListItem { .. } => {
 | 
				
			||||||
                            self.out.write_str("<li")?;
 | 
					                        out.write_str("<li")?;
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                        Container::DescriptionList => self.out.write_str("<dl")?,
 | 
					                    Container::DescriptionList => out.write_str("<dl")?,
 | 
				
			||||||
                        Container::DescriptionDetails => self.out.write_str("<dd")?,
 | 
					                    Container::DescriptionDetails => out.write_str("<dd")?,
 | 
				
			||||||
                    Container::Footnote { number, .. } => {
 | 
					                    Container::Footnote { number, .. } => {
 | 
				
			||||||
                        assert!(self.footnote_number.is_none());
 | 
					                        assert!(self.footnote_number.is_none());
 | 
				
			||||||
                        self.footnote_number = Some((*number).try_into().unwrap());
 | 
					                        self.footnote_number = Some((*number).try_into().unwrap());
 | 
				
			||||||
                        if !self.encountered_footnote {
 | 
					                        if !self.encountered_footnote {
 | 
				
			||||||
                            self.encountered_footnote = true;
 | 
					                            self.encountered_footnote = true;
 | 
				
			||||||
                                self.out
 | 
					                            out.write_str("<section role=\"doc-endnotes\">\n<hr>\n<ol>\n")?;
 | 
				
			||||||
                                    .write_str("<section role=\"doc-endnotes\">\n<hr>\n<ol>\n")?;
 | 
					 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                            write!(self.out, "<li id=\"fn{}\">", number)?;
 | 
					                        write!(out, "<li id=\"fn{}\">", number)?;
 | 
				
			||||||
                            self.footnote_backlink_written = false;
 | 
					                        return Ok(());
 | 
				
			||||||
                            continue;
 | 
					 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                        Container::Table => self.out.write_str("<table")?,
 | 
					                    Container::Table => out.write_str("<table")?,
 | 
				
			||||||
                        Container::TableRow { .. } => self.out.write_str("<tr")?,
 | 
					                    Container::TableRow { .. } => out.write_str("<tr")?,
 | 
				
			||||||
                        Container::Section { .. } => self.out.write_str("<section")?,
 | 
					                    Container::Section { .. } => out.write_str("<section")?,
 | 
				
			||||||
                        Container::Div { .. } => self.out.write_str("<div")?,
 | 
					                    Container::Div { .. } => out.write_str("<div")?,
 | 
				
			||||||
                    Container::Paragraph => {
 | 
					                    Container::Paragraph => {
 | 
				
			||||||
                        if matches!(self.list_tightness.last(), Some(true)) {
 | 
					                        if matches!(self.list_tightness.last(), Some(true)) {
 | 
				
			||||||
                                continue;
 | 
					                            return Ok(());
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                            self.out.write_str("<p")?;
 | 
					                        out.write_str("<p")?;
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                        Container::Heading { level, .. } => write!(self.out, "<h{}", level)?,
 | 
					                    Container::Heading { level, .. } => write!(out, "<h{}", level)?,
 | 
				
			||||||
                        Container::TableCell { head: false, .. } => self.out.write_str("<td")?,
 | 
					                    Container::TableCell { head: false, .. } => out.write_str("<td")?,
 | 
				
			||||||
                        Container::TableCell { head: true, .. } => self.out.write_str("<th")?,
 | 
					                    Container::TableCell { head: true, .. } => out.write_str("<th")?,
 | 
				
			||||||
                        Container::Caption => self.out.write_str("<caption")?,
 | 
					                    Container::Caption => out.write_str("<caption")?,
 | 
				
			||||||
                        Container::DescriptionTerm => self.out.write_str("<dt")?,
 | 
					                    Container::DescriptionTerm => out.write_str("<dt")?,
 | 
				
			||||||
                        Container::CodeBlock { .. } => self.out.write_str("<pre")?,
 | 
					                    Container::CodeBlock { .. } => out.write_str("<pre")?,
 | 
				
			||||||
                        Container::Span | Container::Math { .. } => self.out.write_str("<span")?,
 | 
					                    Container::Span | Container::Math { .. } => out.write_str("<span")?,
 | 
				
			||||||
                    Container::Link(dst, ty) => {
 | 
					                    Container::Link(dst, ty) => {
 | 
				
			||||||
                        if matches!(ty, LinkType::Span(SpanLinkType::Unresolved)) {
 | 
					                        if matches!(ty, LinkType::Span(SpanLinkType::Unresolved)) {
 | 
				
			||||||
                                self.out.write_str("<a")?;
 | 
					                            out.write_str("<a")?;
 | 
				
			||||||
                        } else {
 | 
					                        } else {
 | 
				
			||||||
                                self.out.write_str(r#"<a href=""#)?;
 | 
					                            out.write_str(r#"<a href=""#)?;
 | 
				
			||||||
                            if matches!(ty, LinkType::Email) {
 | 
					                            if matches!(ty, LinkType::Email) {
 | 
				
			||||||
                                    self.out.write_str("mailto:")?;
 | 
					                                out.write_str("mailto:")?;
 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
                                self.write_attr(dst)?;
 | 
					                            write_attr(dst, &mut out)?;
 | 
				
			||||||
                                self.out.write_char('"')?;
 | 
					                            out.write_char('"')?;
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    Container::Image(..) => {
 | 
					                    Container::Image(..) => {
 | 
				
			||||||
                        self.img_alt_text += 1;
 | 
					                        self.img_alt_text += 1;
 | 
				
			||||||
                        if self.img_alt_text == 1 {
 | 
					                        if self.img_alt_text == 1 {
 | 
				
			||||||
                                self.out.write_str("<img")?;
 | 
					                            out.write_str("<img")?;
 | 
				
			||||||
                        } else {
 | 
					                        } else {
 | 
				
			||||||
                                continue;
 | 
					                            return Ok(());
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                        Container::Verbatim => self.out.write_str("<code")?,
 | 
					                    Container::Verbatim => out.write_str("<code")?,
 | 
				
			||||||
                    Container::RawBlock { format } | Container::RawInline { format } => {
 | 
					                    Container::RawBlock { format } | Container::RawInline { format } => {
 | 
				
			||||||
                        self.raw = if format == &"html" {
 | 
					                        self.raw = if format == &"html" {
 | 
				
			||||||
                            Raw::Html
 | 
					                            Raw::Html
 | 
				
			||||||
                        } else {
 | 
					                        } else {
 | 
				
			||||||
                            Raw::Other
 | 
					                            Raw::Other
 | 
				
			||||||
                        };
 | 
					                        };
 | 
				
			||||||
                            continue;
 | 
					                        return Ok(());
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                        Container::Subscript => self.out.write_str("<sub")?,
 | 
					                    Container::Subscript => out.write_str("<sub")?,
 | 
				
			||||||
                        Container::Superscript => self.out.write_str("<sup")?,
 | 
					                    Container::Superscript => out.write_str("<sup")?,
 | 
				
			||||||
                        Container::Insert => self.out.write_str("<ins")?,
 | 
					                    Container::Insert => out.write_str("<ins")?,
 | 
				
			||||||
                        Container::Delete => self.out.write_str("<del")?,
 | 
					                    Container::Delete => out.write_str("<del")?,
 | 
				
			||||||
                        Container::Strong => self.out.write_str("<strong")?,
 | 
					                    Container::Strong => out.write_str("<strong")?,
 | 
				
			||||||
                        Container::Emphasis => self.out.write_str("<em")?,
 | 
					                    Container::Emphasis => out.write_str("<em")?,
 | 
				
			||||||
                        Container::Mark => self.out.write_str("<mark")?,
 | 
					                    Container::Mark => out.write_str("<mark")?,
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                for (a, v) in attrs.iter().filter(|(a, _)| *a != "class") {
 | 
					                for (a, v) in attrs.iter().filter(|(a, _)| *a != "class") {
 | 
				
			||||||
                        write!(self.out, r#" {}=""#, a)?;
 | 
					                    write!(out, r#" {}=""#, a)?;
 | 
				
			||||||
                        v.parts().try_for_each(|part| self.write_attr(part))?;
 | 
					                    v.parts().try_for_each(|part| write_attr(part, &mut out))?;
 | 
				
			||||||
                        self.out.write_char('"')?;
 | 
					                    out.write_char('"')?;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if let Container::Heading {
 | 
					                if let Container::Heading {
 | 
				
			||||||
| 
						 | 
					@ -215,9 +174,9 @@ impl<'s, I: Iterator<Item = Event<'s>>, W: std::fmt::Write> Writer<'s, I, W> {
 | 
				
			||||||
                | Container::Section { id } = &c
 | 
					                | Container::Section { id } = &c
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    if !attrs.iter().any(|(a, _)| a == "id") {
 | 
					                    if !attrs.iter().any(|(a, _)| a == "id") {
 | 
				
			||||||
                            self.out.write_str(r#" id=""#)?;
 | 
					                        out.write_str(r#" id=""#)?;
 | 
				
			||||||
                            self.write_attr(id)?;
 | 
					                        write_attr(id, &mut out)?;
 | 
				
			||||||
                            self.out.write_char('"')?;
 | 
					                        out.write_char('"')?;
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -233,7 +192,7 @@ impl<'s, I: Iterator<Item = Event<'s>>, W: std::fmt::Write> Writer<'s, I, W> {
 | 
				
			||||||
                            | Container::TaskListItem { .. }
 | 
					                            | Container::TaskListItem { .. }
 | 
				
			||||||
                    )
 | 
					                    )
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                        self.out.write_str(r#" class=""#)?;
 | 
					                    out.write_str(r#" class=""#)?;
 | 
				
			||||||
                    let mut first_written = false;
 | 
					                    let mut first_written = false;
 | 
				
			||||||
                    if let Some(cls) = match c {
 | 
					                    if let Some(cls) = match c {
 | 
				
			||||||
                        Container::List {
 | 
					                        Container::List {
 | 
				
			||||||
| 
						 | 
					@ -247,7 +206,7 @@ impl<'s, I: Iterator<Item = Event<'s>>, W: std::fmt::Write> Writer<'s, I, W> {
 | 
				
			||||||
                        _ => None,
 | 
					                        _ => None,
 | 
				
			||||||
                    } {
 | 
					                    } {
 | 
				
			||||||
                        first_written = true;
 | 
					                        first_written = true;
 | 
				
			||||||
                            self.out.write_str(cls)?;
 | 
					                        out.write_str(cls)?;
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    for cls in attrs
 | 
					                    for cls in attrs
 | 
				
			||||||
                        .iter()
 | 
					                        .iter()
 | 
				
			||||||
| 
						 | 
					@ -255,19 +214,20 @@ impl<'s, I: Iterator<Item = Event<'s>>, W: std::fmt::Write> Writer<'s, I, W> {
 | 
				
			||||||
                        .map(|(_, cls)| cls)
 | 
					                        .map(|(_, cls)| cls)
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        if first_written {
 | 
					                        if first_written {
 | 
				
			||||||
                                self.out.write_char(' ')?;
 | 
					                            out.write_char(' ')?;
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                        first_written = true;
 | 
					                        first_written = true;
 | 
				
			||||||
                            cls.parts().try_for_each(|part| self.write_attr(part))?;
 | 
					                        cls.parts()
 | 
				
			||||||
 | 
					                            .try_for_each(|part| write_attr(part, &mut out))?;
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    // div class goes after classes from attrs
 | 
					                    // div class goes after classes from attrs
 | 
				
			||||||
                    if let Container::Div { class: Some(cls) } = c {
 | 
					                    if let Container::Div { class: Some(cls) } = c {
 | 
				
			||||||
                        if first_written {
 | 
					                        if first_written {
 | 
				
			||||||
                                self.out.write_char(' ')?;
 | 
					                            out.write_char(' ')?;
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                            self.out.write_str(cls)?;
 | 
					                        out.write_str(cls)?;
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                        self.out.write_char('"')?;
 | 
					                    out.write_char('"')?;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                match c {
 | 
					                match c {
 | 
				
			||||||
| 
						 | 
					@ -280,109 +240,101 @@ impl<'s, I: Iterator<Item = Event<'s>>, W: std::fmt::Write> Writer<'s, I, W> {
 | 
				
			||||||
                            Alignment::Center => "center",
 | 
					                            Alignment::Center => "center",
 | 
				
			||||||
                            Alignment::Right => "right",
 | 
					                            Alignment::Right => "right",
 | 
				
			||||||
                        };
 | 
					                        };
 | 
				
			||||||
                            write!(self.out, r#" style="text-align: {};">"#, a)?;
 | 
					                        write!(out, r#" style="text-align: {};">"#, a)?;
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    Container::CodeBlock { lang } => {
 | 
					                    Container::CodeBlock { lang } => {
 | 
				
			||||||
                        if let Some(l) = lang {
 | 
					                        if let Some(l) = lang {
 | 
				
			||||||
                                self.out.write_str(r#"><code class="language-"#)?;
 | 
					                            out.write_str(r#"><code class="language-"#)?;
 | 
				
			||||||
                                self.write_attr(l)?;
 | 
					                            write_attr(l, &mut out)?;
 | 
				
			||||||
                                self.out.write_str(r#"">"#)?;
 | 
					                            out.write_str(r#"">"#)?;
 | 
				
			||||||
                        } else {
 | 
					                        } else {
 | 
				
			||||||
                                self.out.write_str("><code>")?;
 | 
					                            out.write_str("><code>")?;
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    Container::Image(..) => {
 | 
					                    Container::Image(..) => {
 | 
				
			||||||
                        if self.img_alt_text == 1 {
 | 
					                        if self.img_alt_text == 1 {
 | 
				
			||||||
                                self.out.write_str(r#" alt=""#)?;
 | 
					                            out.write_str(r#" alt=""#)?;
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    Container::Math { display } => {
 | 
					                    Container::Math { display } => {
 | 
				
			||||||
                            self.out
 | 
					                        out.write_str(if *display { r#">\["# } else { r#">\("# })?;
 | 
				
			||||||
                                .write_str(if display { r#">\["# } else { r#">\("# })?;
 | 
					 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                        _ => self.out.write_char('>')?,
 | 
					                    _ => out.write_char('>')?,
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            Event::End(c) => {
 | 
					            Event::End(c) => {
 | 
				
			||||||
                if c.is_block_container() && !matches!(c, Container::Footnote { .. }) {
 | 
					                if c.is_block_container() && !matches!(c, Container::Footnote { .. }) {
 | 
				
			||||||
                        self.out.write_char('\n')?;
 | 
					                    out.write_char('\n')?;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                if self.img_alt_text > 0 && !matches!(c, Container::Image(..)) {
 | 
					                if self.img_alt_text > 0 && !matches!(c, Container::Image(..)) {
 | 
				
			||||||
                        continue;
 | 
					                    return Ok(());
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                match c {
 | 
					                match c {
 | 
				
			||||||
                        Container::Blockquote => self.out.write_str("</blockquote>")?,
 | 
					                    Container::Blockquote => out.write_str("</blockquote>")?,
 | 
				
			||||||
                    Container::List {
 | 
					                    Container::List {
 | 
				
			||||||
                        kind: ListKind::Unordered | ListKind::Task,
 | 
					                        kind: ListKind::Unordered | ListKind::Task,
 | 
				
			||||||
                        ..
 | 
					                        ..
 | 
				
			||||||
                    } => {
 | 
					                    } => {
 | 
				
			||||||
                        self.list_tightness.pop();
 | 
					                        self.list_tightness.pop();
 | 
				
			||||||
                            self.out.write_str("</ul>")?;
 | 
					                        out.write_str("</ul>")?;
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    Container::List {
 | 
					                    Container::List {
 | 
				
			||||||
                        kind: ListKind::Ordered { .. },
 | 
					                        kind: ListKind::Ordered { .. },
 | 
				
			||||||
                        ..
 | 
					                        ..
 | 
				
			||||||
                        } => self.out.write_str("</ol>")?,
 | 
					                    } => out.write_str("</ol>")?,
 | 
				
			||||||
                    Container::ListItem | Container::TaskListItem { .. } => {
 | 
					                    Container::ListItem | Container::TaskListItem { .. } => {
 | 
				
			||||||
                            self.out.write_str("</li>")?;
 | 
					                        out.write_str("</li>")?;
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                        Container::DescriptionList => self.out.write_str("</dl>")?,
 | 
					                    Container::DescriptionList => out.write_str("</dl>")?,
 | 
				
			||||||
                        Container::DescriptionDetails => self.out.write_str("</dd>")?,
 | 
					                    Container::DescriptionDetails => out.write_str("</dd>")?,
 | 
				
			||||||
                    Container::Footnote { number, .. } => {
 | 
					                    Container::Footnote { number, .. } => {
 | 
				
			||||||
                            if !self.footnote_backlink_written {
 | 
					                        if !close_para {
 | 
				
			||||||
 | 
					                            // create a new paragraph
 | 
				
			||||||
 | 
					                            out.write_str("\n<p>")?;
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
                        write!(
 | 
					                        write!(
 | 
				
			||||||
                                    self.out,
 | 
					                            out,
 | 
				
			||||||
                                    "\n<p><a href=\"#fnref{}\" role=\"doc-backlink\">↩︎︎</a></p>",
 | 
					                            r##"<a href="#fnref{}" role="doc-backlink">↩︎︎</a></p>"##,
 | 
				
			||||||
                            number,
 | 
					                            number,
 | 
				
			||||||
                        )?;
 | 
					                        )?;
 | 
				
			||||||
                            }
 | 
					                        out.write_str("\n</li>")?;
 | 
				
			||||||
                            self.out.write_str("\n</li>")?;
 | 
					 | 
				
			||||||
                        self.footnote_number = None;
 | 
					                        self.footnote_number = None;
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                        Container::Table => self.out.write_str("</table>")?,
 | 
					                    Container::Table => out.write_str("</table>")?,
 | 
				
			||||||
                        Container::TableRow { .. } => self.out.write_str("</tr>")?,
 | 
					                    Container::TableRow { .. } => out.write_str("</tr>")?,
 | 
				
			||||||
                        Container::Section { .. } => self.out.write_str("</section>")?,
 | 
					                    Container::Section { .. } => out.write_str("</section>")?,
 | 
				
			||||||
                        Container::Div { .. } => self.out.write_str("</div>")?,
 | 
					                    Container::Div { .. } => out.write_str("</div>")?,
 | 
				
			||||||
                    Container::Paragraph => {
 | 
					                    Container::Paragraph => {
 | 
				
			||||||
                        if matches!(self.list_tightness.last(), Some(true)) {
 | 
					                        if matches!(self.list_tightness.last(), Some(true)) {
 | 
				
			||||||
                                continue;
 | 
					                            return Ok(());
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                            if let Some(num) = self.footnote_number {
 | 
					                        if self.footnote_number.is_none() {
 | 
				
			||||||
                                if matches!(
 | 
					                            out.write_str("</p>")?;
 | 
				
			||||||
                                    self.events.peek(),
 | 
					                        } else {
 | 
				
			||||||
                                    Some(Event::End(Container::Footnote { .. }))
 | 
					                            self.close_para = true;
 | 
				
			||||||
                                ) {
 | 
					 | 
				
			||||||
                                    write!(
 | 
					 | 
				
			||||||
                                        self.out,
 | 
					 | 
				
			||||||
                                        r##"<a href="#fnref{}" role="doc-backlink">↩︎︎</a>"##,
 | 
					 | 
				
			||||||
                                        num
 | 
					 | 
				
			||||||
                                    )?;
 | 
					 | 
				
			||||||
                                    self.footnote_backlink_written = true;
 | 
					 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                            self.out.write_str("</p>")?;
 | 
					                    Container::Heading { level, .. } => write!(out, "</h{}>", level)?,
 | 
				
			||||||
                        }
 | 
					                    Container::TableCell { head: false, .. } => out.write_str("</td>")?,
 | 
				
			||||||
                        Container::Heading { level, .. } => write!(self.out, "</h{}>", level)?,
 | 
					                    Container::TableCell { head: true, .. } => out.write_str("</th>")?,
 | 
				
			||||||
                        Container::TableCell { head: false, .. } => self.out.write_str("</td>")?,
 | 
					                    Container::Caption => out.write_str("</caption>")?,
 | 
				
			||||||
                        Container::TableCell { head: true, .. } => self.out.write_str("</th>")?,
 | 
					                    Container::DescriptionTerm => out.write_str("</dt>")?,
 | 
				
			||||||
                        Container::Caption => self.out.write_str("</caption>")?,
 | 
					                    Container::CodeBlock { .. } => out.write_str("</code></pre>")?,
 | 
				
			||||||
                        Container::DescriptionTerm => self.out.write_str("</dt>")?,
 | 
					                    Container::Span => out.write_str("</span>")?,
 | 
				
			||||||
                        Container::CodeBlock { .. } => self.out.write_str("</code></pre>")?,
 | 
					                    Container::Link(..) => out.write_str("</a>")?,
 | 
				
			||||||
                        Container::Span => self.out.write_str("</span>")?,
 | 
					 | 
				
			||||||
                        Container::Link(..) => self.out.write_str("</a>")?,
 | 
					 | 
				
			||||||
                    Container::Image(src, ..) => {
 | 
					                    Container::Image(src, ..) => {
 | 
				
			||||||
                        if self.img_alt_text == 1 {
 | 
					                        if self.img_alt_text == 1 {
 | 
				
			||||||
                            if !src.is_empty() {
 | 
					                            if !src.is_empty() {
 | 
				
			||||||
                                    self.out.write_str(r#"" src=""#)?;
 | 
					                                out.write_str(r#"" src=""#)?;
 | 
				
			||||||
                                    self.write_attr(&src)?;
 | 
					                                write_attr(src, &mut out)?;
 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
                                self.out.write_str(r#"">"#)?;
 | 
					                            out.write_str(r#"">"#)?;
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                        self.img_alt_text -= 1;
 | 
					                        self.img_alt_text -= 1;
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                        Container::Verbatim => self.out.write_str("</code>")?,
 | 
					                    Container::Verbatim => out.write_str("</code>")?,
 | 
				
			||||||
                    Container::Math { display } => {
 | 
					                    Container::Math { display } => {
 | 
				
			||||||
                            self.out.write_str(if display {
 | 
					                        out.write_str(if *display {
 | 
				
			||||||
                            r#"\]</span>"#
 | 
					                            r#"\]</span>"#
 | 
				
			||||||
                        } else {
 | 
					                        } else {
 | 
				
			||||||
                            r#"\)</span>"#
 | 
					                            r#"\)</span>"#
 | 
				
			||||||
| 
						 | 
					@ -391,62 +343,88 @@ impl<'s, I: Iterator<Item = Event<'s>>, W: std::fmt::Write> Writer<'s, I, W> {
 | 
				
			||||||
                    Container::RawBlock { .. } | Container::RawInline { .. } => {
 | 
					                    Container::RawBlock { .. } | Container::RawInline { .. } => {
 | 
				
			||||||
                        self.raw = Raw::None;
 | 
					                        self.raw = Raw::None;
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                        Container::Subscript => self.out.write_str("</sub>")?,
 | 
					                    Container::Subscript => out.write_str("</sub>")?,
 | 
				
			||||||
                        Container::Superscript => self.out.write_str("</sup>")?,
 | 
					                    Container::Superscript => out.write_str("</sup>")?,
 | 
				
			||||||
                        Container::Insert => self.out.write_str("</ins>")?,
 | 
					                    Container::Insert => out.write_str("</ins>")?,
 | 
				
			||||||
                        Container::Delete => self.out.write_str("</del>")?,
 | 
					                    Container::Delete => out.write_str("</del>")?,
 | 
				
			||||||
                        Container::Strong => self.out.write_str("</strong>")?,
 | 
					                    Container::Strong => out.write_str("</strong>")?,
 | 
				
			||||||
                        Container::Emphasis => self.out.write_str("</em>")?,
 | 
					                    Container::Emphasis => out.write_str("</em>")?,
 | 
				
			||||||
                        Container::Mark => self.out.write_str("</mark>")?,
 | 
					                    Container::Mark => out.write_str("</mark>")?,
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            Event::Str(s) => match self.raw {
 | 
					            Event::Str(s) => match self.raw {
 | 
				
			||||||
                    Raw::None if self.img_alt_text > 0 => self.write_attr(&s)?,
 | 
					                Raw::None if self.img_alt_text > 0 => write_attr(s, &mut out)?,
 | 
				
			||||||
                    Raw::None => self.write_text(&s)?,
 | 
					                Raw::None => write_text(s, &mut out)?,
 | 
				
			||||||
                    Raw::Html => self.out.write_str(&s)?,
 | 
					                Raw::Html => out.write_str(s)?,
 | 
				
			||||||
                Raw::Other => {}
 | 
					                Raw::Other => {}
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            Event::FootnoteReference(_tag, number) => {
 | 
					            Event::FootnoteReference(_tag, number) => {
 | 
				
			||||||
                if self.img_alt_text == 0 {
 | 
					                if self.img_alt_text == 0 {
 | 
				
			||||||
                    write!(
 | 
					                    write!(
 | 
				
			||||||
                            self.out,
 | 
					                        out,
 | 
				
			||||||
                        r##"<a id="fnref{}" href="#fn{}" role="doc-noteref"><sup>{}</sup></a>"##,
 | 
					                        r##"<a id="fnref{}" href="#fn{}" role="doc-noteref"><sup>{}</sup></a>"##,
 | 
				
			||||||
                        number, number, number
 | 
					                        number, number, number
 | 
				
			||||||
                    )?;
 | 
					                    )?;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
                Event::Symbol(sym) => write!(self.out, ":{}:", sym)?,
 | 
					            Event::Symbol(sym) => write!(out, ":{}:", sym)?,
 | 
				
			||||||
                Event::LeftSingleQuote => self.out.write_str("‘")?,
 | 
					            Event::LeftSingleQuote => out.write_str("‘")?,
 | 
				
			||||||
                Event::RightSingleQuote => self.out.write_str("’")?,
 | 
					            Event::RightSingleQuote => out.write_str("’")?,
 | 
				
			||||||
                Event::LeftDoubleQuote => self.out.write_str("“")?,
 | 
					            Event::LeftDoubleQuote => out.write_str("“")?,
 | 
				
			||||||
                Event::RightDoubleQuote => self.out.write_str("”")?,
 | 
					            Event::RightDoubleQuote => out.write_str("”")?,
 | 
				
			||||||
                Event::Ellipsis => self.out.write_str("…")?,
 | 
					            Event::Ellipsis => out.write_str("…")?,
 | 
				
			||||||
                Event::EnDash => self.out.write_str("–")?,
 | 
					            Event::EnDash => out.write_str("–")?,
 | 
				
			||||||
                Event::EmDash => self.out.write_str("—")?,
 | 
					            Event::EmDash => out.write_str("—")?,
 | 
				
			||||||
                Event::NonBreakingSpace => self.out.write_str(" ")?,
 | 
					            Event::NonBreakingSpace => out.write_str(" ")?,
 | 
				
			||||||
                Event::Hardbreak => self.out.write_str("<br>\n")?,
 | 
					            Event::Hardbreak => out.write_str("<br>\n")?,
 | 
				
			||||||
                Event::Softbreak => self.out.write_char('\n')?,
 | 
					            Event::Softbreak => out.write_char('\n')?,
 | 
				
			||||||
            Event::Escape | Event::Blankline => unreachable!("filtered out"),
 | 
					            Event::Escape | Event::Blankline => unreachable!("filtered out"),
 | 
				
			||||||
            Event::ThematicBreak(attrs) => {
 | 
					            Event::ThematicBreak(attrs) => {
 | 
				
			||||||
                    self.out.write_str("\n<hr")?;
 | 
					                out.write_str("\n<hr")?;
 | 
				
			||||||
                for (a, v) in attrs.iter() {
 | 
					                for (a, v) in attrs.iter() {
 | 
				
			||||||
                        write!(self.out, r#" {}=""#, a)?;
 | 
					                    write!(out, r#" {}=""#, a)?;
 | 
				
			||||||
                        v.parts().try_for_each(|part| self.write_attr(part))?;
 | 
					                    v.parts().try_for_each(|part| write_attr(part, &mut out))?;
 | 
				
			||||||
                        self.out.write_char('"')?;
 | 
					                    out.write_char('"')?;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                    self.out.write_str(">")?;
 | 
					                out.write_str(">")?;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        self.first_line = false;
 | 
					        self.first_line = false;
 | 
				
			||||||
        }
 | 
					
 | 
				
			||||||
        if self.encountered_footnote {
 | 
					 | 
				
			||||||
            self.out.write_str("\n</ol>\n</section>")?;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        self.out.write_char('\n')?;
 | 
					 | 
				
			||||||
        Ok(())
 | 
					        Ok(())
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn write_escape(&mut self, mut s: &str, escape_quotes: bool) -> std::fmt::Result {
 | 
					    fn render_epilogue<W>(&mut self, mut out: W) -> std::fmt::Result
 | 
				
			||||||
 | 
					    where
 | 
				
			||||||
 | 
					        W: std::fmt::Write,
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if self.encountered_footnote {
 | 
				
			||||||
 | 
					            out.write_str("\n</ol>\n</section>")?;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        out.write_char('\n')?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Ok(())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn write_text<W>(s: &str, out: W) -> std::fmt::Result
 | 
				
			||||||
 | 
					where
 | 
				
			||||||
 | 
					    W: std::fmt::Write,
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    write_escape(s, false, out)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn write_attr<W>(s: &str, out: W) -> std::fmt::Result
 | 
				
			||||||
 | 
					where
 | 
				
			||||||
 | 
					    W: std::fmt::Write,
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    write_escape(s, true, out)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn write_escape<W>(mut s: &str, escape_quotes: bool, mut out: W) -> std::fmt::Result
 | 
				
			||||||
 | 
					where
 | 
				
			||||||
 | 
					    W: std::fmt::Write,
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
    let mut ent = "";
 | 
					    let mut ent = "";
 | 
				
			||||||
    while let Some(i) = s.find(|c| {
 | 
					    while let Some(i) = s.find(|c| {
 | 
				
			||||||
        match c {
 | 
					        match c {
 | 
				
			||||||
| 
						 | 
					@ -461,18 +439,9 @@ impl<'s, I: Iterator<Item = Event<'s>>, W: std::fmt::Write> Writer<'s, I, W> {
 | 
				
			||||||
            true
 | 
					            true
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
    }) {
 | 
					    }) {
 | 
				
			||||||
            self.out.write_str(&s[..i])?;
 | 
					        out.write_str(&s[..i])?;
 | 
				
			||||||
            self.out.write_str(ent)?;
 | 
					        out.write_str(ent)?;
 | 
				
			||||||
        s = &s[i + 1..];
 | 
					        s = &s[i + 1..];
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
        self.out.write_str(s)
 | 
					    out.write_str(s)
 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn write_text(&mut self, s: &str) -> std::fmt::Result {
 | 
					 | 
				
			||||||
        self.write_escape(s, false)
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn write_attr(&mut self, s: &str) -> std::fmt::Result {
 | 
					 | 
				
			||||||
        self.write_escape(s, true)
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										186
									
								
								src/lib.rs
									
										
									
									
									
								
							
							
						
						
									
										186
									
								
								src/lib.rs
									
										
									
									
									
								
							| 
						 | 
					@ -20,7 +20,7 @@
 | 
				
			||||||
//! let djot_input = "hello *world*!";
 | 
					//! let djot_input = "hello *world*!";
 | 
				
			||||||
//! let events = jotdown::Parser::new(djot_input);
 | 
					//! let events = jotdown::Parser::new(djot_input);
 | 
				
			||||||
//! let mut html = String::new();
 | 
					//! let mut html = String::new();
 | 
				
			||||||
//! jotdown::html::Renderer.push(events, &mut html);
 | 
					//! jotdown::html::Renderer::default().push(events, &mut html);
 | 
				
			||||||
//! assert_eq!(html, "<p>hello <strong>world</strong>!</p>\n");
 | 
					//! assert_eq!(html, "<p>hello <strong>world</strong>!</p>\n");
 | 
				
			||||||
//! # }
 | 
					//! # }
 | 
				
			||||||
//! ```
 | 
					//! ```
 | 
				
			||||||
| 
						 | 
					@ -41,7 +41,7 @@
 | 
				
			||||||
//!         e => e,
 | 
					//!         e => e,
 | 
				
			||||||
//!     });
 | 
					//!     });
 | 
				
			||||||
//! let mut html = String::new();
 | 
					//! let mut html = String::new();
 | 
				
			||||||
//! jotdown::html::Renderer.push(events, &mut html);
 | 
					//! jotdown::html::Renderer::default().push(events, &mut html);
 | 
				
			||||||
//! assert_eq!(html, "<p>a <a href=\"https://example.net\">link</a></p>\n");
 | 
					//! assert_eq!(html, "<p>a <a href=\"https://example.net\">link</a></p>\n");
 | 
				
			||||||
//! # }
 | 
					//! # }
 | 
				
			||||||
//! ```
 | 
					//! ```
 | 
				
			||||||
| 
						 | 
					@ -67,52 +67,162 @@ pub use attr::{AttributeValue, AttributeValueParts, Attributes};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type CowStr<'s> = std::borrow::Cow<'s, str>;
 | 
					type CowStr<'s> = std::borrow::Cow<'s, str>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// A trait for rendering [`Event`]s to an output format.
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// The output can be written to either a [`std::fmt::Write`] or a [`std::io::Write`] object.
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// If ownership of the [`Event`]s cannot be given to the renderer, use [`Render::push_borrowed`]
 | 
				
			||||||
 | 
					/// or [`Render::write_borrowed`].
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// An implementor needs to at least implement the [`Render::render_event`] function that renders a
 | 
				
			||||||
 | 
					/// single event to the output. If anything needs to be rendered at the beginning or end of the
 | 
				
			||||||
 | 
					/// output, the [`Render::render_prologue`] and [`Render::render_epilogue`] can be implemented as
 | 
				
			||||||
 | 
					/// well.
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// # Examples
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// Push to a [`String`] (implements [`std::fmt::Write`]):
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// ```
 | 
				
			||||||
 | 
					/// # use jotdown::Render;
 | 
				
			||||||
 | 
					/// # let events = std::iter::empty();
 | 
				
			||||||
 | 
					/// let mut output = String::new();
 | 
				
			||||||
 | 
					/// let mut renderer = jotdown::html::Renderer::default();
 | 
				
			||||||
 | 
					/// renderer.push(events, &mut output);
 | 
				
			||||||
 | 
					/// ```
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// Write to standard output with buffering ([`std::io::Stdout`] implements [`std::io::Write`]):
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// ```
 | 
				
			||||||
 | 
					/// # use jotdown::Render;
 | 
				
			||||||
 | 
					/// # let events = std::iter::empty();
 | 
				
			||||||
 | 
					/// let mut out = std::io::BufWriter::new(std::io::stdout());
 | 
				
			||||||
 | 
					/// let mut renderer = jotdown::html::Renderer::default();
 | 
				
			||||||
 | 
					/// renderer.write(events, &mut out).unwrap();
 | 
				
			||||||
 | 
					/// ```
 | 
				
			||||||
pub trait Render {
 | 
					pub trait Render {
 | 
				
			||||||
    /// Push [`Event`]s to a unicode-accepting buffer or stream.
 | 
					    /// Render a single event.
 | 
				
			||||||
    fn push<'s, I: Iterator<Item = Event<'s>>, W: fmt::Write>(
 | 
					    fn render_event<'s, W>(&mut self, e: &Event<'s>, out: W) -> std::fmt::Result
 | 
				
			||||||
        &self,
 | 
					    where
 | 
				
			||||||
        events: I,
 | 
					        W: std::fmt::Write;
 | 
				
			||||||
        out: W,
 | 
					 | 
				
			||||||
    ) -> fmt::Result;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Write [`Event`]s to a byte sink, encoded as UTF-8.
 | 
					    /// Render something before any events have been provided.
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// This does nothing by default, but an implementation may choose to prepend data at the
 | 
				
			||||||
 | 
					    /// beginning of the output if needed.
 | 
				
			||||||
 | 
					    fn render_prologue<W>(&mut self, _out: W) -> std::fmt::Result
 | 
				
			||||||
 | 
					    where
 | 
				
			||||||
 | 
					        W: std::fmt::Write,
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        Ok(())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Render something after all events have been provided.
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// This does nothing by default, but an implementation may choose to append extra data at the
 | 
				
			||||||
 | 
					    /// end of the output if needed.
 | 
				
			||||||
 | 
					    fn render_epilogue<W>(&mut self, _out: W) -> std::fmt::Result
 | 
				
			||||||
 | 
					    where
 | 
				
			||||||
 | 
					        W: std::fmt::Write,
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        Ok(())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Push owned [`Event`]s to a unicode-accepting buffer or stream.
 | 
				
			||||||
 | 
					    fn push<'s, I, W>(&mut self, mut events: I, mut out: W) -> fmt::Result
 | 
				
			||||||
 | 
					    where
 | 
				
			||||||
 | 
					        I: Iterator<Item = Event<'s>>,
 | 
				
			||||||
 | 
					        W: fmt::Write,
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        self.render_prologue(&mut out)?;
 | 
				
			||||||
 | 
					        events.try_for_each(|e| self.render_event(&e, &mut out))?;
 | 
				
			||||||
 | 
					        self.render_epilogue(&mut out)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Write owned [`Event`]s to a byte sink, encoded as UTF-8.
 | 
				
			||||||
    ///
 | 
					    ///
 | 
				
			||||||
    /// NOTE: This performs many small writes, so IO writes should be buffered with e.g.
 | 
					    /// NOTE: This performs many small writes, so IO writes should be buffered with e.g.
 | 
				
			||||||
    /// [`std::io::BufWriter`].
 | 
					    /// [`std::io::BufWriter`].
 | 
				
			||||||
    fn write<'s, I: Iterator<Item = Event<'s>>, W: io::Write>(
 | 
					    fn write<'s, I, W>(&mut self, events: I, out: W) -> io::Result<()>
 | 
				
			||||||
        &self,
 | 
					    where
 | 
				
			||||||
        events: I,
 | 
					        I: Iterator<Item = Event<'s>>,
 | 
				
			||||||
        out: W,
 | 
					        W: io::Write,
 | 
				
			||||||
    ) -> io::Result<()> {
 | 
					    {
 | 
				
			||||||
        struct Adapter<T: io::Write> {
 | 
					        let mut out = WriteAdapter {
 | 
				
			||||||
            inner: T,
 | 
					 | 
				
			||||||
            error: io::Result<()>,
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        impl<T: io::Write> fmt::Write for Adapter<T> {
 | 
					 | 
				
			||||||
            fn write_str(&mut self, s: &str) -> fmt::Result {
 | 
					 | 
				
			||||||
                match self.inner.write_all(s.as_bytes()) {
 | 
					 | 
				
			||||||
                    Ok(()) => Ok(()),
 | 
					 | 
				
			||||||
                    Err(e) => {
 | 
					 | 
				
			||||||
                        self.error = Err(e);
 | 
					 | 
				
			||||||
                        Err(fmt::Error)
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let mut out = Adapter {
 | 
					 | 
				
			||||||
            inner: out,
 | 
					            inner: out,
 | 
				
			||||||
            error: Ok(()),
 | 
					            error: Ok(()),
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        match self.push(events, &mut out) {
 | 
					        self.push(events, &mut out).map_err(|_| match out.error {
 | 
				
			||||||
            Ok(()) => Ok(()),
 | 
					            Err(e) => e,
 | 
				
			||||||
            Err(_) => match out.error {
 | 
					            _ => io::Error::new(io::ErrorKind::Other, "formatter error"),
 | 
				
			||||||
                Err(_) => out.error,
 | 
					        })
 | 
				
			||||||
                _ => Err(io::Error::new(io::ErrorKind::Other, "formatter error")),
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Push borrowed [`Event`]s to a unicode-accepting buffer or stream.
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// # Examples
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// Render a borrowed slice of [`Event`]s.
 | 
				
			||||||
 | 
					    /// ```
 | 
				
			||||||
 | 
					    /// # use jotdown::Render;
 | 
				
			||||||
 | 
					    /// # let events: &[jotdown::Event] = &[];
 | 
				
			||||||
 | 
					    /// let mut output = String::new();
 | 
				
			||||||
 | 
					    /// let mut renderer = jotdown::html::Renderer::default();
 | 
				
			||||||
 | 
					    /// renderer.push_borrowed(events.iter(), &mut output);
 | 
				
			||||||
 | 
					    /// ```
 | 
				
			||||||
 | 
					    fn push_borrowed<'s, E, I, W>(&mut self, mut events: I, mut out: W) -> fmt::Result
 | 
				
			||||||
 | 
					    where
 | 
				
			||||||
 | 
					        E: AsRef<Event<'s>>,
 | 
				
			||||||
 | 
					        I: Iterator<Item = E>,
 | 
				
			||||||
 | 
					        W: fmt::Write,
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        self.render_prologue(&mut out)?;
 | 
				
			||||||
 | 
					        events.try_for_each(|e| self.render_event(e.as_ref(), &mut out))?;
 | 
				
			||||||
 | 
					        self.render_epilogue(&mut out)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Write borrowed [`Event`]s to a byte sink, encoded as UTF-8.
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// NOTE: This performs many small writes, so IO writes should be buffered with e.g.
 | 
				
			||||||
 | 
					    /// [`std::io::BufWriter`].
 | 
				
			||||||
 | 
					    fn write_borrowed<'s, E, I, W>(&mut self, events: I, out: W) -> io::Result<()>
 | 
				
			||||||
 | 
					    where
 | 
				
			||||||
 | 
					        E: AsRef<Event<'s>>,
 | 
				
			||||||
 | 
					        I: Iterator<Item = E>,
 | 
				
			||||||
 | 
					        W: io::Write,
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        let mut out = WriteAdapter {
 | 
				
			||||||
 | 
					            inner: out,
 | 
				
			||||||
 | 
					            error: Ok(()),
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.push_borrowed(events, &mut out)
 | 
				
			||||||
 | 
					            .map_err(|_| match out.error {
 | 
				
			||||||
 | 
					                Err(e) => e,
 | 
				
			||||||
 | 
					                _ => io::Error::new(io::ErrorKind::Other, "formatter error"),
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct WriteAdapter<T: io::Write> {
 | 
				
			||||||
 | 
					    inner: T,
 | 
				
			||||||
 | 
					    error: io::Result<()>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl<T: io::Write> fmt::Write for WriteAdapter<T> {
 | 
				
			||||||
 | 
					    fn write_str(&mut self, s: &str) -> fmt::Result {
 | 
				
			||||||
 | 
					        self.inner.write_all(s.as_bytes()).map_err(|e| {
 | 
				
			||||||
 | 
					            self.error = Err(e);
 | 
				
			||||||
 | 
					            fmt::Error
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// XXX why is this not a blanket implementation?
 | 
				
			||||||
 | 
					impl<'s> AsRef<Event<'s>> for &Event<'s> {
 | 
				
			||||||
 | 
					    fn as_ref(&self) -> &Event<'s> {
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -68,11 +68,11 @@ fn run() -> Result<(), std::io::Error> {
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let parser = jotdown::Parser::new(&content);
 | 
					    let parser = jotdown::Parser::new(&content);
 | 
				
			||||||
    let html = jotdown::html::Renderer;
 | 
					    let mut renderer = jotdown::html::Renderer::default();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    match app.output {
 | 
					    match app.output {
 | 
				
			||||||
        Some(path) => html.write(parser, File::create(path)?)?,
 | 
					        Some(path) => renderer.write(parser, File::create(path)?)?,
 | 
				
			||||||
        None => html.write(parser, BufWriter::new(std::io::stdout()))?,
 | 
					        None => renderer.write(parser, BufWriter::new(std::io::stdout()))?,
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Ok(())
 | 
					    Ok(())
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -19,7 +19,9 @@ pub fn html(data: &[u8]) {
 | 
				
			||||||
        if !s.contains("=html") {
 | 
					        if !s.contains("=html") {
 | 
				
			||||||
            let p = jotdown::Parser::new(s);
 | 
					            let p = jotdown::Parser::new(s);
 | 
				
			||||||
            let mut html = "<!DOCTYPE html>\n".to_string();
 | 
					            let mut html = "<!DOCTYPE html>\n".to_string();
 | 
				
			||||||
            jotdown::html::Renderer.push(p, &mut html).unwrap();
 | 
					            jotdown::html::Renderer::default()
 | 
				
			||||||
 | 
					                .push(p, &mut html)
 | 
				
			||||||
 | 
					                .unwrap();
 | 
				
			||||||
            validate_html(&html);
 | 
					            validate_html(&html);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -14,7 +14,9 @@ macro_rules! suite_test {
 | 
				
			||||||
        let expected = $expected;
 | 
					        let expected = $expected;
 | 
				
			||||||
        let p = jotdown::Parser::new(src);
 | 
					        let p = jotdown::Parser::new(src);
 | 
				
			||||||
        let mut actual = String::new();
 | 
					        let mut actual = String::new();
 | 
				
			||||||
        jotdown::html::Renderer.push(p, &mut actual).unwrap();
 | 
					        jotdown::html::Renderer::default()
 | 
				
			||||||
 | 
					            .push(p, &mut actual)
 | 
				
			||||||
 | 
					            .unwrap();
 | 
				
			||||||
        assert_eq!(
 | 
					        assert_eq!(
 | 
				
			||||||
            actual.trim(),
 | 
					            actual.trim(),
 | 
				
			||||||
            expected.trim(),
 | 
					            expected.trim(),
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue