markdown-ppp 2.9.2

Feature-rich Markdown Parsing and Pretty-Printing library
Documentation
use crate::ast::*;
use crate::html_printer::util::tag;
use crate::html_printer::ToDoc;
use pretty::{Arena, DocAllocator, DocBuilder};

impl<'a> ToDoc<'a> for Vec<Block> {
    fn to_doc(&self, state: &'a crate::html_printer::State<'a>) -> DocBuilder<'a, Arena<'a>, ()> {
        state
            .arena
            .concat(self.iter().map(|block| block.to_doc(state)))
    }
}

impl<'a> ToDoc<'a> for Vec<&Block> {
    fn to_doc(&self, state: &'a crate::html_printer::State<'a>) -> DocBuilder<'a, Arena<'a>, ()> {
        state
            .arena
            .concat(self.iter().map(|block| block.to_doc(state)))
    }
}

impl<'a> ToDoc<'a> for Block {
    fn to_doc(&self, state: &'a crate::html_printer::State<'a>) -> DocBuilder<'a, Arena<'a>, ()> {
        match self {
            Block::Paragraph(inlines) => {
                let inner = state
                    .arena
                    .concat(inlines.iter().map(|inline| inline.to_doc(state)));
                tag(state, "p", Vec::new(), inner)
            }
            Block::Heading(v) => {
                let htag = match v.kind {
                    HeadingKind::Atx(1) => "h1",
                    HeadingKind::Atx(2) => "h2",
                    HeadingKind::Atx(3) => "h3",
                    HeadingKind::Atx(4) => "h4",
                    HeadingKind::Atx(5) => "h5",
                    HeadingKind::Atx(_) => "h6",
                    HeadingKind::Setext(SetextHeading::Level1) => "h1",
                    HeadingKind::Setext(SetextHeading::Level2) => "h2",
                };
                let inner = state
                    .arena
                    .concat(v.content.iter().map(|inline| inline.to_doc(state)));
                tag(state, htag, Vec::new(), inner)
            }
            Block::ThematicBreak => tag(state, "hr", Vec::new(), state.arena.nil()),
            Block::BlockQuote(inner) => {
                let inner = state
                    .arena
                    .concat(inner.iter().map(|inline| inline.to_doc(state)));
                tag(state, "blockquote", Vec::new(), inner)
            }
            Block::List(v) => v.to_doc(state),
            Block::CodeBlock(v) => v.to_doc(state),
            Block::HtmlBlock(html) => state.arena.text(html.clone()),
            Block::Definition(_) => state.arena.nil(),
            Block::Empty => state.arena.nil(),
            Block::Table(v) => v.to_doc(state),
            Block::FootnoteDefinition(def) => def.to_doc(state),
            Block::GitHubAlert(alert) => alert.to_doc(state),
        }
    }
}

impl<'a> ToDoc<'a> for List {
    fn to_doc(&self, state: &'a crate::html_printer::State<'a>) -> DocBuilder<'a, Arena<'a>, ()> {
        let items = state
            .arena
            .concat(self.items.iter().map(|item| item.to_doc(state)));
        match self.kind {
            ListKind::Ordered(ListOrderedKindOptions { start }) => tag(
                state,
                "ol",
                vec![("start".to_owned(), format!("{start}"))],
                items,
            ),
            ListKind::Bullet(kind) => {
                let style = match kind {
                    ListBulletKind::Dash => "markdown-list-kind-dash",
                    ListBulletKind::Star => "markdown-list-kind-star",
                    ListBulletKind::Plus => "markdown-list-kind-plus",
                };
                tag(
                    state,
                    "ul",
                    vec![("class".to_owned(), style.to_owned())],
                    items,
                )
            }
        }
    }
}

impl<'a> ToDoc<'a> for ListItem {
    fn to_doc(&self, state: &'a crate::html_printer::State<'a>) -> DocBuilder<'a, Arena<'a>, ()> {
        let task = match self.task {
            Some(TaskState::Complete) => tag(
                state,
                "span",
                vec![("class".to_owned(), "markdown-list-task-complete".to_owned())],
                state.arena.text("[X] "),
            ),
            Some(TaskState::Incomplete) => tag(
                state,
                "span",
                vec![(
                    "class".to_owned(),
                    "markdown-list-task-incomplete".to_owned(),
                )],
                state.arena.text("[ ] "),
            ),
            None => state.arena.nil(),
        };
        let content = task.append(
            state
                .arena
                .concat(self.blocks.iter().map(|block| block.to_doc(state))),
        );

        tag(state, "li", vec![], content)
    }
}

impl<'a> ToDoc<'a> for CodeBlock {
    fn to_doc(&self, state: &'a crate::html_printer::State<'a>) -> DocBuilder<'a, Arena<'a>, ()> {
        tag(
            state,
            "pre",
            Vec::new(),
            tag(
                state,
                "code",
                Vec::new(),
                state
                    .arena
                    .text(crate::html_printer::util::escape(&self.literal)),
            ),
        )
    }
}

impl<'a> ToDoc<'a> for Table {
    fn to_doc(&self, state: &'a crate::html_printer::State<'a>) -> DocBuilder<'a, Arena<'a>, ()> {
        let first_row = table_row_to_doc(state, self.rows.first().unwrap(), "th", &self.alignments);
        let mut acc = state.arena.nil();
        for row in self.rows.iter().skip(1) {
            acc = acc.append(table_row_to_doc(state, row, "td", &self.alignments));
        }

        let content =
            tag(state, "thead", Vec::new(), first_row).append(tag(state, "tbody", Vec::new(), acc));

        tag(state, "table", Vec::new(), content)
    }
}

fn table_row_to_doc<'a>(
    state: &'a crate::html_printer::State<'a>,
    row: &TableRow,
    row_tag: &'static str,
    alignments: &[Alignment],
) -> DocBuilder<'a, Arena<'a>, ()> {
    let mut acc = state.arena.nil();
    for (i, cell) in row.iter().enumerate() {
        let alignment = match alignments.get(i) {
            Some(Alignment::Left) | Some(Alignment::None) => "left",
            Some(Alignment::Right) => "right",
            Some(Alignment::Center) => "center",
            None => "left",
        };
        let alignment_class = format!("markdown-table-align-{alignment}");
        let attributes = vec![("class".to_owned(), alignment_class.to_owned())];
        acc = acc.append(tag(state, row_tag, attributes, cell.to_doc(state)));
    }

    tag(state, "tr", Vec::new(), acc)
}

impl<'a> ToDoc<'a> for FootnoteDefinition {
    fn to_doc(&self, state: &'a crate::html_printer::State<'a>) -> DocBuilder<'a, Arena<'a>, ()> {
        let index = match state.get_footnote_index(&self.label) {
            Some(v) => v,
            None => return state.arena.nil(),
        };
        tag(
            state,
            "div",
            vec![(
                "class".to_owned(),
                "markdown-footnote-definition".to_owned(),
            )],
            tag(
                state,
                "span",
                vec![(
                    "class".to_owned(),
                    "markdown-footnote-definition-index".to_owned(),
                )],
                state.arena.text(format!("{index}. ")),
            )
            .append(tag(
                state,
                "span",
                vec![(
                    "class".to_owned(),
                    "markdown-footnote-definition-content".to_owned(),
                )],
                self.blocks.to_doc(state),
            )),
        )
    }
}