use std::cell::RefCell;
use std::collections::{HashMap, VecDeque};
use std::default::Default;
+use std::ffi::CString;
use std::fmt::{self, Write};
use std::str;
use syntax::feature_gate::UnstableFeatures;
use html::render::derive_id;
use html::toc::TocBuilder;
use html::highlight;
+use html::escape::Escape;
use test;
use pulldown_cmark::{html, Event, Tag, Parser};
use pulldown_cmark::{Options, OPTION_ENABLE_FOOTNOTES, OPTION_ENABLE_TABLES};
+#[derive(PartialEq, Debug, Clone, Copy)]
+pub enum RenderType {
+ Hoedown,
+ Pulldown,
+}
+
/// A unit struct which has the `fmt::Display` trait implemented. When
/// formatted, this struct will emit the HTML corresponding to the rendered
/// version of the contained markdown string.
// The second parameter is whether we need a shorter version or not.
-pub struct Markdown<'a>(pub &'a str);
+pub struct Markdown<'a>(pub &'a str, pub RenderType);
/// A unit struct like `Markdown`, that renders the markdown with a
/// table of contents.
-pub struct MarkdownWithToc<'a>(pub &'a str);
+pub struct MarkdownWithToc<'a>(pub &'a str, pub RenderType);
/// A unit struct like `Markdown`, that renders the markdown escaping HTML tags.
-pub struct MarkdownHtml<'a>(pub &'a str);
+pub struct MarkdownHtml<'a>(pub &'a str, pub RenderType);
/// A unit struct like `Markdown`, that renders only the first paragraph.
pub struct MarkdownSummaryLine<'a>(pub &'a str);
}
}
+/// Returns a new string with all consecutive whitespace collapsed into
+/// single spaces.
+///
+/// Any leading or trailing whitespace will be trimmed.
+fn collapse_whitespace(s: &str) -> String {
+ s.split_whitespace().collect::<Vec<_>>().join(" ")
+}
+
/// Convert chars from a title for an id.
///
/// "Hello, world!" -> "hello-world"
const HOEDOWN_EXT_STRIKETHROUGH: libc::c_uint = 1 << 4;
const HOEDOWN_EXT_SUPERSCRIPT: libc::c_uint = 1 << 8;
const HOEDOWN_EXT_FOOTNOTES: libc::c_uint = 1 << 2;
+const HOEDOWN_HTML_ESCAPE: libc::c_uint = 1 << 1;
const HOEDOWN_EXTENSIONS: libc::c_uint =
HOEDOWN_EXT_NO_INTRA_EMPHASIS | HOEDOWN_EXT_TABLES |
unit: libc::size_t,
}
+struct MyOpaque {
+ dfltblk: extern "C" fn(*mut hoedown_buffer, *const hoedown_buffer,
+ *const hoedown_buffer, *const hoedown_renderer_data,
+ libc::size_t),
+ toc_builder: Option<TocBuilder>,
+}
+
extern {
fn hoedown_html_renderer_new(render_flags: libc::c_uint,
nesting_level: libc::c_int)
fn hoedown_document_free(md: *mut hoedown_document);
fn hoedown_buffer_new(unit: libc::size_t) -> *mut hoedown_buffer;
+ fn hoedown_buffer_puts(b: *mut hoedown_buffer, c: *const libc::c_char);
fn hoedown_buffer_free(b: *mut hoedown_buffer);
}
}
}
+pub fn render(w: &mut fmt::Formatter,
+ s: &str,
+ print_toc: bool,
+ html_flags: libc::c_uint) -> fmt::Result {
+ extern fn block(ob: *mut hoedown_buffer, orig_text: *const hoedown_buffer,
+ lang: *const hoedown_buffer, data: *const hoedown_renderer_data,
+ line: libc::size_t) {
+ unsafe {
+ if orig_text.is_null() { return }
+
+ let opaque = (*data).opaque as *mut hoedown_html_renderer_state;
+ let my_opaque: &MyOpaque = &*((*opaque).opaque as *const MyOpaque);
+ let text = (*orig_text).as_bytes();
+ let origtext = str::from_utf8(text).unwrap();
+ let origtext = origtext.trim_left();
+ debug!("docblock: ==============\n{:?}\n=======", text);
+ let rendered = if lang.is_null() || origtext.is_empty() {
+ false
+ } else {
+ let rlang = (*lang).as_bytes();
+ let rlang = str::from_utf8(rlang).unwrap();
+ if !LangString::parse(rlang).rust {
+ (my_opaque.dfltblk)(ob, orig_text, lang,
+ opaque as *const hoedown_renderer_data,
+ line);
+ true
+ } else {
+ false
+ }
+ };
+
+ let lines = origtext.lines().filter(|l| {
+ stripped_filtered_line(*l).is_none()
+ });
+ let text = lines.collect::<Vec<&str>>().join("\n");
+ if rendered { return }
+ PLAYGROUND.with(|play| {
+ // insert newline to clearly separate it from the
+ // previous block so we can shorten the html output
+ let mut s = String::from("\n");
+ let playground_button = play.borrow().as_ref().and_then(|&(ref krate, ref url)| {
+ if url.is_empty() {
+ return None;
+ }
+ let test = origtext.lines().map(|l| {
+ stripped_filtered_line(l).unwrap_or(l)
+ }).collect::<Vec<&str>>().join("\n");
+ let krate = krate.as_ref().map(|s| &**s);
+ let test = test::maketest(&test, krate, false,
+ &Default::default());
+ let channel = if test.contains("#![feature(") {
+ "&version=nightly"
+ } else {
+ ""
+ };
+ // 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!(
+ r#"<a class="test-arrow" target="_blank" href="{}?code={}{}">Run</a>"#,
+ url, test_escaped, channel
+ ))
+ });
+ s.push_str(&highlight::render_with_highlighting(
+ &text,
+ Some("rust-example-rendered"),
+ None,
+ playground_button.as_ref().map(String::as_str)));
+ let output = CString::new(s).unwrap();
+ hoedown_buffer_puts(ob, output.as_ptr());
+ })
+ }
+ }
+
+ extern fn header(ob: *mut hoedown_buffer, text: *const hoedown_buffer,
+ level: libc::c_int, data: *const hoedown_renderer_data,
+ _: libc::size_t) {
+ // hoedown does this, we may as well too
+ unsafe { hoedown_buffer_puts(ob, "\n\0".as_ptr() as *const _); }
+
+ // Extract the text provided
+ let s = if text.is_null() {
+ "".to_owned()
+ } else {
+ let s = unsafe { (*text).as_bytes() };
+ str::from_utf8(&s).unwrap().to_owned()
+ };
+
+ // Discard '<em>', '<code>' tags and some escaped characters,
+ // transform the contents of the header into a hyphenated string
+ // without non-alphanumeric characters other than '-' and '_'.
+ //
+ // This is a terrible hack working around how hoedown gives us rendered
+ // html for text rather than the raw text.
+ let mut id = s.clone();
+ let repl_sub = vec!["<em>", "</em>", "<code>", "</code>",
+ "<strong>", "</strong>",
+ "<", ">", "&", "'", """];
+ for sub in repl_sub {
+ id = id.replace(sub, "");
+ }
+ let id = id.chars().filter_map(|c| {
+ if c.is_alphanumeric() || c == '-' || c == '_' {
+ if c.is_ascii() {
+ Some(c.to_ascii_lowercase())
+ } else {
+ Some(c)
+ }
+ } else if c.is_whitespace() && c.is_ascii() {
+ Some('-')
+ } else {
+ None
+ }
+ }).collect::<String>();
+
+ let opaque = unsafe { (*data).opaque as *mut hoedown_html_renderer_state };
+ let opaque = unsafe { &mut *((*opaque).opaque as *mut MyOpaque) };
+
+ let id = derive_id(id);
+
+ let sec = opaque.toc_builder.as_mut().map_or("".to_owned(), |builder| {
+ format!("{} ", builder.push(level as u32, s.clone(), id.clone()))
+ });
+
+ // Render the HTML
+ let text = format!("<h{lvl} id='{id}' class='section-header'>\
+ <a href='#{id}'>{sec}{}</a></h{lvl}>",
+ s, lvl = level, id = id, sec = sec);
+
+ let text = CString::new(text).unwrap();
+ unsafe { hoedown_buffer_puts(ob, text.as_ptr()) }
+ }
+
+ extern fn codespan(
+ ob: *mut hoedown_buffer,
+ text: *const hoedown_buffer,
+ _: *const hoedown_renderer_data,
+ _: libc::size_t
+ ) -> libc::c_int {
+ let content = if text.is_null() {
+ "".to_owned()
+ } else {
+ let bytes = unsafe { (*text).as_bytes() };
+ let s = str::from_utf8(bytes).unwrap();
+ collapse_whitespace(s)
+ };
+
+ let content = format!("<code>{}</code>", Escape(&content));
+ let element = CString::new(content).unwrap();
+ unsafe { hoedown_buffer_puts(ob, element.as_ptr()); }
+ // Return anything except 0, which would mean "also print the code span verbatim".
+ 1
+ }
+
+ unsafe {
+ let ob = hoedown_buffer_new(DEF_OUNIT);
+ let renderer = hoedown_html_renderer_new(html_flags, 0);
+ let mut opaque = MyOpaque {
+ dfltblk: (*renderer).blockcode.unwrap(),
+ toc_builder: if print_toc {Some(TocBuilder::new())} else {None}
+ };
+ (*((*renderer).opaque as *mut hoedown_html_renderer_state)).opaque
+ = &mut opaque as *mut _ as *mut libc::c_void;
+ (*renderer).blockcode = Some(block);
+ (*renderer).header = Some(header);
+ (*renderer).codespan = Some(codespan);
+
+ let document = hoedown_document_new(renderer, HOEDOWN_EXTENSIONS, 16);
+ hoedown_document_render(document, ob, s.as_ptr(),
+ s.len() as libc::size_t);
+ hoedown_document_free(document);
+
+ hoedown_html_renderer_free(renderer);
+
+ let mut ret = opaque.toc_builder.map_or(Ok(()), |builder| {
+ write!(w, "<nav id=\"TOC\">{}</nav>", builder.into_toc())
+ });
+
+ if ret.is_ok() {
+ let buf = (*ob).as_bytes();
+ ret = w.write_str(str::from_utf8(buf).unwrap());
+ }
+ hoedown_buffer_free(ob);
+ ret
+ }
+}
+
pub fn old_find_testable_code(doc: &str, tests: &mut ::test::Collector, position: Span) {
extern fn block(_ob: *mut hoedown_buffer,
text: *const hoedown_buffer,
stripped_filtered_line(l).unwrap_or(l)
});
let filename = tests.get_filename();
- tests.add_old_test(lines.collect::<Vec<&str>>().join("\n"), filename);
+
+ if tests.render_type == RenderType::Hoedown {
+ let text = (*text).as_bytes();
+ let text = str::from_utf8(text).unwrap();
+ let lines = text.lines().map(|l| {
+ stripped_filtered_line(l).unwrap_or(l)
+ });
+ let text = lines.collect::<Vec<&str>>().join("\n");
+ tests.add_test(text.to_owned(),
+ block_info.should_panic, block_info.no_run,
+ block_info.ignore, block_info.test_harness,
+ block_info.compile_fail, block_info.error_codes,
+ line, filename);
+ } else {
+ tests.add_old_test(lines.collect::<Vec<&str>>().join("\n"), filename);
+ }
}
}
}
tests.set_position(position);
-
unsafe {
let ob = hoedown_buffer_new(DEF_OUNIT);
let renderer = hoedown_html_renderer_new(0, 0);
impl<'a> fmt::Display for Markdown<'a> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
- let Markdown(md) = *self;
+ let Markdown(md, render_type) = *self;
+
// This is actually common enough to special-case
if md.is_empty() { return Ok(()) }
+ if render_type == RenderType::Hoedown {
+ render(fmt, md, false, 0)
+ } else {
+ let mut opts = Options::empty();
+ opts.insert(OPTION_ENABLE_TABLES);
+ opts.insert(OPTION_ENABLE_FOOTNOTES);
- let mut opts = Options::empty();
- opts.insert(OPTION_ENABLE_TABLES);
- opts.insert(OPTION_ENABLE_FOOTNOTES);
-
- let p = Parser::new_ext(md, opts);
+ let p = Parser::new_ext(md, opts);
- let mut s = String::with_capacity(md.len() * 3 / 2);
+ let mut s = String::with_capacity(md.len() * 3 / 2);
- html::push_html(&mut s,
- Footnotes::new(CodeBlocks::new(HeadingLinks::new(p, None))));
+ html::push_html(&mut s,
+ Footnotes::new(CodeBlocks::new(HeadingLinks::new(p, None))));
- fmt.write_str(&s)
+ fmt.write_str(&s)
+ }
}
}
impl<'a> fmt::Display for MarkdownWithToc<'a> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
- let MarkdownWithToc(md) = *self;
+ let MarkdownWithToc(md, render_type) = *self;
- let mut opts = Options::empty();
- opts.insert(OPTION_ENABLE_TABLES);
- opts.insert(OPTION_ENABLE_FOOTNOTES);
+ if render_type == RenderType::Hoedown {
+ render(fmt, md, true, 0)
+ } else {
+ let mut opts = Options::empty();
+ opts.insert(OPTION_ENABLE_TABLES);
+ opts.insert(OPTION_ENABLE_FOOTNOTES);
- let p = Parser::new_ext(md, opts);
+ let p = Parser::new_ext(md, opts);
- let mut s = String::with_capacity(md.len() * 3 / 2);
+ let mut s = String::with_capacity(md.len() * 3 / 2);
- let mut toc = TocBuilder::new();
+ let mut toc = TocBuilder::new();
- html::push_html(&mut s,
- Footnotes::new(CodeBlocks::new(HeadingLinks::new(p, Some(&mut toc)))));
+ html::push_html(&mut s,
+ Footnotes::new(CodeBlocks::new(HeadingLinks::new(p, Some(&mut toc)))));
- write!(fmt, "<nav id=\"TOC\">{}</nav>", toc.into_toc())?;
+ write!(fmt, "<nav id=\"TOC\">{}</nav>", toc.into_toc())?;
- fmt.write_str(&s)
+ fmt.write_str(&s)
+ }
}
}
impl<'a> fmt::Display for MarkdownHtml<'a> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
- let MarkdownHtml(md) = *self;
+ let MarkdownHtml(md, render_type) = *self;
+
// This is actually common enough to special-case
if md.is_empty() { return Ok(()) }
+ if render_type == RenderType::Hoedown {
+ render(fmt, md, false, HOEDOWN_HTML_ESCAPE)
+ } else {
+ let mut opts = Options::empty();
+ opts.insert(OPTION_ENABLE_TABLES);
+ opts.insert(OPTION_ENABLE_FOOTNOTES);
- let mut opts = Options::empty();
- opts.insert(OPTION_ENABLE_TABLES);
- opts.insert(OPTION_ENABLE_FOOTNOTES);
-
- let p = Parser::new_ext(md, opts);
+ let p = Parser::new_ext(md, opts);
- // Treat inline HTML as plain text.
- let p = p.map(|event| match event {
- Event::Html(text) | Event::InlineHtml(text) => Event::Text(text),
- _ => event
- });
+ // Treat inline HTML as plain text.
+ let p = p.map(|event| match event {
+ Event::Html(text) | Event::InlineHtml(text) => Event::Text(text),
+ _ => event
+ });
- let mut s = String::with_capacity(md.len() * 3 / 2);
+ let mut s = String::with_capacity(md.len() * 3 / 2);
- html::push_html(&mut s,
- Footnotes::new(CodeBlocks::new(HeadingLinks::new(p, None))));
+ html::push_html(&mut s,
+ Footnotes::new(CodeBlocks::new(HeadingLinks::new(p, None))));
- fmt.write_str(&s)
+ fmt.write_str(&s)
+ }
}
}
use html::format::{VisSpace, Method, UnsafetySpace, MutableSpace};
use html::format::fmt_impl_for_trait_page;
use html::item_type::ItemType;
-use html::markdown::{self, Markdown, MarkdownHtml, MarkdownSummaryLine};
+use html::markdown::{self, Markdown, MarkdownHtml, MarkdownSummaryLine, RenderType};
use html::{highlight, layout};
/// A pair of name and its optional document.
/// publicly reused items to redirect to the right location.
pub render_redirect_pages: bool,
pub shared: Arc<SharedContext>,
+ pub render_type: RenderType,
}
pub struct SharedContext {
dst: PathBuf,
passes: FxHashSet<String>,
css_file_extension: Option<PathBuf>,
- renderinfo: RenderInfo) -> Result<(), Error> {
+ renderinfo: RenderInfo,
+ render_type: RenderType) -> Result<(), Error> {
let src_root = match krate.src.parent() {
Some(p) => p.to_path_buf(),
None => PathBuf::new(),
dst: dst,
render_redirect_pages: false,
shared: Arc::new(scx),
+ render_type: render_type,
};
// Crawl the crate to build various caches used for the output
fn document(w: &mut fmt::Formatter, cx: &Context, item: &clean::Item) -> fmt::Result {
document_stability(w, cx, item)?;
- document_full(w, item)?;
+ document_full(w, item, cx.render_type)?;
Ok(())
}
-fn document_short(w: &mut fmt::Formatter, item: &clean::Item, link: AssocItemLink) -> fmt::Result {
+fn document_short(w: &mut fmt::Formatter, item: &clean::Item, link: AssocItemLink,
+ render_type: RenderType) -> fmt::Result {
if let Some(s) = item.doc_value() {
let markdown = if s.contains('\n') {
format!("{} [Read more]({})",
format!("{}", &plain_summary_line(Some(s)))
};
write!(w, "<div class='docblock'>{}</div>",
- Markdown(&markdown))?;
+ Markdown(&markdown, render_type))?;
}
Ok(())
}
}
}
-fn document_full(w: &mut fmt::Formatter, item: &clean::Item) -> fmt::Result {
+fn document_full(w: &mut fmt::Formatter, item: &clean::Item,
+ render_type: RenderType) -> fmt::Result {
if let Some(s) = get_doc_value(item) {
write!(w, "<div class='docblock'>{}</div>",
- Markdown(&format!("{}{}", md_render_assoc_item(item), s)))?;
+ Markdown(&format!("{}{}", md_render_assoc_item(item), s), render_type))?;
}
Ok(())
}
</tr>",
name = *myitem.name.as_ref().unwrap(),
stab_docs = stab_docs,
- docs = MarkdownSummaryLine(doc_value),
+ docs = if cx.render_type == RenderType::Hoedown {
+ format!("{}",
+ shorter(Some(&Markdown(doc_value,
+ RenderType::Hoedown).to_string())))
+ } else {
+ format!("{}", MarkdownSummaryLine(doc_value))
+ },
class = myitem.type_(),
stab = myitem.stability_class().unwrap_or("".to_string()),
unsafety_flag = unsafety_flag,
} else {
String::new()
};
- let text = format!("Deprecated{}{}", since, MarkdownHtml(&deprecated_reason));
+ let text = format!("Deprecated{}{}",
+ since,
+ MarkdownHtml(&deprecated_reason, cx.render_type));
stability.push(format!("<div class='stab deprecated'>{}</div>", text))
};
let text = format!("<summary><span class=microscope>🔬</span> \
This is a nightly-only experimental API. {}\
</summary>{}",
- unstable_extra, MarkdownHtml(&stab.unstable_reason));
+ unstable_extra,
+ MarkdownHtml(&stab.unstable_reason, cx.render_type));
stability.push(format!("<div class='stab unstable'><details>{}</details></div>",
text));
}
String::new()
};
- let text = format!("Deprecated{}{}", since, MarkdownHtml(¬e));
+ let text = format!("Deprecated{}{}", since, MarkdownHtml(¬e, cx.render_type));
stability.push(format!("<div class='stab deprecated'>{}</div>", text))
}
write!(w, "</span>")?;
write!(w, "</h3>\n")?;
if let Some(ref dox) = i.impl_item.doc_value() {
- write!(w, "<div class='docblock'>{}</div>", Markdown(dox))?;
+ write!(w, "<div class='docblock'>{}</div>", Markdown(dox, cx.render_type))?;
}
}
// because impls can't have a stability.
document_stability(w, cx, it)?;
if get_doc_value(item).is_some() {
- document_full(w, item)?;
+ document_full(w, item, cx.render_type)?;
} else {
// In case the item isn't documented,
// provide short documentation from the trait.
- document_short(w, it, link)?;
+ document_short(w, it, link, cx.render_type)?;
}
}
} else {
}
} else {
document_stability(w, cx, item)?;
- document_short(w, item, link)?;
+ document_short(w, item, link, cx.render_type)?;
}
}
Ok(())
use clean::AttributesExt;
+use html::markdown::RenderType;
+
struct Output {
krate: clean::Crate,
renderinfo: html::render::RenderInfo,
"URL to send code snippets to, may be reset by --markdown-playground-url \
or `#![doc(html_playground_url=...)]`",
"URL")),
+ unstable(optflag("", "enable-commonmark", "to enable commonmark doc rendering/testing")),
]
}
let css_file_extension = matches.opt_str("e").map(|s| PathBuf::from(&s));
let cfgs = matches.opt_strs("cfg");
+ let render_type = if matches.opt_present("enable-commonmark") {
+ RenderType::Pulldown
+ } else {
+ RenderType::Hoedown
+ };
+
if let Some(ref p) = css_file_extension {
if !p.is_file() {
writeln!(
match (should_test, markdown_input) {
(true, true) => {
- return markdown::test(input, cfgs, libs, externs, test_args, maybe_sysroot)
+ return markdown::test(input, cfgs, libs, externs, test_args, maybe_sysroot, render_type)
}
(true, false) => {
- return test::run(input, cfgs, libs, externs, test_args, crate_name, maybe_sysroot)
+ return test::run(input, cfgs, libs, externs, test_args, crate_name, maybe_sysroot,
+ render_type)
}
(false, true) => return markdown::render(input,
output.unwrap_or(PathBuf::from("doc")),
&matches, &external_html,
- !matches.opt_present("markdown-no-toc")),
+ !matches.opt_present("markdown-no-toc"),
+ render_type),
(false, false) => {}
}
output.unwrap_or(PathBuf::from("doc")),
passes.into_iter().collect(),
css_file_extension,
- renderinfo)
+ renderinfo,
+ render_type)
.expect("failed to generate documentation");
0
}
use html::escape::Escape;
use html::markdown;
use html::markdown::{Markdown, MarkdownWithToc, find_testable_code, old_find_testable_code};
+use html::markdown::RenderType;
use test::{TestOptions, Collector};
/// Separate any lines at the start of the file that begin with `# ` or `%`.
/// Render `input` (e.g. "foo.md") into an HTML file in `output`
/// (e.g. output = "bar" => "bar/foo.html").
pub fn render(input: &str, mut output: PathBuf, matches: &getopts::Matches,
- external_html: &ExternalHtml, include_toc: bool) -> isize {
+ external_html: &ExternalHtml, include_toc: bool,
+ render_type: RenderType) -> isize {
let input_p = Path::new(input);
output.push(input_p.file_stem().unwrap());
output.set_extension("html");
reset_ids(false);
let rendered = if include_toc {
- format!("{}", MarkdownWithToc(text))
+ format!("{}", MarkdownWithToc(text, render_type))
} else {
- format!("{}", Markdown(text))
+ format!("{}", Markdown(text, render_type))
};
let err = write!(
/// Run any tests/code examples in the markdown file `input`.
pub fn test(input: &str, cfgs: Vec<String>, libs: SearchPaths, externs: Externs,
- mut test_args: Vec<String>, maybe_sysroot: Option<PathBuf>) -> isize {
+ mut test_args: Vec<String>, maybe_sysroot: Option<PathBuf>,
+ render_type: RenderType) -> isize {
let input_str = match load_string(input) {
Ok(s) => s,
Err(LoadStringError::ReadFail) => return 1,
opts.no_crate_inject = true;
let mut collector = Collector::new(input.to_string(), cfgs, libs, externs,
true, opts, maybe_sysroot, None,
- Some(input.to_owned()));
+ Some(input.to_owned()),
+ render_type);
old_find_testable_code(&input_str, &mut collector, DUMMY_SP);
find_testable_code(&input_str, &mut collector, DUMMY_SP);
test_args.insert(0, "rustdoctest".to_string());
use errors::emitter::ColorConfig;
use clean::Attributes;
-use html::markdown;
+use html::markdown::{self, RenderType};
#[derive(Clone, Default)]
pub struct TestOptions {
externs: Externs,
mut test_args: Vec<String>,
crate_name: Option<String>,
- maybe_sysroot: Option<PathBuf>)
+ maybe_sysroot: Option<PathBuf>,
+ render_type: RenderType)
-> isize {
let input_path = PathBuf::from(input);
let input = config::Input::File(input_path.clone());
opts,
maybe_sysroot,
Some(codemap),
- None);
+ None,
+ render_type);
{
let dep_graph = DepGraph::new(false);
position: Span,
codemap: Option<Rc<CodeMap>>,
filename: Option<String>,
+ // to be removed when hoedown will be removed as well
+ pub render_type: RenderType,
}
impl Collector {
pub fn new(cratename: String, cfgs: Vec<String>, libs: SearchPaths, externs: Externs,
use_headers: bool, opts: TestOptions, maybe_sysroot: Option<PathBuf>,
- codemap: Option<Rc<CodeMap>>, filename: Option<String>) -> Collector {
+ codemap: Option<Rc<CodeMap>>, filename: Option<String>,
+ render_type: RenderType) -> Collector {
Collector {
tests: Vec::new(),
old_tests: HashMap::new(),
position: DUMMY_SP,
codemap: codemap,
filename: filename,
+ render_type: render_type,
}
}
as_test_harness: bool, compile_fail: bool, error_codes: Vec<String>,
line: usize, filename: String) {
let name = self.generate_name(line, &filename);
- let name_beg = self.generate_name_beginning(&filename);
- let mut found = false;
- // to be removed when hoedown is removed
- let test = test.trim().to_owned();
- if let Some(entry) = self.old_tests.get_mut(&name_beg) {
- found = entry.remove_item(&test).is_some();
- }
- if !found {
- let _ = writeln!(&mut io::stderr(),
- "WARNING: {} Code block is not currently run as a test, but will in \
- future versions of rustdoc. Please ensure this code block is a \
- runnable test, or use the `ignore` directive.",
- name);
- return
+ if self.render_type == RenderType::Pulldown {
+ let name_beg = self.generate_name_beginning(&filename);
+ let mut found = false;
+ // to be removed when hoedown is removed
+ let test = test.trim().to_owned();
+ if let Some(entry) = self.old_tests.get_mut(&name_beg) {
+ found = entry.remove_item(&test).is_some();
+ }
+ if !found {
+ let _ = writeln!(&mut io::stderr(),
+ "WARNING: {} Code block is not currently run as a test, but will in \
+ future versions of rustdoc. Please ensure this code block is a \
+ runnable test, or use the `ignore` directive.",
+ name);
+ return
+ }
}
let cfgs = self.cfgs.clone();
let libs = self.libs.clone();
attrs.unindent_doc_comments();
if let Some(doc) = attrs.doc_value() {
self.collector.cnt = 0;
- markdown::old_find_testable_code(doc, self.collector,
+ if self.collector.render_type == RenderType::Pulldown {
+ markdown::old_find_testable_code(doc, self.collector,
+ attrs.span.unwrap_or(DUMMY_SP));
+ markdown::find_testable_code(doc, self.collector,
attrs.span.unwrap_or(DUMMY_SP));
- markdown::find_testable_code(doc, self.collector,
- attrs.span.unwrap_or(DUMMY_SP));
+ } else {
+ markdown::old_find_testable_code(doc, self.collector,
+ attrs.span.unwrap_or(DUMMY_SP));
+ }
}
nested(self);
+++ /dev/null
-// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-#![crate_name = "foo"]
-
-// ignore-tidy-end-whitespace
-
-// @has foo/fn.f.html
-// @has - '<p>hard break:<br />'
-// @has - 'after hard break</p>'
-/// hard break:
-/// after hard break
-pub fn f() {}
+++ /dev/null
-// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-#![crate_name = "foo"]
-
-// ignore-tidy-linelength
-
-// @has foo/fn.f.html
-// @has - '<p>markdown test</p>'
-// @has - '<p>this is a <a href="https://example.com" title="this is a title">link</a>.</p>'
-// @has - '<hr />'
-// @has - '<p>a footnote<sup id="supref1"><a href="#ref1">1</a></sup>.</p>'
-// @has - '<p>another footnote<sup id="supref2"><a href="#ref2">2</a></sup>.</p>'
-// @has - '<p><img src="https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png" alt="Rust" /></p>'
-// @has - '<div class="footnotes"><hr><ol><li id="ref1">'
-// @has - '<p>Thing <a href="#supref1" rev="footnote">↩</a></p></li><li id="ref2">'
-// @has - '<p>Another Thing <a href="#supref2" rev="footnote">↩</a></p></li></ol></div>'
-/// markdown test
-///
-/// this is a [link].
-///
-/// [link]: https://example.com "this is a title"
-///
-/// -----------
-///
-/// a footnote[^footnote].
-///
-/// another footnote[^footnotebis].
-///
-/// [^footnote]: Thing
-///
-///
-/// [^footnotebis]: Another Thing
-///
-///
-/// ![Rust](https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png)
-pub fn f() {}