]> git.proxmox.com Git - rustc.git/blob - src/vendor/pulldown-cmark/src/html.rs
New upstream version 1.18.0+dfsg1
[rustc.git] / src / vendor / pulldown-cmark / src / html.rs
1 // Copyright 2015 Google Inc. All rights reserved.
2 //
3 // Permission is hereby granted, free of charge, to any person obtaining a copy
4 // of this software and associated documentation files (the "Software"), to deal
5 // in the Software without restriction, including without limitation the rights
6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 // copies of the Software, and to permit persons to whom the Software is
8 // furnished to do so, subject to the following conditions:
9 //
10 // The above copyright notice and this permission notice shall be included in
11 // all copies or substantial portions of the Software.
12 //
13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 // THE SOFTWARE.
20
21 //! HTML renderer that takes an iterator of events as input.
22
23 use std::borrow::Cow;
24 use std::collections::HashMap;
25 use std::fmt::Write;
26
27 use parse::{Event, Tag};
28 use parse::Event::{Start, End, Text, Html, InlineHtml, SoftBreak, HardBreak, FootnoteReference};
29 use parse::Alignment;
30 use escape::{escape_html, escape_href};
31
32 enum TableState {
33 Head,
34 Body,
35 }
36
37 struct Ctx<'b, I> {
38 iter: I,
39 buf: &'b mut String,
40 table_state: TableState,
41 table_alignments: Vec<Alignment>,
42 table_cell_index: usize,
43 }
44
45 impl<'a, 'b, I: Iterator<Item=Event<'a>>> Ctx<'b, I> {
46 fn fresh_line(&mut self) {
47 if !(self.buf.is_empty() || self.buf.ends_with('\n')) {
48 self.buf.push('\n');
49 }
50 }
51
52 pub fn run(&mut self) {
53 let mut numbers = HashMap::new();
54 while let Some(event) = self.iter.next() {
55 match event {
56 Start(tag) => self.start_tag(tag, &mut numbers),
57 End(tag) => self.end_tag(tag),
58 Text(text) => escape_html(self.buf, &text, false),
59 Html(html) |
60 InlineHtml(html) => self.buf.push_str(&html),
61 SoftBreak => self.buf.push('\n'),
62 HardBreak => self.buf.push_str("<br />\n"),
63 FootnoteReference(name) => {
64 let len = numbers.len() + 1;
65 self.buf.push_str("<sup class=\"footnote-reference\"><a href=\"#");
66 escape_html(self.buf, &*name, false);
67 self.buf.push_str("\">");
68 let number = numbers.entry(name).or_insert(len);
69 self.buf.push_str(&*format!("{}", number));
70 self.buf.push_str("</a></sup>");
71 },
72 }
73 }
74 }
75
76 fn start_tag(&mut self, tag: Tag<'a>, numbers: &mut HashMap<Cow<'a, str>, usize>) {
77 match tag {
78 Tag::Paragraph => {
79 self.fresh_line();
80 self.buf.push_str("<p>");
81 }
82 Tag::Rule => {
83 self.fresh_line();
84 self.buf.push_str("<hr />\n")
85 }
86 Tag::Header(level) => {
87 self.fresh_line();
88 self.buf.push_str("<h");
89 self.buf.push((b'0' + level as u8) as char);
90 self.buf.push('>');
91 }
92 Tag::Table(alignments) => {
93 self.table_alignments = alignments;
94 self.buf.push_str("<table>");
95 }
96 Tag::TableHead => {
97 self.table_state = TableState::Head;
98 self.buf.push_str("<thead><tr>");
99 }
100 Tag::TableRow => {
101 self.table_cell_index = 0;
102 self.buf.push_str("<tr>");
103 }
104 Tag::TableCell => {
105 match self.table_state {
106 TableState::Head => self.buf.push_str("<th"),
107 TableState::Body => self.buf.push_str("<td"),
108 }
109 match self.table_alignments.get(self.table_cell_index) {
110 Some(&Alignment::Left) => self.buf.push_str(" align=\"left\""),
111 Some(&Alignment::Center) => self.buf.push_str(" align=\"center\""),
112 Some(&Alignment::Right) => self.buf.push_str(" align=\"right\""),
113 _ => (),
114 }
115 self.buf.push_str(">");
116 }
117 Tag::BlockQuote => {
118 self.fresh_line();
119 self.buf.push_str("<blockquote>\n");
120 }
121 Tag::CodeBlock(info) => {
122 self.fresh_line();
123 let lang = info.split(' ').next().unwrap();
124 if lang.is_empty() {
125 self.buf.push_str("<pre><code>");
126 } else {
127 self.buf.push_str("<pre><code class=\"language-");
128 escape_html(self.buf, lang, false);
129 self.buf.push_str("\">");
130 }
131 }
132 Tag::List(Some(1)) => {
133 self.fresh_line();
134 self.buf.push_str("<ol>\n");
135 }
136 Tag::List(Some(start)) => {
137 self.fresh_line();
138 let _ = write!(self.buf, "<ol start=\"{}\">\n", start);
139 }
140 Tag::List(None) => {
141 self.fresh_line();
142 self.buf.push_str("<ul>\n");
143 }
144 Tag::Item => {
145 self.fresh_line();
146 self.buf.push_str("<li>");
147 }
148 Tag::Emphasis => self.buf.push_str("<em>"),
149 Tag::Strong => self.buf.push_str("<strong>"),
150 Tag::Code => self.buf.push_str("<code>"),
151 Tag::Link(dest, title) => {
152 self.buf.push_str("<a href=\"");
153 escape_href(self.buf, &dest);
154 if !title.is_empty() {
155 self.buf.push_str("\" title=\"");
156 escape_html(self.buf, &title, false);
157 }
158 self.buf.push_str("\">");
159 }
160 Tag::Image(dest, title) => {
161 self.buf.push_str("<img src=\"");
162 escape_href(self.buf, &dest);
163 self.buf.push_str("\" alt=\"");
164 self.raw_text(numbers);
165 if !title.is_empty() {
166 self.buf.push_str("\" title=\"");
167 escape_html(self.buf, &title, false);
168 }
169 self.buf.push_str("\" />")
170 }
171 Tag::FootnoteDefinition(name) => {
172 self.fresh_line();
173 let len = numbers.len() + 1;
174 self.buf.push_str("<div class=\"footnote-definition\" id=\"");
175 escape_html(self.buf, &*name, false);
176 self.buf.push_str("\"><sup class=\"footnote-definition-label\">");
177 let number = numbers.entry(name).or_insert(len);
178 self.buf.push_str(&*format!("{}", number));
179 self.buf.push_str("</sup>");
180 }
181 }
182 }
183
184 fn end_tag(&mut self, tag: Tag) {
185 match tag {
186 Tag::Paragraph => self.buf.push_str("</p>\n"),
187 Tag::Rule => (),
188 Tag::Header(level) => {
189 self.buf.push_str("</h");
190 self.buf.push((b'0' + level as u8) as char);
191 self.buf.push_str(">\n");
192 }
193 Tag::Table(_) => {
194 self.buf.push_str("</tbody></table>\n");
195 }
196 Tag::TableHead => {
197 self.buf.push_str("</tr></thead><tbody>\n");
198 self.table_state = TableState::Body;
199 }
200 Tag::TableRow => {
201 self.buf.push_str("</tr>\n");
202 }
203 Tag::TableCell => {
204 match self.table_state {
205 TableState::Head => self.buf.push_str("</th>"),
206 TableState::Body => self.buf.push_str("</td>"),
207 }
208 self.table_cell_index += 1;
209 }
210 Tag::BlockQuote => self.buf.push_str("</blockquote>\n"),
211 Tag::CodeBlock(_) => self.buf.push_str("</code></pre>\n"),
212 Tag::List(Some(_)) => self.buf.push_str("</ol>\n"),
213 Tag::List(None) => self.buf.push_str("</ul>\n"),
214 Tag::Item => self.buf.push_str("</li>\n"),
215 Tag::Emphasis => self.buf.push_str("</em>"),
216 Tag::Strong => self.buf.push_str("</strong>"),
217 Tag::Code => self.buf.push_str("</code>"),
218 Tag::Link(_, _) => self.buf.push_str("</a>"),
219 Tag::Image(_, _) => (), // shouldn't happen, handled in start
220 Tag::FootnoteDefinition(_) => self.buf.push_str("</div>\n"),
221 }
222 }
223
224 // run raw text, consuming end tag
225 fn raw_text<'c>(&mut self, numbers: &'c mut HashMap<Cow<'a, str>, usize>) {
226 let mut nest = 0;
227 while let Some(event) = self.iter.next() {
228 match event {
229 Start(_) => nest += 1,
230 End(_) => {
231 if nest == 0 { break; }
232 nest -= 1;
233 }
234 Text(text) => escape_html(self.buf, &text, false),
235 Html(_) => (),
236 InlineHtml(html) => escape_html(self.buf, &html, false),
237 SoftBreak | HardBreak => self.buf.push(' '),
238 FootnoteReference(name) => {
239 let len = numbers.len() + 1;
240 let number = numbers.entry(name).or_insert(len);
241 self.buf.push_str(&*format!("[{}]", number));
242 }
243 }
244 }
245 }
246 }
247
248 pub fn push_html<'a, I: Iterator<Item=Event<'a>>>(buf: &mut String, iter: I) {
249 let mut ctx = Ctx {
250 iter: iter,
251 buf: buf,
252 table_state: TableState::Head,
253 table_alignments: vec![],
254 table_cell_index: 0,
255 };
256 ctx.run();
257 }