about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2017-03-29 07:06:13 +0000
committerbors <bors@rust-lang.org>2017-03-29 07:06:13 +0000
commitabf5592510156e08b4de3830afaabbc0b65ce054 (patch)
tree9695a70dd5011efd447aa114cc04e533b8cd6f59
parent4465f22ef6498db65059a41595d354a7af56cbea (diff)
parenta7c6d3e16a71fc0dcce4c9be09d031d746f84a69 (diff)
downloadrust-abf5592510156e08b4de3830afaabbc0b65ce054.tar.gz
rust-abf5592510156e08b4de3830afaabbc0b65ce054.zip
Auto merge of #40338 - GuillaumeGomez:pulldown-switch, r=frewsxcv,steveklabnik
Replace hoedown with pull in rustdoc

cc @rust-lang/docs
-rw-r--r--.gitmodules4
-rw-r--r--src/Cargo.lock62
-rw-r--r--src/libcore/result.rs2
-rw-r--r--src/librustdoc/Cargo.toml2
-rw-r--r--src/librustdoc/build.rs29
-rw-r--r--src/librustdoc/html/markdown.rs951
-rw-r--r--src/librustdoc/html/render.rs13
-rw-r--r--src/librustdoc/lib.rs3
-rw-r--r--src/librustdoc/markdown.rs4
m---------src/rt/hoedown0
-rw-r--r--src/rustc/rustdoc.rs2
-rw-r--r--src/tools/error_index_generator/main.rs4
12 files changed, 555 insertions, 521 deletions
diff --git a/.gitmodules b/.gitmodules
index d2e1fb868a9..53d17874924 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -5,10 +5,6 @@
 [submodule "src/compiler-rt"]
 	path = src/compiler-rt
 	url = https://github.com/rust-lang/compiler-rt.git
-[submodule "src/rt/hoedown"]
-	path = src/rt/hoedown
-	url = https://github.com/rust-lang/hoedown.git
-	branch = rust-2015-09-21-do-not-delete
 [submodule "src/jemalloc"]
 	path = src/jemalloc
 	url = https://github.com/rust-lang/jemalloc.git
