From f9fb381b2a13260c3fcdae2f6d9546ab2c87e717 Mon Sep 17 00:00:00 2001
From: Oliver Middleton
Date: Thu, 6 Apr 2017 13:09:20 +0100
Subject: [PATCH] rustdoc: Use pulldown-cmark for Markdown HTML rendering
Instead of rendering all of the HTML in rustdoc this relies on
pulldown-cmark's `push_html` to do most of the work. A few iterator
adapters are used to make rustdoc specific modifications to the output.
This also fixes MarkdownHtml and link titles in plain_summary_line.
---
src/librustdoc/html/markdown.rs | 728 +++++++-----------
src/librustdoc/html/render.rs | 12 +-
src/librustdoc/markdown.rs | 4 +-
src/test/rustdoc/check-hard-break.rs | 3 +-
src/test/rustdoc/check-rule-image-footnote.rs | 14 +-
src/test/rustdoc/test-lists.rs | 14 +-
src/tools/error_index_generator/main.rs | 4 +-
7 files changed, 296 insertions(+), 483 deletions(-)
diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs
index 245a3946a3709..1e687d63f5875 100644
--- a/src/librustdoc/html/markdown.rs
+++ b/src/librustdoc/html/markdown.rs
@@ -16,10 +16,10 @@
//! of `fmt::Display`. Example usage:
//!
//! ```rust,ignore
-//! use rustdoc::html::markdown::{Markdown, MarkdownOutputStyle};
+//! use rustdoc::html::markdown::Markdown;
//!
//! let s = "My *markdown* _text_";
-//! let html = format!("{}", Markdown(s, MarkdownOutputStyle::Fancy));
+//! let html = format!("{}", Markdown(s));
//! // ... something using html
//! ```
@@ -27,7 +27,7 @@
use std::ascii::AsciiExt;
use std::cell::RefCell;
-use std::collections::HashMap;
+use std::collections::{HashMap, VecDeque};
use std::default::Default;
use std::fmt::{self, Write};
use std::str;
@@ -37,43 +37,23 @@ use syntax::codemap::Span;
use html::render::derive_id;
use html::toc::TocBuilder;
use html::highlight;
-use html::escape::Escape;
use test;
-use pulldown_cmark::{self, Event, Parser, Tag};
-
-#[derive(Copy, Clone)]
-pub enum MarkdownOutputStyle {
- Compact,
- Fancy,
-}
-
-impl MarkdownOutputStyle {
- pub fn is_compact(&self) -> bool {
- match *self {
- MarkdownOutputStyle::Compact => true,
- _ => false,
- }
- }
-
- pub fn is_fancy(&self) -> bool {
- match *self {
- MarkdownOutputStyle::Fancy => true,
- _ => false,
- }
- }
-}
+use pulldown_cmark::{html, Event, Tag, Parser};
+use pulldown_cmark::{Options, OPTION_ENABLE_FOOTNOTES, OPTION_ENABLE_TABLES};
/// A unit struct which has the `fmt::Display` trait implemented. When
/// formatted, this struct will emit the HTML corresponding to the rendered
/// version of the contained markdown string.
// The second parameter is whether we need a shorter version or not.
-pub struct Markdown<'a>(pub &'a str, pub MarkdownOutputStyle);
+pub struct Markdown<'a>(pub &'a str);
/// A unit struct like `Markdown`, that renders the markdown with a
/// table of contents.
pub struct MarkdownWithToc<'a>(pub &'a str);
/// A unit struct like `Markdown`, that renders the markdown escaping HTML tags.
pub struct MarkdownHtml<'a>(pub &'a str);
+/// A unit struct like `Markdown`, that renders only the first paragraph.
+pub struct MarkdownSummaryLine<'a>(pub &'a str);
/// Returns Some(code) if `s` is a line that should be stripped from
/// documentation but used in example code. `code` is the portion of
@@ -90,12 +70,21 @@ fn stripped_filtered_line<'a>(s: &'a str) -> Option<&'a str> {
}
}
-/// Returns a new string with all consecutive whitespace collapsed into
-/// single spaces.
+/// Convert chars from a title for an id.
///
-/// Any leading or trailing whitespace will be trimmed.
-fn collapse_whitespace(s: &str) -> String {
- s.split_whitespace().collect::>().join(" ")
+/// "Hello, world!" -> "hello-world"
+fn slugify(c: char) -> Option {
+ if c.is_alphanumeric() || c == '-' || c == '_' {
+ if c.is_ascii() {
+ Some(c.to_ascii_lowercase())
+ } else {
+ Some(c)
+ }
+ } else if c.is_whitespace() && c.is_ascii() {
+ Some('-')
+ } else {
+ None
+ }
}
// Information about the playground if a URL has been specified, containing an
@@ -104,103 +93,50 @@ thread_local!(pub static PLAYGROUND: RefCell, String)>> =
RefCell::new(None)
});
-macro_rules! event_loop_break {
- ($parser:expr, $toc_builder:expr, $shorter:expr, $buf:expr, $escape:expr, $id:expr,
- $($end_event:pat)|*) => {{
- fn inner(id: &mut Option<&mut String>, s: &str) {
- if let Some(ref mut id) = *id {
- id.push_str(s);
- }
- }
- while let Some(event) = $parser.next() {
- match event {
- $($end_event)|* => break,
- Event::Text(ref s) => {
- debug!("Text");
- inner($id, s);
- if $escape {
- $buf.push_str(&format!("{}", Escape(s)));
- } else {
- $buf.push_str(s);
- }
- }
- Event::SoftBreak => {
- debug!("SoftBreak");
- if !$buf.is_empty() {
- $buf.push(' ');
- }
- }
- x => {
- looper($parser, &mut $buf, Some(x), $toc_builder, $shorter, $id);
- }
- }
- }
- }}
-}
-
-struct ParserWrapper<'a> {
- parser: Parser<'a>,
- // The key is the footnote reference. The value is the footnote definition and the id.
- footnotes: HashMap,
+/// Adds syntax highlighting and playground Run buttons to rust code blocks.
+struct CodeBlocks<'a, I: Iterator- >> {
+ inner: I,
}
-impl<'a> ParserWrapper<'a> {
- pub fn new(s: &'a str) -> ParserWrapper<'a> {
- ParserWrapper {
- parser: Parser::new_ext(s, pulldown_cmark::OPTION_ENABLE_TABLES |
- pulldown_cmark::OPTION_ENABLE_FOOTNOTES),
- footnotes: HashMap::new(),
+impl<'a, I: Iterator
- >> CodeBlocks<'a, I> {
+ fn new(iter: I) -> Self {
+ CodeBlocks {
+ inner: iter,
}
}
+}
- pub fn next(&mut self) -> Option
> {
- self.parser.next()
- }
+impl<'a, I: Iterator- >> Iterator for CodeBlocks<'a, I> {
+ type Item = Event<'a>;
- pub fn get_entry(&mut self, key: &str) -> &mut (String, u16) {
- let new_id = self.footnotes.keys().count() + 1;
- let key = key.to_owned();
- self.footnotes.entry(key).or_insert((String::new(), new_id as u16))
- }
-}
+ fn next(&mut self) -> Option
{
+ let event = self.inner.next();
+ if let Some(Event::Start(Tag::CodeBlock(lang))) = event {
+ if !LangString::parse(&lang).rust {
+ return Some(Event::Start(Tag::CodeBlock(lang)));
+ }
+ } else {
+ return event;
+ }
-pub fn render(w: &mut fmt::Formatter,
- s: &str,
- print_toc: bool,
- shorter: MarkdownOutputStyle) -> fmt::Result {
- fn code_block(parser: &mut ParserWrapper, buffer: &mut String, lang: &str) {
- debug!("CodeBlock");
let mut origtext = String::new();
- while let Some(event) = parser.next() {
+ for event in &mut self.inner {
match event {
- Event::End(Tag::CodeBlock(_)) => break,
+ Event::End(Tag::CodeBlock(..)) => break,
Event::Text(ref s) => {
origtext.push_str(s);
}
_ => {}
}
}
- let origtext = origtext.trim_left();
- debug!("docblock: ==============\n{:?}\n=======", origtext);
-
let lines = origtext.lines().filter(|l| {
stripped_filtered_line(*l).is_none()
});
let text = lines.collect::>().join("\n");
- let block_info = if lang.is_empty() {
- LangString::all_false()
- } else {
- LangString::parse(lang)
- };
- if !block_info.rust {
- buffer.push_str(&format!("{}
",
- lang, text));
- return
- }
PLAYGROUND.with(|play| {
// insert newline to clearly separate it from the
// previous block so we can shorten the html output
- buffer.push('\n');
+ let mut s = String::from("\n");
let playground_button = play.borrow().as_ref().and_then(|&(ref krate, ref url)| {
if url.is_empty() {
return None;
@@ -210,7 +146,7 @@ pub fn render(w: &mut fmt::Formatter,
}).collect::>().join("\n");
let krate = krate.as_ref().map(|s| &**s);
let test = test::maketest(&test, krate, false,
- &Default::default());
+ &Default::default());
let channel = if test.contains("#![feature(") {
"&version=nightly"
} else {
@@ -239,376 +175,186 @@ pub fn render(w: &mut fmt::Formatter,
url, test_escaped, channel
))
});
- buffer.push_str(&highlight::render_with_highlighting(
- &text,
- Some("rust-example-rendered"),
- None,
- playground_button.as_ref().map(String::as_str)));
- });
- }
-
- fn heading(parser: &mut ParserWrapper, buffer: &mut String,
- toc_builder: &mut Option, shorter: MarkdownOutputStyle, level: i32) {
- debug!("Heading");
- let mut ret = String::new();
- let mut id = String::new();
- event_loop_break!(parser, toc_builder, shorter, ret, true, &mut Some(&mut id),
- Event::End(Tag::Header(_)));
- ret = ret.trim_right().to_owned();
-
- let id = id.chars().filter_map(|c| {
- if c.is_alphanumeric() || c == '-' || c == '_' {
- if c.is_ascii() {
- Some(c.to_ascii_lowercase())
- } else {
- Some(c)
- }
- } else if c.is_whitespace() && c.is_ascii() {
- Some('-')
- } else {
- None
- }
- }).collect::();
-
- let id = derive_id(id);
-
- let sec = toc_builder.as_mut().map_or("".to_owned(), |builder| {
- format!("{} ", builder.push(level as u32, ret.clone(), id.clone()))
- });
-
- // Render the HTML
- buffer.push_str(&format!("",
- ret, lvl = level, id = id, sec = sec));
+ s.push_str(&highlight::render_with_highlighting(
+ &text,
+ Some("rust-example-rendered"),
+ None,
+ playground_button.as_ref().map(String::as_str)));
+ Some(Event::Html(s.into()))
+ })
}
+}
- fn inline_code(parser: &mut ParserWrapper, buffer: &mut String,
- toc_builder: &mut Option, shorter: MarkdownOutputStyle,
- id: &mut Option<&mut String>) {
- debug!("InlineCode");
- let mut content = String::new();
- event_loop_break!(parser, toc_builder, shorter, content, false, id, Event::End(Tag::Code));
- buffer.push_str(&format!("{}
",
- Escape(&collapse_whitespace(content.trim_right()))));
- }
+/// Make headings links with anchor ids and build up TOC.
+struct HeadingLinks<'a, 'b, I: Iterator- >> {
+ inner: I,
+ toc: Option<&'b mut TocBuilder>,
+ buf: VecDeque
>,
+}
- fn link(parser: &mut ParserWrapper, buffer: &mut String, toc_builder: &mut Option,
- shorter: MarkdownOutputStyle, url: &str, title: &str,
- id: &mut Option<&mut String>) {
- debug!("Link");
- let mut content = String::new();
- event_loop_break!(parser, toc_builder, shorter, content, true, id,
- Event::End(Tag::Link(_, _)));
- if title.is_empty() {
- buffer.push_str(&format!("{} ", url, content));
- } else {
- buffer.push_str(&format!("{} ",
- url, Escape(title), content));
+impl<'a, 'b, I: Iterator- >> HeadingLinks<'a, 'b, I> {
+ fn new(iter: I, toc: Option<&'b mut TocBuilder>) -> Self {
+ HeadingLinks {
+ inner: iter,
+ toc: toc,
+ buf: VecDeque::new(),
}
}
+}
- fn image(parser: &mut ParserWrapper, buffer: &mut String, toc_builder: &mut Option
,
- shorter: MarkdownOutputStyle, url: &str, mut title: String,
- id: &mut Option<&mut String>) {
- debug!("Image");
- event_loop_break!(parser, toc_builder, shorter, title, true, id,
- Event::End(Tag::Image(_, _)));
- buffer.push_str(&format!(" ", url, title));
- }
-
- fn paragraph(parser: &mut ParserWrapper, buffer: &mut String,
- toc_builder: &mut Option, shorter: MarkdownOutputStyle,
- id: &mut Option<&mut String>) {
- debug!("Paragraph");
- let mut content = String::new();
- event_loop_break!(parser, toc_builder, shorter, content, true, id,
- Event::End(Tag::Paragraph));
- buffer.push_str(&format!("{}
", content.trim_right()));
- }
-
- fn table_cell(parser: &mut ParserWrapper, buffer: &mut String,
- toc_builder: &mut Option, shorter: MarkdownOutputStyle) {
- debug!("TableCell");
- let mut content = String::new();
- event_loop_break!(parser, toc_builder, shorter, content, true, &mut None,
- Event::End(Tag::TableHead) |
- Event::End(Tag::Table(_)) |
- Event::End(Tag::TableRow) |
- Event::End(Tag::TableCell));
- buffer.push_str(&format!("{} ", content.trim()));
- }
+impl<'a, 'b, I: Iterator- >> Iterator for HeadingLinks<'a, 'b, I> {
+ type Item = Event<'a>;
- fn table_row(parser: &mut ParserWrapper, buffer: &mut String,
- toc_builder: &mut Option
, shorter: MarkdownOutputStyle) {
- debug!("TableRow");
- let mut content = String::new();
- while let Some(event) = parser.next() {
- match event {
- Event::End(Tag::TableHead) |
- Event::End(Tag::Table(_)) |
- Event::End(Tag::TableRow) => break,
- Event::Start(Tag::TableCell) => {
- table_cell(parser, &mut content, toc_builder, shorter);
- }
- x => {
- looper(parser, &mut content, Some(x), toc_builder, shorter, &mut None);
- }
- }
+ fn next(&mut self) -> Option {
+ if let Some(e) = self.buf.pop_front() {
+ return Some(e);
}
- buffer.push_str(&format!("{} ", content));
- }
- fn table_head(parser: &mut ParserWrapper, buffer: &mut String,
- toc_builder: &mut Option, shorter: MarkdownOutputStyle) {
- debug!("TableHead");
- let mut content = String::new();
- while let Some(event) = parser.next() {
- match event {
- Event::End(Tag::TableHead) | Event::End(Tag::Table(_)) => break,
- Event::Start(Tag::TableCell) => {
- table_cell(parser, &mut content, toc_builder, shorter);
- }
- x => {
- looper(parser, &mut content, Some(x), toc_builder, shorter, &mut None);
+ let event = self.inner.next();
+ if let Some(Event::Start(Tag::Header(level))) = event {
+ let mut id = String::new();
+ for event in &mut self.inner {
+ match event {
+ Event::End(Tag::Header(..)) => break,
+ Event::Text(ref text) => id.extend(text.chars().filter_map(slugify)),
+ _ => {},
}
+ self.buf.push_back(event);
}
- }
- if !content.is_empty() {
- buffer.push_str(&format!("{} ", content.replace("td>", "th>")));
- }
- }
+ let id = derive_id(id);
- fn table(parser: &mut ParserWrapper, buffer: &mut String, toc_builder: &mut Option,
- shorter: MarkdownOutputStyle) {
- debug!("Table");
- let mut content = String::new();
- let mut rows = String::new();
- while let Some(event) = parser.next() {
- match event {
- Event::End(Tag::Table(_)) => break,
- Event::Start(Tag::TableHead) => {
- table_head(parser, &mut content, toc_builder, shorter);
- }
- Event::Start(Tag::TableRow) => {
- table_row(parser, &mut rows, toc_builder, shorter);
- }
- _ => {}
+ if let Some(ref mut builder) = self.toc {
+ let mut html_header = String::new();
+ html::push_html(&mut html_header, self.buf.iter().cloned());
+ let sec = builder.push(level as u32, html_header, id.clone());
+ self.buf.push_front(Event::InlineHtml(format!("{} ", sec).into()));
}
- }
- buffer.push_str(&format!("",
- content,
- if shorter.is_compact() || rows.is_empty() {
- String::new()
- } else {
- format!("{} ", rows)
- }));
- }
- fn blockquote(parser: &mut ParserWrapper, buffer: &mut String,
- toc_builder: &mut Option, shorter: MarkdownOutputStyle) {
- debug!("BlockQuote");
- let mut content = String::new();
- event_loop_break!(parser, toc_builder, shorter, content, true, &mut None,
- Event::End(Tag::BlockQuote));
- buffer.push_str(&format!("{} ", content.trim_right()));
- }
+ self.buf.push_back(Event::InlineHtml(format!("", level).into()));
- fn list_item(parser: &mut ParserWrapper, buffer: &mut String,
- toc_builder: &mut Option, shorter: MarkdownOutputStyle) {
- debug!("ListItem");
- let mut content = String::new();
- while let Some(event) = parser.next() {
- match event {
- Event::End(Tag::Item) => break,
- Event::Text(ref s) => {
- content.push_str(&format!("{}", Escape(s)));
- }
- x => {
- looper(parser, &mut content, Some(x), toc_builder, shorter, &mut None);
- }
- }
- if shorter.is_compact() {
- break
- }
+ let start_tags = format!("
",
- cur_id,
- if content.ends_with("") {
- &content[..content.len() - 4]
- } else {
- &content
- }));
- }
- Event::FootnoteReference(ref reference) => {
- debug!("FootnoteReference");
- let entry = parser.get_entry(reference.as_ref());
- buffer.push_str(&format!("{0} \
- ",
- (*entry).1));
- }
- Event::HardBreak => {
- debug!("HardBreak");
- if shorter.is_fancy() {
- buffer.push_str(" ");
- } else if !buffer.is_empty() {
- buffer.push(' ');
+impl<'a, I: Iterator- >> Iterator for Footnotes<'a, I> {
+ type Item = Event<'a>;
+
+ fn next(&mut self) -> Option
{
+ loop {
+ match self.inner.next() {
+ Some(Event::FootnoteReference(ref reference)) => {
+ let entry = self.get_entry(&reference);
+ let reference = format!("{0}\
+ ",
+ (*entry).1);
+ return Some(Event::Html(reference.into()));
+ }
+ Some(Event::Start(Tag::FootnoteDefinition(def))) => {
+ let mut content = Vec::new();
+ for event in &mut self.inner {
+ if let Event::End(Tag::FootnoteDefinition(..)) = event {
+ break;
+ }
+ content.push(event);
+ }
+ let entry = self.get_entry(&def);
+ (*entry).0 = content;
+ }
+ Some(e) => return Some(e),
+ None => {
+ if !self.footnotes.is_empty() {
+ let mut v: Vec<_> = self.footnotes.drain().map(|(_, x)| x).collect();
+ v.sort_by(|a, b| a.1.cmp(&b.1));
+ let mut ret = String::from("");
+ return Some(Event::Html(ret.into()));
+ } else {
+ return None;
}
}
- Event::Html(h) | Event::InlineHtml(h) => {
- debug!("Html/InlineHtml");
- buffer.push_str(&*h);
- }
- _ => {}
}
- shorter.is_fancy()
- } else {
- false
}
}
-
- let mut toc_builder = if print_toc {
- Some(TocBuilder::new())
- } else {
- None
- };
- let mut buffer = String::new();
- let mut parser = ParserWrapper::new(s);
- loop {
- let next_event = parser.next();
- if !looper(&mut parser, &mut buffer, next_event, &mut toc_builder, shorter, &mut None) {
- break
- }
- }
- if !parser.footnotes.is_empty() {
- let mut v: Vec<_> = parser.footnotes.values().collect();
- v.sort_by(|a, b| a.1.cmp(&b.1));
- buffer.push_str(&format!("",
- v.iter()
- .map(|s| s.0.as_str())
- .collect::>()
- .join("")));
- }
- let mut ret = toc_builder.map_or(Ok(()), |builder| {
- write!(w, "{} ", builder.into_toc())
- });
-
- if ret.is_ok() {
- ret = w.write_str(&buffer);
- }
- ret
}
pub fn find_testable_code(doc: &str, tests: &mut ::test::Collector, position: Span) {
@@ -755,17 +501,45 @@ impl LangString {
impl<'a> fmt::Display for Markdown<'a> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
- let Markdown(md, shorter) = *self;
+ let Markdown(md) = *self;
// This is actually common enough to special-case
if md.is_empty() { return Ok(()) }
- render(fmt, md, false, shorter)
+
+ let mut opts = Options::empty();
+ opts.insert(OPTION_ENABLE_TABLES);
+ opts.insert(OPTION_ENABLE_FOOTNOTES);
+
+ let p = Parser::new_ext(md, opts);
+
+ let mut s = String::with_capacity(md.len() * 3 / 2);
+
+ html::push_html(&mut s,
+ Footnotes::new(CodeBlocks::new(HeadingLinks::new(p, None))));
+
+ fmt.write_str(&s)
}
}
impl<'a> fmt::Display for MarkdownWithToc<'a> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
let MarkdownWithToc(md) = *self;
- render(fmt, md, true, MarkdownOutputStyle::Fancy)
+
+ let mut opts = Options::empty();
+ opts.insert(OPTION_ENABLE_TABLES);
+ opts.insert(OPTION_ENABLE_FOOTNOTES);
+
+ let p = Parser::new_ext(md, opts);
+
+ let mut s = String::with_capacity(md.len() * 3 / 2);
+
+ let mut toc = TocBuilder::new();
+
+ html::push_html(&mut s,
+ Footnotes::new(CodeBlocks::new(HeadingLinks::new(p, Some(&mut toc)))));
+
+ write!(fmt, "{} ", toc.into_toc())?;
+
+ fmt.write_str(&s)
}
}
@@ -774,7 +548,41 @@ impl<'a> fmt::Display for MarkdownHtml<'a> {
let MarkdownHtml(md) = *self;
// This is actually common enough to special-case
if md.is_empty() { return Ok(()) }
- render(fmt, md, false, MarkdownOutputStyle::Fancy)
+
+ let mut opts = Options::empty();
+ opts.insert(OPTION_ENABLE_TABLES);
+ opts.insert(OPTION_ENABLE_FOOTNOTES);
+
+ let p = Parser::new_ext(md, opts);
+
+ // Treat inline HTML as plain text.
+ let p = p.map(|event| match event {
+ Event::Html(text) | Event::InlineHtml(text) => Event::Text(text),
+ _ => event
+ });
+
+ let mut s = String::with_capacity(md.len() * 3 / 2);
+
+ html::push_html(&mut s,
+ Footnotes::new(CodeBlocks::new(HeadingLinks::new(p, None))));
+
+ fmt.write_str(&s)
+ }
+}
+
+impl<'a> fmt::Display for MarkdownSummaryLine<'a> {
+ fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+ let MarkdownSummaryLine(md) = *self;
+ // This is actually common enough to special-case
+ if md.is_empty() { return Ok(()) }
+
+ let p = Parser::new(md);
+
+ let mut s = String::new();
+
+ html::push_html(&mut s, SummaryLine::new(p));
+
+ fmt.write_str(&s)
}
}
@@ -796,14 +604,10 @@ pub fn plain_summary_line(md: &str) -> String {
let next_event = next_event.unwrap();
let (ret, is_in) = match next_event {
Event::Start(Tag::Paragraph) => (None, 1),
- Event::Start(Tag::Link(_, ref t)) if !self.is_first => {
- (Some(t.as_ref().to_owned()), 1)
- }
Event::Start(Tag::Code) => (Some("`".to_owned()), 1),
Event::End(Tag::Code) => (Some("`".to_owned()), -1),
Event::Start(Tag::Header(_)) => (None, 1),
Event::Text(ref s) if self.is_in > 0 => (Some(s.as_ref().to_owned()), 0),
- Event::End(Tag::Link(_, ref t)) => (Some(t.as_ref().to_owned()), -1),
Event::End(Tag::Paragraph) | Event::End(Tag::Header(_)) => (None, -1),
_ => (None, 0),
};
@@ -834,7 +638,7 @@ pub fn plain_summary_line(md: &str) -> String {
#[cfg(test)]
mod tests {
- use super::{LangString, Markdown, MarkdownHtml, MarkdownOutputStyle};
+ use super::{LangString, Markdown, MarkdownHtml};
use super::plain_summary_line;
use html::render::reset_ids;
@@ -874,14 +678,14 @@ mod tests {
#[test]
fn issue_17736() {
let markdown = "# title";
- format!("{}", Markdown(markdown, MarkdownOutputStyle::Fancy));
+ format!("{}", Markdown(markdown));
reset_ids(true);
}
#[test]
fn test_header() {
fn t(input: &str, expect: &str) {
- let output = format!("{}", Markdown(input, MarkdownOutputStyle::Fancy));
+ let output = format!("{}", Markdown(input));
assert_eq!(output, expect, "original: {}", input);
reset_ids(true);
}
@@ -903,7 +707,7 @@ mod tests {
#[test]
fn test_header_ids_multiple_blocks() {
fn t(input: &str, expect: &str) {
- let output = format!("{}", Markdown(input, MarkdownOutputStyle::Fancy));
+ let output = format!("{}", Markdown(input));
assert_eq!(output, expect, "original: {}", input);
}
@@ -934,6 +738,7 @@ mod tests {
}
t("hello [Rust](https://www.rust-lang.org) :)", "hello Rust :)");
+ t("hello [Rust](https://www.rust-lang.org \"Rust\") :)", "hello Rust :)");
t("code `let x = i32;` ...", "code `let x = i32;` ...");
t("type `Type<'static>` ...", "type `Type<'static>` ...");
t("# top header", "top header");
@@ -947,7 +752,8 @@ mod tests {
assert_eq!(output, expect, "original: {}", input);
}
- t("`Struct<'a, T>`", "Struct<'a, T>
");
- t("Struct<'a, T>", "Struct<'a, T>
");
+ t("`Struct<'a, T>`", "Struct<'a, T>
\n");
+ t("Struct<'a, T>", "Struct<'a, T>
\n");
+ t("Struct ", "Struct<br>
\n");
}
}
diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs
index f0b624105e347..1e1202f04005c 100644
--- a/src/librustdoc/html/render.rs
+++ b/src/librustdoc/html/render.rs
@@ -72,7 +72,7 @@ use html::format::{TyParamBounds, WhereClause, href, AbiSpace};
use html::format::{VisSpace, Method, UnsafetySpace, MutableSpace};
use html::format::fmt_impl_for_trait_page;
use html::item_type::ItemType;
-use html::markdown::{self, Markdown, MarkdownHtml, MarkdownOutputStyle};
+use html::markdown::{self, Markdown, MarkdownHtml, MarkdownSummaryLine};
use html::{highlight, layout};
/// A pair of name and its optional document.
@@ -1651,7 +1651,7 @@ fn document_short(w: &mut fmt::Formatter, item: &clean::Item, link: AssocItemLin
format!("{}", &plain_summary_line(Some(s)))
};
write!(w, "{}
",
- Markdown(&markdown, MarkdownOutputStyle::Fancy))?;
+ Markdown(&markdown))?;
}
Ok(())
}
@@ -1684,8 +1684,7 @@ fn get_doc_value(item: &clean::Item) -> Option<&str> {
fn document_full(w: &mut fmt::Formatter, item: &clean::Item) -> fmt::Result {
if let Some(s) = get_doc_value(item) {
write!(w, "{}
",
- Markdown(&format!("{}{}", md_render_assoc_item(item), s),
- MarkdownOutputStyle::Fancy))?;
+ Markdown(&format!("{}{}", md_render_assoc_item(item), s)))?;
}
Ok(())
}
@@ -1873,8 +1872,7 @@ fn item_module(w: &mut fmt::Formatter, cx: &Context,
",
name = *myitem.name.as_ref().unwrap(),
stab_docs = stab_docs,
- docs = shorter(Some(&Markdown(doc_value,
- MarkdownOutputStyle::Compact).to_string())),
+ docs = MarkdownSummaryLine(doc_value),
class = myitem.type_(),
stab = myitem.stability_class().unwrap_or("".to_string()),
unsafety_flag = unsafety_flag,
@@ -2904,7 +2902,7 @@ fn render_impl(w: &mut fmt::Formatter, cx: &Context, i: &Impl, link: AssocItemLi
write!(w, "")?;
write!(w, "\n")?;
if let Some(ref dox) = i.impl_item.doc_value() {
- write!(w, "{}
", Markdown(dox, MarkdownOutputStyle::Fancy))?;
+ write!(w, "{}
", Markdown(dox))?;
}
}
diff --git a/src/librustdoc/markdown.rs b/src/librustdoc/markdown.rs
index 5cc0f03e1f629..5fadda030a4b4 100644
--- a/src/librustdoc/markdown.rs
+++ b/src/librustdoc/markdown.rs
@@ -25,7 +25,7 @@ use externalfiles::{ExternalHtml, LoadStringError, load_string};
use html::render::reset_ids;
use html::escape::Escape;
use html::markdown;
-use html::markdown::{Markdown, MarkdownWithToc, MarkdownOutputStyle, find_testable_code};
+use html::markdown::{Markdown, MarkdownWithToc, find_testable_code};
use test::{TestOptions, Collector};
/// Separate any lines at the start of the file that begin with `# ` or `%`.
@@ -96,7 +96,7 @@ pub fn render(input: &str, mut output: PathBuf, matches: &getopts::Matches,
let rendered = if include_toc {
format!("{}", MarkdownWithToc(text))
} else {
- format!("{}", Markdown(text, MarkdownOutputStyle::Fancy))
+ format!("{}", Markdown(text))
};
let err = write!(
diff --git a/src/test/rustdoc/check-hard-break.rs b/src/test/rustdoc/check-hard-break.rs
index 5c5e3f8136c73..f048b64d104ab 100644
--- a/src/test/rustdoc/check-hard-break.rs
+++ b/src/test/rustdoc/check-hard-break.rs
@@ -13,7 +13,8 @@
// ignore-tidy-end-whitespace
// @has foo/fn.f.html
-// @has - 'hard break: after hard break
'
+// @has - 'hard break: '
+// @has - 'after hard break
'
/// hard break:
/// after hard break
pub fn f() {}
diff --git a/src/test/rustdoc/check-rule-image-footnote.rs b/src/test/rustdoc/check-rule-image-footnote.rs
index 4d3bea20ba895..46542677857fc 100644
--- a/src/test/rustdoc/check-rule-image-footnote.rs
+++ b/src/test/rustdoc/check-rule-image-footnote.rs
@@ -13,16 +13,21 @@
// ignore-tidy-linelength
// @has foo/fn.f.html
-// @has - 'markdown test
this is a link .
hard break: after hard break
a footnote1 .
another footnote2 .
'
+// @has - 'markdown test
'
+// @has - 'this is a link .
'
+// @has - ' '
+// @has - 'a footnote1 .
'
+// @has - 'another footnote2 .
'
+// @has - '
'
+// @has - ''
/// markdown test
///
/// this is a [link].
///
/// [link]: https://example.com "this is a title"
///
-/// hard break:
-/// after hard break
-///
/// -----------
///
/// a footnote[^footnote].
@@ -36,5 +41,4 @@
///
///
/// ![Rust](https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png)
-#[deprecated(note = "Struct")]
pub fn f() {}
diff --git a/src/test/rustdoc/test-lists.rs b/src/test/rustdoc/test-lists.rs
index 71a826a2bed7f..29f157e0425c9 100644
--- a/src/test/rustdoc/test-lists.rs
+++ b/src/test/rustdoc/test-lists.rs
@@ -10,10 +10,11 @@
#![crate_name = "foo"]
-// ignore-tidy-linelength
-
// @has foo/fn.f.html
-// @has - "pub fn f() listfooooo x foo "
+// @has - //ol/li "list"
+// @has - //ol/li/ol/li "fooooo"
+// @has - //ol/li/ol/li "x"
+// @has - //ol/li "foo"
/// 1. list
/// 1. fooooo
/// 2. x
@@ -21,7 +22,10 @@
pub fn f() {}
// @has foo/fn.foo2.html
-// @has - "
pub fn foo2() normal listsub list
new elem still same elem
and again same elem!
new big elem "
+// @has - //ul/li "normal list"
+// @has - //ul/li/ul/li "sub list"
+// @has - //ul/li/ul/li "new elem still same elem and again same elem!"
+// @has - //ul/li "new big elem"
/// * normal list
/// * sub list
/// * new elem
@@ -29,4 +33,4 @@ pub fn f() {}
///
/// and again same elem!
/// * new big elem
-pub fn foo2() {}
\ No newline at end of file
+pub fn foo2() {}
diff --git a/src/tools/error_index_generator/main.rs b/src/tools/error_index_generator/main.rs
index 5db2ad83a0a7a..efadde992277f 100644
--- a/src/tools/error_index_generator/main.rs
+++ b/src/tools/error_index_generator/main.rs
@@ -24,7 +24,7 @@ use std::path::PathBuf;
use syntax::diagnostics::metadata::{get_metadata_dir, ErrorMetadataMap, ErrorMetadata};
-use rustdoc::html::markdown::{Markdown, MarkdownOutputStyle, PLAYGROUND};
+use rustdoc::html::markdown::{Markdown, PLAYGROUND};
use rustc_serialize::json;
enum OutputFormat {
@@ -100,7 +100,7 @@ impl Formatter for HTMLFormatter {
// Description rendered as markdown.
match info.description {
- Some(ref desc) => write!(output, "{}", Markdown(desc, MarkdownOutputStyle::Fancy))?,
+ Some(ref desc) => write!(output, "{}", Markdown(desc))?,
None => write!(output, "
No description.
\n")?,
}