prettify_cmark/printer.rs
1use std::fmt::{Result, Write};
2
3use pulldown_cmark::{Event, Tag};
4
5use writer::{Frame, Writer};
6
7/// Event-driven pretty printer for CommonMark documents.
8///
9/// The printer can be driven by pushing events into it, which can be obtained
10/// using `pulldown_cmark::Parser`.
11///
12/// # Examples
13///
14/// ```rust
15/// extern crate pulldown_cmark;
16/// extern crate prettify_cmark;
17///
18/// use pulldown_cmark::Parser;
19/// use prettify_cmark::PrettyPrinter;
20///
21/// fn main() {
22/// let events = Parser::new("Lorem _ipsum_!\n\nDolor `sit`.");
23/// let mut printer = PrettyPrinter::new_with_prefix(String::new(), "///");
24/// printer.push_events(events).unwrap();
25///
26/// assert_eq!(printer.into_inner(), "/// Lorem *ipsum*!\n///\n/// Dolor `sit`.")
27/// }
28/// ```
29pub struct PrettyPrinter<W = String> {
30 writer: Writer<W>,
31 needs_break: bool
32}
33
34impl<W: Write> PrettyPrinter<W> {
35 /// Create a new pretty printer that wraps around a writer.
36 pub fn new(write: W) -> PrettyPrinter<W> {
37 PrettyPrinter::new_with_prefix(write, "")
38 }
39
40 /// Create a new pretty printer with a prefix that wraps around
41 /// a writer.
42 ///
43 /// The prefix will be applied to all lines that are produced by
44 /// the printer.
45 pub fn new_with_prefix(write: W, prefix: &str) -> PrettyPrinter<W> {
46 PrettyPrinter {
47 writer: Writer::new(write, prefix.to_string()),
48 needs_break: false
49 }
50 }
51
52 /// Push a single event into the printer.
53 ///
54 /// Events can be obtained using `pulldown_cmark::Parser`.
55 pub fn push_event<'a>(&mut self, event: Event<'a>) -> Result {
56 match event {
57 Event::Start(tag) => {
58 match tag {
59 Tag::Paragraph => {
60 self.flush_break()?;
61 },
62 Tag::Rule => {
63 self.flush_break()?;
64 self.writer.write_text("---")?;
65 },
66 Tag::Header(indent) => {
67 self.flush_break()?;
68 self.writer.write_text(&"#".repeat(indent as usize))?;
69 self.writer.write_non_breaking_space()?;
70 },
71 Tag::List(start) => {
72 self.flush_break()?;
73 self.writer.push_frame(Frame::ListItem(start));
74 },
75 Tag::Item => {
76 match self.writer.pop_frame() {
77 Some(Frame::ListItem(None)) => {
78 self.flush_break()?;
79 self.writer.write_text("-")?;
80 self.writer.write_non_breaking_space()?;
81 self.writer.push_frame(Frame::ListItem(None));
82 },
83 Some(Frame::ListItem(Some(index))) => {
84 self.flush_break()?;
85 write!(self.writer, "{}.", index)?;
86 self.writer.write_non_breaking_space()?;
87 self.writer.push_frame(Frame::ListItem(Some(index + 1)));
88 },
89 _ => {}
90 }
91 },
92 Tag::BlockQuote => {
93 self.flush_break()?;
94 self.writer.write_text(">")?;
95 self.writer.write_non_breaking_space()?;
96 self.writer.push_frame(Frame::BlockQuote);
97 },
98 Tag::CodeBlock(note) => {
99 self.flush_break()?;
100 write!(self.writer, "```{}", note)?;
101 self.writer.write_hard_break()?;
102 self.writer.write_indent()?;
103 },
104 Tag::Emphasis => {
105 self.writer.write_text("*")?;
106 },
107 Tag::Strong => {
108 self.writer.write_text("**")?;
109 },
110 Tag::Code => {
111 self.writer.write_text("`")?;
112 },
113 Tag::Link(_, _) => {
114 self.writer.write_text("[")?;
115 },
116 Tag::Image(_, _) => {
117 self.writer.write_text("", url)?;
164 } else {
165 write!(self.writer, "]({} \"{}\")", url, title)?;
166 }
167 },
168 Tag::FootnoteDefinition(_) => { /* not supported for now */ },
169 Tag::Table(_) => { /* not supported for now */ },
170 Tag::TableHead => { /* not supported for now */ },
171 Tag::TableRow => { /* not supported for now */ },
172 Tag::TableCell => { /* not supported for now */ }
173 }
174 },
175 Event::Text(text) => {
176 for (i, line) in text.split('\n').enumerate() {
177 if i > 0 {
178 self.writer.write_hard_break()?;
179 self.writer.write_indent()?;
180 }
181 self.writer.write_text(line)?;
182 }
183 },
184 Event::Html(_html) => {
185 // not supported for now
186 },
187 Event::InlineHtml(html) => {
188 self.writer.write_text(html.as_ref())?
189 },
190 Event::FootnoteReference(_) => {
191 // not supported for now
192 },
193 Event::SoftBreak => {
194 self.writer.write_soft_break()?
195 },
196 Event::HardBreak => {
197 self.writer.write_text("\\")?;
198 self.writer.write_hard_break()?;
199 self.writer.write_indent()?;
200 }
201 };
202
203 Ok(())
204 }
205
206 /// Push a series of events into the printer.
207 ///
208 /// Events can be obtained using `pulldown_cmark::Parser`.
209 pub fn push_events<'a, I>(&mut self, events: I) -> Result
210 where I: IntoIterator<Item=Event<'a>>
211 {
212 for event in events {
213 self.push_event(event)?;
214 }
215 Ok(())
216 }
217
218 /// Unwrap the printer, returning the underlying writer.
219 pub fn into_inner(self) -> W {
220 self.writer.into_inner()
221 }
222
223 fn flush_break(&mut self) -> Result {
224 if self.needs_break {
225 self.writer.write_hard_break()?;
226 self.writer.write_indent()?;
227 self.writer.write_hard_break()?;
228 self.writer.write_indent()?;
229 }
230 self.needs_break = false;
231 Ok(())
232 }
233}
234
235impl Default for PrettyPrinter {
236 fn default() -> PrettyPrinter {
237 PrettyPrinter::new(String::new())
238 }
239}