diff --git a/src/Cargo.lock b/src/Cargo.lock
index a0b47f4f0b2..a9021dc34e2 100644
--- a/src/Cargo.lock
+++ b/src/Cargo.lock
@@ -8,7 +8,7 @@ dependencies = [
 
 [[package]]
 name = "aho-corasick"
-version = "0.6.2"
+version = "0.6.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "memchr 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -27,7 +27,7 @@ version = "0.0.0"
 dependencies = [
  "build_helper 0.1.0",
  "core 0.0.0",
- "gcc 0.3.44 (registry+https://github.com/rust-lang/crates.io-index)",
+ "gcc 0.3.45 (registry+https://github.com/rust-lang/crates.io-index)",
  "libc 0.0.0",
 ]
 
@@ -65,7 +65,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "bitflags"
-version = "0.8.0"
+version = "0.8.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
@@ -75,7 +75,7 @@ dependencies = [
  "build_helper 0.1.0",
  "cmake 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)",
  "filetime 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
- "gcc 0.3.44 (registry+https://github.com/rust-lang/crates.io-index)",
+ "gcc 0.3.45 (registry+https://github.com/rust-lang/crates.io-index)",
  "getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
  "libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
  "num_cpus 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -104,12 +104,12 @@ version = "0.1.0"
 
 [[package]]
 name = "clap"
-version = "2.21.1"
+version = "2.22.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "atty 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "bitflags 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "bitflags 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "term_size 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "unicode-segmentation 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -122,7 +122,7 @@ name = "cmake"
 version = "0.1.22"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "gcc 0.3.44 (registry+https://github.com/rust-lang/crates.io-index)",
+ "gcc 0.3.45 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
@@ -140,7 +140,7 @@ version = "0.0.0"
 dependencies = [
  "build_helper 0.1.0",
  "core 0.0.0",
- "gcc 0.3.44 (registry+https://github.com/rust-lang/crates.io-index)",
+ "gcc 0.3.45 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
@@ -188,7 +188,7 @@ name = "flate"
 version = "0.0.0"
 dependencies = [
  "build_helper 0.1.0",
- "gcc 0.3.44 (registry+https://github.com/rust-lang/crates.io-index)",
+ "gcc 0.3.45 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
@@ -197,7 +197,7 @@ version = "0.0.0"
 
 [[package]]
 name = "gcc"
-version = "0.3.44"
+version = "0.3.45"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
@@ -215,10 +215,10 @@ version = "0.0.0"
 
 [[package]]
 name = "handlebars"
-version = "0.25.1"
+version = "0.25.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "lazy_static 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
  "pest 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "quick-error 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -243,7 +243,7 @@ dependencies = [
 
 [[package]]
 name = "lazy_static"
-version = "0.2.4"
+version = "0.2.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
@@ -272,9 +272,9 @@ name = "mdbook"
 version = "0.0.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "clap 2.21.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "clap 2.22.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "env_logger 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "handlebars 0.25.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "handlebars 0.25.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
  "open 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "pulldown-cmark 0.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -359,6 +359,14 @@ dependencies = [
 ]
 
 [[package]]
+name = "pulldown-cmark"
+version = "0.0.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "bitflags 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
 name = "qemu-test-client"
 version = "0.1.0"
 
@@ -383,7 +391,7 @@ name = "regex"
 version = "0.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "aho-corasick 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "aho-corasick 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "memchr 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "regex-syntax 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "thread_local 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -416,7 +424,7 @@ dependencies = [
 name = "rustbook"
 version = "0.1.0"
 dependencies = [
- "clap 2.21.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "clap 2.22.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "mdbook 0.0.18 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
@@ -593,7 +601,7 @@ name = "rustc_llvm"
 version = "0.0.0"
 dependencies = [
  "build_helper 0.1.0",
- "gcc 0.3.44 (registry+https://github.com/rust-lang/crates.io-index)",
+ "gcc 0.3.45 (registry+https://github.com/rust-lang/crates.io-index)",
  "rustc_bitflags 0.0.0",
 ]
 
@@ -770,8 +778,9 @@ dependencies = [
  "arena 0.0.0",
  "build_helper 0.1.0",
  "env_logger 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "gcc 0.3.44 (registry+https://github.com/rust-lang/crates.io-index)",
+ "gcc 0.3.45 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "pulldown-cmark 0.0.14 (registry+https://github.com/rust-lang/crates.io-index)",
  "rustc 0.0.0",
  "rustc_back 0.0.0",
  "rustc_const_eval 0.0.0",
@@ -818,7 +827,7 @@ dependencies = [
  "collections 0.0.0",
  "compiler_builtins 0.0.0",
  "core 0.0.0",
- "gcc 0.3.44 (registry+https://github.com/rust-lang/crates.io-index)",
+ "gcc 0.3.45 (registry+https://github.com/rust-lang/crates.io-index)",
  "libc 0.0.0",
  "panic_abort 0.0.0",
  "panic_unwind 0.0.0",
@@ -978,22 +987,22 @@ version = "0.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [metadata]
-"checksum aho-corasick 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0638fd549427caa90c499814196d1b9e3725eb4d15d7339d6de073a680ed0ca2"
+"checksum aho-corasick 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "500909c4f87a9e52355b26626d890833e9e1d53ac566db76c36faa984b889699"
 "checksum ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "23ac7c30002a5accbf7e8987d0632fa6de155b7c3d39d0067317a391e00a2ef6"
 "checksum atty 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d912da0db7fa85514874458ca3651fe2cddace8d0b0505571dbdcd41ab490159"
 "checksum bitflags 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4f67931368edf3a9a51d29886d245f1c3db2f1ef0dcc9e35ff70341b78c10d23"
-"checksum bitflags 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e1ab483fc81a8143faa7203c4a3c02888ebd1a782e37e41fa34753ba9a162"
-"checksum clap 2.21.1 (registry+https://github.com/rust-lang/crates.io-index)" = "74a80f603221c9cd9aa27a28f52af452850051598537bb6b359c38a7d61e5cda"
+"checksum bitflags 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1370e9fc2a6ae53aea8b7a5110edbd08836ed87c88736dfabccade1c2b44bff4"
+"checksum clap 2.22.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e17a4a72ffea176f77d6e2db609c6c919ef221f23862c9915e687fb54d833485"
 "checksum cmake 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)" = "d18d68987ed4c516dcc3e7913659bfa4076f5182eea4a7e0038bb060953e76ac"
 "checksum dtoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "80c8b71fd71146990a9742fc06dcbbde19161a267e0ad4e572c35162f4578c90"
 "checksum env_logger 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e3856f1697098606fc6cb97a93de88ca3f3bc35bb878c725920e6e82ecf05e83"
 "checksum filetime 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "5363ab8e4139b8568a6237db5248646e5a8a2f89bd5ccb02092182b11fd3e922"
-"checksum gcc 0.3.44 (registry+https://github.com/rust-lang/crates.io-index)" = "a32cd40070d7611ab76343dcb3204b2bb28c8a9450989a83a3d590248142f439"
+"checksum gcc 0.3.45 (registry+https://github.com/rust-lang/crates.io-index)" = "40899336fb50db0c78710f53e87afc54d8c7266fb76262fecc78ca1a7f09deae"
 "checksum getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "d9047cfbd08a437050b363d35ef160452c5fe8ea5187ae0a624708c91581d685"
-"checksum handlebars 0.25.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b2249f6f0dc5a3bb2b3b1a8f797dfccbc4b053344d773d654ad565e51427d335"
+"checksum handlebars 0.25.2 (registry+https://github.com/rust-lang/crates.io-index)" = "663e1728d8037fb0d4e13bcd1b1909fb5d913690a9929eb385922df157c2ff8f"
 "checksum itoa 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "eb2f404fbc66fd9aac13e998248505e7ecb2ad8e44ab6388684c5fb11c6c251c"
 "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
-"checksum lazy_static 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "7291b1dd97d331f752620b02dfdbc231df7fc01bf282a00769e1cdb963c460dc"
+"checksum lazy_static 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "4732c563b9a21a406565c4747daa7b46742f082911ae4753f390dc9ec7ee1a97"
 "checksum libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)" = "88ee81885f9f04bff991e306fea7c1c60a5f0f9e409e99f6b40e3311a3363135"
 "checksum log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "5141eca02775a762cc6cd564d8d2c50f67c0ea3a372cbf1c51592b3e029e10ad"
 "checksum mdbook 0.0.18 (registry+https://github.com/rust-lang/crates.io-index)" = "06a68e8738e42b38a02755d3ce5fa12d559e17acb238e4326cbc3cc056e65280"
@@ -1002,6 +1011,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 "checksum num_cpus 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "cee7e88156f3f9e19bdd598f8d6c9db7bf4078f99f8381f43a55b09648d1a6e3"
 "checksum open 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3478ed1686bd1300c8a981a940abc92b06fac9cbef747f4c668d4e032ff7b842"
 "checksum pest 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0a6dda33d67c26f0aac90d324ab2eb7239c819fc7b2552fe9faa4fe88441edc8"
+"checksum pulldown-cmark 0.0.14 (registry+https://github.com/rust-lang/crates.io-index)" = "d9ab1e588ef8efd702c7ed9d2bd774db5e6f4d878bb5a1a9f371828fbdff6973"
 "checksum pulldown-cmark 0.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "1058d7bb927ca067656537eec4e02c2b4b70eaaa129664c5b90c111e20326f41"
 "checksum quick-error 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0aad603e8d7fb67da22dbdf1f4b826ce8829e406124109e73cf1b2454b93a71c"
 "checksum regex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4278c17d0f6d62dfef0ab00028feb45bd7d2102843f80763474eeb1be8a10c01"
diff --git a/src/libcore/result.rs b/src/libcore/result.rs
index 6ec8a37dfa4..c46b0c1324d 100644
--- a/src/libcore/result.rs
+++ b/src/libcore/result.rs
@@ -838,10 +838,10 @@ impl<T: Default, E> Result<T, E> {
     ///
     /// assert_eq!(1909, good_year);
     /// assert_eq!(0, bad_year);
+    /// ```
     ///
     /// [`parse`]: ../../std/primitive.str.html#method.parse
     /// [`FromStr`]: ../../std/str/trait.FromStr.html
-    /// ```
     #[inline]
     #[stable(feature = "result_unwrap_or_default", since = "1.16.0")]
     pub fn unwrap_or_default(self) -> T {
diff --git a/src/librustdoc/Cargo.toml b/src/librustdoc/Cargo.toml
index 1c479ce1d01..52f5d99838d 100644
--- a/src/librustdoc/Cargo.toml
+++ b/src/librustdoc/Cargo.toml
@@ -2,7 +2,6 @@
 authors = ["The Rust Project Developers"]
 name = "rustdoc"
 version = "0.0.0"
-build = "build.rs"
 
 [lib]
 name = "rustdoc"
@@ -26,6 +25,7 @@ rustc_trans = { path = "../librustc_trans" }
 serialize = { path = "../libserialize" }
 syntax = { path = "../libsyntax" }
 syntax_pos = { path = "../libsyntax_pos" }
+pulldown-cmark = { version = "0.0.14", default-features = false }
 
 [build-dependencies]
 build_helper = { path = "../build_helper" }
diff --git a/src/librustdoc/build.rs b/src/librustdoc/build.rs
deleted file mode 100644
index 9fa6406c1d8..00000000000
--- a/src/librustdoc/build.rs
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2015 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.
-
-extern crate build_helper;
-extern crate gcc;
-
-fn main() {
-    let src_dir = std::path::Path::new("../rt/hoedown/src");
-    build_helper::rerun_if_changed_anything_in_dir(src_dir);
-    let mut cfg = gcc::Config::new();
-    cfg.file("../rt/hoedown/src/autolink.c")
-       .file("../rt/hoedown/src/buffer.c")
-       .file("../rt/hoedown/src/document.c")
-       .file("../rt/hoedown/src/escape.c")
-       .file("../rt/hoedown/src/html.c")
-       .file("../rt/hoedown/src/html_blocks.c")
-       .file("../rt/hoedown/src/html_smartypants.c")
-       .file("../rt/hoedown/src/stack.c")
-       .file("../rt/hoedown/src/version.c")
-       .include(src_dir)
-       .compile("libhoedown.a");
-}
diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs
index c7c5aabab97..117cfbabb52 100644
--- a/src/librustdoc/html/markdown.rs
+++ b/src/librustdoc/html/markdown.rs
@@ -10,29 +10,25 @@
 
 //! Markdown formatting for rustdoc
 //!
-//! This module implements markdown formatting through the hoedown C-library
-//! (bundled into the rust runtime). This module self-contains the C bindings
-//! and necessary legwork to render markdown, and exposes all of the
+//! This module implements markdown formatting through the pulldown-cmark
+//! rust-library. This module exposes all of the
 //! functionality through a unit-struct, `Markdown`, which has an implementation
 //! of `fmt::Display`. Example usage:
 //!
 //! ```rust,ignore
-//! use rustdoc::html::markdown::Markdown;
+//! use rustdoc::html::markdown::{Markdown, MarkdownOutputStyle};
 //!
 //! let s = "My *markdown* _text_";
-//! let html = format!("{}", Markdown(s));
+//! let html = format!("{}", Markdown(s, MarkdownOutputStyle::Fancy));
 //! // ... something using html
 //! ```
 
 #![allow(non_camel_case_types)]
 
-use libc;
 use std::ascii::AsciiExt;
 use std::cell::RefCell;
 use std::default::Default;
-use std::ffi::CString;
 use std::fmt::{self, Write};
-use std::slice;
 use std::str;
 use syntax::feature_gate::UnstableFeatures;
 use syntax::codemap::Span;
@@ -43,156 +39,41 @@ use html::highlight;
 use html::escape::Escape;
 use test;
 
+use pulldown_cmark::{self, Event, Parser, Tag};
+
+#[derive(Copy, Clone)]
+pub enum MarkdownOutputStyle {
+    Compact,
+    Fancy,
+}
+
+impl MarkdownOutputStyle {
+    pub fn is_compact(&self) -> bool {
+        match *self {
+            MarkdownOutputStyle::Compact => true,
+            _ => false,
+        }
+    }
+
+    pub fn is_fancy(&self) -> bool {
+        match *self {
+            MarkdownOutputStyle::Fancy => true,
+            _ => false,
+        }
+    }
+}
+
 /// 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.
-pub struct Markdown<'a>(pub &'a str);
+// The second parameter is whether we need a shorter version or not.
+pub struct Markdown<'a>(pub &'a str, pub MarkdownOutputStyle);
 /// A unit struct like `Markdown`, that renders the markdown with a
 /// table of contents.
 pub struct MarkdownWithToc<'a>(pub &'a str);
 /// A unit struct like `Markdown`, that renders the markdown escaping HTML tags.
 pub struct MarkdownHtml<'a>(pub &'a str);
 
-const DEF_OUNIT: libc::size_t = 64;
-const HOEDOWN_EXT_NO_INTRA_EMPHASIS: libc::c_uint = 1 << 11;
-const HOEDOWN_EXT_TABLES: libc::c_uint = 1 << 0;
-const HOEDOWN_EXT_FENCED_CODE: libc::c_uint = 1 << 1;
-const HOEDOWN_EXT_AUTOLINK: libc::c_uint = 1 << 3;
-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 |
-    HOEDOWN_EXT_FENCED_CODE | HOEDOWN_EXT_AUTOLINK |
-    HOEDOWN_EXT_STRIKETHROUGH | HOEDOWN_EXT_SUPERSCRIPT |
-    HOEDOWN_EXT_FOOTNOTES;
-
-enum hoedown_document {}
-
-type blockcodefn = extern "C" fn(*mut hoedown_buffer, *const hoedown_buffer,
-                                 *const hoedown_buffer, *const hoedown_renderer_data,
-                                 libc::size_t);
-
-type blockquotefn = extern "C" fn(*mut hoedown_buffer, *const hoedown_buffer,
-                                  *const hoedown_renderer_data, libc::size_t);
-
-type headerfn = extern "C" fn(*mut hoedown_buffer, *const hoedown_buffer,
-                              libc::c_int, *const hoedown_renderer_data,
-                              libc::size_t);
-
-type blockhtmlfn = extern "C" fn(*mut hoedown_buffer, *const hoedown_buffer,
-                                 *const hoedown_renderer_data, libc::size_t);
-
-type codespanfn = extern "C" fn(*mut hoedown_buffer, *const hoedown_buffer,
-                                *const hoedown_renderer_data, libc::size_t) -> libc::c_int;
-
-type linkfn = extern "C" fn (*mut hoedown_buffer, *const hoedown_buffer,
-                             *const hoedown_buffer, *const hoedown_buffer,
-                             *const hoedown_renderer_data, libc::size_t) -> libc::c_int;
-
-type entityfn = extern "C" fn (*mut hoedown_buffer, *const hoedown_buffer,
-                               *const hoedown_renderer_data, libc::size_t);
-
-type normaltextfn = extern "C" fn(*mut hoedown_buffer, *const hoedown_buffer,
-                                  *const hoedown_renderer_data, libc::size_t);
-
-#[repr(C)]
-struct hoedown_renderer_data {
-    opaque: *mut libc::c_void,
-}
-
-#[repr(C)]
-struct hoedown_renderer {
-    opaque: *mut libc::c_void,
-
-    blockcode: Option<blockcodefn>,
-    blockquote: Option<blockquotefn>,
-    header: Option<headerfn>,
-
-    other_block_level_callbacks: [libc::size_t; 11],
-
-    blockhtml: Option<blockhtmlfn>,
-
-    /* span level callbacks - NULL or return 0 prints the span verbatim */
-    autolink: libc::size_t, // unused
-    codespan: Option<codespanfn>,
-    other_span_level_callbacks_1: [libc::size_t; 7],
-    link: Option<linkfn>,
-    other_span_level_callbacks_2: [libc::size_t; 6],
-
-    /* low level callbacks - NULL copies input directly into the output */
-    entity: Option<entityfn>,
-    normal_text: Option<normaltextfn>,
-
-    /* header and footer */
-    other_callbacks: [libc::size_t; 2],
-}
-
-#[repr(C)]
-struct hoedown_html_renderer_state {
-    opaque: *mut libc::c_void,
-    toc_data: html_toc_data,
-    flags: libc::c_uint,
-    link_attributes: Option<extern "C" fn(*mut hoedown_buffer,
-                                          *const hoedown_buffer,
-                                          *const hoedown_renderer_data)>,
-}
-
-#[repr(C)]
-struct html_toc_data {
-    header_count: libc::c_int,
-    current_level: libc::c_int,
-    level_offset: libc::c_int,
-    nesting_level: libc::c_int,
-}
-
-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>,
-}
-
-#[repr(C)]
-struct hoedown_buffer {
-    data: *const u8,
-    size: libc::size_t,
-    asize: libc::size_t,
-    unit: libc::size_t,
-}
-
-extern {
-    fn hoedown_html_renderer_new(render_flags: libc::c_uint,
-                                 nesting_level: libc::c_int)
-        -> *mut hoedown_renderer;
-    fn hoedown_html_renderer_free(renderer: *mut hoedown_renderer);
-
-    fn hoedown_document_new(rndr: *const hoedown_renderer,
-                            extensions: libc::c_uint,
-                            max_nesting: libc::size_t) -> *mut hoedown_document;
-    fn hoedown_document_render(doc: *mut hoedown_document,
-                               ob: *mut hoedown_buffer,
-                               document: *const u8,
-                               doc_size: libc::size_t);
-    fn hoedown_document_free(md: *mut hoedown_document);
-
-    fn hoedown_buffer_new(unit: libc::size_t) -> *mut hoedown_buffer;
-    fn hoedown_buffer_put(b: *mut hoedown_buffer, c: *const libc::c_char,
-                          n: libc::size_t);
-    fn hoedown_buffer_puts(b: *mut hoedown_buffer, c: *const libc::c_char);
-    fn hoedown_buffer_free(b: *mut hoedown_buffer);
-
-}
-
-// hoedown_buffer helpers
-impl hoedown_buffer {
-    fn as_bytes(&self) -> &[u8] {
-        unsafe { slice::from_raw_parts(self.data, self.size as usize) }
-    }
-}
-
 /// Returns Some(code) if `s` is a line that should be stripped from
 /// documentation but used in example code. `code` is the portion of
 /// `s` that should be used in tests. (None for lines that should be
@@ -222,123 +103,126 @@ thread_local!(pub static PLAYGROUND: RefCell<Option<(Option<String>, String)>> =
     RefCell::new(None)
 });
 
+macro_rules! event_loop_break {
+    ($parser:expr, $toc_builder:expr, $shorter:expr, $buf:expr, $escape:expr, $id:expr,
+     $($end_event:pat)|*) => {{
+        fn inner(id: &mut Option<&mut String>, s: &str) {
+            if let Some(ref mut id) = *id {
+                id.push_str(s);
+            }
+        }
+        while let Some(event) = $parser.next() {
+            match event {
+                $($end_event)|* => break,
+                Event::Text(ref s) => {
+                    inner($id, s);
+                    if $escape {
+                        $buf.push_str(&format!("{}", Escape(s)));
+                    } else {
+                        $buf.push_str(s);
+                    }
+                }
+                Event::SoftBreak | Event::HardBreak if !$buf.is_empty() => {
+                    $buf.push(' ');
+                }
+                x => {
+                    looper($parser, &mut $buf, Some(x), $toc_builder, $shorter, $id);
+                }
+            }
+        }
+    }}
+}
 
 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
+              shorter: MarkdownOutputStyle) -> fmt::Result {
+    fn code_block(parser: &mut Parser, buffer: &mut String, lang: &str) {
+        let mut origtext = String::new();
+        while let Some(event) = parser.next() {
+            match event {
+                Event::End(Tag::CodeBlock(_)) => break,
+                Event::Text(ref s) => {
+                    origtext.push_str(s);
                 }
-            };
+                _ => {}
+            }
+        }
+        let origtext = origtext.trim_left();
+        debug!("docblock: ==============\n{:?}\n=======", origtext);
 
-            let lines = origtext.lines().filter(|l| {
-                stripped_filtered_line(*l).is_none()
-            });
-            let text = lines.collect::<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(") {
-                        "&amp;version=nightly"
+        let lines = origtext.lines().filter(|l| {
+            stripped_filtered_line(*l).is_none()
+        });
+        let text = lines.collect::<Vec<&str>>().join("\n");
+        let block_info = if lang.is_empty() {
+            LangString::all_false()
+        } else {
+            LangString::parse(lang)
+        };
+        if !block_info.rust {
+            buffer.push_str(&format!("<pre><code class=\"language-{}\">{}</code></pre>",
+                            lang, text));
+            return
+        }
+        PLAYGROUND.with(|play| {
+            // insert newline to clearly separate it from the
+            // previous block so we can shorten the html output
+            buffer.push('\n');
+            let 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(") {
+                    "&amp;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 {
-                        ""
-                    };
-                    // 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'*'
+                        write!(test_escaped, "%{:02X}", b).unwrap();
                     }
-                    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());
-            })
-        }
+                }
+                Some(format!(
+                    r#"<a class="test-arrow" target="_blank" href="{}?code={}{}">Run</a>"#,
+                    url, test_escaped, channel
+                ))
+            });
+            buffer.push_str(&highlight::render_with_highlighting(
+                            &text,
+                            Some("rust-example-rendered"),
+                            None,
+                            playground_button.as_ref().map(String::as_str)));
+        });
     }
 
