1use crate::render::MarkdownRenderer;
2use pulldown_cmark::{CodeBlockKind, Event, HeadingLevel, Options, Parser as MdParser, Tag};
3use std::io;
4use termcolor::{Color, ColorSpec, WriteColor};
5
6pub fn render_markdown(
7 md: &str,
8 preserve_fences: bool,
9 output: &mut impl WriteColor,
10) -> io::Result<()> {
11 let mut options = Options::empty();
12 options.insert(Options::ENABLE_STRIKETHROUGH);
13 options.insert(Options::ENABLE_TABLES);
14 options.insert(Options::ENABLE_FOOTNOTES);
15 options.insert(Options::ENABLE_TASKLISTS);
16
17 let parser = MdParser::new_ext(md, options);
18 let mut renderer = MarkdownRenderer::new(output, preserve_fences);
19
20 for event in parser {
21 match event {
22 Event::Start(tag) => match tag {
23 Tag::Paragraph => {
24 }
26 Tag::Heading { level, .. } => {
27 let num = match level {
28 HeadingLevel::H1 => 1,
29 HeadingLevel::H2 => 2,
30 HeadingLevel::H3 => 3,
31 HeadingLevel::H4 => 4,
32 HeadingLevel::H5 => 5,
33 HeadingLevel::H6 => 6,
34 };
35 renderer.start_heading(num);
36 }
37 Tag::List(start_number) => {
38 renderer.start_list(start_number)?;
39 }
40 Tag::Item => {
41 renderer.render_list_item_start()?;
42 }
43 Tag::Emphasis => renderer.set_emphasis(true),
44 Tag::Strong => renderer.set_strong(true),
45 Tag::Strikethrough => renderer.set_strikethrough(true),
46 Tag::Link { dest_url, .. } => renderer.start_link(&dest_url),
47 Tag::Image { dest_url, .. } => renderer.start_image(&dest_url),
48 Tag::CodeBlock(kind) => {
49 renderer.start_code_block(match kind {
50 CodeBlockKind::Fenced(info) => pulldown_cmark::CodeBlockKind::Fenced(info),
51 CodeBlockKind::Indented => pulldown_cmark::CodeBlockKind::Indented,
52 })?;
53 }
54 Tag::BlockQuote(_) => {
55 renderer.start_blockquote()?;
56 }
57 Tag::Table(_) => {
58 renderer.start_table()?;
59 }
60 Tag::TableHead => renderer.start_table_head(),
61 Tag::TableRow => renderer.start_table_row(),
62 Tag::TableCell => renderer.start_table_cell(),
63 _ => {}
64 },
65 Event::End(tag_end) => match tag_end {
66 pulldown_cmark::TagEnd::TableCell => renderer.end_table_cell(),
67 pulldown_cmark::TagEnd::TableRow => renderer.end_table_row(),
68 pulldown_cmark::TagEnd::TableHead => renderer.end_table_head(),
69 pulldown_cmark::TagEnd::Table => renderer.end_table()?,
70 pulldown_cmark::TagEnd::Paragraph => renderer.end_paragraph()?,
71 pulldown_cmark::TagEnd::Heading(_) => renderer.end_heading()?,
72 pulldown_cmark::TagEnd::List(_) => renderer.end_list(),
73 pulldown_cmark::TagEnd::Item => renderer.end_item()?,
74 pulldown_cmark::TagEnd::Emphasis => renderer.set_emphasis(false),
75 pulldown_cmark::TagEnd::Strong => renderer.set_strong(false),
76 pulldown_cmark::TagEnd::Strikethrough => renderer.set_strikethrough(false),
77 pulldown_cmark::TagEnd::Link => renderer.end_link()?,
78 pulldown_cmark::TagEnd::Image => renderer.end_image()?,
79 pulldown_cmark::TagEnd::CodeBlock => renderer.end_code_block()?,
80 pulldown_cmark::TagEnd::BlockQuote(_) => renderer.end_blockquote(),
81 _ => {}
82 },
83 Event::Text(text) => renderer.write_event_text(&text)?,
84 Event::Code(code) => renderer.write_event_code(&code)?,
85 Event::Html(html) => {
86 write!(renderer.output, "{}", html)?;
87 }
88 Event::SoftBreak => renderer.soft_break()?,
89 Event::HardBreak => renderer.hard_break()?,
90 Event::Rule => renderer.render_rule()?,
91 Event::FootnoteReference(name) => {
92 let mut spec = ColorSpec::new();
93 spec.set_fg(Some(Color::Blue)).set_bold(true);
94 renderer.output.set_color(&spec)?;
95 write!(renderer.output, "[^{}]", name)?;
96 renderer.output.reset()?;
97 }
98 Event::TaskListMarker(checked) => renderer.render_task_list_item(checked)?,
99 _ => {}
100 }
101 }
102
103 renderer.flush()?;
104 Ok(())
105}