markdown-ppp 2.9.2

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

fn is_visible_block(block: &Block) -> bool {
    !matches!(block, Block::HtmlBlock(_) | Block::Definition(_) | Block::Empty)
}

impl<'a> ToDoc<'a> for Vec<Block> {
    fn to_doc(
        &self,
        state: &'a crate::plaintext_printer::State<'a>,
    ) -> DocBuilder<'a, Arena<'a>, ()> {
        let visible: Vec<_> = self.iter().filter(|b| is_visible_block(b)).collect();
        let len = visible.len();
        let mut result = state.arena.nil();
        for (i, block) in visible.into_iter().enumerate() {
            result = result.append(block.to_doc(state));
            if i + 1 < len {
                result = result.append(state.arena.hardline()).append(state.arena.hardline());
            }
        }
        result
    }
}

impl<'a> ToDoc<'a> for Block {
    fn to_doc(
        &self,
        state: &'a crate::plaintext_printer::State<'a>,
    ) -> DocBuilder<'a, Arena<'a>, ()> {
        match self {
            Block::Paragraph(inlines) => inlines.to_doc(state),
            Block::Heading(v) => v.content.to_doc(state),
            Block::ThematicBreak => state.arena.text("---"),
            Block::BlockQuote(blocks) => blocks.to_doc(state),
            Block::List(v) => v.to_doc(state),
            Block::CodeBlock(v) => state.arena.text(v.literal.trim_end_matches('\n').to_string()),
            Block::HtmlBlock(_) => state.arena.nil(),
            Block::Definition(_) => state.arena.nil(),
            Block::Table(v) => v.to_doc(state),
            Block::FootnoteDefinition(def) => def.to_doc(state),
            Block::GitHubAlert(alert) => alert.to_doc(state),
            Block::Empty => state.arena.nil(),
        }
    }
}

impl<'a> ToDoc<'a> for List {
    fn to_doc(
        &self,
        state: &'a crate::plaintext_printer::State<'a>,
    ) -> DocBuilder<'a, Arena<'a>, ()> {
        let mut result = state.arena.nil();
        for (i, item) in self.items.iter().enumerate() {
            if i > 0 {
                result = result.append(state.arena.hardline());
            }

            let prefix = match self.kind {
                ListKind::Ordered(ListOrderedKindOptions { start }) => {
                    format!("{}. ", start.saturating_add(i as u64))
                }
                ListKind::Bullet(_) => "- ".into(),
            };

            let task = match item.task {
                Some(TaskState::Complete) => "[x] ",
                Some(TaskState::Incomplete) => "[ ] ",
                None => "",
            };

            let content = item.blocks.to_doc(state);
            result = result
                .append(state.arena.text(prefix))
                .append(state.arena.text(task))
                .append(content);
        }
        result
    }
}

impl<'a> ToDoc<'a> for Table {
    fn to_doc(
        &self,
        state: &'a crate::plaintext_printer::State<'a>,
    ) -> DocBuilder<'a, Arena<'a>, ()> {
        let mut result = state.arena.nil();
        for (i, row) in self.rows.iter().enumerate() {
            if i > 0 {
                result = result.append(state.arena.hardline());
            }
            let mut row_doc = state.arena.nil();
            for (j, cell) in row.iter().enumerate() {
                if j > 0 {
                    row_doc = row_doc.append(state.arena.text(" | "));
                }
                row_doc = row_doc.append(cell.to_doc(state));
            }
            result = result.append(row_doc);
        }
        result
    }
}

impl<'a> ToDoc<'a> for FootnoteDefinition {
    fn to_doc(
        &self,
        state: &'a crate::plaintext_printer::State<'a>,
    ) -> DocBuilder<'a, Arena<'a>, ()> {
        let index = match state.get_footnote_index(&self.label) {
            Some(v) => v,
            None => return state.arena.nil(),
        };
        state
            .arena
            .text(format!("[{index}] "))
            .append(self.blocks.to_doc(state))
    }
}

impl<'a> ToDoc<'a> for GitHubAlert {
    fn to_doc(
        &self,
        state: &'a crate::plaintext_printer::State<'a>,
    ) -> DocBuilder<'a, Arena<'a>, ()> {
        let title = match &self.alert_type {
            GitHubAlertType::Note => "Note",
            GitHubAlertType::Tip => "Tip",
            GitHubAlertType::Important => "Important",
            GitHubAlertType::Warning => "Warning",
            GitHubAlertType::Caution => "Caution",
            GitHubAlertType::Custom(s) => s.as_str(),
        };
        state
            .arena
            .text(format!("[{title}]"))
            .append(state.arena.hardline())
            .append(self.blocks.to_doc(state))
    }
}