-    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 _); }
+    fn heading(parser: &mut Parser, buffer: &mut String, toc_builder: &mut Option<TocBuilder>,
+               shorter: MarkdownOutputStyle, level: i32) {
+        let mut ret = String::new();
+        let mut id = String::new();
+        event_loop_break!(parser, toc_builder, shorter, ret, true, &mut Some(&mut id),
+                          Event::End(Tag::Header(_)));
+        ret = ret.trim_right().to_owned();
 
-        // 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>",
-                            "&lt;", "&gt;", "&amp;", "&#39;", "&quot;"];
-        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() {
@@ -353,145 +237,309 @@ pub fn render(w: &mut fmt::Formatter,
             }
         }).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()))
+        let sec = toc_builder.as_mut().map_or("".to_owned(), |builder| {
+            format!("{} ", builder.push(level as u32, ret.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)
-        };
+        buffer.push_str(&format!("<h{lvl} id=\"{id}\" class=\"section-header\">\
+                                  <a href=\"#{id}\">{sec}{}</a></h{lvl}>",
+                                 ret, lvl = level, id = id, sec = sec));
+    }
 
-        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
+    fn inline_code(parser: &mut Parser, buffer: &mut String, toc_builder: &mut Option<TocBuilder>,
+                   shorter: MarkdownOutputStyle, id: &mut Option<&mut String>) {
+        let mut content = String::new();
+        event_loop_break!(parser, toc_builder, shorter, content, false, id, Event::End(Tag::Code));
+        buffer.push_str(&format!("<code>{}</code>",
+                                 Escape(&collapse_whitespace(content.trim_right()))));
     }
 
-    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);
+    fn link(parser: &mut Parser, buffer: &mut String, toc_builder: &mut Option<TocBuilder>,
+            shorter: MarkdownOutputStyle, url: &str, mut title: String,
+            id: &mut Option<&mut String>) {
+        event_loop_break!(parser, toc_builder, shorter, title, true, id,
+                          Event::End(Tag::Link(_, _)));
+        buffer.push_str(&format!("<a href=\"{}\">{}</a>", url, title));
+    }
 
