Skip to content

Commit db28c29

Browse files
committed
rustdoc: Use sundown for markdown highlighting
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 #7380 cc #3546
1 parent 6aba140 commit db28c29

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)