Skip to content

Commit 08a741e

Browse files
Add support for image, rules and footnotes
1 parent cf69238 commit 08a741e

File tree

2 files changed

+146
-28
lines changed

2 files changed

+146
-28
lines changed

src/librustdoc/html/markdown.rs

+111-28
Original file line numberDiff line numberDiff line change
@@ -133,11 +133,37 @@ macro_rules! event_loop_break {
133133
}}
134134
}
135135

136+
struct ParserWrapper<'a> {
137+
parser: Parser<'a>,
138+
footnotes: Vec<String>,
139+
current_footnote_id: u16,
140+
}
141+
142+
impl<'a> ParserWrapper<'a> {
143+
pub fn new(s: &'a str) -> ParserWrapper<'a> {
144+
ParserWrapper {
145+
parser: Parser::new_ext(s, pulldown_cmark::OPTION_ENABLE_TABLES |
146+
pulldown_cmark::OPTION_ENABLE_FOOTNOTES),
147+
footnotes: Vec::new(),
148+
current_footnote_id: 1,
149+
}
150+
}
151+
pub fn next(&mut self) -> Option<Event<'a>> {
152+
self.parser.next()
153+
}
154+
155+
pub fn get_next_footnote_id(&mut self) -> u16 {
156+
let tmp = self.current_footnote_id;
157+
self.current_footnote_id += 1;
158+
tmp
159+
}
160+
}
161+
136162
pub fn render(w: &mut fmt::Formatter,
137163
s: &str,
138164
print_toc: bool,
139165
shorter: MarkdownOutputStyle) -> fmt::Result {
140-
fn code_block(parser: &mut Parser, buffer: &mut String, lang: &str) {
166+
fn code_block(parser: &mut ParserWrapper, buffer: &mut String, lang: &str) {
141167
let mut origtext = String::new();
142168
while let Some(event) = parser.next() {
143169
match event {
@@ -215,8 +241,8 @@ pub fn render(w: &mut fmt::Formatter,
215241
});
216242
}
217243

218-
fn heading(parser: &mut Parser, buffer: &mut String, toc_builder: &mut Option<TocBuilder>,
219-
shorter: MarkdownOutputStyle, level: i32) {
244+
fn heading(parser: &mut ParserWrapper, buffer: &mut String,
245+
toc_builder: &mut Option<TocBuilder>, shorter: MarkdownOutputStyle, level: i32) {
220246
let mut ret = String::new();
221247
let mut id = String::new();
222248
event_loop_break!(parser, toc_builder, shorter, ret, true, &mut Some(&mut id),
@@ -249,32 +275,48 @@ pub fn render(w: &mut fmt::Formatter,
249275
ret, lvl = level, id = id, sec = sec));
250276
}
251277

252-
fn inline_code(parser: &mut Parser, buffer: &mut String, toc_builder: &mut Option<TocBuilder>,
253-
shorter: MarkdownOutputStyle, id: &mut Option<&mut String>) {
278+
fn inline_code(parser: &mut ParserWrapper, buffer: &mut String,
279+
toc_builder: &mut Option<TocBuilder>, shorter: MarkdownOutputStyle,
280+
id: &mut Option<&mut String>) {
254281
let mut content = String::new();
255282
event_loop_break!(parser, toc_builder, shorter, content, false, id, Event::End(Tag::Code));
256283
buffer.push_str(&format!("<code>{}</code>",
257284
Escape(&collapse_whitespace(content.trim_right()))));
258285
}
259286

260-
fn link(parser: &mut Parser, buffer: &mut String, toc_builder: &mut Option<TocBuilder>,
287+
fn link(parser: &mut ParserWrapper, buffer: &mut String, toc_builder: &mut Option<TocBuilder>,
288+
shorter: MarkdownOutputStyle, url: &str, title: &str,
289+
id: &mut Option<&mut String>) {
290+
let mut content = String::new();
291+
event_loop_break!(parser, toc_builder, shorter, content, true, id,
292+
Event::End(Tag::Link(_, _)));
293+
if title.is_empty() {
294+
buffer.push_str(&format!("<a href=\"{}\">{}</a>", url, content));
295+
} else {
296+
buffer.push_str(&format!("<a href=\"{}\" title=\"{}\">{}</a>",
297+
url, Escape(title), content));
298+
}
299+
}
300+
301+
fn image(parser: &mut ParserWrapper, buffer: &mut String, toc_builder: &mut Option<TocBuilder>,
261302
shorter: MarkdownOutputStyle, url: &str, mut title: String,
262303
id: &mut Option<&mut String>) {
263304
event_loop_break!(parser, toc_builder, shorter, title, true, id,
264-
Event::End(Tag::Link(_, _)));
265-
buffer.push_str(&format!("<a href=\"{}\">{}</a>", url, title));
305+
Event::End(Tag::Image(_, _)));
306+
buffer.push_str(&format!("<img src=\"{}\" alt=\"{}\">", url, title));
266307
}
267308

268-
fn paragraph(parser: &mut Parser, buffer: &mut String, toc_builder: &mut Option<TocBuilder>,
269-
shorter: MarkdownOutputStyle, id: &mut Option<&mut String>) {
309+
fn paragraph(parser: &mut ParserWrapper, buffer: &mut String,
310+
toc_builder: &mut Option<TocBuilder>, shorter: MarkdownOutputStyle,
311+
id: &mut Option<&mut String>) {
270312
let mut content = String::new();
271313
event_loop_break!(parser, toc_builder, shorter, content, true, id,
272314
Event::End(Tag::Paragraph));
273315
buffer.push_str(&format!("<p>{}</p>", content.trim_right()));
274316
}
275317

276-
fn table_cell(parser: &mut Parser, buffer: &mut String, toc_builder: &mut Option<TocBuilder>,
277-
shorter: MarkdownOutputStyle) {
318+
fn table_cell(parser: &mut ParserWrapper, buffer: &mut String,
319+
toc_builder: &mut Option<TocBuilder>, shorter: MarkdownOutputStyle) {
278320
let mut content = String::new();
279321
event_loop_break!(parser, toc_builder, shorter, content, true, &mut None,
280322
Event::End(Tag::TableHead) |
@@ -284,8 +326,8 @@ pub fn render(w: &mut fmt::Formatter,
284326
buffer.push_str(&format!("<td>{}</td>", content.trim()));
285327
}
286328

287-
fn table_row(parser: &mut Parser, buffer: &mut String, toc_builder: &mut Option<TocBuilder>,
288-
shorter: MarkdownOutputStyle) {
329+
fn table_row(parser: &mut ParserWrapper, buffer: &mut String,
330+
toc_builder: &mut Option<TocBuilder>, shorter: MarkdownOutputStyle) {
289331
let mut content = String::new();
290332
while let Some(event) = parser.next() {
291333
match event {
@@ -303,8 +345,8 @@ pub fn render(w: &mut fmt::Formatter,
303345
buffer.push_str(&format!("<tr>{}</tr>", content));
304346
}
305347

306-
fn table_head(parser: &mut Parser, buffer: &mut String, toc_builder: &mut Option<TocBuilder>,
307-
shorter: MarkdownOutputStyle) {
348+
fn table_head(parser: &mut ParserWrapper, buffer: &mut String,
349+
toc_builder: &mut Option<TocBuilder>, shorter: MarkdownOutputStyle) {
308350
let mut content = String::new();
309351
while let Some(event) = parser.next() {
310352
match event {
@@ -322,7 +364,7 @@ pub fn render(w: &mut fmt::Formatter,
322364
}
323365
}
324366

325-
fn table(parser: &mut Parser, buffer: &mut String, toc_builder: &mut Option<TocBuilder>,
367+
fn table(parser: &mut ParserWrapper, buffer: &mut String, toc_builder: &mut Option<TocBuilder>,
326368
shorter: MarkdownOutputStyle) {
327369
let mut content = String::new();
328370
let mut rows = String::new();
@@ -347,16 +389,16 @@ pub fn render(w: &mut fmt::Formatter,
347389
}));
348390
}
349391

350-
fn blockquote(parser: &mut Parser, buffer: &mut String, toc_builder: &mut Option<TocBuilder>,
351-
shorter: MarkdownOutputStyle) {
392+
fn blockquote(parser: &mut ParserWrapper, buffer: &mut String,
393+
toc_builder: &mut Option<TocBuilder>, shorter: MarkdownOutputStyle) {
352394
let mut content = String::new();
353395
event_loop_break!(parser, toc_builder, shorter, content, true, &mut None,
354396
Event::End(Tag::BlockQuote));
355397
buffer.push_str(&format!("<blockquote>{}</blockquote>", content.trim_right()));
356398
}
357399

358-
fn list_item(parser: &mut Parser, buffer: &mut String, toc_builder: &mut Option<TocBuilder>,
359-
shorter: MarkdownOutputStyle) {
400+
fn list_item(parser: &mut ParserWrapper, buffer: &mut String,
401+
toc_builder: &mut Option<TocBuilder>, shorter: MarkdownOutputStyle) {
360402
let mut content = String::new();
361403
while let Some(event) = parser.next() {
362404
match event {
@@ -372,7 +414,7 @@ pub fn render(w: &mut fmt::Formatter,
372414
buffer.push_str(&format!("<li>{}</li>", content));
373415
}
374416

375-
fn list(parser: &mut Parser, buffer: &mut String, toc_builder: &mut Option<TocBuilder>,
417+
fn list(parser: &mut ParserWrapper, buffer: &mut String, toc_builder: &mut Option<TocBuilder>,
376418
shorter: MarkdownOutputStyle) {
377419
let mut content = String::new();
378420
while let Some(event) = parser.next() {
@@ -389,23 +431,40 @@ pub fn render(w: &mut fmt::Formatter,
389431
buffer.push_str(&format!("<ul>{}</ul>", content));
390432
}
391433

392-
fn emphasis(parser: &mut Parser, buffer: &mut String, toc_builder: &mut Option<TocBuilder>,
393-
shorter: MarkdownOutputStyle, id: &mut Option<&mut String>) {
434+
fn emphasis(parser: &mut ParserWrapper, buffer: &mut String,
435+
toc_builder: &mut Option<TocBuilder>, shorter: MarkdownOutputStyle,
436+
id: &mut Option<&mut String>) {
394437
let mut content = String::new();
395438
event_loop_break!(parser, toc_builder, shorter, content, false, id,
396439
Event::End(Tag::Emphasis));
397440
buffer.push_str(&format!("<em>{}</em>", content));
398441
}
399442

400-
fn strong(parser: &mut Parser, buffer: &mut String, toc_builder: &mut Option<TocBuilder>,
443+
fn strong(parser: &mut ParserWrapper, buffer: &mut String, toc_builder: &mut Option<TocBuilder>,
401444
shorter: MarkdownOutputStyle, id: &mut Option<&mut String>) {
402445
let mut content = String::new();
403446
event_loop_break!(parser, toc_builder, shorter, content, false, id,
404447
Event::End(Tag::Strong));
405448
buffer.push_str(&format!("<strong>{}</strong>", content));
406449
}
407450

408-
fn looper<'a>(parser: &'a mut Parser, buffer: &mut String, next_event: Option<Event<'a>>,
451+
fn footnote(parser: &mut ParserWrapper, buffer: &mut String,
452+
toc_builder: &mut Option<TocBuilder>, shorter: MarkdownOutputStyle,
453+
mut definition: String, id: &mut Option<&mut String>) {
454+
event_loop_break!(parser, toc_builder, shorter, definition, true, id,
455+
Event::End(Tag::FootnoteDefinition(_)));
456+
buffer.push_str(&definition);
457+
}
458+
459+
fn rule(parser: &mut ParserWrapper, buffer: &mut String, toc_builder: &mut Option<TocBuilder>,
460+
shorter: MarkdownOutputStyle, id: &mut Option<&mut String>) {
461+
let mut content = String::new();
462+
event_loop_break!(parser, toc_builder, shorter, content, true, id,
463+
Event::End(Tag::Rule));
464+
buffer.push_str("<hr>");
465+
}
466+
467+
fn looper<'a>(parser: &'a mut ParserWrapper, buffer: &mut String, next_event: Option<Event<'a>>,
409468
toc_builder: &mut Option<TocBuilder>, shorter: MarkdownOutputStyle,
410469
id: &mut Option<&mut String>) -> bool {
411470
if let Some(event) = next_event {
@@ -423,7 +482,10 @@ pub fn render(w: &mut fmt::Formatter,
423482
paragraph(parser, buffer, toc_builder, shorter, id);
424483
}
425484
Event::Start(Tag::Link(ref url, ref t)) => {
426-
link(parser, buffer, toc_builder, shorter, url, t.as_ref().to_owned(), id);
485+
link(parser, buffer, toc_builder, shorter, url, t.as_ref(), id);
486+
}
487+
Event::Start(Tag::Image(ref url, ref t)) => {
488+
image(parser, buffer, toc_builder, shorter, url, t.as_ref().to_owned(), id);
427489
}
428490
Event::Start(Tag::Table(_)) => {
429491
table(parser, buffer, toc_builder, shorter);
@@ -440,6 +502,23 @@ pub fn render(w: &mut fmt::Formatter,
440502
Event::Start(Tag::Strong) => {
441503
strong(parser, buffer, toc_builder, shorter, id);
442504
}
505+
Event::Start(Tag::Rule) => {
506+
rule(parser, buffer, toc_builder, shorter, id);
507+
}
508+
Event::Start(Tag::FootnoteDefinition(ref def)) => {
509+
let mut content = String::new();
510+
footnote(parser, &mut content, toc_builder, shorter, def.as_ref().to_owned(),
511+
id);
512+
let cur_len = parser.footnotes.len() + 1;
513+
parser.footnotes.push(format!("<li id=\"ref{}\">{}<a href=\"#supref{0}\" \
514+
rev=\"footnote\">↩</a></li>",
515+
cur_len, content));
516+
}
517+
Event::FootnoteReference(_) => {
518+
buffer.push_str(&format!("<sup id=\"supref{0}\"><a href=\"#ref{0}\">{0}</a>\
519+
</sup>",
520+
parser.get_next_footnote_id()));
521+
}
443522
Event::Html(h) | Event::InlineHtml(h) => {
444523
buffer.push_str(&*h);
445524
}
@@ -457,13 +536,17 @@ pub fn render(w: &mut fmt::Formatter,
457536
None
458537
};
459538
let mut buffer = String::new();
460-
let mut parser = Parser::new_ext(s, pulldown_cmark::OPTION_ENABLE_TABLES);
539+
let mut parser = ParserWrapper::new(s);
461540
loop {
462541
let next_event = parser.next();
463542
if !looper(&mut parser, &mut buffer, next_event, &mut toc_builder, shorter, &mut None) {
464543
break
465544
}
466545
}
546+
if !parser.footnotes.is_empty() {
547+
buffer.push_str(&format!("<div class=\"footnotes\"><hr><ol>{}</ol></div>",
548+
parser.footnotes.join("")));
549+
}
467550
let mut ret = toc_builder.map_or(Ok(()), |builder| {
468551
write!(w, "<nav id=\"TOC\">{}</nav>", builder.into_toc())
469552
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![crate_name = "foo"]
12+
13+
// @has foo/fn.f.html
14+
// @has - '<p>hard break: after hard break</p><hr>'
15+
// @has - '<img src="https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png" alt="Rust">'
16+
// @has - '<li id="ref1">'
17+
// @has - '<sup id="supref1"><a href="#ref1">1</a></sup>'
18+
/// markdown test
19+
///
20+
/// this is a [link].
21+
///
22+
/// [link]: https://example.com "this is a title"
23+
///
24+
/// hard break:
25+
/// after hard break
26+
///
27+
/// -----------
28+
///
29+
/// a footnote[^footnote].
30+
///
31+
/// [^footnote]: Thing
32+
///
33+
/// ![Rust](https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png)
34+
#[deprecated(note = "Struct<T>")]
35+
pub fn f() {}

0 commit comments

Comments
 (0)