-        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);
+    fn paragraph(parser: &mut Parser, buffer: &mut String, toc_builder: &mut Option<TocBuilder>,
+                 shorter: MarkdownOutputStyle, id: &mut Option<&mut String>) {
+        let mut content = String::new();
+        event_loop_break!(parser, toc_builder, shorter, content, true, id,
+                          Event::End(Tag::Paragraph));
+        buffer.push_str(&format!("<p>{}</p>", content.trim_right()));
+    }
 
-        hoedown_html_renderer_free(renderer);
+    fn table_cell(parser: &mut Parser, buffer: &mut String, toc_builder: &mut Option<TocBuilder>,
+                  shorter: MarkdownOutputStyle) {
+        let mut content = String::new();
+        event_loop_break!(parser, toc_builder, shorter, content, true, &mut None,
+                          Event::End(Tag::TableHead) |
+                              Event::End(Tag::Table(_)) |
+                              Event::End(Tag::TableRow) |
+                              Event::End(Tag::TableCell));
+        buffer.push_str(&format!("<td>{}</td>", content.trim()));
+    }
 
-        let mut ret = opaque.toc_builder.map_or(Ok(()), |builder| {
-            write!(w, "<nav id=\"TOC\">{}</nav>", builder.into_toc())
-        });
+    fn table_row(parser: &mut Parser, buffer: &mut String, toc_builder: &mut Option<TocBuilder>,
+                 shorter: MarkdownOutputStyle) {
+        let mut content = String::new();
+        while let Some(event) = parser.next() {
+            match event {
+                Event::End(Tag::TableHead) |
+                    Event::End(Tag::Table(_)) |
+                    Event::End(Tag::TableRow) => break,
+                Event::Start(Tag::TableCell) => {
+                    table_cell(parser, &mut content, toc_builder, shorter);
+                }
+                x => {
+                    looper(parser, &mut content, Some(x), toc_builder, shorter, &mut None);
+                }
+            }
+        }
+        buffer.push_str(&format!("<tr>{}</tr>", content));
+    }
 
