2019-02-08 13:53:55 +00:00
|
|
|
//! Markdown formatting for rustdoc.
|
2013-10-03 17:24:40 +00:00
|
|
|
//!
|
2017-03-28 17:54:11 +00:00
|
|
|
//! This module implements markdown formatting through the pulldown-cmark
|
|
|
|
//! rust-library. This module exposes all of the
|
2019-02-08 13:53:55 +00:00
|
|
|
//! functionality through a unit struct, `Markdown`, which has an implementation
|
2015-12-02 23:06:26 +00:00
|
|
|
//! of `fmt::Display`. Example usage:
|
2013-10-03 17:24:40 +00:00
|
|
|
//!
|
2017-06-20 07:15:16 +00:00
|
|
|
//! ```
|
2017-09-02 14:02:32 +00:00
|
|
|
//! #![feature(rustc_private)]
|
|
|
|
//!
|
2018-07-22 13:25:00 +00:00
|
|
|
//! use rustdoc::html::markdown::{IdMap, Markdown, ErrorCodes};
|
|
|
|
//! use std::cell::RefCell;
|
2014-01-25 05:00:31 +00:00
|
|
|
//!
|
2013-10-03 17:24:40 +00:00
|
|
|
//! let s = "My *markdown* _text_";
|
2018-07-22 13:25:00 +00:00
|
|
|
//! let mut id_map = IdMap::new();
|
|
|
|
//! let html = format!("{}", Markdown(s, &[], RefCell::new(&mut id_map), ErrorCodes::Yes));
|
2013-10-03 17:24:40 +00:00
|
|
|
//! // ... something using html
|
|
|
|
//! ```
|
|
|
|
|
2014-03-22 01:05:05 +00:00
|
|
|
#![allow(non_camel_case_types)]
|
2014-02-10 14:36:31 +00:00
|
|
|
|
2018-08-18 10:55:43 +00:00
|
|
|
use rustc_data_structures::fx::FxHashMap;
|
2015-01-12 09:37:01 +00:00
|
|
|
use std::cell::RefCell;
|
2018-08-18 10:55:43 +00:00
|
|
|
use std::collections::VecDeque;
|
2015-04-07 01:39:39 +00:00
|
|
|
use std::default::Default;
|
2016-10-11 08:56:30 +00:00
|
|
|
use std::fmt::{self, Write};
|
2018-06-03 10:22:24 +00:00
|
|
|
use std::borrow::Cow;
|
|
|
|
use std::ops::Range;
|
2014-04-29 03:36:08 +00:00
|
|
|
use std::str;
|
2018-09-18 06:55:15 +00:00
|
|
|
use syntax::edition::Edition;
|
2013-09-19 05:18:38 +00:00
|
|
|
|
2019-02-23 07:40:07 +00:00
|
|
|
use crate::html::toc::TocBuilder;
|
|
|
|
use crate::html::highlight;
|
|
|
|
use crate::test;
|
2014-02-20 09:14:51 +00:00
|
|
|
|
2019-04-20 17:03:59 +00:00
|
|
|
use pulldown_cmark::{html, CowStr, Event, Options, Parser, Tag};
|
|
|
|
|
|
|
|
fn opts() -> Options {
|
|
|
|
Options::ENABLE_TABLES | Options::ENABLE_FOOTNOTES
|
|
|
|
}
|
2017-03-11 00:43:36 +00:00
|
|
|
|
2015-12-02 23:06:26 +00:00
|
|
|
/// A unit struct which has the `fmt::Display` trait implemented. When
|
2013-10-03 17:24:40 +00:00
|
|
|
/// formatted, this struct will emit the HTML corresponding to the rendered
|
|
|
|
/// version of the contained markdown string.
|
2019-04-18 01:17:12 +00:00
|
|
|
///
|
|
|
|
/// The second parameter is a list of link replacements.
|
|
|
|
///
|
|
|
|
/// The third is the current list of used header IDs.
|
|
|
|
///
|
|
|
|
/// The fourth is whether to allow the use of explicit error codes in doctest lang strings.
|
|
|
|
///
|
|
|
|
/// The fifth is what default edition to use when parsing doctests (to add a `fn main`).
|
2018-07-22 13:25:00 +00:00
|
|
|
pub struct Markdown<'a>(
|
2019-04-18 01:17:12 +00:00
|
|
|
pub &'a str, pub &'a [(String, String)], pub RefCell<&'a mut IdMap>, pub ErrorCodes, pub Edition);
|
2014-03-07 14:13:17 +00:00
|
|
|
/// A unit struct like `Markdown`, that renders the markdown with a
|
|
|
|
/// table of contents.
|
2019-04-18 01:17:12 +00:00
|
|
|
pub struct MarkdownWithToc<'a>(pub &'a str, pub RefCell<&'a mut IdMap>, pub ErrorCodes, pub Edition);
|
2016-12-12 23:18:22 +00:00
|
|
|
/// A unit struct like `Markdown`, that renders the markdown escaping HTML tags.
|
2019-04-18 01:17:12 +00:00
|
|
|
pub struct MarkdownHtml<'a>(pub &'a str, pub RefCell<&'a mut IdMap>, pub ErrorCodes, pub Edition);
|
2017-04-06 12:09:20 +00:00
|
|
|
/// A unit struct like `Markdown`, that renders only the first paragraph.
|
2017-12-28 10:59:20 +00:00
|
|
|
pub struct MarkdownSummaryLine<'a>(pub &'a str, pub &'a [(String, String)]);
|
2013-09-19 05:18:38 +00:00
|
|
|
|
2018-07-22 17:10:19 +00:00
|
|
|
#[derive(Copy, Clone, PartialEq, Debug)]
|
|
|
|
pub enum ErrorCodes {
|
|
|
|
Yes,
|
|
|
|
No,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ErrorCodes {
|
|
|
|
pub fn from(b: bool) -> Self {
|
|
|
|
match b {
|
|
|
|
true => ErrorCodes::Yes,
|
|
|
|
false => ErrorCodes::No,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn as_bool(self) -> bool {
|
|
|
|
match self {
|
|
|
|
ErrorCodes::Yes => true,
|
|
|
|
ErrorCodes::No => false,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-06 13:08:41 +00:00
|
|
|
/// Controls whether a line will be hidden or shown in HTML output.
|
|
|
|
///
|
|
|
|
/// All lines are used in documentation tests.
|
|
|
|
enum Line<'a> {
|
|
|
|
Hidden(&'a str),
|
2018-06-26 01:26:31 +00:00
|
|
|
Shown(Cow<'a, str>),
|
2017-05-06 13:08:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> Line<'a> {
|
2018-06-26 01:26:31 +00:00
|
|
|
fn for_html(self) -> Option<Cow<'a, str>> {
|
2017-05-06 13:08:41 +00:00
|
|
|
match self {
|
|
|
|
Line::Shown(l) => Some(l),
|
|
|
|
Line::Hidden(_) => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-26 01:26:31 +00:00
|
|
|
fn for_code(self) -> Cow<'a, str> {
|
2017-05-06 13:08:41 +00:00
|
|
|
match self {
|
2018-06-26 01:26:31 +00:00
|
|
|
Line::Shown(l) => l,
|
|
|
|
Line::Hidden(l) => Cow::Borrowed(l),
|
2017-05-06 13:08:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// FIXME: There is a minor inconsistency here. For lines that start with ##, we
|
|
|
|
// have no easy way of removing a potential single space after the hashes, which
|
|
|
|
// is done in the single # case. This inconsistency seems okay, if non-ideal. In
|
|
|
|
// order to fix it we'd have to iterate to find the first non-# character, and
|
|
|
|
// then reallocate to remove it; which would make us return a String.
|
2019-02-23 07:40:07 +00:00
|
|
|
fn map_line(s: &str) -> Line<'_> {
|
2013-12-30 05:31:24 +00:00
|
|
|
let trimmed = s.trim();
|
2017-05-06 13:08:41 +00:00
|
|
|
if trimmed.starts_with("##") {
|
2018-06-26 01:26:31 +00:00
|
|
|
Line::Shown(Cow::Owned(s.replacen("##", "#", 1)))
|
2015-04-07 22:30:05 +00:00
|
|
|
} else if trimmed.starts_with("# ") {
|
2017-05-06 13:08:41 +00:00
|
|
|
// # text
|
|
|
|
Line::Hidden(&trimmed[2..])
|
|
|
|
} else if trimmed == "#" {
|
|
|
|
// We cannot handle '#text' because it could be #[attr].
|
|
|
|
Line::Hidden("")
|
2013-12-30 05:31:24 +00:00
|
|
|
} else {
|
2018-06-26 01:26:31 +00:00
|
|
|
Line::Shown(Cow::Borrowed(s))
|
2013-12-30 05:31:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-06 12:09:20 +00:00
|
|
|
/// Convert chars from a title for an id.
|
2015-04-06 17:06:39 +00:00
|
|
|
///
|
2017-04-06 12:09:20 +00:00
|
|
|
/// "Hello, world!" -> "hello-world"
|
|
|
|
fn slugify(c: char) -> Option<char> {
|
|
|
|
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
|
|
|
|
}
|
2015-04-06 17:06:39 +00:00
|
|
|
}
|
|
|
|
|
2016-10-11 08:56:30 +00:00
|
|
|
// Information about the playground if a URL has been specified, containing an
|
|
|
|
// optional crate name and the URL.
|
|
|
|
thread_local!(pub static PLAYGROUND: RefCell<Option<(Option<String>, String)>> = {
|
2014-11-14 22:20:57 +00:00
|
|
|
RefCell::new(None)
|
2014-11-14 17:18:10 +00:00
|
|
|
});
|
2014-03-04 19:24:20 +00:00
|
|
|
|
2019-02-08 13:53:55 +00:00
|
|
|
/// Adds syntax highlighting and playground Run buttons to Rust code blocks.
|
2017-04-06 12:09:20 +00:00
|
|
|
struct CodeBlocks<'a, I: Iterator<Item = Event<'a>>> {
|
|
|
|
inner: I,
|
2018-07-22 17:10:19 +00:00
|
|
|
check_error_codes: ErrorCodes,
|
2019-04-18 01:17:12 +00:00
|
|
|
edition: Edition,
|
2017-03-30 01:48:06 +00:00
|
|
|
}
|
|
|
|
|
2017-04-06 12:09:20 +00:00
|
|
|
impl<'a, I: Iterator<Item = Event<'a>>> CodeBlocks<'a, I> {
|
2019-04-18 01:17:12 +00:00
|
|
|
fn new(iter: I, error_codes: ErrorCodes, edition: Edition) -> Self {
|
2017-04-06 12:09:20 +00:00
|
|
|
CodeBlocks {
|
|
|
|
inner: iter,
|
2018-07-22 17:10:19 +00:00
|
|
|
check_error_codes: error_codes,
|
2019-04-18 01:17:12 +00:00
|
|
|
edition,
|
2017-03-30 01:48:06 +00:00
|
|
|
}
|
|
|
|
}
|
2017-04-06 12:09:20 +00:00
|
|
|
}
|
2017-03-30 23:29:54 +00:00
|
|
|
|
2017-04-06 12:09:20 +00:00
|
|
|
impl<'a, I: Iterator<Item = Event<'a>>> Iterator for CodeBlocks<'a, I> {
|
|
|
|
type Item = Event<'a>;
|
2017-03-30 01:48:06 +00:00
|
|
|
|
2017-04-06 12:09:20 +00:00
|
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
|
|
let event = self.inner.next();
|
2017-09-06 22:08:39 +00:00
|
|
|
let compile_fail;
|
|
|
|
let ignore;
|
2018-09-19 09:27:36 +00:00
|
|
|
let edition;
|
2017-04-06 12:09:20 +00:00
|
|
|
if let Some(Event::Start(Tag::CodeBlock(lang))) = event {
|
2018-07-21 22:12:16 +00:00
|
|
|
let parse_result = LangString::parse(&lang, self.check_error_codes);
|
2017-09-06 22:08:39 +00:00
|
|
|
if !parse_result.rust {
|
2017-04-06 12:09:20 +00:00
|
|
|
return Some(Event::Start(Tag::CodeBlock(lang)));
|
|
|
|
}
|
2017-09-06 22:08:39 +00:00
|
|
|
compile_fail = parse_result.compile_fail;
|
|
|
|
ignore = parse_result.ignore;
|
2018-09-19 09:27:36 +00:00
|
|
|
edition = parse_result.edition;
|
2017-04-06 12:09:20 +00:00
|
|
|
} else {
|
|
|
|
return event;
|
|
|
|
}
|
2017-03-30 01:48:06 +00:00
|
|
|
|
2019-04-18 01:17:12 +00:00
|
|
|
let explicit_edition = edition.is_some();
|
|
|
|
let edition = edition.unwrap_or(self.edition);
|
|
|
|
|
2017-03-08 00:01:23 +00:00
|
|
|
let mut origtext = String::new();
|
2017-04-06 12:09:20 +00:00
|
|
|
for event in &mut self.inner {
|
2017-03-11 00:43:36 +00:00
|
|
|
match event {
|
2017-04-06 12:09:20 +00:00
|
|
|
Event::End(Tag::CodeBlock(..)) => break,
|
2017-03-11 00:43:36 +00:00
|
|
|
Event::Text(ref s) => {
|
|
|
|
origtext.push_str(s);
|
2017-03-08 00:01:23 +00:00
|
|
|
}
|
2017-03-11 00:43:36 +00:00
|
|
|
_ => {}
|
2017-03-08 00:01:23 +00:00
|
|
|
}
|
|
|
|
}
|
2017-05-06 13:08:41 +00:00
|
|
|
let lines = origtext.lines().filter_map(|l| map_line(l).for_html());
|
2019-02-23 07:40:07 +00:00
|
|
|
let text = lines.collect::<Vec<Cow<'_, str>>>().join("\n");
|
2017-03-08 00:01:23 +00:00
|
|
|
PLAYGROUND.with(|play| {
|
|
|
|
// insert newline to clearly separate it from the
|
|
|
|
// previous block so we can shorten the html output
|
2017-04-06 12:09:20 +00:00
|
|
|
let mut s = String::from("\n");
|
2017-03-08 00:01:23 +00:00
|
|
|
let playground_button = play.borrow().as_ref().and_then(|&(ref krate, ref url)| {
|
|
|
|
if url.is_empty() {
|
|
|
|
return None;
|
|
|
|
}
|
2017-05-06 13:08:41 +00:00
|
|
|
let test = origtext.lines()
|
|
|
|
.map(|l| map_line(l).for_code())
|
2019-02-23 07:40:07 +00:00
|
|
|
.collect::<Vec<Cow<'_, str>>>().join("\n");
|
2017-03-08 00:01:23 +00:00
|
|
|
let krate = krate.as_ref().map(|s| &**s);
|
2018-01-08 14:47:23 +00:00
|
|
|
let (test, _) = test::make_test(&test, krate, false,
|
2019-04-18 01:17:12 +00:00
|
|
|
&Default::default(), edition);
|
2017-03-08 00:01:23 +00:00
|
|
|
let channel = if test.contains("#![feature(") {
|
|
|
|
"&version=nightly"
|
|
|
|
} else {
|
|
|
|
""
|
|
|
|
};
|
2018-09-19 09:27:36 +00:00
|
|
|
|
2019-04-18 01:17:12 +00:00
|
|
|
let edition_string = format!("&edition={}", edition);
|
2018-09-19 09:27:36 +00:00
|
|
|
|
2017-03-08 00:01:23 +00:00
|
|
|
// These characters don't need to be escaped in a URI.
|
|
|
|
// FIXME: use a library function for percent encoding.
|
|
|
|
fn dont_escape(c: u8) -> bool {
|
|
|
|
(b'a' <= c && c <= b'z') ||
|
|
|
|
(b'A' <= c && c <= b'Z') ||
|
|
|
|
(b'0' <= c && c <= b'9') ||
|
|
|
|
c == b'-' || c == b'_' || c == b'.' ||
|
|
|
|
c == b'~' || c == b'!' || c == b'\'' ||
|
|
|
|
c == b'(' || c == b')' || c == b'*'
|
|
|
|
}
|
|
|
|
let mut test_escaped = String::new();
|
|
|
|
for b in test.bytes() {
|
|
|
|
if dont_escape(b) {
|
|
|
|
test_escaped.push(char::from(b));
|
|
|
|
} else {
|
|
|
|
write!(test_escaped, "%{:02X}", b).unwrap();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Some(format!(
|
2018-09-19 09:27:36 +00:00
|
|
|
r#"<a class="test-arrow" target="_blank" href="{}?code={}{}{}">Run</a>"#,
|
|
|
|
url, test_escaped, channel, edition_string
|
2017-03-08 00:01:23 +00:00
|
|
|
))
|
|
|
|
});
|
2018-09-19 09:27:36 +00:00
|
|
|
|
2017-09-09 13:33:57 +00:00
|
|
|
let tooltip = if ignore {
|
2018-09-19 09:27:36 +00:00
|
|
|
Some(("This example is not tested".to_owned(), "ignore"))
|
2017-09-06 22:08:39 +00:00
|
|
|
} else if compile_fail {
|
2018-09-19 09:27:36 +00:00
|
|
|
Some(("This example deliberately fails to compile".to_owned(), "compile_fail"))
|
2019-04-18 01:17:12 +00:00
|
|
|
} else if explicit_edition {
|
|
|
|
Some((format!("This code runs with edition {}", edition), "edition"))
|
2017-09-06 22:08:39 +00:00
|
|
|
} else {
|
|
|
|
None
|
|
|
|
};
|
2018-09-19 09:27:36 +00:00
|
|
|
|
|
|
|
if let Some((s1, s2)) = tooltip {
|
|
|
|
s.push_str(&highlight::render_with_highlighting(
|
|
|
|
&text,
|
|
|
|
Some(&format!("rust-example-rendered{}",
|
|
|
|
if ignore { " ignore" }
|
|
|
|
else if compile_fail { " compile_fail" }
|
2019-04-18 01:17:12 +00:00
|
|
|
else if explicit_edition { " edition " }
|
2018-09-19 09:27:36 +00:00
|
|
|
else { "" })),
|
|
|
|
playground_button.as_ref().map(String::as_str),
|
|
|
|
Some((s1.as_str(), s2))));
|
|
|
|
Some(Event::Html(s.into()))
|
|
|
|
} else {
|
|
|
|
s.push_str(&highlight::render_with_highlighting(
|
|
|
|
&text,
|
|
|
|
Some(&format!("rust-example-rendered{}",
|
|
|
|
if ignore { " ignore" }
|
|
|
|
else if compile_fail { " compile_fail" }
|
2019-04-18 01:17:12 +00:00
|
|
|
else if explicit_edition { " edition " }
|
2018-09-19 09:27:36 +00:00
|
|
|
else { "" })),
|
|
|
|
playground_button.as_ref().map(String::as_str),
|
|
|
|
None));
|
|
|
|
Some(Event::Html(s.into()))
|
|
|
|
}
|
2017-04-06 12:09:20 +00:00
|
|
|
})
|
2013-12-22 19:23:04 +00:00
|
|
|
}
|
2017-04-06 12:09:20 +00:00
|
|
|
}
|
2014-05-03 00:56:35 +00:00
|
|
|
|
2019-02-08 13:53:55 +00:00
|
|
|
/// Make headings links with anchor IDs and build up TOC.
|
2017-12-28 10:59:20 +00:00
|
|
|
struct LinkReplacer<'a, 'b, I: Iterator<Item = Event<'a>>> {
|
|
|
|
inner: I,
|
2018-03-19 22:25:55 +00:00
|
|
|
links: &'b [(String, String)],
|
2017-12-28 10:59:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a, 'b, I: Iterator<Item = Event<'a>>> LinkReplacer<'a, 'b, I> {
|
|
|
|
fn new(iter: I, links: &'b [(String, String)]) -> Self {
|
|
|
|
LinkReplacer {
|
|
|
|
inner: iter,
|
2018-03-19 22:25:55 +00:00
|
|
|
links,
|
2017-12-28 10:59:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a, 'b, I: Iterator<Item = Event<'a>>> Iterator for LinkReplacer<'a, 'b, I> {
|
|
|
|
type Item = Event<'a>;
|
|
|
|
|
|
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
|
|
let event = self.inner.next();
|
2019-04-20 17:03:59 +00:00
|
|
|
if let Some(Event::Start(Tag::Link(kind, dest, text))) = event {
|
|
|
|
if let Some(&(_, ref replace)) = self.links.iter().find(|link| link.0 == *dest) {
|
|
|
|
Some(Event::Start(Tag::Link(kind, replace.to_owned().into(), text)))
|
2017-12-28 10:59:20 +00:00
|
|
|
} else {
|
2019-04-20 17:03:59 +00:00
|
|
|
Some(Event::Start(Tag::Link(kind, dest, text)))
|
2017-12-28 10:59:20 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
event
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-08 13:53:55 +00:00
|
|
|
/// Make headings links with anchor IDs and build up TOC.
|
2018-07-22 13:25:00 +00:00
|
|
|
struct HeadingLinks<'a, 'b, 'ids, I: Iterator<Item = Event<'a>>> {
|
2017-04-06 12:09:20 +00:00
|
|
|
inner: I,
|
|
|
|
toc: Option<&'b mut TocBuilder>,
|
|
|
|
buf: VecDeque<Event<'a>>,
|
2018-07-22 13:25:00 +00:00
|
|
|
id_map: &'ids mut IdMap,
|
2017-04-06 12:09:20 +00:00
|
|
|
}
|
2013-12-22 19:23:04 +00:00
|
|
|
|
2018-07-22 13:25:00 +00:00
|
|
|
impl<'a, 'b, 'ids, I: Iterator<Item = Event<'a>>> HeadingLinks<'a, 'b, 'ids, I> {
|
|
|
|
fn new(iter: I, toc: Option<&'b mut TocBuilder>, ids: &'ids mut IdMap) -> Self {
|
2017-04-06 12:09:20 +00:00
|
|
|
HeadingLinks {
|
|
|
|
inner: iter,
|
2017-08-07 05:54:09 +00:00
|
|
|
toc,
|
2017-04-06 12:09:20 +00:00
|
|
|
buf: VecDeque::new(),
|
2018-07-22 13:25:00 +00:00
|
|
|
id_map: ids,
|
2017-03-30 01:48:06 +00:00
|
|
|
}
|
|
|
|
}
|
2017-04-06 12:09:20 +00:00
|
|
|
}
|
2017-03-30 01:48:06 +00:00
|
|
|
|
2018-07-22 13:25:00 +00:00
|
|
|
impl<'a, 'b, 'ids, I: Iterator<Item = Event<'a>>> Iterator for HeadingLinks<'a, 'b, 'ids, I> {
|
2017-04-06 12:09:20 +00:00
|
|
|
type Item = Event<'a>;
|
2017-03-10 13:06:24 +00:00
|
|
|
|
2017-04-06 12:09:20 +00:00
|
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
|
|
if let Some(e) = self.buf.pop_front() {
|
|
|
|
return Some(e);
|
2017-03-10 13:06:24 +00:00
|
|
|
}
|
|
|
|
|
2017-04-06 12:09:20 +00:00
|
|
|
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)),
|
|
|
|
_ => {},
|
2017-03-10 13:06:24 +00:00
|
|
|
}
|
2017-04-06 12:09:20 +00:00
|
|
|
self.buf.push_back(event);
|
2017-03-10 13:06:24 +00:00
|
|
|
}
|
2018-07-22 13:25:00 +00:00
|
|
|
let id = self.id_map.derive(id);
|
2017-03-10 13:06:24 +00:00
|
|
|
|
2017-04-06 12:09:20 +00:00
|
|
|
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()));
|
2017-03-10 13:06:24 +00:00
|
|
|
}
|
|
|
|
|
2017-04-06 12:09:20 +00:00
|
|
|
self.buf.push_back(Event::InlineHtml(format!("</a></h{}>", level).into()));
|
2017-03-11 00:43:36 +00:00
|
|
|
|
2017-04-06 12:09:20 +00:00
|
|
|
let start_tags = format!("<h{level} id=\"{id}\" class=\"section-header\">\
|
|
|
|
<a href=\"#{id}\">",
|
|
|
|
id = id,
|
|
|
|
level = level);
|
|
|
|
return Some(Event::InlineHtml(start_tags.into()));
|
2017-03-11 00:43:36 +00:00
|
|
|
}
|
2017-04-06 12:09:20 +00:00
|
|
|
event
|
2017-03-11 00:43:36 +00:00
|
|
|
}
|
2017-04-06 12:09:20 +00:00
|
|
|
}
|
2017-03-11 00:43:36 +00:00
|
|
|
|
2017-04-06 12:09:20 +00:00
|
|
|
/// Extracts just the first paragraph.
|
|
|
|
struct SummaryLine<'a, I: Iterator<Item = Event<'a>>> {
|
|
|
|
inner: I,
|
|
|
|
started: bool,
|
|
|
|
depth: u32,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a, I: Iterator<Item = Event<'a>>> SummaryLine<'a, I> {
|
|
|
|
fn new(iter: I) -> Self {
|
|
|
|
SummaryLine {
|
|
|
|
inner: iter,
|
|
|
|
started: false,
|
|
|
|
depth: 0,
|
2017-03-11 00:43:36 +00:00
|
|
|
}
|
|
|
|
}
|
2017-04-06 12:09:20 +00:00
|
|
|
}
|
2017-03-11 00:43:36 +00:00
|
|
|
|
2019-02-23 07:40:07 +00:00
|
|
|
fn check_if_allowed_tag(t: &Tag<'_>) -> bool {
|
2018-06-27 23:55:43 +00:00
|
|
|
match *t {
|
|
|
|
Tag::Paragraph
|
|
|
|
| Tag::Item
|
|
|
|
| Tag::Emphasis
|
|
|
|
| Tag::Strong
|
|
|
|
| Tag::Code
|
2019-04-20 17:03:59 +00:00
|
|
|
| Tag::Link(..)
|
2018-06-27 23:55:43 +00:00
|
|
|
| Tag::BlockQuote => true,
|
|
|
|
_ => false,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-06 12:09:20 +00:00
|
|
|
impl<'a, I: Iterator<Item = Event<'a>>> Iterator for SummaryLine<'a, I> {
|
|
|
|
type Item = Event<'a>;
|
2017-03-11 00:43:36 +00:00
|
|
|
|
2017-04-06 12:09:20 +00:00
|
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
|
|
if self.started && self.depth == 0 {
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
if !self.started {
|
|
|
|
self.started = true;
|
|
|
|
}
|
2018-10-16 18:21:34 +00:00
|
|
|
while let Some(event) = self.inner.next() {
|
|
|
|
let mut is_start = true;
|
|
|
|
let is_allowed_tag = match event {
|
|
|
|
Event::Start(Tag::CodeBlock(_)) | Event::End(Tag::CodeBlock(_)) => {
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
Event::Start(ref c) => {
|
|
|
|
self.depth += 1;
|
|
|
|
check_if_allowed_tag(c)
|
|
|
|
}
|
|
|
|
Event::End(ref c) => {
|
|
|
|
self.depth -= 1;
|
|
|
|
is_start = false;
|
|
|
|
check_if_allowed_tag(c)
|
|
|
|
}
|
|
|
|
_ => {
|
|
|
|
true
|
|
|
|
}
|
|
|
|
};
|
|
|
|
return if is_allowed_tag == false {
|
|
|
|
if is_start {
|
|
|
|
Some(Event::Start(Tag::Paragraph))
|
|
|
|
} else {
|
|
|
|
Some(Event::End(Tag::Paragraph))
|
|
|
|
}
|
2018-06-27 23:55:43 +00:00
|
|
|
} else {
|
2018-10-16 18:21:34 +00:00
|
|
|
Some(event)
|
|
|
|
};
|
2017-04-06 12:09:20 +00:00
|
|
|
}
|
2018-10-16 18:21:34 +00:00
|
|
|
None
|
2017-03-11 00:43:36 +00:00
|
|
|
}
|
2017-04-06 12:09:20 +00:00
|
|
|
}
|
2017-03-11 00:43:36 +00:00
|
|
|
|
2017-04-06 12:09:20 +00:00
|
|
|
/// Moves all footnote definitions to the end and add back links to the
|
|
|
|
/// references.
|
|
|
|
struct Footnotes<'a, I: Iterator<Item = Event<'a>>> {
|
|
|
|
inner: I,
|
2018-08-18 10:55:43 +00:00
|
|
|
footnotes: FxHashMap<String, (Vec<Event<'a>>, u16)>,
|
2017-04-06 12:09:20 +00:00
|
|
|
}
|
2017-03-30 01:48:06 +00:00
|
|
|
|
2017-04-06 12:09:20 +00:00
|
|
|
impl<'a, I: Iterator<Item = Event<'a>>> Footnotes<'a, I> {
|
|
|
|
fn new(iter: I) -> Self {
|
|
|
|
Footnotes {
|
|
|
|
inner: iter,
|
2018-08-18 10:55:43 +00:00
|
|
|
footnotes: FxHashMap::default(),
|
2017-04-06 12:09:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
fn get_entry(&mut self, key: &str) -> &mut (Vec<Event<'a>>, u16) {
|
|
|
|
let new_id = self.footnotes.keys().count() + 1;
|
|
|
|
let key = key.to_owned();
|
|
|
|
self.footnotes.entry(key).or_insert((Vec::new(), new_id as u16))
|
2017-03-30 01:48:06 +00:00
|
|
|
}
|
2017-04-06 12:09:20 +00:00
|
|
|
}
|
2017-03-30 01:48:06 +00:00
|
|
|
|
2017-04-06 12:09:20 +00:00
|
|
|
impl<'a, I: Iterator<Item = Event<'a>>> Iterator for Footnotes<'a, I> {
|
|
|
|
type Item = Event<'a>;
|
|
|
|
|
|
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
|
|
loop {
|
|
|
|
match self.inner.next() {
|
|
|
|
Some(Event::FootnoteReference(ref reference)) => {
|
|
|
|
let entry = self.get_entry(&reference);
|
2017-10-21 20:00:26 +00:00
|
|
|
let reference = format!("<sup id=\"fnref{0}\"><a href=\"#fn{0}\">{0}\
|
2017-04-06 12:09:20 +00:00
|
|
|
</a></sup>",
|
|
|
|
(*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("<div class=\"footnotes\"><hr><ol>");
|
|
|
|
for (mut content, id) in v {
|
2017-10-21 20:00:26 +00:00
|
|
|
write!(ret, "<li id=\"fn{}\">", id).unwrap();
|
2017-04-06 12:09:20 +00:00
|
|
|
let mut is_paragraph = false;
|
|
|
|
if let Some(&Event::End(Tag::Paragraph)) = content.last() {
|
|
|
|
content.pop();
|
|
|
|
is_paragraph = true;
|
|
|
|
}
|
|
|
|
html::push_html(&mut ret, content.into_iter());
|
|
|
|
write!(ret,
|
2017-10-21 20:00:26 +00:00
|
|
|
" <a href=\"#fnref{}\" rev=\"footnote\">↩</a>",
|
2017-04-06 12:09:20 +00:00
|
|
|
id).unwrap();
|
|
|
|
if is_paragraph {
|
|
|
|
ret.push_str("</p>");
|
|
|
|
}
|
|
|
|
ret.push_str("</li>");
|
|
|
|
}
|
|
|
|
ret.push_str("</ol></div>");
|
|
|
|
return Some(Event::Html(ret.into()));
|
|
|
|
} else {
|
|
|
|
return None;
|
2017-03-31 17:02:46 +00:00
|
|
|
}
|
|
|
|
}
|
2017-03-08 00:01:23 +00:00
|
|
|
}
|
2017-03-08 22:56:00 +00:00
|
|
|
}
|
|
|
|
}
|
2017-03-08 00:01:23 +00:00
|
|
|
}
|
2014-05-03 00:56:35 +00:00
|
|
|
|
2019-04-20 17:03:59 +00:00
|
|
|
pub fn find_testable_code<T: test::Tester>(doc: &str, tests: &mut T, error_codes: ErrorCodes) {
|
2017-03-08 00:01:23 +00:00
|
|
|
let mut parser = Parser::new(doc);
|
|
|
|
let mut prev_offset = 0;
|
|
|
|
let mut nb_lines = 0;
|
|
|
|
let mut register_header = None;
|
2019-04-20 17:03:59 +00:00
|
|
|
while let Some(event) = parser.next() {
|
2017-03-10 13:06:24 +00:00
|
|
|
match event {
|
|
|
|
Event::Start(Tag::CodeBlock(s)) => {
|
2019-04-20 17:03:59 +00:00
|
|
|
let offset = parser.get_offset();
|
|
|
|
|
2017-03-10 13:06:24 +00:00
|
|
|
let block_info = if s.is_empty() {
|
|
|
|
LangString::all_false()
|
|
|
|
} else {
|
2018-07-22 17:10:19 +00:00
|
|
|
LangString::parse(&*s, error_codes)
|
2017-03-10 13:06:24 +00:00
|
|
|
};
|
|
|
|
if !block_info.rust {
|
2019-04-20 17:03:59 +00:00
|
|
|
continue;
|
2017-03-10 13:06:24 +00:00
|
|
|
}
|
|
|
|
let mut test_s = String::new();
|
2019-04-20 17:03:59 +00:00
|
|
|
|
|
|
|
while let Some(Event::Text(s)) = parser.next() {
|
|
|
|
test_s.push_str(&s);
|
2018-02-20 19:30:29 +00:00
|
|
|
}
|
2019-04-20 17:03:59 +00:00
|
|
|
|
|
|
|
let text = test_s
|
|
|
|
.lines()
|
|
|
|
.map(|l| map_line(l).for_code())
|
|
|
|
.collect::<Vec<Cow<'_, str>>>()
|
|
|
|
.join("\n");
|
|
|
|
nb_lines += doc[prev_offset..offset].lines().count();
|
|
|
|
let line = tests.get_line() + nb_lines;
|
|
|
|
tests.add_test(text, block_info, line);
|
|
|
|
prev_offset = offset;
|
2017-03-08 00:01:23 +00:00
|
|
|
}
|
2017-03-10 13:06:24 +00:00
|
|
|
Event::Start(Tag::Header(level)) => {
|
|
|
|
register_header = Some(level as u32);
|
|
|
|
}
|
|
|
|
Event::Text(ref s) if register_header.is_some() => {
|
|
|
|
let level = register_header.unwrap();
|
|
|
|
if s.is_empty() {
|
|
|
|
tests.register_header("", level);
|
|
|
|
} else {
|
|
|
|
tests.register_header(s, level);
|
|
|
|
}
|
|
|
|
register_header = None;
|
|
|
|
}
|
|
|
|
_ => {}
|
2017-03-08 00:01:23 +00:00
|
|
|
}
|
2013-12-22 19:23:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-28 13:34:18 +00:00
|
|
|
#[derive(Eq, PartialEq, Clone, Debug)]
|
2018-07-21 22:54:30 +00:00
|
|
|
pub struct LangString {
|
2016-09-07 14:43:18 +00:00
|
|
|
original: String,
|
2018-07-21 22:54:30 +00:00
|
|
|
pub should_panic: bool,
|
|
|
|
pub no_run: bool,
|
|
|
|
pub ignore: bool,
|
|
|
|
pub rust: bool,
|
|
|
|
pub test_harness: bool,
|
|
|
|
pub compile_fail: bool,
|
|
|
|
pub error_codes: Vec<String>,
|
|
|
|
pub allow_fail: bool,
|
2018-09-18 06:55:15 +00:00
|
|
|
pub edition: Option<Edition>
|
2014-06-19 12:38:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl LangString {
|
|
|
|
fn all_false() -> LangString {
|
|
|
|
LangString {
|
2016-09-07 14:43:18 +00:00
|
|
|
original: String::new(),
|
2015-03-26 20:30:33 +00:00
|
|
|
should_panic: false,
|
2014-06-19 12:38:01 +00:00
|
|
|
no_run: false,
|
|
|
|
ignore: false,
|
2014-12-24 02:47:32 +00:00
|
|
|
rust: true, // NB This used to be `notrust = false`
|
2014-06-19 13:11:18 +00:00
|
|
|
test_harness: false,
|
2016-01-05 22:38:11 +00:00
|
|
|
compile_fail: false,
|
2016-06-09 21:50:52 +00:00
|
|
|
error_codes: Vec::new(),
|
2017-05-24 17:58:37 +00:00
|
|
|
allow_fail: false,
|
2018-09-18 06:55:15 +00:00
|
|
|
edition: None,
|
2014-05-31 22:33:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-22 17:10:19 +00:00
|
|
|
fn parse(string: &str, allow_error_code_check: ErrorCodes) -> LangString {
|
|
|
|
let allow_error_code_check = allow_error_code_check.as_bool();
|
2014-06-19 12:38:01 +00:00
|
|
|
let mut seen_rust_tags = false;
|
|
|
|
let mut seen_other_tags = false;
|
|
|
|
let mut data = LangString::all_false();
|
|
|
|
|
2016-09-07 14:43:18 +00:00
|
|
|
data.original = string.to_owned();
|
2015-02-01 17:44:15 +00:00
|
|
|
let tokens = string.split(|c: char|
|
2014-06-19 12:38:01 +00:00
|
|
|
!(c == '_' || c == '-' || c.is_alphanumeric())
|
|
|
|
);
|
|
|
|
|
|
|
|
for token in tokens {
|
2017-04-10 12:35:28 +00:00
|
|
|
match token.trim() {
|
2014-06-19 12:38:01 +00:00
|
|
|
"" => {},
|
2017-04-09 16:31:59 +00:00
|
|
|
"should_panic" => {
|
|
|
|
data.should_panic = true;
|
|
|
|
seen_rust_tags = seen_other_tags == false;
|
|
|
|
}
|
2017-04-10 12:35:28 +00:00
|
|
|
"no_run" => { data.no_run = true; seen_rust_tags = !seen_other_tags; }
|
|
|
|
"ignore" => { data.ignore = true; seen_rust_tags = !seen_other_tags; }
|
2017-05-24 17:58:37 +00:00
|
|
|
"allow_fail" => { data.allow_fail = true; seen_rust_tags = !seen_other_tags; }
|
2017-04-09 16:31:59 +00:00
|
|
|
"rust" => { data.rust = true; seen_rust_tags = true; }
|
|
|
|
"test_harness" => {
|
|
|
|
data.test_harness = true;
|
2017-04-10 12:35:28 +00:00
|
|
|
seen_rust_tags = !seen_other_tags || seen_rust_tags;
|
2017-04-09 16:31:59 +00:00
|
|
|
}
|
2017-08-17 20:11:41 +00:00
|
|
|
"compile_fail" => {
|
2016-02-09 06:18:35 +00:00
|
|
|
data.compile_fail = true;
|
2017-04-10 12:35:28 +00:00
|
|
|
seen_rust_tags = !seen_other_tags || seen_rust_tags;
|
2016-02-09 06:18:35 +00:00
|
|
|
data.no_run = true;
|
2016-06-09 21:50:52 +00:00
|
|
|
}
|
2018-09-18 06:55:15 +00:00
|
|
|
x if allow_error_code_check && x.starts_with("edition") => {
|
|
|
|
// allow_error_code_check is true if we're on nightly, which
|
|
|
|
// is needed for edition support
|
|
|
|
data.edition = x[7..].parse::<Edition>().ok();
|
|
|
|
}
|
2016-06-09 21:50:52 +00:00
|
|
|
x if allow_error_code_check && x.starts_with("E") && x.len() == 5 => {
|
2018-07-27 11:20:13 +00:00
|
|
|
if x[1..].parse::<u32>().is_ok() {
|
2016-06-09 21:50:52 +00:00
|
|
|
data.error_codes.push(x.to_owned());
|
2017-04-10 12:35:28 +00:00
|
|
|
seen_rust_tags = !seen_other_tags || seen_rust_tags;
|
2016-06-09 21:50:52 +00:00
|
|
|
} else {
|
|
|
|
seen_other_tags = true;
|
|
|
|
}
|
|
|
|
}
|
2014-06-19 12:38:01 +00:00
|
|
|
_ => { seen_other_tags = true }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-12-24 02:47:32 +00:00
|
|
|
data.rust &= !seen_other_tags || seen_rust_tags;
|
2014-05-31 22:33:32 +00:00
|
|
|
|
2014-06-19 12:38:01 +00:00
|
|
|
data
|
|
|
|
}
|
2014-05-31 22:33:32 +00:00
|
|
|
}
|
|
|
|
|
2015-01-20 23:45:07 +00:00
|
|
|
impl<'a> fmt::Display for Markdown<'a> {
|
2019-02-23 07:40:07 +00:00
|
|
|
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
2019-04-18 01:17:12 +00:00
|
|
|
let Markdown(md, links, ref ids, codes, edition) = *self;
|
2018-07-22 13:25:00 +00:00
|
|
|
let mut ids = ids.borrow_mut();
|
2017-04-20 22:32:23 +00:00
|
|
|
|
2013-09-23 23:55:48 +00:00
|
|
|
// This is actually common enough to special-case
|
2015-03-24 23:53:34 +00:00
|
|
|
if md.is_empty() { return Ok(()) }
|
2018-02-18 22:32:34 +00:00
|
|
|
let replacer = |_: &str, s: &str| {
|
|
|
|
if let Some(&(_, ref replace)) = links.into_iter().find(|link| &*link.0 == s) {
|
|
|
|
Some((replace.clone(), s.to_owned()))
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2019-04-20 17:03:59 +00:00
|
|
|
let p = Parser::new_with_broken_link_callback(md, opts(), Some(&replacer));
|
2017-04-06 12:09:20 +00:00
|
|
|
|
Remove hoedown from rustdoc
Is it really time? Have our months, no, *years* of suffering come to an end? Are we finally able to cast off the pall of Hoedown? The weight which has dragged us down for so long?
-----
So, timeline for those who need to catch up:
* Way back in December 2016, [we decided we wanted to switch out the markdown renderer](https://github.com/rust-lang/rust/issues/38400). However, this was put on hold because the build system at the time made it difficult to pull in dependencies from crates.io.
* A few months later, in March 2017, [the first PR was done, to switch out the renderers entirely](https://github.com/rust-lang/rust/pull/40338). The PR itself was fraught with CI and build system issues, but eventually landed.
* However, not all was well in the Rustdoc world. During the PR and shortly after, we noticed [some differences in the way the two parsers handled some things](https://github.com/rust-lang/rust/issues/40912), and some of these differences were major enough to break the docs for some crates.
* A couple weeks afterward, [Hoedown was put back in](https://github.com/rust-lang/rust/pull/41290), at this point just to catch tests that Pulldown was "spuriously" running. This would at least provide some warning about spurious tests, rather than just breaking spontaneously.
* However, the problems had created enough noise by this point that just a few days after that, [Hoedown was switched back to the default](https://github.com/rust-lang/rust/pull/41431) while we came up with a solution for properly warning about the differences.
* That solution came a few weeks later, [as a series of warnings when the HTML emitted by the two parsers was semantically different](https://github.com/rust-lang/rust/pull/41991). But that came at a cost, as now rustdoc needed proc-macro support (the new crate needed some custom derives farther down its dependency tree), and the build system was not equipped to handle it at the time. It was worked on for three months as the issue stumped more and more people.
* In that time, [bootstrap was completely reworked](https://github.com/rust-lang/rust/pull/43059) to change how it ordered compilation, and [the method by which it built rustdoc would change](https://github.com/rust-lang/rust/pull/43482), as well. This allowed it to only be built after stage1, when proc-macros would be available, allowing the "rendering differences" PR to finally land.
* The warnings were not perfect, and revealed a few [spurious](https://github.com/rust-lang/rust/pull/44368) [differences](https://github.com/rust-lang/rust/pull/45421) between how we handled the renderers.
* Once these were handled, [we flipped the switch to turn on the "rendering difference" warnings all the time](https://github.com/rust-lang/rust/pull/45324), in October 2017. This began the "warning cycle" for this change, and landed in stable in 1.23, on 2018-01-04.
* Once those warnings hit stable, and after a couple weeks of seeing whether we would get any more reports than what we got from sitting on nightly/beta, [we switched the renderers](https://github.com/rust-lang/rust/pull/47398), making Pulldown the default but still offering the option to use Hoedown.
And that brings us to the present. We haven't received more new issues from this in the meantime, and the "switch by default" is now on beta. Our reasoning is that, at this point, anyone who would have been affected by this has run into it already.
2018-02-16 14:09:19 +00:00
|
|
|
let mut s = String::with_capacity(md.len() * 3 / 2);
|
2017-04-06 12:09:20 +00:00
|
|
|
|
2018-07-22 13:25:00 +00:00
|
|
|
let p = HeadingLinks::new(p, None, &mut ids);
|
2018-07-22 13:39:52 +00:00
|
|
|
let p = LinkReplacer::new(p, links);
|
2019-04-18 01:17:12 +00:00
|
|
|
let p = CodeBlocks::new(p, codes, edition);
|
2018-07-22 13:39:52 +00:00
|
|
|
let p = Footnotes::new(p);
|
|
|
|
html::push_html(&mut s, p);
|
2017-04-06 12:09:20 +00:00
|
|
|
|
Remove hoedown from rustdoc
Is it really time? Have our months, no, *years* of suffering come to an end? Are we finally able to cast off the pall of Hoedown? The weight which has dragged us down for so long?
-----
So, timeline for those who need to catch up:
* Way back in December 2016, [we decided we wanted to switch out the markdown renderer](https://github.com/rust-lang/rust/issues/38400). However, this was put on hold because the build system at the time made it difficult to pull in dependencies from crates.io.
* A few months later, in March 2017, [the first PR was done, to switch out the renderers entirely](https://github.com/rust-lang/rust/pull/40338). The PR itself was fraught with CI and build system issues, but eventually landed.
* However, not all was well in the Rustdoc world. During the PR and shortly after, we noticed [some differences in the way the two parsers handled some things](https://github.com/rust-lang/rust/issues/40912), and some of these differences were major enough to break the docs for some crates.
* A couple weeks afterward, [Hoedown was put back in](https://github.com/rust-lang/rust/pull/41290), at this point just to catch tests that Pulldown was "spuriously" running. This would at least provide some warning about spurious tests, rather than just breaking spontaneously.
* However, the problems had created enough noise by this point that just a few days after that, [Hoedown was switched back to the default](https://github.com/rust-lang/rust/pull/41431) while we came up with a solution for properly warning about the differences.
* That solution came a few weeks later, [as a series of warnings when the HTML emitted by the two parsers was semantically different](https://github.com/rust-lang/rust/pull/41991). But that came at a cost, as now rustdoc needed proc-macro support (the new crate needed some custom derives farther down its dependency tree), and the build system was not equipped to handle it at the time. It was worked on for three months as the issue stumped more and more people.
* In that time, [bootstrap was completely reworked](https://github.com/rust-lang/rust/pull/43059) to change how it ordered compilation, and [the method by which it built rustdoc would change](https://github.com/rust-lang/rust/pull/43482), as well. This allowed it to only be built after stage1, when proc-macros would be available, allowing the "rendering differences" PR to finally land.
* The warnings were not perfect, and revealed a few [spurious](https://github.com/rust-lang/rust/pull/44368) [differences](https://github.com/rust-lang/rust/pull/45421) between how we handled the renderers.
* Once these were handled, [we flipped the switch to turn on the "rendering difference" warnings all the time](https://github.com/rust-lang/rust/pull/45324), in October 2017. This began the "warning cycle" for this change, and landed in stable in 1.23, on 2018-01-04.
* Once those warnings hit stable, and after a couple weeks of seeing whether we would get any more reports than what we got from sitting on nightly/beta, [we switched the renderers](https://github.com/rust-lang/rust/pull/47398), making Pulldown the default but still offering the option to use Hoedown.
And that brings us to the present. We haven't received more new issues from this in the meantime, and the "switch by default" is now on beta. Our reasoning is that, at this point, anyone who would have been affected by this has run into it already.
2018-02-16 14:09:19 +00:00
|
|
|
fmt.write_str(&s)
|
2014-03-07 14:13:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-20 23:45:07 +00:00
|
|
|
impl<'a> fmt::Display for MarkdownWithToc<'a> {
|
2019-02-23 07:40:07 +00:00
|
|
|
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
2019-04-18 01:17:12 +00:00
|
|
|
let MarkdownWithToc(md, ref ids, codes, edition) = *self;
|
2018-07-22 13:25:00 +00:00
|
|
|
let mut ids = ids.borrow_mut();
|
2017-04-06 12:09:20 +00:00
|
|
|
|
2019-04-20 17:03:59 +00:00
|
|
|
let p = Parser::new_ext(md, opts());
|
2017-04-06 12:09:20 +00:00
|
|
|
|
Remove hoedown from rustdoc
Is it really time? Have our months, no, *years* of suffering come to an end? Are we finally able to cast off the pall of Hoedown? The weight which has dragged us down for so long?
-----
So, timeline for those who need to catch up:
* Way back in December 2016, [we decided we wanted to switch out the markdown renderer](https://github.com/rust-lang/rust/issues/38400). However, this was put on hold because the build system at the time made it difficult to pull in dependencies from crates.io.
* A few months later, in March 2017, [the first PR was done, to switch out the renderers entirely](https://github.com/rust-lang/rust/pull/40338). The PR itself was fraught with CI and build system issues, but eventually landed.
* However, not all was well in the Rustdoc world. During the PR and shortly after, we noticed [some differences in the way the two parsers handled some things](https://github.com/rust-lang/rust/issues/40912), and some of these differences were major enough to break the docs for some crates.
* A couple weeks afterward, [Hoedown was put back in](https://github.com/rust-lang/rust/pull/41290), at this point just to catch tests that Pulldown was "spuriously" running. This would at least provide some warning about spurious tests, rather than just breaking spontaneously.
* However, the problems had created enough noise by this point that just a few days after that, [Hoedown was switched back to the default](https://github.com/rust-lang/rust/pull/41431) while we came up with a solution for properly warning about the differences.
* That solution came a few weeks later, [as a series of warnings when the HTML emitted by the two parsers was semantically different](https://github.com/rust-lang/rust/pull/41991). But that came at a cost, as now rustdoc needed proc-macro support (the new crate needed some custom derives farther down its dependency tree), and the build system was not equipped to handle it at the time. It was worked on for three months as the issue stumped more and more people.
* In that time, [bootstrap was completely reworked](https://github.com/rust-lang/rust/pull/43059) to change how it ordered compilation, and [the method by which it built rustdoc would change](https://github.com/rust-lang/rust/pull/43482), as well. This allowed it to only be built after stage1, when proc-macros would be available, allowing the "rendering differences" PR to finally land.
* The warnings were not perfect, and revealed a few [spurious](https://github.com/rust-lang/rust/pull/44368) [differences](https://github.com/rust-lang/rust/pull/45421) between how we handled the renderers.
* Once these were handled, [we flipped the switch to turn on the "rendering difference" warnings all the time](https://github.com/rust-lang/rust/pull/45324), in October 2017. This began the "warning cycle" for this change, and landed in stable in 1.23, on 2018-01-04.
* Once those warnings hit stable, and after a couple weeks of seeing whether we would get any more reports than what we got from sitting on nightly/beta, [we switched the renderers](https://github.com/rust-lang/rust/pull/47398), making Pulldown the default but still offering the option to use Hoedown.
And that brings us to the present. We haven't received more new issues from this in the meantime, and the "switch by default" is now on beta. Our reasoning is that, at this point, anyone who would have been affected by this has run into it already.
2018-02-16 14:09:19 +00:00
|
|
|
let mut s = String::with_capacity(md.len() * 3 / 2);
|
2017-04-06 12:09:20 +00:00
|
|
|
|
Remove hoedown from rustdoc
Is it really time? Have our months, no, *years* of suffering come to an end? Are we finally able to cast off the pall of Hoedown? The weight which has dragged us down for so long?
-----
So, timeline for those who need to catch up:
* Way back in December 2016, [we decided we wanted to switch out the markdown renderer](https://github.com/rust-lang/rust/issues/38400). However, this was put on hold because the build system at the time made it difficult to pull in dependencies from crates.io.
* A few months later, in March 2017, [the first PR was done, to switch out the renderers entirely](https://github.com/rust-lang/rust/pull/40338). The PR itself was fraught with CI and build system issues, but eventually landed.
* However, not all was well in the Rustdoc world. During the PR and shortly after, we noticed [some differences in the way the two parsers handled some things](https://github.com/rust-lang/rust/issues/40912), and some of these differences were major enough to break the docs for some crates.
* A couple weeks afterward, [Hoedown was put back in](https://github.com/rust-lang/rust/pull/41290), at this point just to catch tests that Pulldown was "spuriously" running. This would at least provide some warning about spurious tests, rather than just breaking spontaneously.
* However, the problems had created enough noise by this point that just a few days after that, [Hoedown was switched back to the default](https://github.com/rust-lang/rust/pull/41431) while we came up with a solution for properly warning about the differences.
* That solution came a few weeks later, [as a series of warnings when the HTML emitted by the two parsers was semantically different](https://github.com/rust-lang/rust/pull/41991). But that came at a cost, as now rustdoc needed proc-macro support (the new crate needed some custom derives farther down its dependency tree), and the build system was not equipped to handle it at the time. It was worked on for three months as the issue stumped more and more people.
* In that time, [bootstrap was completely reworked](https://github.com/rust-lang/rust/pull/43059) to change how it ordered compilation, and [the method by which it built rustdoc would change](https://github.com/rust-lang/rust/pull/43482), as well. This allowed it to only be built after stage1, when proc-macros would be available, allowing the "rendering differences" PR to finally land.
* The warnings were not perfect, and revealed a few [spurious](https://github.com/rust-lang/rust/pull/44368) [differences](https://github.com/rust-lang/rust/pull/45421) between how we handled the renderers.
* Once these were handled, [we flipped the switch to turn on the "rendering difference" warnings all the time](https://github.com/rust-lang/rust/pull/45324), in October 2017. This began the "warning cycle" for this change, and landed in stable in 1.23, on 2018-01-04.
* Once those warnings hit stable, and after a couple weeks of seeing whether we would get any more reports than what we got from sitting on nightly/beta, [we switched the renderers](https://github.com/rust-lang/rust/pull/47398), making Pulldown the default but still offering the option to use Hoedown.
And that brings us to the present. We haven't received more new issues from this in the meantime, and the "switch by default" is now on beta. Our reasoning is that, at this point, anyone who would have been affected by this has run into it already.
2018-02-16 14:09:19 +00:00
|
|
|
let mut toc = TocBuilder::new();
|
2017-04-06 12:09:20 +00:00
|
|
|
|
2018-07-22 17:10:19 +00:00
|
|
|
{
|
2018-07-22 13:25:00 +00:00
|
|
|
let p = HeadingLinks::new(p, Some(&mut toc), &mut ids);
|
2019-04-18 01:17:12 +00:00
|
|
|
let p = CodeBlocks::new(p, codes, edition);
|
2018-07-22 17:10:19 +00:00
|
|
|
let p = Footnotes::new(p);
|
|
|
|
html::push_html(&mut s, p);
|
|
|
|
}
|
2017-04-06 12:09:20 +00:00
|
|
|
|
Remove hoedown from rustdoc
Is it really time? Have our months, no, *years* of suffering come to an end? Are we finally able to cast off the pall of Hoedown? The weight which has dragged us down for so long?
-----
So, timeline for those who need to catch up:
* Way back in December 2016, [we decided we wanted to switch out the markdown renderer](https://github.com/rust-lang/rust/issues/38400). However, this was put on hold because the build system at the time made it difficult to pull in dependencies from crates.io.
* A few months later, in March 2017, [the first PR was done, to switch out the renderers entirely](https://github.com/rust-lang/rust/pull/40338). The PR itself was fraught with CI and build system issues, but eventually landed.
* However, not all was well in the Rustdoc world. During the PR and shortly after, we noticed [some differences in the way the two parsers handled some things](https://github.com/rust-lang/rust/issues/40912), and some of these differences were major enough to break the docs for some crates.
* A couple weeks afterward, [Hoedown was put back in](https://github.com/rust-lang/rust/pull/41290), at this point just to catch tests that Pulldown was "spuriously" running. This would at least provide some warning about spurious tests, rather than just breaking spontaneously.
* However, the problems had created enough noise by this point that just a few days after that, [Hoedown was switched back to the default](https://github.com/rust-lang/rust/pull/41431) while we came up with a solution for properly warning about the differences.
* That solution came a few weeks later, [as a series of warnings when the HTML emitted by the two parsers was semantically different](https://github.com/rust-lang/rust/pull/41991). But that came at a cost, as now rustdoc needed proc-macro support (the new crate needed some custom derives farther down its dependency tree), and the build system was not equipped to handle it at the time. It was worked on for three months as the issue stumped more and more people.
* In that time, [bootstrap was completely reworked](https://github.com/rust-lang/rust/pull/43059) to change how it ordered compilation, and [the method by which it built rustdoc would change](https://github.com/rust-lang/rust/pull/43482), as well. This allowed it to only be built after stage1, when proc-macros would be available, allowing the "rendering differences" PR to finally land.
* The warnings were not perfect, and revealed a few [spurious](https://github.com/rust-lang/rust/pull/44368) [differences](https://github.com/rust-lang/rust/pull/45421) between how we handled the renderers.
* Once these were handled, [we flipped the switch to turn on the "rendering difference" warnings all the time](https://github.com/rust-lang/rust/pull/45324), in October 2017. This began the "warning cycle" for this change, and landed in stable in 1.23, on 2018-01-04.
* Once those warnings hit stable, and after a couple weeks of seeing whether we would get any more reports than what we got from sitting on nightly/beta, [we switched the renderers](https://github.com/rust-lang/rust/pull/47398), making Pulldown the default but still offering the option to use Hoedown.
And that brings us to the present. We haven't received more new issues from this in the meantime, and the "switch by default" is now on beta. Our reasoning is that, at this point, anyone who would have been affected by this has run into it already.
2018-02-16 14:09:19 +00:00
|
|
|
write!(fmt, "<nav id=\"TOC\">{}</nav>", toc.into_toc())?;
|
2017-04-06 12:09:20 +00:00
|
|
|
|
Remove hoedown from rustdoc
Is it really time? Have our months, no, *years* of suffering come to an end? Are we finally able to cast off the pall of Hoedown? The weight which has dragged us down for so long?
-----
So, timeline for those who need to catch up:
* Way back in December 2016, [we decided we wanted to switch out the markdown renderer](https://github.com/rust-lang/rust/issues/38400). However, this was put on hold because the build system at the time made it difficult to pull in dependencies from crates.io.
* A few months later, in March 2017, [the first PR was done, to switch out the renderers entirely](https://github.com/rust-lang/rust/pull/40338). The PR itself was fraught with CI and build system issues, but eventually landed.
* However, not all was well in the Rustdoc world. During the PR and shortly after, we noticed [some differences in the way the two parsers handled some things](https://github.com/rust-lang/rust/issues/40912), and some of these differences were major enough to break the docs for some crates.
* A couple weeks afterward, [Hoedown was put back in](https://github.com/rust-lang/rust/pull/41290), at this point just to catch tests that Pulldown was "spuriously" running. This would at least provide some warning about spurious tests, rather than just breaking spontaneously.
* However, the problems had created enough noise by this point that just a few days after that, [Hoedown was switched back to the default](https://github.com/rust-lang/rust/pull/41431) while we came up with a solution for properly warning about the differences.
* That solution came a few weeks later, [as a series of warnings when the HTML emitted by the two parsers was semantically different](https://github.com/rust-lang/rust/pull/41991). But that came at a cost, as now rustdoc needed proc-macro support (the new crate needed some custom derives farther down its dependency tree), and the build system was not equipped to handle it at the time. It was worked on for three months as the issue stumped more and more people.
* In that time, [bootstrap was completely reworked](https://github.com/rust-lang/rust/pull/43059) to change how it ordered compilation, and [the method by which it built rustdoc would change](https://github.com/rust-lang/rust/pull/43482), as well. This allowed it to only be built after stage1, when proc-macros would be available, allowing the "rendering differences" PR to finally land.
* The warnings were not perfect, and revealed a few [spurious](https://github.com/rust-lang/rust/pull/44368) [differences](https://github.com/rust-lang/rust/pull/45421) between how we handled the renderers.
* Once these were handled, [we flipped the switch to turn on the "rendering difference" warnings all the time](https://github.com/rust-lang/rust/pull/45324), in October 2017. This began the "warning cycle" for this change, and landed in stable in 1.23, on 2018-01-04.
* Once those warnings hit stable, and after a couple weeks of seeing whether we would get any more reports than what we got from sitting on nightly/beta, [we switched the renderers](https://github.com/rust-lang/rust/pull/47398), making Pulldown the default but still offering the option to use Hoedown.
And that brings us to the present. We haven't received more new issues from this in the meantime, and the "switch by default" is now on beta. Our reasoning is that, at this point, anyone who would have been affected by this has run into it already.
2018-02-16 14:09:19 +00:00
|
|
|
fmt.write_str(&s)
|
2016-12-12 23:18:22 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> fmt::Display for MarkdownHtml<'a> {
|
2019-02-23 07:40:07 +00:00
|
|
|
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
2019-04-18 01:17:12 +00:00
|
|
|
let MarkdownHtml(md, ref ids, codes, edition) = *self;
|
2018-07-22 13:25:00 +00:00
|
|
|
let mut ids = ids.borrow_mut();
|
2017-04-20 22:32:23 +00:00
|
|
|
|
2016-12-12 23:18:22 +00:00
|
|
|
// This is actually common enough to special-case
|
|
|
|
if md.is_empty() { return Ok(()) }
|
2019-04-20 17:03:59 +00:00
|
|
|
let p = Parser::new_ext(md, opts());
|
2017-04-06 12:09:20 +00:00
|
|
|
|
Remove hoedown from rustdoc
Is it really time? Have our months, no, *years* of suffering come to an end? Are we finally able to cast off the pall of Hoedown? The weight which has dragged us down for so long?
-----
So, timeline for those who need to catch up:
* Way back in December 2016, [we decided we wanted to switch out the markdown renderer](https://github.com/rust-lang/rust/issues/38400). However, this was put on hold because the build system at the time made it difficult to pull in dependencies from crates.io.
* A few months later, in March 2017, [the first PR was done, to switch out the renderers entirely](https://github.com/rust-lang/rust/pull/40338). The PR itself was fraught with CI and build system issues, but eventually landed.
* However, not all was well in the Rustdoc world. During the PR and shortly after, we noticed [some differences in the way the two parsers handled some things](https://github.com/rust-lang/rust/issues/40912), and some of these differences were major enough to break the docs for some crates.
* A couple weeks afterward, [Hoedown was put back in](https://github.com/rust-lang/rust/pull/41290), at this point just to catch tests that Pulldown was "spuriously" running. This would at least provide some warning about spurious tests, rather than just breaking spontaneously.
* However, the problems had created enough noise by this point that just a few days after that, [Hoedown was switched back to the default](https://github.com/rust-lang/rust/pull/41431) while we came up with a solution for properly warning about the differences.
* That solution came a few weeks later, [as a series of warnings when the HTML emitted by the two parsers was semantically different](https://github.com/rust-lang/rust/pull/41991). But that came at a cost, as now rustdoc needed proc-macro support (the new crate needed some custom derives farther down its dependency tree), and the build system was not equipped to handle it at the time. It was worked on for three months as the issue stumped more and more people.
* In that time, [bootstrap was completely reworked](https://github.com/rust-lang/rust/pull/43059) to change how it ordered compilation, and [the method by which it built rustdoc would change](https://github.com/rust-lang/rust/pull/43482), as well. This allowed it to only be built after stage1, when proc-macros would be available, allowing the "rendering differences" PR to finally land.
* The warnings were not perfect, and revealed a few [spurious](https://github.com/rust-lang/rust/pull/44368) [differences](https://github.com/rust-lang/rust/pull/45421) between how we handled the renderers.
* Once these were handled, [we flipped the switch to turn on the "rendering difference" warnings all the time](https://github.com/rust-lang/rust/pull/45324), in October 2017. This began the "warning cycle" for this change, and landed in stable in 1.23, on 2018-01-04.
* Once those warnings hit stable, and after a couple weeks of seeing whether we would get any more reports than what we got from sitting on nightly/beta, [we switched the renderers](https://github.com/rust-lang/rust/pull/47398), making Pulldown the default but still offering the option to use Hoedown.
And that brings us to the present. We haven't received more new issues from this in the meantime, and the "switch by default" is now on beta. Our reasoning is that, at this point, anyone who would have been affected by this has run into it already.
2018-02-16 14:09:19 +00:00
|
|
|
// Treat inline HTML as plain text.
|
|
|
|
let p = p.map(|event| match event {
|
|
|
|
Event::Html(text) | Event::InlineHtml(text) => Event::Text(text),
|
|
|
|
_ => event
|
|
|
|
});
|
2017-04-06 12:09:20 +00:00
|
|
|
|
Remove hoedown from rustdoc
Is it really time? Have our months, no, *years* of suffering come to an end? Are we finally able to cast off the pall of Hoedown? The weight which has dragged us down for so long?
-----
So, timeline for those who need to catch up:
* Way back in December 2016, [we decided we wanted to switch out the markdown renderer](https://github.com/rust-lang/rust/issues/38400). However, this was put on hold because the build system at the time made it difficult to pull in dependencies from crates.io.
* A few months later, in March 2017, [the first PR was done, to switch out the renderers entirely](https://github.com/rust-lang/rust/pull/40338). The PR itself was fraught with CI and build system issues, but eventually landed.
* However, not all was well in the Rustdoc world. During the PR and shortly after, we noticed [some differences in the way the two parsers handled some things](https://github.com/rust-lang/rust/issues/40912), and some of these differences were major enough to break the docs for some crates.
* A couple weeks afterward, [Hoedown was put back in](https://github.com/rust-lang/rust/pull/41290), at this point just to catch tests that Pulldown was "spuriously" running. This would at least provide some warning about spurious tests, rather than just breaking spontaneously.
* However, the problems had created enough noise by this point that just a few days after that, [Hoedown was switched back to the default](https://github.com/rust-lang/rust/pull/41431) while we came up with a solution for properly warning about the differences.
* That solution came a few weeks later, [as a series of warnings when the HTML emitted by the two parsers was semantically different](https://github.com/rust-lang/rust/pull/41991). But that came at a cost, as now rustdoc needed proc-macro support (the new crate needed some custom derives farther down its dependency tree), and the build system was not equipped to handle it at the time. It was worked on for three months as the issue stumped more and more people.
* In that time, [bootstrap was completely reworked](https://github.com/rust-lang/rust/pull/43059) to change how it ordered compilation, and [the method by which it built rustdoc would change](https://github.com/rust-lang/rust/pull/43482), as well. This allowed it to only be built after stage1, when proc-macros would be available, allowing the "rendering differences" PR to finally land.
* The warnings were not perfect, and revealed a few [spurious](https://github.com/rust-lang/rust/pull/44368) [differences](https://github.com/rust-lang/rust/pull/45421) between how we handled the renderers.
* Once these were handled, [we flipped the switch to turn on the "rendering difference" warnings all the time](https://github.com/rust-lang/rust/pull/45324), in October 2017. This began the "warning cycle" for this change, and landed in stable in 1.23, on 2018-01-04.
* Once those warnings hit stable, and after a couple weeks of seeing whether we would get any more reports than what we got from sitting on nightly/beta, [we switched the renderers](https://github.com/rust-lang/rust/pull/47398), making Pulldown the default but still offering the option to use Hoedown.
And that brings us to the present. We haven't received more new issues from this in the meantime, and the "switch by default" is now on beta. Our reasoning is that, at this point, anyone who would have been affected by this has run into it already.
2018-02-16 14:09:19 +00:00
|
|
|
let mut s = String::with_capacity(md.len() * 3 / 2);
|
2017-04-06 12:09:20 +00:00
|
|
|
|
2018-07-22 13:25:00 +00:00
|
|
|
let p = HeadingLinks::new(p, None, &mut ids);
|
2019-04-18 01:17:12 +00:00
|
|
|
let p = CodeBlocks::new(p, codes, edition);
|
2018-07-22 17:10:19 +00:00
|
|
|
let p = Footnotes::new(p);
|
|
|
|
html::push_html(&mut s, p);
|
2017-04-06 12:09:20 +00:00
|
|
|
|
Remove hoedown from rustdoc
Is it really time? Have our months, no, *years* of suffering come to an end? Are we finally able to cast off the pall of Hoedown? The weight which has dragged us down for so long?
-----
So, timeline for those who need to catch up:
* Way back in December 2016, [we decided we wanted to switch out the markdown renderer](https://github.com/rust-lang/rust/issues/38400). However, this was put on hold because the build system at the time made it difficult to pull in dependencies from crates.io.
* A few months later, in March 2017, [the first PR was done, to switch out the renderers entirely](https://github.com/rust-lang/rust/pull/40338). The PR itself was fraught with CI and build system issues, but eventually landed.
* However, not all was well in the Rustdoc world. During the PR and shortly after, we noticed [some differences in the way the two parsers handled some things](https://github.com/rust-lang/rust/issues/40912), and some of these differences were major enough to break the docs for some crates.
* A couple weeks afterward, [Hoedown was put back in](https://github.com/rust-lang/rust/pull/41290), at this point just to catch tests that Pulldown was "spuriously" running. This would at least provide some warning about spurious tests, rather than just breaking spontaneously.
* However, the problems had created enough noise by this point that just a few days after that, [Hoedown was switched back to the default](https://github.com/rust-lang/rust/pull/41431) while we came up with a solution for properly warning about the differences.
* That solution came a few weeks later, [as a series of warnings when the HTML emitted by the two parsers was semantically different](https://github.com/rust-lang/rust/pull/41991). But that came at a cost, as now rustdoc needed proc-macro support (the new crate needed some custom derives farther down its dependency tree), and the build system was not equipped to handle it at the time. It was worked on for three months as the issue stumped more and more people.
* In that time, [bootstrap was completely reworked](https://github.com/rust-lang/rust/pull/43059) to change how it ordered compilation, and [the method by which it built rustdoc would change](https://github.com/rust-lang/rust/pull/43482), as well. This allowed it to only be built after stage1, when proc-macros would be available, allowing the "rendering differences" PR to finally land.
* The warnings were not perfect, and revealed a few [spurious](https://github.com/rust-lang/rust/pull/44368) [differences](https://github.com/rust-lang/rust/pull/45421) between how we handled the renderers.
* Once these were handled, [we flipped the switch to turn on the "rendering difference" warnings all the time](https://github.com/rust-lang/rust/pull/45324), in October 2017. This began the "warning cycle" for this change, and landed in stable in 1.23, on 2018-01-04.
* Once those warnings hit stable, and after a couple weeks of seeing whether we would get any more reports than what we got from sitting on nightly/beta, [we switched the renderers](https://github.com/rust-lang/rust/pull/47398), making Pulldown the default but still offering the option to use Hoedown.
And that brings us to the present. We haven't received more new issues from this in the meantime, and the "switch by default" is now on beta. Our reasoning is that, at this point, anyone who would have been affected by this has run into it already.
2018-02-16 14:09:19 +00:00
|
|
|
fmt.write_str(&s)
|
2017-04-06 12:09:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> fmt::Display for MarkdownSummaryLine<'a> {
|
2019-02-23 07:40:07 +00:00
|
|
|
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
2017-12-28 10:59:20 +00:00
|
|
|
let MarkdownSummaryLine(md, links) = *self;
|
2017-04-06 12:09:20 +00:00
|
|
|
// This is actually common enough to special-case
|
|
|
|
if md.is_empty() { return Ok(()) }
|
|
|
|
|
2018-02-18 22:32:34 +00:00
|
|
|
let replacer = |_: &str, s: &str| {
|
|
|
|
if let Some(&(_, ref replace)) = links.into_iter().find(|link| &*link.0 == s) {
|
|
|
|
Some((replace.clone(), s.to_owned()))
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2018-06-27 23:55:43 +00:00
|
|
|
let p = Parser::new_with_broken_link_callback(md, Options::empty(), Some(&replacer));
|
2017-04-06 12:09:20 +00:00
|
|
|
|
|
|
|
let mut s = String::new();
|
|
|
|
|
2017-12-28 10:59:20 +00:00
|
|
|
html::push_html(&mut s, LinkReplacer::new(SummaryLine::new(p), links));
|
2017-04-06 12:09:20 +00:00
|
|
|
|
|
|
|
fmt.write_str(&s)
|
2013-09-19 05:18:38 +00:00
|
|
|
}
|
|
|
|
}
|
2014-05-31 22:33:32 +00:00
|
|
|
|
2014-12-25 05:39:29 +00:00
|
|
|
pub fn plain_summary_line(md: &str) -> String {
|
2018-12-11 21:29:40 +00:00
|
|
|
plain_summary_line_full(md, false)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn plain_summary_line_full(md: &str, limit_length: bool) -> String {
|
2017-03-08 00:01:23 +00:00
|
|
|
struct ParserWrapper<'a> {
|
|
|
|
inner: Parser<'a>,
|
|
|
|
is_in: isize,
|
|
|
|
is_first: bool,
|
2014-12-25 05:39:29 +00:00
|
|
|
}
|
|
|
|
|
2017-03-08 00:01:23 +00:00
|
|
|
impl<'a> Iterator for ParserWrapper<'a> {
|
|
|
|
type Item = String;
|
|
|
|
|
|
|
|
fn next(&mut self) -> Option<String> {
|
|
|
|
let next_event = self.inner.next();
|
|
|
|
if next_event.is_none() {
|
|
|
|
return None
|
|
|
|
}
|
|
|
|
let next_event = next_event.unwrap();
|
|
|
|
let (ret, is_in) = match next_event {
|
2017-03-10 13:06:24 +00:00
|
|
|
Event::Start(Tag::Paragraph) => (None, 1),
|
2017-03-25 00:48:33 +00:00
|
|
|
Event::Start(Tag::Code) => (Some("`".to_owned()), 1),
|
|
|
|
Event::End(Tag::Code) => (Some("`".to_owned()), -1),
|
|
|
|
Event::Start(Tag::Header(_)) => (None, 1),
|
2017-03-10 13:06:24 +00:00
|
|
|
Event::Text(ref s) if self.is_in > 0 => (Some(s.as_ref().to_owned()), 0),
|
2017-03-25 00:48:33 +00:00
|
|
|
Event::End(Tag::Paragraph) | Event::End(Tag::Header(_)) => (None, -1),
|
2017-03-08 00:01:23 +00:00
|
|
|
_ => (None, 0),
|
|
|
|
};
|
|
|
|
if is_in > 0 || (is_in < 0 && self.is_in > 0) {
|
|
|
|
self.is_in += is_in;
|
|
|
|
}
|
|
|
|
if ret.is_some() {
|
|
|
|
self.is_first = false;
|
|
|
|
ret
|
|
|
|
} else {
|
|
|
|
Some(String::new())
|
|
|
|
}
|
2014-12-25 05:39:29 +00:00
|
|
|
}
|
|
|
|
}
|
2017-03-08 00:01:23 +00:00
|
|
|
let mut s = String::with_capacity(md.len() * 3 / 2);
|
|
|
|
let mut p = ParserWrapper {
|
|
|
|
inner: Parser::new(md),
|
|
|
|
is_in: 0,
|
|
|
|
is_first: true,
|
|
|
|
};
|
|
|
|
while let Some(t) = p.next() {
|
|
|
|
if !t.is_empty() {
|
|
|
|
s.push_str(&t);
|
|
|
|
}
|
2014-12-25 05:39:29 +00:00
|
|
|
}
|
2018-12-11 21:29:40 +00:00
|
|
|
if limit_length && s.chars().count() > 60 {
|
|
|
|
let mut len = 0;
|
|
|
|
let mut ret = s.split_whitespace()
|
|
|
|
.take_while(|p| {
|
|
|
|
// + 1 for the added character after the word.
|
|
|
|
len += p.chars().count() + 1;
|
|
|
|
len < 60
|
|
|
|
})
|
|
|
|
.collect::<Vec<_>>()
|
|
|
|
.join(" ");
|
|
|
|
ret.push('…');
|
|
|
|
ret
|
2018-12-11 09:57:45 +00:00
|
|
|
} else {
|
|
|
|
s
|
|
|
|
}
|
2014-12-25 05:39:29 +00:00
|
|
|
}
|
|
|
|
|
2018-06-03 10:22:24 +00:00
|
|
|
pub fn markdown_links(md: &str) -> Vec<(String, Option<Range<usize>>)> {
|
2017-12-21 20:54:16 +00:00
|
|
|
if md.is_empty() {
|
|
|
|
return vec![];
|
|
|
|
}
|
|
|
|
|
Remove hoedown from rustdoc
Is it really time? Have our months, no, *years* of suffering come to an end? Are we finally able to cast off the pall of Hoedown? The weight which has dragged us down for so long?
-----
So, timeline for those who need to catch up:
* Way back in December 2016, [we decided we wanted to switch out the markdown renderer](https://github.com/rust-lang/rust/issues/38400). However, this was put on hold because the build system at the time made it difficult to pull in dependencies from crates.io.
* A few months later, in March 2017, [the first PR was done, to switch out the renderers entirely](https://github.com/rust-lang/rust/pull/40338). The PR itself was fraught with CI and build system issues, but eventually landed.
* However, not all was well in the Rustdoc world. During the PR and shortly after, we noticed [some differences in the way the two parsers handled some things](https://github.com/rust-lang/rust/issues/40912), and some of these differences were major enough to break the docs for some crates.
* A couple weeks afterward, [Hoedown was put back in](https://github.com/rust-lang/rust/pull/41290), at this point just to catch tests that Pulldown was "spuriously" running. This would at least provide some warning about spurious tests, rather than just breaking spontaneously.
* However, the problems had created enough noise by this point that just a few days after that, [Hoedown was switched back to the default](https://github.com/rust-lang/rust/pull/41431) while we came up with a solution for properly warning about the differences.
* That solution came a few weeks later, [as a series of warnings when the HTML emitted by the two parsers was semantically different](https://github.com/rust-lang/rust/pull/41991). But that came at a cost, as now rustdoc needed proc-macro support (the new crate needed some custom derives farther down its dependency tree), and the build system was not equipped to handle it at the time. It was worked on for three months as the issue stumped more and more people.
* In that time, [bootstrap was completely reworked](https://github.com/rust-lang/rust/pull/43059) to change how it ordered compilation, and [the method by which it built rustdoc would change](https://github.com/rust-lang/rust/pull/43482), as well. This allowed it to only be built after stage1, when proc-macros would be available, allowing the "rendering differences" PR to finally land.
* The warnings were not perfect, and revealed a few [spurious](https://github.com/rust-lang/rust/pull/44368) [differences](https://github.com/rust-lang/rust/pull/45421) between how we handled the renderers.
* Once these were handled, [we flipped the switch to turn on the "rendering difference" warnings all the time](https://github.com/rust-lang/rust/pull/45324), in October 2017. This began the "warning cycle" for this change, and landed in stable in 1.23, on 2018-01-04.
* Once those warnings hit stable, and after a couple weeks of seeing whether we would get any more reports than what we got from sitting on nightly/beta, [we switched the renderers](https://github.com/rust-lang/rust/pull/47398), making Pulldown the default but still offering the option to use Hoedown.
And that brings us to the present. We haven't received more new issues from this in the meantime, and the "switch by default" is now on beta. Our reasoning is that, at this point, anyone who would have been affected by this has run into it already.
2018-02-16 14:09:19 +00:00
|
|
|
let mut links = vec![];
|
2018-02-18 21:37:52 +00:00
|
|
|
let shortcut_links = RefCell::new(vec![]);
|
|
|
|
|
|
|
|
{
|
2018-06-03 10:22:24 +00:00
|
|
|
let locate = |s: &str| unsafe {
|
|
|
|
let s_start = s.as_ptr();
|
|
|
|
let s_end = s_start.add(s.len());
|
|
|
|
let md_start = md.as_ptr();
|
|
|
|
let md_end = md_start.add(md.len());
|
|
|
|
if md_start <= s_start && s_end <= md_end {
|
|
|
|
let start = s_start.offset_from(md_start) as usize;
|
|
|
|
let end = s_end.offset_from(md_start) as usize;
|
|
|
|
Some(start..end)
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2018-02-18 21:37:52 +00:00
|
|
|
let push = |_: &str, s: &str| {
|
2018-06-03 10:22:24 +00:00
|
|
|
shortcut_links.borrow_mut().push((s.to_owned(), locate(s)));
|
2018-02-18 21:37:52 +00:00
|
|
|
None
|
|
|
|
};
|
2019-04-20 17:03:59 +00:00
|
|
|
let p = Parser::new_with_broken_link_callback(md, opts(), Some(&push));
|
2018-02-18 21:37:52 +00:00
|
|
|
|
2018-07-22 13:25:00 +00:00
|
|
|
// There's no need to thread an IdMap through to here because
|
|
|
|
// the IDs generated aren't going to be emitted anywhere.
|
|
|
|
let mut ids = IdMap::new();
|
|
|
|
let iter = Footnotes::new(HeadingLinks::new(p, None, &mut ids));
|
2018-02-18 21:37:52 +00:00
|
|
|
|
|
|
|
for ev in iter {
|
2019-04-20 17:03:59 +00:00
|
|
|
if let Event::Start(Tag::Link(_, dest, _)) = ev {
|
2018-02-18 21:37:52 +00:00
|
|
|
debug!("found link: {}", dest);
|
2018-06-03 10:22:24 +00:00
|
|
|
links.push(match dest {
|
2019-04-20 17:03:59 +00:00
|
|
|
CowStr::Borrowed(s) => (s.to_owned(), locate(s)),
|
|
|
|
s @ CowStr::Boxed(..) | s @ CowStr::Inlined(..) => (s.into_string(), None),
|
2018-06-03 10:22:24 +00:00
|
|
|
});
|
2018-02-18 21:37:52 +00:00
|
|
|
}
|
2017-12-21 20:54:16 +00:00
|
|
|
}
|
|
|
|
}
|
Remove hoedown from rustdoc
Is it really time? Have our months, no, *years* of suffering come to an end? Are we finally able to cast off the pall of Hoedown? The weight which has dragged us down for so long?
-----
So, timeline for those who need to catch up:
* Way back in December 2016, [we decided we wanted to switch out the markdown renderer](https://github.com/rust-lang/rust/issues/38400). However, this was put on hold because the build system at the time made it difficult to pull in dependencies from crates.io.
* A few months later, in March 2017, [the first PR was done, to switch out the renderers entirely](https://github.com/rust-lang/rust/pull/40338). The PR itself was fraught with CI and build system issues, but eventually landed.
* However, not all was well in the Rustdoc world. During the PR and shortly after, we noticed [some differences in the way the two parsers handled some things](https://github.com/rust-lang/rust/issues/40912), and some of these differences were major enough to break the docs for some crates.
* A couple weeks afterward, [Hoedown was put back in](https://github.com/rust-lang/rust/pull/41290), at this point just to catch tests that Pulldown was "spuriously" running. This would at least provide some warning about spurious tests, rather than just breaking spontaneously.
* However, the problems had created enough noise by this point that just a few days after that, [Hoedown was switched back to the default](https://github.com/rust-lang/rust/pull/41431) while we came up with a solution for properly warning about the differences.
* That solution came a few weeks later, [as a series of warnings when the HTML emitted by the two parsers was semantically different](https://github.com/rust-lang/rust/pull/41991). But that came at a cost, as now rustdoc needed proc-macro support (the new crate needed some custom derives farther down its dependency tree), and the build system was not equipped to handle it at the time. It was worked on for three months as the issue stumped more and more people.
* In that time, [bootstrap was completely reworked](https://github.com/rust-lang/rust/pull/43059) to change how it ordered compilation, and [the method by which it built rustdoc would change](https://github.com/rust-lang/rust/pull/43482), as well. This allowed it to only be built after stage1, when proc-macros would be available, allowing the "rendering differences" PR to finally land.
* The warnings were not perfect, and revealed a few [spurious](https://github.com/rust-lang/rust/pull/44368) [differences](https://github.com/rust-lang/rust/pull/45421) between how we handled the renderers.
* Once these were handled, [we flipped the switch to turn on the "rendering difference" warnings all the time](https://github.com/rust-lang/rust/pull/45324), in October 2017. This began the "warning cycle" for this change, and landed in stable in 1.23, on 2018-01-04.
* Once those warnings hit stable, and after a couple weeks of seeing whether we would get any more reports than what we got from sitting on nightly/beta, [we switched the renderers](https://github.com/rust-lang/rust/pull/47398), making Pulldown the default but still offering the option to use Hoedown.
And that brings us to the present. We haven't received more new issues from this in the meantime, and the "switch by default" is now on beta. Our reasoning is that, at this point, anyone who would have been affected by this has run into it already.
2018-02-16 14:09:19 +00:00
|
|
|
|
2018-02-18 21:37:52 +00:00
|
|
|
let mut shortcut_links = shortcut_links.into_inner();
|
|
|
|
links.extend(shortcut_links.drain(..));
|
|
|
|
|
Remove hoedown from rustdoc
Is it really time? Have our months, no, *years* of suffering come to an end? Are we finally able to cast off the pall of Hoedown? The weight which has dragged us down for so long?
-----
So, timeline for those who need to catch up:
* Way back in December 2016, [we decided we wanted to switch out the markdown renderer](https://github.com/rust-lang/rust/issues/38400). However, this was put on hold because the build system at the time made it difficult to pull in dependencies from crates.io.
* A few months later, in March 2017, [the first PR was done, to switch out the renderers entirely](https://github.com/rust-lang/rust/pull/40338). The PR itself was fraught with CI and build system issues, but eventually landed.
* However, not all was well in the Rustdoc world. During the PR and shortly after, we noticed [some differences in the way the two parsers handled some things](https://github.com/rust-lang/rust/issues/40912), and some of these differences were major enough to break the docs for some crates.
* A couple weeks afterward, [Hoedown was put back in](https://github.com/rust-lang/rust/pull/41290), at this point just to catch tests that Pulldown was "spuriously" running. This would at least provide some warning about spurious tests, rather than just breaking spontaneously.
* However, the problems had created enough noise by this point that just a few days after that, [Hoedown was switched back to the default](https://github.com/rust-lang/rust/pull/41431) while we came up with a solution for properly warning about the differences.
* That solution came a few weeks later, [as a series of warnings when the HTML emitted by the two parsers was semantically different](https://github.com/rust-lang/rust/pull/41991). But that came at a cost, as now rustdoc needed proc-macro support (the new crate needed some custom derives farther down its dependency tree), and the build system was not equipped to handle it at the time. It was worked on for three months as the issue stumped more and more people.
* In that time, [bootstrap was completely reworked](https://github.com/rust-lang/rust/pull/43059) to change how it ordered compilation, and [the method by which it built rustdoc would change](https://github.com/rust-lang/rust/pull/43482), as well. This allowed it to only be built after stage1, when proc-macros would be available, allowing the "rendering differences" PR to finally land.
* The warnings were not perfect, and revealed a few [spurious](https://github.com/rust-lang/rust/pull/44368) [differences](https://github.com/rust-lang/rust/pull/45421) between how we handled the renderers.
* Once these were handled, [we flipped the switch to turn on the "rendering difference" warnings all the time](https://github.com/rust-lang/rust/pull/45324), in October 2017. This began the "warning cycle" for this change, and landed in stable in 1.23, on 2018-01-04.
* Once those warnings hit stable, and after a couple weeks of seeing whether we would get any more reports than what we got from sitting on nightly/beta, [we switched the renderers](https://github.com/rust-lang/rust/pull/47398), making Pulldown the default but still offering the option to use Hoedown.
And that brings us to the present. We haven't received more new issues from this in the meantime, and the "switch by default" is now on beta. Our reasoning is that, at this point, anyone who would have been affected by this has run into it already.
2018-02-16 14:09:19 +00:00
|
|
|
links
|
2017-12-21 20:54:16 +00:00
|
|
|
}
|
|
|
|
|
2018-12-15 21:25:50 +00:00
|
|
|
#[derive(Debug)]
|
|
|
|
crate struct RustCodeBlock {
|
|
|
|
/// The range in the markdown that the code block occupies. Note that this includes the fences
|
|
|
|
/// for fenced code blocks.
|
|
|
|
pub range: Range<usize>,
|
|
|
|
/// The range in the markdown that the code within the code block occupies.
|
|
|
|
pub code: Range<usize>,
|
|
|
|
pub is_fenced: bool,
|
|
|
|
pub syntax: Option<String>,
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns a range of bytes for each code block in the markdown that is tagged as `rust` or
|
|
|
|
/// untagged (and assumed to be rust).
|
|
|
|
crate fn rust_code_blocks(md: &str) -> Vec<RustCodeBlock> {
|
|
|
|
let mut code_blocks = vec![];
|
|
|
|
|
|
|
|
if md.is_empty() {
|
|
|
|
return code_blocks;
|
|
|
|
}
|
|
|
|
|
2019-04-20 17:03:59 +00:00
|
|
|
let mut p = Parser::new_ext(md, opts());
|
2018-12-15 21:25:50 +00:00
|
|
|
|
|
|
|
let mut code_block_start = 0;
|
|
|
|
let mut code_start = 0;
|
|
|
|
let mut is_fenced = false;
|
|
|
|
let mut previous_offset = 0;
|
|
|
|
let mut in_rust_code_block = false;
|
|
|
|
while let Some(event) = p.next() {
|
|
|
|
let offset = p.get_offset();
|
|
|
|
|
|
|
|
match event {
|
|
|
|
Event::Start(Tag::CodeBlock(syntax)) => {
|
|
|
|
let lang_string = if syntax.is_empty() {
|
|
|
|
LangString::all_false()
|
|
|
|
} else {
|
|
|
|
LangString::parse(&*syntax, ErrorCodes::Yes)
|
|
|
|
};
|
|
|
|
|
|
|
|
if lang_string.rust {
|
|
|
|
in_rust_code_block = true;
|
|
|
|
|
|
|
|
code_start = offset;
|
|
|
|
code_block_start = match md[previous_offset..offset].find("```") {
|
|
|
|
Some(fence_idx) => {
|
|
|
|
is_fenced = true;
|
|
|
|
previous_offset + fence_idx
|
|
|
|
}
|
|
|
|
None => offset,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Event::End(Tag::CodeBlock(syntax)) if in_rust_code_block => {
|
|
|
|
in_rust_code_block = false;
|
|
|
|
|
|
|
|
let code_block_end = if is_fenced {
|
|
|
|
let fence_str = &md[previous_offset..offset]
|
|
|
|
.chars()
|
|
|
|
.rev()
|
|
|
|
.collect::<String>();
|
|
|
|
fence_str
|
|
|
|
.find("```")
|
|
|
|
.map(|fence_idx| offset - fence_idx)
|
|
|
|
.unwrap_or_else(|| offset)
|
|
|
|
} else if md
|
|
|
|
.as_bytes()
|
|
|
|
.get(offset)
|
|
|
|
.map(|b| *b == b'\n')
|
|
|
|
.unwrap_or_default()
|
|
|
|
{
|
|
|
|
offset - 1
|
|
|
|
} else {
|
|
|
|
offset
|
|
|
|
};
|
|
|
|
|
|
|
|
let code_end = if is_fenced {
|
|
|
|
previous_offset
|
|
|
|
} else {
|
|
|
|
code_block_end
|
|
|
|
};
|
|
|
|
|
|
|
|
code_blocks.push(RustCodeBlock {
|
|
|
|
is_fenced,
|
|
|
|
range: Range {
|
|
|
|
start: code_block_start,
|
|
|
|
end: code_block_end,
|
|
|
|
},
|
|
|
|
code: Range {
|
|
|
|
start: code_start,
|
|
|
|
end: code_end,
|
|
|
|
},
|
|
|
|
syntax: if !syntax.is_empty() {
|
2019-04-20 17:03:59 +00:00
|
|
|
Some(syntax.into_string())
|
2018-12-15 21:25:50 +00:00
|
|
|
} else {
|
|
|
|
None
|
|
|
|
},
|
|
|
|
});
|
|
|
|
}
|
|
|
|
_ => (),
|
|
|
|
}
|
|
|
|
|
|
|
|
previous_offset = offset;
|
|
|
|
}
|
|
|
|
|
|
|
|
code_blocks
|
|
|
|
}
|
|
|
|
|
2018-11-04 22:39:24 +00:00
|
|
|
#[derive(Clone, Default, Debug)]
|
2018-07-22 13:25:00 +00:00
|
|
|
pub struct IdMap {
|
2018-08-18 10:55:43 +00:00
|
|
|
map: FxHashMap<String, usize>,
|
2018-07-22 13:25:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl IdMap {
|
|
|
|
pub fn new() -> Self {
|
|
|
|
IdMap::default()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn populate<I: IntoIterator<Item=String>>(&mut self, ids: I) {
|
|
|
|
for id in ids {
|
|
|
|
let _ = self.derive(id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn reset(&mut self) {
|
2018-08-18 10:55:43 +00:00
|
|
|
self.map = FxHashMap::default();
|
2018-07-22 13:25:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn derive(&mut self, candidate: String) -> String {
|
|
|
|
let id = match self.map.get_mut(&candidate) {
|
|
|
|
None => candidate,
|
|
|
|
Some(a) => {
|
|
|
|
let id = format!("{}-{}", candidate, *a);
|
|
|
|
*a += 1;
|
|
|
|
id
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
self.map.insert(id.clone(), 1);
|
|
|
|
id
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
#[test]
|
|
|
|
fn test_unique_id() {
|
|
|
|
let input = ["foo", "examples", "examples", "method.into_iter","examples",
|
|
|
|
"method.into_iter", "foo", "main", "search", "methods",
|
|
|
|
"examples", "method.into_iter", "assoc_type.Item", "assoc_type.Item"];
|
|
|
|
let expected = ["foo", "examples", "examples-1", "method.into_iter", "examples-2",
|
|
|
|
"method.into_iter-1", "foo-1", "main", "search", "methods",
|
|
|
|
"examples-3", "method.into_iter-2", "assoc_type.Item", "assoc_type.Item-1"];
|
|
|
|
|
|
|
|
let map = RefCell::new(IdMap::new());
|
|
|
|
let test = || {
|
|
|
|
let mut map = map.borrow_mut();
|
|
|
|
let actual: Vec<String> = input.iter().map(|s| map.derive(s.to_string())).collect();
|
|
|
|
assert_eq!(&actual[..], expected);
|
|
|
|
};
|
|
|
|
test();
|
|
|
|
map.borrow_mut().reset();
|
|
|
|
test();
|
|
|
|
}
|
|
|
|
|
2014-05-31 22:33:32 +00:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
2018-07-22 13:25:00 +00:00
|
|
|
use super::{ErrorCodes, LangString, Markdown, MarkdownHtml, IdMap};
|
2015-09-18 18:39:05 +00:00
|
|
|
use super::plain_summary_line;
|
2018-07-22 13:25:00 +00:00
|
|
|
use std::cell::RefCell;
|
2019-04-18 17:23:54 +00:00
|
|
|
use syntax::edition::{Edition, DEFAULT_EDITION};
|
2014-05-31 22:33:32 +00:00
|
|
|
|
|
|
|
#[test]
|
2014-06-19 12:38:01 +00:00
|
|
|
fn test_lang_string_parse() {
|
2014-06-19 13:11:18 +00:00
|
|
|
fn t(s: &str,
|
2016-01-05 22:38:11 +00:00
|
|
|
should_panic: bool, no_run: bool, ignore: bool, rust: bool, test_harness: bool,
|
2018-09-18 06:55:15 +00:00
|
|
|
compile_fail: bool, allow_fail: bool, error_codes: Vec<String>,
|
|
|
|
edition: Option<Edition>) {
|
2018-07-22 17:10:19 +00:00
|
|
|
assert_eq!(LangString::parse(s, ErrorCodes::Yes), LangString {
|
2017-08-07 05:54:09 +00:00
|
|
|
should_panic,
|
|
|
|
no_run,
|
|
|
|
ignore,
|
|
|
|
rust,
|
|
|
|
test_harness,
|
|
|
|
compile_fail,
|
|
|
|
error_codes,
|
2016-09-07 14:43:18 +00:00
|
|
|
original: s.to_owned(),
|
2017-08-07 05:54:09 +00:00
|
|
|
allow_fail,
|
2018-09-18 06:55:15 +00:00
|
|
|
edition,
|
2014-06-19 12:38:01 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2017-05-25 15:36:18 +00:00
|
|
|
fn v() -> Vec<String> {
|
|
|
|
Vec::new()
|
|
|
|
}
|
|
|
|
|
2018-09-18 06:55:15 +00:00
|
|
|
// ignore-tidy-linelength
|
|
|
|
// marker | should_panic | no_run | ignore | rust | test_harness
|
|
|
|
// | compile_fail | allow_fail | error_codes | edition
|
|
|
|
t("", false, false, false, true, false, false, false, v(), None);
|
|
|
|
t("rust", false, false, false, true, false, false, false, v(), None);
|
|
|
|
t("sh", false, false, false, false, false, false, false, v(), None);
|
|
|
|
t("ignore", false, false, true, true, false, false, false, v(), None);
|
|
|
|
t("should_panic", true, false, false, true, false, false, false, v(), None);
|
|
|
|
t("no_run", false, true, false, true, false, false, false, v(), None);
|
|
|
|
t("test_harness", false, false, false, true, true, false, false, v(), None);
|
|
|
|
t("compile_fail", false, true, false, true, false, true, false, v(), None);
|
|
|
|
t("allow_fail", false, false, false, true, false, false, true, v(), None);
|
|
|
|
t("{.no_run .example}", false, true, false, true, false, false, false, v(), None);
|
|
|
|
t("{.sh .should_panic}", true, false, false, false, false, false, false, v(), None);
|
|
|
|
t("{.example .rust}", false, false, false, true, false, false, false, v(), None);
|
|
|
|
t("{.test_harness .rust}", false, false, false, true, true, false, false, v(), None);
|
|
|
|
t("text, no_run", false, true, false, false, false, false, false, v(), None);
|
|
|
|
t("text,no_run", false, true, false, false, false, false, false, v(), None);
|
|
|
|
t("edition2015", false, false, false, true, false, false, false, v(), Some(Edition::Edition2015));
|
|
|
|
t("edition2018", false, false, false, true, false, false, false, v(), Some(Edition::Edition2018));
|
2014-05-31 22:33:32 +00:00
|
|
|
}
|
2014-10-05 13:00:50 +00:00
|
|
|
|
2015-09-19 01:43:59 +00:00
|
|
|
#[test]
|
|
|
|
fn test_header() {
|
|
|
|
fn t(input: &str, expect: &str) {
|
2018-07-22 13:25:00 +00:00
|
|
|
let mut map = IdMap::new();
|
2019-04-18 17:23:54 +00:00
|
|
|
let output = Markdown(input, &[], RefCell::new(&mut map),
|
|
|
|
ErrorCodes::Yes, DEFAULT_EDITION).to_string();
|
2017-03-25 18:07:52 +00:00
|
|
|
assert_eq!(output, expect, "original: {}", input);
|
2015-09-19 01:43:59 +00:00
|
|
|
}
|
|
|
|
|
2017-03-25 00:48:33 +00:00
|
|
|
t("# Foo bar", "<h1 id=\"foo-bar\" class=\"section-header\">\
|
|
|
|
<a href=\"#foo-bar\">Foo bar</a></h1>");
|
|
|
|
t("## Foo-bar_baz qux", "<h2 id=\"foo-bar_baz-qux\" class=\"section-\
|
|
|
|
header\"><a href=\"#foo-bar_baz-qux\">Foo-bar_baz qux</a></h2>");
|
2015-09-19 01:43:59 +00:00
|
|
|
t("### **Foo** *bar* baz!?!& -_qux_-%",
|
2017-03-25 18:07:52 +00:00
|
|
|
"<h3 id=\"foo-bar-baz--qux-\" class=\"section-header\">\
|
|
|
|
<a href=\"#foo-bar-baz--qux-\"><strong>Foo</strong> \
|
|
|
|
<em>bar</em> baz!?!& -<em>qux</em>-%</a></h3>");
|
|
|
|
t("#### **Foo?** & \\*bar?!* _`baz`_ ❤ #qux",
|
2017-03-25 00:48:33 +00:00
|
|
|
"<h4 id=\"foo--bar--baz--qux\" class=\"section-header\">\
|
|
|
|
<a href=\"#foo--bar--baz--qux\"><strong>Foo?</strong> & *bar?!* \
|
2015-09-19 01:43:59 +00:00
|
|
|
<em><code>baz</code></em> ❤ #qux</a></h4>");
|
|
|
|
}
|
|
|
|
|
2015-12-05 22:09:20 +00:00
|
|
|
#[test]
|
|
|
|
fn test_header_ids_multiple_blocks() {
|
2018-07-22 13:25:00 +00:00
|
|
|
let mut map = IdMap::new();
|
|
|
|
fn t(map: &mut IdMap, input: &str, expect: &str) {
|
2019-04-18 17:23:54 +00:00
|
|
|
let output = Markdown(input, &[], RefCell::new(map),
|
|
|
|
ErrorCodes::Yes, DEFAULT_EDITION).to_string();
|
2017-03-25 18:07:52 +00:00
|
|
|
assert_eq!(output, expect, "original: {}", input);
|
2015-12-05 22:09:20 +00:00
|
|
|
}
|
|
|
|
|
2018-07-22 13:25:00 +00:00
|
|
|
t(&mut map, "# Example", "<h1 id=\"example\" class=\"section-header\">\
|
|
|
|
<a href=\"#example\">Example</a></h1>");
|
|
|
|
t(&mut map, "# Panics", "<h1 id=\"panics\" class=\"section-header\">\
|
|
|
|
<a href=\"#panics\">Panics</a></h1>");
|
|
|
|
t(&mut map, "# Example", "<h1 id=\"example-1\" class=\"section-header\">\
|
|
|
|
<a href=\"#example-1\">Example</a></h1>");
|
|
|
|
t(&mut map, "# Main", "<h1 id=\"main\" class=\"section-header\">\
|
|
|
|
<a href=\"#main\">Main</a></h1>");
|
|
|
|
t(&mut map, "# Example", "<h1 id=\"example-2\" class=\"section-header\">\
|
|
|
|
<a href=\"#example-2\">Example</a></h1>");
|
|
|
|
t(&mut map, "# Panics", "<h1 id=\"panics-1\" class=\"section-header\">\
|
|
|
|
<a href=\"#panics-1\">Panics</a></h1>");
|
2015-12-05 22:09:20 +00:00
|
|
|
}
|
|
|
|
|
2014-12-25 05:39:29 +00:00
|
|
|
#[test]
|
|
|
|
fn test_plain_summary_line() {
|
|
|
|
fn t(input: &str, expect: &str) {
|
|
|
|
let output = plain_summary_line(input);
|
2017-03-25 18:07:52 +00:00
|
|
|
assert_eq!(output, expect, "original: {}", input);
|
2014-12-25 05:39:29 +00:00
|
|
|
}
|
|
|
|
|
2015-08-09 21:15:05 +00:00
|
|
|
t("hello [Rust](https://www.rust-lang.org) :)", "hello Rust :)");
|
2017-04-06 12:09:20 +00:00
|
|
|
t("hello [Rust](https://www.rust-lang.org \"Rust\") :)", "hello Rust :)");
|
2014-12-25 05:39:29 +00:00
|
|
|
t("code `let x = i32;` ...", "code `let x = i32;` ...");
|
|
|
|
t("type `Type<'static>` ...", "type `Type<'static>` ...");
|
|
|
|
t("# top header", "top header");
|
|
|
|
t("## header", "header");
|
|
|
|
}
|
2016-12-23 07:12:56 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_markdown_html_escape() {
|
|
|
|
fn t(input: &str, expect: &str) {
|
2018-07-22 13:25:00 +00:00
|
|
|
let mut idmap = IdMap::new();
|
2019-04-18 17:23:54 +00:00
|
|
|
let output = MarkdownHtml(input, RefCell::new(&mut idmap),
|
|
|
|
ErrorCodes::Yes, DEFAULT_EDITION).to_string();
|
2017-03-25 18:07:52 +00:00
|
|
|
assert_eq!(output, expect, "original: {}", input);
|
2016-12-23 07:12:56 +00:00
|
|
|
}
|
|
|
|
|
2017-04-06 12:09:20 +00:00
|
|
|
t("`Struct<'a, T>`", "<p><code>Struct<'a, T></code></p>\n");
|
|
|
|
t("Struct<'a, T>", "<p>Struct<'a, T></p>\n");
|
|
|
|
t("Struct<br>", "<p>Struct<br></p>\n");
|
2016-12-23 07:12:56 +00:00
|
|
|
}
|
2014-05-30 02:03:06 +00:00
|
|
|
}
|