//! 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::Container;
use crate::Event;
use crate::LinkType;
use crate::ListKind;
use crate::OrderedListNumbering::*;
use crate::Render;
use crate::SpanLinkType;
pub struct Renderer;
impl Render for Renderer {
fn push<'s, I: Iterator
{ self.list_tightness.push(*tight); match kind { ListKind::Unordered | ListKind::Task => out.write_str("{ out.write_str("
1 { write!(out, r#" start="{}""#, start)?; } if let Some(ty) = match numbering { Decimal => None, AlphaLower => Some('a'), AlphaUpper => Some('A'), RomanLower => Some('i'), RomanUpper => Some('I'), } { write!(out, r#" type="{}""#, ty)?; } } } } Container::ListItem | Container::TaskListItem { .. } => { out.write_str("
\n")?; } out.write_char('\n')?; Ok(()) } } fn write_text- out.write_str("
out.write_str("
- { assert!(self.footnote_number.is_none()); self.footnote_number = Some((*number).try_into().unwrap()); if !self.encountered_footnote { self.encountered_footnote = true; out.write_str("
\n ")?, Container::Div { .. } => out.write_str("")?, Container::Paragraph => { if matches!(self.list_tightness.last(), Some(true)) { continue; } if self.footnote_number.is_none() { out.write_str("")?; } else { self.close_para = true; } } Container::Heading { level, .. } => write!(out, "", level)?, Container::TableCell { head: false, .. } => out.write_str("")?, Container::TableCell { head: true, .. } => out.write_str("")?, Container::Caption => out.write_str("")?, Container::DescriptionTerm => out.write_str("")?, Container::CodeBlock { .. } => out.write_str("")?, Container::Span => out.write_str("")?, Container::Link(..) => out.write_str("")?, Container::Image(src, ..) => { if self.img_alt_text == 1 { if !src.is_empty() { out.write_str(r#"" src=""#)?; write_attr(&src, &mut out)?; } out.write_str(r#"">"#)?; } self.img_alt_text -= 1; } Container::Verbatim => out.write_str("")?, Container::Math { display } => { out.write_str(if display { r#"\]"# } else { r#"\)"# })?; } Container::RawBlock { .. } | Container::RawInline { .. } => { self.raw = Raw::None; } Container::Subscript => out.write_str("")?, Container::Superscript => out.write_str("")?, Container::Insert => out.write_str("")?, Container::Delete => out.write_str("")?, Container::Strong => out.write_str("")?, Container::Emphasis => out.write_str("")?, Container::Mark => out.write_str("")?, } } Event::Str(s) => match self.raw { Raw::None if self.img_alt_text > 0 => write_attr(&s, &mut out)?, Raw::None => write_text(&s, &mut out)?, Raw::Html => out.write_str(&s)?, Raw::Other => {} }, Event::FootnoteReference(_tag, number) => { if self.img_alt_text == 0 { write!( out, r##"{}"##, number, number, number )?; } } Event::Symbol(sym) => write!(out, ":{}:", sym)?, Event::LeftSingleQuote => out.write_str("‘")?, Event::RightSingleQuote => out.write_str("’")?, Event::LeftDoubleQuote => out.write_str("“")?, Event::RightDoubleQuote => out.write_str("”")?, Event::Ellipsis => out.write_str("…")?, Event::EnDash => out.write_str("–")?, Event::EmDash => out.write_str("—")?, Event::NonBreakingSpace => out.write_str(" ")?, Event::Hardbreak => out.write_str("
\n\n")?; } write!(out, "
- ", number)?; continue; } Container::Table => out.write_str("
\n")?, Event::Softbreak => out.write_char('\n')?, Event::Escape | Event::Blankline => unreachable!("filtered out"), Event::ThematicBreak(attrs) => { out.write_str("\n
")?; } } self.first_line = false; } if self.encountered_footnote { out.write_str("\n(s: &str, out: W) -> std::fmt::Result where W: std::fmt::Write, { write_escape(s, false, out) } fn write_attr (s: &str, out: W) -> std::fmt::Result where W: std::fmt::Write, { write_escape(s, true, out) } fn write_escape (mut s: &str, escape_quotes: bool, mut out: W) -> std::fmt::Result where W: std::fmt::Write, { let mut ent = ""; while let Some(i) = s.find(|c| { match c { '<' => Some("<"), '>' => Some(">"), '&' => Some("&"), '"' if escape_quotes => Some("""), _ => None, } .map_or(false, |s| { ent = s; true }) }) { out.write_str(&s[..i])?; out.write_str(ent)?; s = &s[i + 1..]; } out.write_str(s) }