-        if ret.is_ok() {
-            let buf = (*ob).as_bytes();
-            ret = w.write_str(str::from_utf8(buf).unwrap());
+    fn table_head(parser: &mut Parser, buffer: &mut String, toc_builder: &mut Option<TocBuilder>,
+                  shorter: MarkdownOutputStyle) {
+        let mut content = String::new();
+        while let Some(event) = parser.next() {
+            match event {
+                Event::End(Tag::TableHead) | Event::End(Tag::Table(_)) => break,
+                Event::Start(Tag::TableCell) => {
+                    table_cell(parser, &mut content, toc_builder, shorter);
+                }
+                x => {
+                    looper(parser, &mut content, Some(x), toc_builder, shorter, &mut None);
+                }
+            }
+        }
+        if !content.is_empty() {
+            buffer.push_str(&format!("<thead><tr>{}</tr></thead>", content.replace("td>", "th>")));
         }
-        hoedown_buffer_free(ob);
-        ret
     }
-}
 
-pub fn find_testable_code(doc: &str, tests: &mut ::test::Collector, position: Span) {
-    extern fn block(_ob: *mut hoedown_buffer,
-                    text: *const hoedown_buffer,
-                    lang: *const hoedown_buffer,
-                    data: *const hoedown_renderer_data,
-                    line: libc::size_t) {
-        unsafe {
-            if text.is_null() { return }
-            let block_info = if lang.is_null() {
-                LangString::all_false()
-            } else {
-                let lang = (*lang).as_bytes();
-                let s = str::from_utf8(lang).unwrap();
-                LangString::parse(s)
-            };
-            if !block_info.rust { return }
-            let text = (*text).as_bytes();
-            let opaque = (*data).opaque as *mut hoedown_html_renderer_state;
-            let tests = &mut *((*opaque).opaque as *mut ::test::Collector);
-            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");
-            let line = tests.get_line() + line;
-            let filename = tests.get_filename();
-            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);
+    fn table(parser: &mut Parser, buffer: &mut String, toc_builder: &mut Option<TocBuilder>,
+             shorter: MarkdownOutputStyle) {
+        let mut content = String::new();
+        let mut rows = String::new();
+        while let Some(event) = parser.next() {
+            match event {
+                Event::End(Tag::Table(_)) => break,
+                Event::Start(Tag::TableHead) => {
+                    table_head(parser, &mut content, toc_builder, shorter);
+                }
+                Event::Start(Tag::TableRow) => {
+                    table_row(parser, &mut rows, toc_builder, shorter);
+                }
+                _ => {}
+            }
         }
+        buffer.push_str(&format!("<table>{}{}</table>",
+                                 content,
+                                 if shorter.is_compact() || rows.is_empty() {
+                                     String::new()
+                                 } else {
+                                     format!("<tbody>{}</tbody>", rows)
+                                 }));
     }
 
-    extern fn header(_ob: *mut hoedown_buffer,
-                     text: *const hoedown_buffer,
-                     level: libc::c_int, data: *const hoedown_renderer_data,
-                     _: libc::size_t) {
-        unsafe {
-            let opaque = (*data).opaque as *mut hoedown_html_renderer_state;
-            let tests = &mut *((*opaque).opaque as *mut ::test::Collector);
-            if text.is_null() {
-                tests.register_header("", level as u32);
-            } else {
-                let text = (*text).as_bytes();
-                let text = str::from_utf8(text).unwrap();
-                tests.register_header(text, level as u32);
+    fn blockquote(parser: &mut Parser, buffer: &mut String, toc_builder: &mut Option<TocBuilder>,
+                  shorter: MarkdownOutputStyle) {
+        let mut content = String::new();
+        event_loop_break!(parser, toc_builder, shorter, content, true, &mut None,
+                          Event::End(Tag::BlockQuote));
+        buffer.push_str(&format!("<blockquote>{}</blockquote>", content.trim_right()));
+    }
+
+    fn list_item(parser: &mut Parser, buffer: &mut String, toc_builder: &mut Option<TocBuilder>,
+                 shorter: MarkdownOutputStyle) {
+        let mut content = String::new();
+        while let Some(event) = parser.next() {
+            match event {
+                Event::End(Tag::Item) => break,
+                Event::Text(ref s) => {
+                    content.push_str(&format!("{}", Escape(s)));
+                }
+                x => {
+                    looper(parser, &mut content, Some(x), toc_builder, shorter, &mut None);
+                }
+            }
+        }
+        buffer.push_str(&format!("<li>{}</li>", content));
+    }
+
+    fn list(parser: &mut Parser, buffer: &mut String, toc_builder: &mut Option<TocBuilder>,
+            shorter: MarkdownOutputStyle) {
+        let mut content = String::new();
+        while let Some(event) = parser.next() {
+            match event {
+                Event::End(Tag::List(_)) => break,
+                Event::Start(Tag::Item) => {
+                    list_item(parser, &mut content, toc_builder, shorter);
+                }
+                x => {
+                    looper(parser, &mut content, Some(x), toc_builder, shorter, &mut None);
+                }
+            }
+        }
+        buffer.push_str(&format!("<ul>{}</ul>", content));
+    }
+
+    fn emphasis(parser: &mut Parser, buffer: &mut String, toc_builder: &mut Option<TocBuilder>,
+                shorter: MarkdownOutputStyle, id: &mut Option<&mut String>) {
+        let mut content = String::new();
+        event_loop_break!(parser, toc_builder, shorter, content, false, id,
+                          Event::End(Tag::Emphasis));
+        buffer.push_str(&format!("<em>{}</em>", content));
+    }
+
+    fn strong(parser: &mut Parser, buffer: &mut String, toc_builder: &mut Option<TocBuilder>,
+              shorter: MarkdownOutputStyle, id: &mut Option<&mut String>) {
+        let mut content = String::new();
+        event_loop_break!(parser, toc_builder, shorter, content, false, id,
+                          Event::End(Tag::Strong));
+        buffer.push_str(&format!("<strong>{}</strong>", content));
+    }
+
+    fn looper<'a>(parser: &'a mut Parser, buffer: &mut String, next_event: Option<Event<'a>>,
+                  toc_builder: &mut Option<TocBuilder>, shorter: MarkdownOutputStyle,
+                  id: &mut Option<&mut String>) -> bool {
+        if let Some(event) = next_event {
+            match event {
+                Event::Start(Tag::CodeBlock(lang)) => {
+                    code_block(parser, buffer, &*lang);
+                }
+                Event::Start(Tag::Header(level)) => {
+                    heading(parser, buffer, toc_builder, shorter, level);
+                }
+                Event::Start(Tag::Code) => {
+                    inline_code(parser, buffer, toc_builder, shorter, id);
+                }
+                Event::Start(Tag::Paragraph) => {
+                    paragraph(parser, buffer, toc_builder, shorter, id);
+                }
+                Event::Start(Tag::Link(ref url, ref t)) => {
+                    link(parser, buffer, toc_builder, shorter, url, t.as_ref().to_owned(), id);
+                }
+                Event::Start(Tag::Table(_)) => {
+                    table(parser, buffer, toc_builder, shorter);
+                }
+                Event::Start(Tag::BlockQuote) => {
+                    blockquote(parser, buffer, toc_builder, shorter);
+                }
+                Event::Start(Tag::List(_)) => {
+                    list(parser, buffer, toc_builder, shorter);
+                }
+                Event::Start(Tag::Emphasis) => {
+                    emphasis(parser, buffer, toc_builder, shorter, id);
+                }
+                Event::Start(Tag::Strong) => {
+                    strong(parser, buffer, toc_builder, shorter, id);
+                }
+                Event::Html(h) | Event::InlineHtml(h) => {
+                    buffer.push_str(&*h);
+                }
+                _ => {}
             }
+            shorter.is_fancy()
+        } else {
+            false
+        }
+    }
+
+    let mut toc_builder = if print_toc {
+        Some(TocBuilder::new())
+    } else {
+        None
+    };
+    let mut buffer = String::new();
+    let mut parser = Parser::new_ext(s, pulldown_cmark::OPTION_ENABLE_TABLES);
+    loop {
+        let next_event = parser.next();
+        if !looper(&mut parser, &mut buffer, next_event, &mut toc_builder, shorter, &mut None) {
+            break
         }
     }
+    let mut ret = toc_builder.map_or(Ok(()), |builder| {
+        write!(w, "<nav id=\"TOC\">{}</nav>", builder.into_toc())
+    });
+
+    if ret.is_ok() {
+        ret = w.write_str(&buffer);
+    }
+    ret
+}
 
+pub fn find_testable_code(doc: &str, tests: &mut ::test::Collector, position: Span) {
     tests.set_position(position);
-    unsafe {
-        let ob = hoedown_buffer_new(DEF_OUNIT);
-        let renderer = hoedown_html_renderer_new(0, 0);
-        (*renderer).blockcode = Some(block);
-        (*renderer).header = Some(header);
-        (*((*renderer).opaque as *mut hoedown_html_renderer_state)).opaque
-                = tests as *mut _ as *mut libc::c_void;
-
-        let document = hoedown_document_new(renderer, HOEDOWN_EXTENSIONS, 16);
-        hoedown_document_render(document, ob, doc.as_ptr(),
-                                doc.len() as libc::size_t);
-        hoedown_document_free(document);
-
-        hoedown_html_renderer_free(renderer);
-        hoedown_buffer_free(ob);
+
+    let mut parser = Parser::new(doc);
+    let mut prev_offset = 0;
+    let mut nb_lines = 0;
+    let mut register_header = None;
+    'main: while let Some(event) = parser.next() {
+        match event {
+            Event::Start(Tag::CodeBlock(s)) => {
+                let block_info = if s.is_empty() {
+                    LangString::all_false()
+                } else {
+                    LangString::parse(&*s)
+                };
+                if !block_info.rust {
+                    continue
+                }
+                let mut test_s = String::new();
+                let mut offset = None;
+                loop {
+                    let event = parser.next();
+                    if let Some(event) = event {
+                        match event {
+                            Event::End(Tag::CodeBlock(_)) => break,
+                            Event::Text(ref s) => {
+                                test_s.push_str(s);
+                                if offset.is_none() {
+                                    offset = Some(parser.get_offset());
+                                }
+                            }
+                            _ => {}
+                        }
+                    } else {
+                        break 'main;
+                    }
+                }
+                let offset = offset.unwrap_or(0);
+                let lines = test_s.lines().map(|l| {
+                    stripped_filtered_line(l).unwrap_or(l)
+                });
+                let text = lines.collect::<Vec<&str>>().join("\n");
+                nb_lines += doc[prev_offset..offset].lines().count();
+                let line = tests.get_line() + (nb_lines - 1);
+                let filename = tests.get_filename();
+                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);
+                prev_offset = offset;
+            }
+            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;
+            }
+            _ => {}
+        }
     }
 }
 
@@ -570,17 +618,17 @@ impl LangString {
 
 impl<'a> fmt::Display for Markdown<'a> {
     fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
-        let Markdown(md) = *self;
+        let Markdown(md, shorter) = *self;
         // This is actually common enough to special-case
         if md.is_empty() { return Ok(()) }
-        render(fmt, md, false, 0)
+        render(fmt, md, false, shorter)
     }
 }
 
 impl<'a> fmt::Display for MarkdownWithToc<'a> {
     fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
         let MarkdownWithToc(md) = *self;
-        render(fmt, md, true, 0)
+        render(fmt, md, true, MarkdownOutputStyle::Fancy)
     }
 }
 
