Skip to content

Commit 19ac447

Browse files
committed
Use sundown for markdown highlighting in rustdoc
This takes rendering times of documentation down from 30s to 0.5s. Kinda sad that none of the parallelism is needed, but oh well! Closes rust-lang#7380 cc rust-lang#3546
1 parent 342f829 commit 19ac447

File tree

2 files changed

+103
-52
lines changed

2 files changed

+103
-52
lines changed

src/librustdoc/html/markdown.rs

Lines changed: 84 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -8,47 +8,98 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11+
#[allow(cstack)]; // each rendering task runs on a fixed stack segment.
12+
1113
use std::fmt;
12-
use std::rt::io::Reader;
13-
use std::rt::io::pipe::PipeStream;
14-
use std::rt::io::process::{ProcessConfig, Process, CreatePipe};
14+
use std::libc;
1515
use std::rt::io;
16+
use std::vec;
1617

1718
pub struct Markdown<'self>(&'self str);
1819

20+
static OUTPUT_UNIT: libc::size_t = 64;
21+
22+
type sd_markdown = libc::c_void; // this is opaque to us
23+
24+
// this is a large struct of callbacks we don't use
25+
type sd_callbacks = [libc::size_t, ..26];
26+
27+
struct html_toc_data {
28+
header_count: libc::c_int,
29+
current_level: libc::c_int,
30+
level_offset: libc::c_int,
31+
}
32+
33+
struct html_renderopt {
34+
toc_data: html_toc_data,
35+
flags: libc::c_uint,
36+
link_attributes: Option<extern "C" fn(*buf, *buf, *libc::c_void)>,
37+
}
38+
39+
struct buf {
40+
data: *u8,
41+
size: libc::size_t,
42+
asize: libc::size_t,
43+
unit: libc::size_t,
44+
}
45+
46+
// sundown FFI
47+
extern {
48+
fn sdhtml_renderer(callbacks: *sd_callbacks,
49+
options_ptr: *html_renderopt,
50+
render_flags: libc::c_uint);
51+
fn sd_markdown_new(extensions: libc::c_uint,
52+
max_nesting: libc::size_t,
53+
callbacks: *sd_callbacks,
54+
opaque: *libc::c_void) -> *sd_markdown;
55+
fn sd_markdown_render(ob: *buf,
56+
document: *u8,
57+
doc_size: libc::size_t,
58+
md: *sd_markdown);
59+
fn sd_markdown_free(md: *sd_markdown);
60+
61+
fn bufnew(unit: libc::size_t) -> *buf;
62+
fn bufrelease(b: *buf);
63+
64+
}
65+
66+
fn render(w: &mut io::Writer, s: &str) {
67+
// This code is all lifted from examples/sundown.c in the sundown repo
68+
unsafe {
69+
let ob = bufnew(OUTPUT_UNIT);
70+
let options = html_renderopt {
71+
toc_data: html_toc_data {
72+
header_count: 0,
73+
current_level: 0,
74+
level_offset: 0,
75+
},
76+
flags: 0,
77+
link_attributes: None,
78+
};
79+
let callbacks: sd_callbacks = [0, ..26];
80+
81+
sdhtml_renderer(&callbacks, &options, 0);
82+
let markdown = sd_markdown_new(0, 16, &callbacks,
83+
&options as *html_renderopt as *libc::c_void);
84+
85+
do s.as_imm_buf |data, len| {
86+
sd_markdown_render(ob, data, len as libc::size_t, markdown);
87+
}
88+
sd_markdown_free(markdown);
89+
90+
do vec::raw::buf_as_slice((*ob).data, (*ob).size as uint) |buf| {
91+
w.write(buf);
92+
}
93+
94+
bufrelease(ob);
95+
}
96+
}
97+
1998
impl<'self> fmt::Default for Markdown<'self> {
2099
fn fmt(md: &Markdown<'self>, fmt: &mut fmt::Formatter) {
100+
// This is actually common enough to special-case
21101
if md.len() == 0 { return; }
22102

23-
// Create the pandoc process
24-
do io::io_error::cond.trap(|err| {
25-
fail2!("Error executing `pandoc`: {}", err.desc);
26-
}).inside {
27-
let io = ~[CreatePipe(PipeStream::new().unwrap(), true, false),
28-
CreatePipe(PipeStream::new().unwrap(), false, true)];
29-
let args = ProcessConfig {
30-
program: "pandoc",
31-
args: [],
32-
env: None,
33-
cwd: None,
34-
io: io,
35-
};
36-
let mut p = Process::new(args).expect("couldn't fork for pandoc");
37-
38-
// Write the markdown to stdin and close it.
39-
p.io[0].get_mut_ref().write(md.as_bytes());
40-
p.io[0] = None;
41-
42-
// Ferry the output from pandoc over to the destination buffer.
43-
let mut buf = [0, ..1024];
44-
loop {
45-
match p.io[1].get_mut_ref().read(buf) {
46-
None | Some(0) => { break }
47-
Some(n) => {
48-
fmt.buf.write(buf.slice_to(n));
49-
}
50-
}
51-
}
52-
}
103+
render(fmt.buf, md.as_slice());
53104
}
54105
}

src/librustdoc/html/render.rs

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -401,8 +401,16 @@ impl Context {
401401
let mut task = task::task();
402402
task.unlinked(); // we kill things manually
403403
task.name(format!("worker{}", i));
404-
do task.spawn_with(cache.clone()) |cache| {
404+
task.spawn_with(cache.clone(),
405+
|cache| worker(cache, &port, &chan, &prog_chan));
406+
407+
fn worker(cache: RWArc<Cache>,
408+
port: &SharedPort<Work>,
409+
chan: &SharedChan<Work>,
410+
prog_chan: &SharedChan<Progress>) {
411+
#[fixed_stack_segment]; // we hit markdown FFI *a lot*
405412
local_data::set(cache_key, cache);
413+
406414
loop {
407415
match port.recv() {
408416
Process(cx, item) => {
@@ -425,28 +433,20 @@ impl Context {
425433
}
426434
}
427435

428-
let watcher_chan = chan.clone();
429-
let (done_port, done_chan) = comm::stream();
430-
do task::spawn {
431-
let mut jobs = 0;
432-
loop {
433-
match prog_port.recv() {
434-
JobNew => jobs += 1,
435-
JobDone => jobs -= 1,
436-
}
437-
438-
if jobs == 0 { break }
436+
chan.send(Process(self, item));
437+
let mut jobs = 1;
438+
loop {
439+
match prog_port.recv() {
440+
JobNew => jobs += 1,
441+
JobDone => jobs -= 1,
439442
}
440443

441-
for _ in range(0, WORKERS) {
442-
watcher_chan.send(Die);
443-
}
444-
done_chan.send(());
444+
if jobs == 0 { break }
445445
}
446446

447-
prog_chan.send(JobNew);
448-
chan.send(Process(self, item));
449-
done_port.recv();
447+
for _ in range(0, WORKERS) {
448+
chan.send(Die);
449+
}
450450
}
451451

452452
fn item(&mut self, item: clean::Item, f: &fn(&mut Context, clean::Item)) {

0 commit comments

Comments
 (0)