@@ -589,62 +637,67 @@ impl<'a> fmt::Display for MarkdownHtml<'a> {
         let MarkdownHtml(md) = *self;
         // This is actually common enough to special-case
         if md.is_empty() { return Ok(()) }
-        render(fmt, md, false, HOEDOWN_HTML_ESCAPE)
+        render(fmt, md, false, MarkdownOutputStyle::Fancy)
     }
 }
 
 pub fn plain_summary_line(md: &str) -> String {
-    extern fn link(_ob: *mut hoedown_buffer,
-                       _link: *const hoedown_buffer,
-                       _title: *const hoedown_buffer,
-                       content: *const hoedown_buffer,
-                       data: *const hoedown_renderer_data,
-                       _: libc::size_t) -> libc::c_int
-    {
-        unsafe {
-            if !content.is_null() && (*content).size > 0 {
-                let ob = (*data).opaque as *mut hoedown_buffer;
-                hoedown_buffer_put(ob, (*content).data as *const libc::c_char,
-                                   (*content).size);
-            }
-        }
-        1
-    }
-
-    extern fn normal_text(_ob: *mut hoedown_buffer,
-                          text: *const hoedown_buffer,
-                          data: *const hoedown_renderer_data,
-                          _: libc::size_t)
-    {
-        unsafe {
-            let ob = (*data).opaque as *mut hoedown_buffer;
-            hoedown_buffer_put(ob, (*text).data as *const libc::c_char,
-                               (*text).size);
-        }
+    struct ParserWrapper<'a> {
+        inner: Parser<'a>,
+        is_in: isize,
+        is_first: bool,
     }
 
-    unsafe {
-        let ob = hoedown_buffer_new(DEF_OUNIT);
-        let mut plain_renderer: hoedown_renderer = ::std::mem::zeroed();
-        let renderer: *mut hoedown_renderer = &mut plain_renderer;
-        (*renderer).opaque = ob as *mut libc::c_void;
-        (*renderer).link = Some(link);
-        (*renderer).normal_text = Some(normal_text);
+    impl<'a> Iterator for ParserWrapper<'a> {
+        type Item = String;
 
-        let document = hoedown_document_new(renderer, HOEDOWN_EXTENSIONS, 16);
-        hoedown_document_render(document, ob, md.as_ptr(),
-                                md.len() as libc::size_t);
-        hoedown_document_free(document);
-        let plain_slice = (*ob).as_bytes();
-        let plain = str::from_utf8(plain_slice).unwrap_or("").to_owned();
-        hoedown_buffer_free(ob);
-        plain
+        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 {
+                Event::Start(Tag::Paragraph) => (None, 1),
+                Event::Start(Tag::Link(_, ref t)) if !self.is_first => {
+                    (Some(t.as_ref().to_owned()), 1)
+                }
+                Event::Start(Tag::Code) => (Some("`".to_owned()), 1),
+                Event::End(Tag::Code) => (Some("`".to_owned()), -1),
+                Event::Start(Tag::Header(_)) => (None, 1),
+                Event::Text(ref s) if self.is_in > 0 => (Some(s.as_ref().to_owned()), 0),
+                Event::End(Tag::Link(_, ref t)) => (Some(t.as_ref().to_owned()), -1),
+                Event::End(Tag::Paragraph) | Event::End(Tag::Header(_)) => (None, -1),
+                _ => (None, 0),
+            };
+            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())
+            }
+        }
+    }
+    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);
+        }
     }
+    s
 }
 
 #[cfg(test)]
 mod tests {
-    use super::{LangString, Markdown, MarkdownHtml};
+    use super::{LangString, Markdown, MarkdownHtml, MarkdownOutputStyle};
     use super::plain_summary_line;
     use html::render::reset_ids;
 
@@ -684,52 +737,52 @@ mod tests {
     #[test]
     fn issue_17736() {
         let markdown = "# title";
-        format!("{}", Markdown(markdown));
+        format!("{}", Markdown(markdown, MarkdownOutputStyle::Fancy));
         reset_ids(true);
     }
 
     #[test]
     fn test_header() {
         fn t(input: &str, expect: &str) {
-            let output = format!("{}", Markdown(input));
-            assert_eq!(output, expect);
+            let output = format!("{}", Markdown(input, MarkdownOutputStyle::Fancy));
+            assert_eq!(output, expect, "original: {}", input);
             reset_ids(true);
         }
 
-        t("# Foo bar", "\n<h1 id='foo-bar' class='section-header'>\
-          <a href='#foo-bar'>Foo bar</a></h1>");
-        t("## Foo-bar_baz qux", "\n<h2 id='foo-bar_baz-qux' class=\'section-\
-          header'><a href='#foo-bar_baz-qux'>Foo-bar_baz qux</a></h2>");
+        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>");
         t("### **Foo** *bar* baz!?!& -_qux_-%",
-          "\n<h3 id='foo-bar-baz--_qux_-' class='section-header'>\
-          <a href='#foo-bar-baz--_qux_-'><strong>Foo</strong> \
-          <em>bar</em> baz!?!&amp; -_qux_-%</a></h3>");
-        t("####**Foo?** & \\*bar?!*  _`baz`_ ❤ #qux",
-          "\n<h4 id='foo--bar--baz--qux' class='section-header'>\
-          <a href='#foo--bar--baz--qux'><strong>Foo?</strong> &amp; *bar?!*  \
+          "<h3 id=\"foo-bar-baz--qux-\" class=\"section-header\">\
+          <a href=\"#foo-bar-baz--qux-\"><strong>Foo</strong> \
+          <em>bar</em> baz!?!&amp; -<em>qux</em>-%</a></h3>");
+        t("#### **Foo?** & \\*bar?!*  _`baz`_ ❤ #qux",
+          "<h4 id=\"foo--bar--baz--qux\" class=\"section-header\">\
+          <a href=\"#foo--bar--baz--qux\"><strong>Foo?</strong> &amp; *bar?!*  \
           <em><code>baz</code></em> ❤ #qux</a></h4>");
     }
 
     #[test]
     fn test_header_ids_multiple_blocks() {
         fn t(input: &str, expect: &str) {
-            let output = format!("{}", Markdown(input));
-            assert_eq!(output, expect);
+            let output = format!("{}", Markdown(input, MarkdownOutputStyle::Fancy));
+            assert_eq!(output, expect, "original: {}", input);
         }
 
         let test = || {
-            t("# Example", "\n<h1 id='example' class='section-header'>\
-              <a href='#example'>Example</a></h1>");
-            t("# Panics", "\n<h1 id='panics' class='section-header'>\
-              <a href='#panics'>Panics</a></h1>");
-            t("# Example", "\n<h1 id='example-1' class='section-header'>\
-              <a href='#example-1'>Example</a></h1>");
-            t("# Main", "\n<h1 id='main-1' class='section-header'>\
-              <a href='#main-1'>Main</a></h1>");
-            t("# Example", "\n<h1 id='example-2' class='section-header'>\
-              <a href='#example-2'>Example</a></h1>");
-            t("# Panics", "\n<h1 id='panics-1' class='section-header'>\
-              <a href='#panics-1'>Panics</a></h1>");
+            t("# Example", "<h1 id=\"example\" class=\"section-header\">\
+              <a href=\"#example\">Example</a></h1>");
+            t("# Panics", "<h1 id=\"panics\" class=\"section-header\">\
+              <a href=\"#panics\">Panics</a></h1>");
+            t("# Example", "<h1 id=\"example-1\" class=\"section-header\">\
+              <a href=\"#example-1\">Example</a></h1>");
+            t("# Main", "<h1 id=\"main-1\" class=\"section-header\">\
+              <a href=\"#main-1\">Main</a></h1>");
+            t("# Example", "<h1 id=\"example-2\" class=\"section-header\">\
+              <a href=\"#example-2\">Example</a></h1>");
+            t("# Panics", "<h1 id=\"panics-1\" class=\"section-header\">\
+              <a href=\"#panics-1\">Panics</a></h1>");
         };
         test();
         reset_ids(true);
@@ -740,7 +793,7 @@ mod tests {
     fn test_plain_summary_line() {
         fn t(input: &str, expect: &str) {
             let output = plain_summary_line(input);
-            assert_eq!(output, expect);
+            assert_eq!(output, expect, "original: {}", input);
         }
 
         t("hello [Rust](https://www.rust-lang.org) :)", "hello Rust :)");
@@ -754,10 +807,10 @@ mod tests {
     fn test_markdown_html_escape() {
         fn t(input: &str, expect: &str) {
             let output = format!("{}", MarkdownHtml(input));
-            assert_eq!(output, expect);
+            assert_eq!(output, expect, "original: {}", input);
         }
 
-        t("`Struct<'a, T>`", "<p><code>Struct&lt;&#39;a, T&gt;</code></p>\n");
-        t("Struct<'a, T>", "<p>Struct&lt;&#39;a, T&gt;</p>\n");
+        t("`Struct<'a, T>`", "<p><code>Struct&lt;&#39;a, T&gt;</code></p>");
+        t("Struct<'a, T>", "<p>Struct&lt;&#39;a, T&gt;</p>");
     }
 }
diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs
index 612e765a499..f0b624105e3 100644
--- a/src/librustdoc/html/render.rs
+++ b/src/librustdoc/html/render.rs
@@ -72,7 +72,7 @@ use html::format::{TyParamBounds, WhereClause, href, AbiSpace};
 use html::format::{VisSpace, Method, UnsafetySpace, MutableSpace};
 use html::format::fmt_impl_for_trait_page;
 use html::item_type::ItemType;
-use html::markdown::{self, Markdown, MarkdownHtml};
+use html::markdown::{self, Markdown, MarkdownHtml, MarkdownOutputStyle};
 use html::{highlight, layout};
 
 /// A pair of name and its optional document.
@@ -1650,7 +1650,8 @@ fn document_short(w: &mut fmt::Formatter, item: &clean::Item, link: AssocItemLin
         } else {
             format!("{}", &plain_summary_line(Some(s)))
         };
-        write!(w, "<div class='docblock'>{}</div>", Markdown(&markdown))?;
+        write!(w, "<div class='docblock'>{}</div>",
+               Markdown(&markdown, MarkdownOutputStyle::Fancy))?;
     }
     Ok(())
 }
@@ -1683,7 +1684,8 @@ fn get_doc_value(item: &clean::Item) -> Option<&str> {
 fn document_full(w: &mut fmt::Formatter, item: &clean::Item) -> fmt::Result {
     if let Some(s) = get_doc_value(item) {
         write!(w, "<div class='docblock'>{}</div>",
-               Markdown(&format!("{}{}", md_render_assoc_item(item), s)))?;
+               Markdown(&format!("{}{}", md_render_assoc_item(item), s),
+                                 MarkdownOutputStyle::Fancy))?;
     }
     Ok(())
 }
@@ -1871,7 +1873,8 @@ fn item_module(w: &mut fmt::Formatter, cx: &Context,
                        </tr>",
                        name = *myitem.name.as_ref().unwrap(),
                        stab_docs = stab_docs,
-                       docs = shorter(Some(&Markdown(doc_value).to_string())),
+                       docs = shorter(Some(&Markdown(doc_value,
+                                                     MarkdownOutputStyle::Compact).to_string())),
                        class = myitem.type_(),
                        stab = myitem.stability_class().unwrap_or("".to_string()),
                        unsafety_flag = unsafety_flag,
@@ -2901,7 +2904,7 @@ fn render_impl(w: &mut fmt::Formatter, cx: &Context, i: &Impl, link: AssocItemLi
         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, MarkdownOutputStyle::Fancy))?;
         }
     }
 
diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs
index 8dd03f6edc4..447d60018d9 100644
--- a/src/librustdoc/lib.rs
+++ b/src/librustdoc/lib.rs
@@ -9,7 +9,7 @@
 // except according to those terms.
 
 #![crate_name = "rustdoc"]
-#![unstable(feature = "rustdoc", issue = "27812")]
+#![unstable(feature = "rustc_private", issue = "27812")]
 #![crate_type = "dylib"]
 #![crate_type = "rlib"]
 #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
@@ -48,6 +48,7 @@ extern crate test as testing;
 extern crate std_unicode;
 #[macro_use] extern crate log;
 extern crate rustc_errors as errors;
+extern crate pulldown_cmark;
 
 extern crate serialize as rustc_serialize; // used by deriving
 
diff --git a/src/librustdoc/markdown.rs b/src/librustdoc/markdown.rs
index 5fadda030a4..5cc0f03e1f6 100644
--- a/src/librustdoc/markdown.rs
+++ b/src/librustdoc/markdown.rs
@@ -25,7 +25,7 @@ use externalfiles::{ExternalHtml, LoadStringError, load_string};
 use html::render::reset_ids;
 use html::escape::Escape;
 use html::markdown;
-use html::markdown::{Markdown, MarkdownWithToc, find_testable_code};
+use html::markdown::{Markdown, MarkdownWithToc, MarkdownOutputStyle, find_testable_code};
 use test::{TestOptions, Collector};
 
 /// Separate any lines at the start of the file that begin with `# ` or `%`.
@@ -96,7 +96,7 @@ pub fn render(input: &str, mut output: PathBuf, matches: &getopts::Matches,
     let rendered = if include_toc {
         format!("{}", MarkdownWithToc(text))
     } else {
-        format!("{}", Markdown(text))
+        format!("{}", Markdown(text, MarkdownOutputStyle::Fancy))
     };
 
     let err = write!(
diff --git a/src/rt/hoedown b/src/rt/hoedown
deleted file mode 160000
-Subproject da282f1bb7277b4d30fa1599ee29ad8eb4dd2a9
diff --git a/src/rustc/rustdoc.rs b/src/rustc/rustdoc.rs
index 6fecd3a27a8..a4f43c42623 100644
--- a/src/rustc/rustdoc.rs
+++ b/src/rustc/rustdoc.rs
@@ -8,7 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#![feature(rustdoc)]
+#![feature(rustc_private)]
 
 extern crate rustdoc;
 
diff --git a/src/tools/error_index_generator/main.rs b/src/tools/error_index_generator/main.rs
index e33df0dfbc8..1d4f2c60d54 100644
--- a/src/tools/error_index_generator/main.rs
+++ b/src/tools/error_index_generator/main.rs
@@ -24,7 +24,7 @@ use std::path::PathBuf;
 
 use syntax::diagnostics::metadata::{get_metadata_dir, ErrorMetadataMap, ErrorMetadata};
 
-use rustdoc::html::markdown::{Markdown, PLAYGROUND};
+use rustdoc::html::markdown::{Markdown, MarkdownOutputStyle, PLAYGROUND};
 use rustc_serialize::json;
 
 enum OutputFormat {
@@ -100,7 +100,7 @@ impl Formatter for HTMLFormatter {
 
         // Description rendered as markdown.
         match info.description {
-            Some(ref desc) => write!(output, "{}", Markdown(desc))?,
+            Some(ref desc) => write!(output, "{}", Markdown(desc, MarkdownOutputStyle::Fancy))?,
             None => write!(output, "<p>No description.</p>\n")?,
         }