2012-01-17 19:44:32 -06:00
|
|
|
#[doc = "Generate markdown from a document tree"];
|
|
|
|
|
2012-03-01 00:26:28 -06:00
|
|
|
import markdown_writer::writer;
|
|
|
|
import markdown_writer::writer_util;
|
2012-03-04 01:56:38 -06:00
|
|
|
import markdown_writer::writer_factory;
|
2012-03-01 00:26:28 -06:00
|
|
|
|
2012-01-17 18:12:50 -06:00
|
|
|
export mk_pass;
|
2012-03-02 17:17:13 -06:00
|
|
|
export header_kind, header_name, header_text;
|
2012-01-17 18:12:50 -06:00
|
|
|
|
2012-05-24 16:49:39 -05:00
|
|
|
fn mk_pass(+writer_factory: writer_factory) -> pass {
|
2012-03-02 20:33:25 -06:00
|
|
|
let f = fn~(srv: astsrv::srv, doc: doc::doc) -> doc::doc {
|
2012-05-24 16:49:39 -05:00
|
|
|
run(srv, doc, copy writer_factory)
|
2012-02-27 20:07:16 -06:00
|
|
|
};
|
|
|
|
|
|
|
|
{
|
|
|
|
name: "markdown",
|
|
|
|
f: f
|
2012-01-29 16:51:09 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-02-29 20:10:40 -06:00
|
|
|
fn run(
|
|
|
|
srv: astsrv::srv,
|
2012-03-02 20:33:25 -06:00
|
|
|
doc: doc::doc,
|
2012-05-24 16:49:39 -05:00
|
|
|
+writer_factory: writer_factory
|
2012-03-02 20:33:25 -06:00
|
|
|
) -> doc::doc {
|
2012-02-29 20:10:40 -06:00
|
|
|
|
|
|
|
fn mods_last(item1: doc::itemtag, item2: doc::itemtag) -> bool {
|
|
|
|
fn is_mod(item: doc::itemtag) -> bool {
|
|
|
|
alt item {
|
|
|
|
doc::modtag(_) { true }
|
|
|
|
_ { false }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let lteq = !is_mod(item1) || is_mod(item2);
|
|
|
|
lteq
|
|
|
|
}
|
|
|
|
|
2012-02-29 21:06:50 -06:00
|
|
|
// Sort the items so mods come last. All mods will be
|
|
|
|
// output at the same header level so sorting mods last
|
|
|
|
// makes the headers come out nested correctly.
|
|
|
|
let sorted_doc = sort_pass::mk_pass(
|
|
|
|
"mods last", mods_last
|
|
|
|
).f(srv, doc);
|
2012-02-29 20:10:40 -06:00
|
|
|
|
2012-03-04 01:56:38 -06:00
|
|
|
write_markdown(sorted_doc, writer_factory);
|
2012-02-29 21:06:50 -06:00
|
|
|
|
|
|
|
ret doc;
|
2012-02-29 20:10:40 -06:00
|
|
|
}
|
|
|
|
|
2012-01-29 16:51:09 -06:00
|
|
|
#[test]
|
|
|
|
fn should_write_modules_last() {
|
|
|
|
/*
|
|
|
|
Because the markdown pass writes all modules at the same level of
|
|
|
|
indentation (it doesn't 'nest' them), we need to make sure that we
|
|
|
|
write all of the modules contained in each module after all other
|
|
|
|
types of items, or else the header nesting will end up wrong, with
|
|
|
|
modules appearing to contain items that they do not.
|
|
|
|
*/
|
|
|
|
let markdown = test::render(
|
|
|
|
"mod a { }\
|
|
|
|
fn b() { }\
|
|
|
|
mod c { }\
|
|
|
|
fn d() { }"
|
|
|
|
);
|
|
|
|
|
2012-02-23 09:59:30 -06:00
|
|
|
let idx_a = option::get(str::find_str(markdown, "# Module `a`"));
|
|
|
|
let idx_b = option::get(str::find_str(markdown, "## Function `b`"));
|
|
|
|
let idx_c = option::get(str::find_str(markdown, "# Module `c`"));
|
|
|
|
let idx_d = option::get(str::find_str(markdown, "## Function `d`"));
|
2012-01-29 16:51:09 -06:00
|
|
|
|
|
|
|
assert idx_b < idx_d;
|
|
|
|
assert idx_d < idx_a;
|
|
|
|
assert idx_a < idx_c;
|
2012-01-17 18:12:50 -06:00
|
|
|
}
|
2012-01-16 15:23:42 -06:00
|
|
|
|
2012-01-16 01:01:05 -06:00
|
|
|
type ctxt = {
|
2012-02-29 21:06:50 -06:00
|
|
|
w: writer
|
2012-01-16 00:58:57 -06:00
|
|
|
};
|
|
|
|
|
2012-01-16 02:33:36 -06:00
|
|
|
fn write_markdown(
|
2012-03-02 20:33:25 -06:00
|
|
|
doc: doc::doc,
|
2012-05-24 16:49:39 -05:00
|
|
|
+writer_factory: writer_factory
|
2012-01-16 02:33:36 -06:00
|
|
|
) {
|
2012-03-06 17:01:40 -06:00
|
|
|
par::anymap(doc.pages) {|page|
|
2012-03-06 16:07:34 -06:00
|
|
|
let ctxt = {
|
|
|
|
w: writer_factory(page)
|
|
|
|
};
|
2012-03-06 17:01:40 -06:00
|
|
|
write_page(ctxt, page)
|
|
|
|
};
|
2012-03-06 16:07:34 -06:00
|
|
|
}
|
2012-01-16 02:33:36 -06:00
|
|
|
|
2012-03-06 16:07:34 -06:00
|
|
|
fn write_page(ctxt: ctxt, page: doc::page) {
|
2012-03-10 18:21:23 -06:00
|
|
|
write_title(ctxt, page);
|
2012-03-06 16:07:34 -06:00
|
|
|
alt page {
|
|
|
|
doc::cratepage(doc) {
|
|
|
|
write_crate(ctxt, doc);
|
|
|
|
}
|
|
|
|
doc::itempage(doc) {
|
2012-03-10 18:21:23 -06:00
|
|
|
// We don't write a header for item's pages because their
|
|
|
|
// header in the html output is created by the page title
|
|
|
|
write_item_no_header(ctxt, doc);
|
2012-03-06 16:07:34 -06:00
|
|
|
}
|
|
|
|
}
|
2012-02-29 21:06:50 -06:00
|
|
|
ctxt.w.write_done();
|
2012-01-16 23:37:29 -06:00
|
|
|
}
|
|
|
|
|
2012-03-06 16:07:34 -06:00
|
|
|
#[test]
|
|
|
|
fn should_request_new_writer_for_each_page() {
|
|
|
|
// This port will send us a (page, str) pair for every writer
|
|
|
|
// that was created
|
|
|
|
let (writer_factory, po) = markdown_writer::future_writer_factory();
|
|
|
|
let (srv, doc) = test::create_doc_srv("mod a { }");
|
|
|
|
// Split the document up into pages
|
|
|
|
let doc = page_pass::mk_pass(config::doc_per_mod).f(srv, doc);
|
|
|
|
write_markdown(doc, writer_factory);
|
|
|
|
// We expect two pages to have been written
|
|
|
|
iter::repeat(2u) {||
|
|
|
|
comm::recv(po);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-03-10 18:21:23 -06:00
|
|
|
fn write_title(ctxt: ctxt, page: doc::page) {
|
|
|
|
ctxt.w.write_line(#fmt("%% %s", make_title(page)));
|
|
|
|
ctxt.w.write_line("");
|
|
|
|
}
|
|
|
|
|
|
|
|
fn make_title(page: doc::page) -> str {
|
|
|
|
let item = alt page {
|
|
|
|
doc::cratepage(cratedoc) {
|
|
|
|
doc::modtag(cratedoc.topmod)
|
|
|
|
}
|
|
|
|
doc::itempage(itemtag) {
|
|
|
|
itemtag
|
|
|
|
}
|
|
|
|
};
|
|
|
|
let title = markdown_pass::header_text(item);
|
|
|
|
let title = str::replace(title, "`", "");
|
|
|
|
ret title;
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn should_write_title_for_each_page() {
|
|
|
|
let (writer_factory, po) = markdown_writer::future_writer_factory();
|
|
|
|
let (srv, doc) = test::create_doc_srv(
|
|
|
|
"#[link(name = \"core\")]; mod a { }");
|
|
|
|
let doc = page_pass::mk_pass(config::doc_per_mod).f(srv, doc);
|
|
|
|
write_markdown(doc, writer_factory);
|
|
|
|
iter::repeat(2u) {||
|
|
|
|
let (page, markdown) = comm::recv(po);
|
|
|
|
alt page {
|
|
|
|
doc::cratepage(_) {
|
|
|
|
assert str::contains(markdown, "% Crate core");
|
|
|
|
}
|
|
|
|
doc::itempage(_) {
|
|
|
|
assert str::contains(markdown, "% Module a");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-01-19 18:21:33 -06:00
|
|
|
enum hlvl {
|
2012-01-19 21:29:21 -06:00
|
|
|
h1 = 1,
|
|
|
|
h2 = 2,
|
2012-03-09 18:37:19 -06:00
|
|
|
h3 = 3,
|
|
|
|
h4 = 4
|
2012-01-16 02:59:18 -06:00
|
|
|
}
|
|
|
|
|
2012-03-01 20:13:22 -06:00
|
|
|
fn write_header(ctxt: ctxt, lvl: hlvl, doc: doc::itemtag) {
|
|
|
|
let text = header_text(doc);
|
|
|
|
write_header_(ctxt, lvl, text);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn write_header_(ctxt: ctxt, lvl: hlvl, title: str) {
|
2012-03-12 17:52:30 -05:00
|
|
|
let hashes = str::from_chars(vec::from_elem(lvl as uint, '#'));
|
2012-01-19 01:54:56 -06:00
|
|
|
ctxt.w.write_line(#fmt("%s %s", hashes, title));
|
|
|
|
ctxt.w.write_line("");
|
2012-01-17 00:01:25 -06:00
|
|
|
}
|
|
|
|
|
2012-03-02 17:17:13 -06:00
|
|
|
fn header_kind(doc: doc::itemtag) -> str {
|
2012-03-01 20:13:22 -06:00
|
|
|
alt doc {
|
|
|
|
doc::modtag(_) {
|
2012-05-13 19:12:56 -05:00
|
|
|
if doc.id() == syntax::ast::crate_node_id {
|
2012-03-02 17:17:13 -06:00
|
|
|
"Crate"
|
2012-03-01 20:13:22 -06:00
|
|
|
} else {
|
2012-03-02 17:17:13 -06:00
|
|
|
"Module"
|
2012-03-01 20:13:22 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
doc::nmodtag(_) {
|
2012-03-02 17:17:13 -06:00
|
|
|
"Native module"
|
2012-03-01 20:13:22 -06:00
|
|
|
}
|
|
|
|
doc::fntag(_) {
|
2012-03-02 17:17:13 -06:00
|
|
|
"Function"
|
2012-03-01 20:13:22 -06:00
|
|
|
}
|
|
|
|
doc::consttag(_) {
|
2012-03-02 17:17:13 -06:00
|
|
|
"Const"
|
2012-03-01 20:13:22 -06:00
|
|
|
}
|
|
|
|
doc::enumtag(_) {
|
2012-03-02 17:17:13 -06:00
|
|
|
"Enum"
|
2012-03-01 20:13:22 -06:00
|
|
|
}
|
|
|
|
doc::ifacetag(_) {
|
2012-03-02 17:17:13 -06:00
|
|
|
"Interface"
|
|
|
|
}
|
|
|
|
doc::impltag(doc) {
|
|
|
|
"Implementation"
|
|
|
|
}
|
|
|
|
doc::tytag(_) {
|
|
|
|
"Type"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn header_name(doc: doc::itemtag) -> str {
|
2012-06-29 18:26:56 -05:00
|
|
|
let fullpath = str::connect(doc.path() + ~[doc.name()], "::");
|
2012-03-02 17:17:13 -06:00
|
|
|
alt doc {
|
2012-05-13 19:12:56 -05:00
|
|
|
doc::modtag(_) if doc.id() != syntax::ast::crate_node_id {
|
2012-03-02 17:17:13 -06:00
|
|
|
fullpath
|
|
|
|
}
|
|
|
|
doc::nmodtag(_) {
|
|
|
|
fullpath
|
2012-03-01 20:13:22 -06:00
|
|
|
}
|
|
|
|
doc::impltag(doc) {
|
|
|
|
assert option::is_some(doc.self_ty);
|
|
|
|
let self_ty = option::get(doc.self_ty);
|
|
|
|
alt doc.iface_ty {
|
|
|
|
some(iface_ty) {
|
2012-03-02 17:17:13 -06:00
|
|
|
#fmt("%s of %s for %s", doc.name(), iface_ty, self_ty)
|
2012-03-01 20:13:22 -06:00
|
|
|
}
|
|
|
|
none {
|
2012-03-02 17:17:13 -06:00
|
|
|
#fmt("%s for %s", doc.name(), self_ty)
|
2012-03-01 20:13:22 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2012-03-02 17:17:13 -06:00
|
|
|
_ {
|
|
|
|
doc.name()
|
2012-03-01 20:13:22 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-03-02 17:17:13 -06:00
|
|
|
fn header_text(doc: doc::itemtag) -> str {
|
|
|
|
header_text_(header_kind(doc), header_name(doc))
|
|
|
|
}
|
|
|
|
|
2012-03-01 20:13:22 -06:00
|
|
|
fn header_text_(kind: str, name: str) -> str {
|
|
|
|
#fmt("%s `%s`", kind, name)
|
|
|
|
}
|
|
|
|
|
2012-01-16 23:37:29 -06:00
|
|
|
fn write_crate(
|
|
|
|
ctxt: ctxt,
|
|
|
|
doc: doc::cratedoc
|
|
|
|
) {
|
|
|
|
write_top_module(ctxt, doc.topmod);
|
2012-01-16 17:44:10 -06:00
|
|
|
}
|
|
|
|
|
2012-01-16 02:59:18 -06:00
|
|
|
fn write_top_module(
|
|
|
|
ctxt: ctxt,
|
|
|
|
moddoc: doc::moddoc
|
|
|
|
) {
|
2012-01-16 17:38:40 -06:00
|
|
|
write_mod_contents(ctxt, moddoc);
|
2012-01-16 02:59:18 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
fn write_mod(
|
|
|
|
ctxt: ctxt,
|
|
|
|
moddoc: doc::moddoc
|
|
|
|
) {
|
2012-01-16 17:38:40 -06:00
|
|
|
write_mod_contents(ctxt, moddoc);
|
2012-01-16 02:59:18 -06:00
|
|
|
}
|
|
|
|
|
2012-01-19 02:03:45 -06:00
|
|
|
#[test]
|
|
|
|
fn should_write_full_path_to_mod() {
|
|
|
|
let markdown = test::render("mod a { mod b { mod c { } } }");
|
2012-01-27 01:16:42 -06:00
|
|
|
assert str::contains(markdown, "# Module `a::b::c`");
|
2012-01-19 02:03:45 -06:00
|
|
|
}
|
|
|
|
|
2012-03-09 18:37:19 -06:00
|
|
|
fn write_common(
|
|
|
|
ctxt: ctxt,
|
|
|
|
desc: option<str>,
|
2012-06-29 18:26:56 -05:00
|
|
|
sections: ~[doc::section]
|
2012-03-09 18:37:19 -06:00
|
|
|
) {
|
|
|
|
write_desc(ctxt, desc);
|
|
|
|
write_sections(ctxt, sections);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn write_desc(
|
|
|
|
ctxt: ctxt,
|
|
|
|
desc: option<str>
|
|
|
|
) {
|
|
|
|
alt desc {
|
|
|
|
some(desc) {
|
|
|
|
ctxt.w.write_line(desc);
|
|
|
|
ctxt.w.write_line("");
|
|
|
|
}
|
|
|
|
none { }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-06-29 18:26:56 -05:00
|
|
|
fn write_sections(ctxt: ctxt, sections: ~[doc::section]) {
|
2012-03-09 18:37:19 -06:00
|
|
|
vec::iter(sections) {|section|
|
|
|
|
write_section(ctxt, section);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn write_section(ctxt: ctxt, section: doc::section) {
|
|
|
|
write_header_(ctxt, h4, section.header);
|
|
|
|
ctxt.w.write_line(section.body);
|
|
|
|
ctxt.w.write_line("");
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn should_write_sections() {
|
|
|
|
let markdown = test::render(
|
|
|
|
"#[doc = \"\
|
|
|
|
# Header\n\
|
|
|
|
Body\"]\
|
|
|
|
mod a { }");
|
|
|
|
assert str::contains(markdown, "#### Header\n\nBody\n\n");
|
|
|
|
}
|
|
|
|
|
2012-01-16 02:59:18 -06:00
|
|
|
fn write_mod_contents(
|
|
|
|
ctxt: ctxt,
|
2012-01-18 23:33:37 -06:00
|
|
|
doc: doc::moddoc
|
2012-01-16 02:59:18 -06:00
|
|
|
) {
|
2012-03-09 19:40:40 -06:00
|
|
|
write_common(ctxt, doc.desc(), doc.sections());
|
2012-03-02 18:20:36 -06:00
|
|
|
if option::is_some(doc.index) {
|
|
|
|
write_index(ctxt, option::get(doc.index));
|
|
|
|
}
|
2012-01-18 23:33:37 -06:00
|
|
|
|
2012-04-06 13:01:43 -05:00
|
|
|
for doc.items.each {|itemtag|
|
2012-03-06 16:07:34 -06:00
|
|
|
write_item(ctxt, itemtag);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn write_item(ctxt: ctxt, doc: doc::itemtag) {
|
2012-03-10 18:21:23 -06:00
|
|
|
write_item_(ctxt, doc, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn write_item_no_header(ctxt: ctxt, doc: doc::itemtag) {
|
|
|
|
write_item_(ctxt, doc, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn write_item_(ctxt: ctxt, doc: doc::itemtag, write_header: bool) {
|
|
|
|
if write_header {
|
|
|
|
write_item_header(ctxt, doc);
|
|
|
|
}
|
|
|
|
|
2012-03-06 16:07:34 -06:00
|
|
|
alt doc {
|
|
|
|
doc::modtag(moddoc) { write_mod(ctxt, moddoc) }
|
|
|
|
doc::nmodtag(nmoddoc) { write_nmod(ctxt, nmoddoc) }
|
|
|
|
doc::fntag(fndoc) { write_fn(ctxt, fndoc) }
|
|
|
|
doc::consttag(constdoc) { write_const(ctxt, constdoc) }
|
|
|
|
doc::enumtag(enumdoc) { write_enum(ctxt, enumdoc) }
|
|
|
|
doc::ifacetag(ifacedoc) { write_iface(ctxt, ifacedoc) }
|
|
|
|
doc::impltag(impldoc) { write_impl(ctxt, impldoc) }
|
|
|
|
doc::tytag(tydoc) { write_type(ctxt, tydoc) }
|
2012-01-28 16:36:35 -06:00
|
|
|
}
|
2012-01-16 02:59:18 -06:00
|
|
|
}
|
|
|
|
|
2012-03-10 18:21:23 -06:00
|
|
|
fn write_item_header(ctxt: ctxt, doc: doc::itemtag) {
|
|
|
|
write_header(ctxt, item_header_lvl(doc), doc);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn item_header_lvl(doc: doc::itemtag) -> hlvl {
|
|
|
|
alt doc {
|
|
|
|
doc::modtag(_) | doc::nmodtag(_) { h1 }
|
|
|
|
_ { h2 }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-01-18 23:33:37 -06:00
|
|
|
#[test]
|
|
|
|
fn should_write_crate_description() {
|
|
|
|
let markdown = test::render("#[doc = \"this is the crate\"];");
|
|
|
|
assert str::contains(markdown, "this is the crate");
|
|
|
|
}
|
|
|
|
|
2012-03-02 18:20:36 -06:00
|
|
|
fn write_index(ctxt: ctxt, index: doc::index) {
|
|
|
|
if vec::is_empty(index.entries) {
|
|
|
|
ret;
|
|
|
|
}
|
2012-03-02 18:27:31 -06:00
|
|
|
|
2012-04-06 13:01:43 -05:00
|
|
|
for index.entries.each {|entry|
|
2012-03-02 18:20:36 -06:00
|
|
|
let header = header_text_(entry.kind, entry.name);
|
|
|
|
let id = entry.link;
|
2012-03-06 18:51:40 -06:00
|
|
|
if option::is_some(entry.brief) {
|
2012-06-28 12:42:19 -05:00
|
|
|
ctxt.w.write_line(#fmt("* [%s](%s) - %s",
|
2012-03-06 18:51:40 -06:00
|
|
|
header, id, option::get(entry.brief)));
|
|
|
|
} else {
|
2012-06-28 12:42:19 -05:00
|
|
|
ctxt.w.write_line(#fmt("* [%s](%s)", header, id));
|
2012-03-06 18:51:40 -06:00
|
|
|
}
|
2012-03-02 18:20:36 -06:00
|
|
|
}
|
|
|
|
ctxt.w.write_line("");
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn should_write_index() {
|
|
|
|
let markdown = test::render("mod a { } mod b { }");
|
|
|
|
assert str::contains(
|
|
|
|
markdown,
|
2012-06-28 12:42:19 -05:00
|
|
|
"\n\n* [Module `a`](#module-a)\n\
|
|
|
|
* [Module `b`](#module-b)\n\n"
|
2012-03-02 18:20:36 -06:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2012-03-06 18:51:40 -06:00
|
|
|
#[test]
|
|
|
|
fn should_write_index_brief() {
|
2012-03-09 20:12:15 -06:00
|
|
|
let markdown = test::render("#[doc = \"test\"] mod a { }");
|
2012-03-06 18:51:40 -06:00
|
|
|
assert str::contains(markdown, "(#module-a) - test\n");
|
|
|
|
}
|
|
|
|
|
2012-03-02 18:20:36 -06:00
|
|
|
#[test]
|
|
|
|
fn should_not_write_index_if_no_entries() {
|
|
|
|
let markdown = test::render("");
|
|
|
|
assert !str::contains(markdown, "\n\n\n");
|
|
|
|
}
|
|
|
|
|
2012-03-11 18:36:20 -05:00
|
|
|
#[test]
|
2012-06-26 18:18:37 -05:00
|
|
|
fn should_write_index_for_foreign_mods() {
|
2012-03-11 18:36:20 -05:00
|
|
|
let markdown = test::render("native mod a { fn a(); }");
|
|
|
|
assert str::contains(
|
|
|
|
markdown,
|
2012-06-28 12:42:19 -05:00
|
|
|
"\n\n* [Function `a`](#function-a)\n\n"
|
2012-03-11 18:36:20 -05:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2012-02-24 17:07:08 -06:00
|
|
|
fn write_nmod(ctxt: ctxt, doc: doc::nmoddoc) {
|
2012-03-09 19:40:40 -06:00
|
|
|
write_common(ctxt, doc.desc(), doc.sections());
|
2012-03-11 18:36:20 -05:00
|
|
|
if option::is_some(doc.index) {
|
|
|
|
write_index(ctxt, option::get(doc.index));
|
|
|
|
}
|
2012-02-24 17:07:08 -06:00
|
|
|
|
2012-04-06 13:01:43 -05:00
|
|
|
for doc.fns.each {|fndoc|
|
2012-03-10 18:43:38 -06:00
|
|
|
write_item_header(ctxt, doc::fntag(fndoc));
|
2012-02-24 17:07:08 -06:00
|
|
|
write_fn(ctxt, fndoc);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2012-06-26 18:18:37 -05:00
|
|
|
fn should_write_foreign_mods() {
|
2012-02-24 17:07:08 -06:00
|
|
|
let markdown = test::render("#[doc = \"test\"] native mod a { }");
|
2012-02-24 17:22:57 -06:00
|
|
|
assert str::contains(markdown, "Native module `a`");
|
2012-02-24 17:07:08 -06:00
|
|
|
assert str::contains(markdown, "test");
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2012-06-26 18:18:37 -05:00
|
|
|
fn should_write_foreign_fns() {
|
2012-02-24 17:07:08 -06:00
|
|
|
let markdown = test::render("native mod a { #[doc = \"test\"] fn a(); }");
|
|
|
|
assert str::contains(markdown, "test");
|
|
|
|
}
|
|
|
|
|
2012-03-10 18:43:38 -06:00
|
|
|
#[test]
|
2012-06-26 18:18:37 -05:00
|
|
|
fn should_write_foreign_fn_headers() {
|
2012-03-10 18:43:38 -06:00
|
|
|
let markdown = test::render("native mod a { #[doc = \"test\"] fn a(); }");
|
|
|
|
assert str::contains(markdown, "## Function `a`");
|
|
|
|
}
|
|
|
|
|
2012-01-16 02:59:18 -06:00
|
|
|
fn write_fn(
|
|
|
|
ctxt: ctxt,
|
2012-01-16 17:44:10 -06:00
|
|
|
doc: doc::fndoc
|
2012-01-16 02:59:18 -06:00
|
|
|
) {
|
2012-01-30 22:53:52 -06:00
|
|
|
write_fnlike(
|
|
|
|
ctxt,
|
|
|
|
doc.sig,
|
2012-02-17 16:46:30 -06:00
|
|
|
doc.desc(),
|
2012-03-09 19:23:56 -06:00
|
|
|
doc.sections()
|
2012-01-30 22:53:52 -06:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn write_fnlike(
|
|
|
|
ctxt: ctxt,
|
|
|
|
sig: option<str>,
|
|
|
|
desc: option<str>,
|
2012-06-29 18:26:56 -05:00
|
|
|
sections: ~[doc::section]
|
2012-01-30 22:53:52 -06:00
|
|
|
) {
|
|
|
|
write_sig(ctxt, sig);
|
2012-03-09 19:40:40 -06:00
|
|
|
write_common(ctxt, desc, sections);
|
2012-01-18 01:39:22 -06:00
|
|
|
}
|
|
|
|
|
2012-01-19 20:58:44 -06:00
|
|
|
fn write_sig(ctxt: ctxt, sig: option<str>) {
|
|
|
|
alt sig {
|
|
|
|
some(sig) {
|
2012-01-23 19:58:40 -06:00
|
|
|
ctxt.w.write_line(code_block_indent(sig));
|
2012-01-19 20:58:44 -06:00
|
|
|
ctxt.w.write_line("");
|
|
|
|
}
|
|
|
|
none { fail "unimplemented" }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-01-23 19:58:40 -06:00
|
|
|
fn code_block_indent(s: str) -> str {
|
|
|
|
let lines = str::lines_any(s);
|
2012-02-27 01:47:27 -06:00
|
|
|
let indented = par::seqmap(lines, { |line| #fmt(" %s", line) });
|
2012-01-23 19:58:40 -06:00
|
|
|
str::connect(indented, "\n")
|
|
|
|
}
|
|
|
|
|
2012-01-27 00:45:37 -06:00
|
|
|
#[test]
|
|
|
|
fn write_markdown_should_write_function_header() {
|
|
|
|
let markdown = test::render("fn func() { }");
|
2012-01-27 01:16:42 -06:00
|
|
|
assert str::contains(markdown, "## Function `func`");
|
2012-01-27 00:45:37 -06:00
|
|
|
}
|
|
|
|
|
2012-01-19 20:58:44 -06:00
|
|
|
#[test]
|
|
|
|
fn should_write_the_function_signature() {
|
|
|
|
let markdown = test::render("#[doc = \"f\"] fn a() { }");
|
2012-01-23 19:14:55 -06:00
|
|
|
assert str::contains(markdown, "\n fn a()\n");
|
2012-01-19 20:58:44 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn should_insert_blank_line_after_fn_signature() {
|
|
|
|
let markdown = test::render("#[doc = \"f\"] fn a() { }");
|
2012-01-23 19:14:55 -06:00
|
|
|
assert str::contains(markdown, "fn a()\n\n");
|
2012-01-19 20:58:44 -06:00
|
|
|
}
|
|
|
|
|
2012-01-23 19:58:40 -06:00
|
|
|
#[test]
|
|
|
|
fn should_correctly_indent_fn_signature() {
|
|
|
|
let doc = test::create_doc("fn a() { }");
|
2012-01-30 15:05:25 -06:00
|
|
|
let doc = {
|
2012-06-29 18:26:56 -05:00
|
|
|
pages: ~[
|
2012-03-02 20:33:25 -06:00
|
|
|
doc::cratepage({
|
|
|
|
topmod: {
|
2012-06-29 18:26:56 -05:00
|
|
|
items: ~[doc::fntag({
|
2012-03-02 20:33:25 -06:00
|
|
|
sig: some("line 1\nline 2")
|
|
|
|
with doc.cratemod().fns()[0]
|
2012-06-29 18:26:56 -05:00
|
|
|
})]
|
2012-03-02 20:33:25 -06:00
|
|
|
with doc.cratemod()
|
|
|
|
}
|
|
|
|
with doc.cratedoc()
|
|
|
|
})
|
2012-06-29 18:26:56 -05:00
|
|
|
]
|
2012-01-23 19:58:40 -06:00
|
|
|
};
|
|
|
|
let markdown = test::write_markdown_str(doc);
|
|
|
|
assert str::contains(markdown, " line 1\n line 2");
|
|
|
|
}
|
|
|
|
|
2012-01-27 00:45:37 -06:00
|
|
|
#[test]
|
|
|
|
fn should_leave_blank_line_between_fn_header_and_sig() {
|
2012-03-09 19:40:40 -06:00
|
|
|
let markdown = test::render("fn a() { }");
|
2012-01-27 00:45:37 -06:00
|
|
|
assert str::contains(markdown, "Function `a`\n\n fn a()");
|
|
|
|
}
|
|
|
|
|
2012-01-24 02:51:19 -06:00
|
|
|
fn write_const(
|
|
|
|
ctxt: ctxt,
|
|
|
|
doc: doc::constdoc
|
|
|
|
) {
|
2012-03-09 19:23:56 -06:00
|
|
|
write_sig(ctxt, doc.sig);
|
2012-03-09 19:40:40 -06:00
|
|
|
write_common(ctxt, doc.desc(), doc.sections());
|
2012-01-24 02:51:19 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn should_write_const_header() {
|
|
|
|
let markdown = test::render("const a: bool = true;");
|
2012-01-27 01:16:42 -06:00
|
|
|
assert str::contains(markdown, "## Const `a`\n\n");
|
2012-01-24 02:51:19 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn should_write_const_description() {
|
|
|
|
let markdown = test::render(
|
2012-03-09 19:40:40 -06:00
|
|
|
"#[doc = \"b\"]\
|
2012-01-24 02:51:19 -06:00
|
|
|
const a: bool = true;");
|
2012-03-09 19:40:40 -06:00
|
|
|
assert str::contains(markdown, "\n\nb\n\n");
|
2012-01-24 02:51:19 -06:00
|
|
|
}
|
|
|
|
|
2012-01-25 22:55:55 -06:00
|
|
|
fn write_enum(
|
|
|
|
ctxt: ctxt,
|
|
|
|
doc: doc::enumdoc
|
|
|
|
) {
|
2012-03-09 19:40:40 -06:00
|
|
|
write_common(ctxt, doc.desc(), doc.sections());
|
2012-01-25 22:55:55 -06:00
|
|
|
write_variants(ctxt, doc.variants);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn should_write_enum_header() {
|
|
|
|
let markdown = test::render("enum a { b }");
|
2012-01-27 01:16:42 -06:00
|
|
|
assert str::contains(markdown, "## Enum `a`\n\n");
|
2012-01-25 22:55:55 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn should_write_enum_description() {
|
|
|
|
let markdown = test::render(
|
2012-03-09 19:40:40 -06:00
|
|
|
"#[doc = \"b\"] enum a { b }");
|
|
|
|
assert str::contains(markdown, "\n\nb\n\n");
|
2012-01-25 22:55:55 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
fn write_variants(
|
|
|
|
ctxt: ctxt,
|
2012-06-29 18:26:56 -05:00
|
|
|
docs: ~[doc::variantdoc]
|
2012-01-25 22:55:55 -06:00
|
|
|
) {
|
|
|
|
if vec::is_empty(docs) {
|
|
|
|
ret;
|
|
|
|
}
|
|
|
|
|
2012-03-09 23:20:51 -06:00
|
|
|
write_header_(ctxt, h4, "Variants");
|
2012-01-25 22:55:55 -06:00
|
|
|
|
|
|
|
vec::iter(docs, {|variant| write_variant(ctxt, variant) });
|
|
|
|
|
|
|
|
ctxt.w.write_line("");
|
|
|
|
}
|
|
|
|
|
|
|
|
fn write_variant(ctxt: ctxt, doc: doc::variantdoc) {
|
|
|
|
assert option::is_some(doc.sig);
|
|
|
|
let sig = option::get(doc.sig);
|
|
|
|
alt doc.desc {
|
|
|
|
some(desc) {
|
|
|
|
ctxt.w.write_line(#fmt("* `%s` - %s", sig, desc));
|
|
|
|
}
|
|
|
|
none {
|
|
|
|
ctxt.w.write_line(#fmt("* `%s`", sig));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn should_write_variant_list() {
|
|
|
|
let markdown = test::render(
|
|
|
|
"enum a { \
|
|
|
|
#[doc = \"test\"] b, \
|
|
|
|
#[doc = \"test\"] c }");
|
|
|
|
assert str::contains(
|
|
|
|
markdown,
|
2012-03-09 23:20:51 -06:00
|
|
|
"\n\n#### Variants\n\
|
2012-01-25 22:55:55 -06:00
|
|
|
\n* `b` - test\
|
|
|
|
\n* `c` - test\n\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn should_write_variant_list_without_descs() {
|
|
|
|
let markdown = test::render("enum a { b, c }");
|
|
|
|
assert str::contains(
|
|
|
|
markdown,
|
2012-03-09 23:20:51 -06:00
|
|
|
"\n\n#### Variants\n\
|
2012-01-25 22:55:55 -06:00
|
|
|
\n* `b`\
|
|
|
|
\n* `c`\n\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn should_write_variant_list_with_signatures() {
|
|
|
|
let markdown = test::render("enum a { b(int), #[doc = \"a\"] c(int) }");
|
|
|
|
assert str::contains(
|
|
|
|
markdown,
|
2012-03-09 23:20:51 -06:00
|
|
|
"\n\n#### Variants\n\
|
2012-01-25 22:55:55 -06:00
|
|
|
\n* `b(int)`\
|
|
|
|
\n* `c(int)` - a\n\n");
|
|
|
|
}
|
|
|
|
|
2012-01-30 22:53:52 -06:00
|
|
|
fn write_iface(ctxt: ctxt, doc: doc::ifacedoc) {
|
2012-03-09 19:40:40 -06:00
|
|
|
write_common(ctxt, doc.desc(), doc.sections());
|
2012-01-30 22:53:52 -06:00
|
|
|
write_methods(ctxt, doc.methods);
|
|
|
|
}
|
|
|
|
|
2012-06-29 18:26:56 -05:00
|
|
|
fn write_methods(ctxt: ctxt, docs: ~[doc::methoddoc]) {
|
2012-01-30 22:53:52 -06:00
|
|
|
vec::iter(docs) {|doc| write_method(ctxt, doc) }
|
|
|
|
}
|
|
|
|
|
|
|
|
fn write_method(ctxt: ctxt, doc: doc::methoddoc) {
|
2012-03-01 20:13:22 -06:00
|
|
|
write_header_(ctxt, h3, header_text_("Method", doc.name));
|
2012-01-30 22:53:52 -06:00
|
|
|
write_fnlike(
|
|
|
|
ctxt,
|
|
|
|
doc.sig,
|
|
|
|
doc.desc,
|
2012-03-09 19:23:56 -06:00
|
|
|
doc.sections
|
2012-01-30 22:53:52 -06:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn should_write_iface_header() {
|
|
|
|
let markdown = test::render("iface i { fn a(); }");
|
|
|
|
assert str::contains(markdown, "## Interface `i`");
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn should_write_iface_desc() {
|
|
|
|
let markdown = test::render(
|
2012-03-09 20:12:15 -06:00
|
|
|
"#[doc = \"desc\"] iface i { fn a(); }");
|
2012-01-30 22:53:52 -06:00
|
|
|
assert str::contains(markdown, "desc");
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn should_write_iface_method_header() {
|
|
|
|
let markdown = test::render(
|
|
|
|
"iface i { fn a(); }");
|
|
|
|
assert str::contains(markdown, "### Method `a`");
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn should_write_iface_method_signature() {
|
|
|
|
let markdown = test::render(
|
|
|
|
"iface i { fn a(); }");
|
|
|
|
assert str::contains(markdown, "\n fn a()");
|
|
|
|
}
|
|
|
|
|
2012-01-31 22:54:46 -06:00
|
|
|
fn write_impl(ctxt: ctxt, doc: doc::impldoc) {
|
2012-03-09 19:40:40 -06:00
|
|
|
write_common(ctxt, doc.desc(), doc.sections());
|
2012-01-31 22:54:46 -06:00
|
|
|
write_methods(ctxt, doc.methods);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn should_write_impl_header() {
|
|
|
|
let markdown = test::render("impl i for int { fn a() { } }");
|
2012-03-01 20:13:22 -06:00
|
|
|
assert str::contains(markdown, "## Implementation `i for int`");
|
2012-01-31 22:54:46 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn should_write_impl_header_with_iface() {
|
|
|
|
let markdown = test::render("impl i of j for int { fn a() { } }");
|
2012-03-01 20:13:22 -06:00
|
|
|
assert str::contains(markdown, "## Implementation `i of j for int`");
|
2012-01-31 22:54:46 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn should_write_impl_desc() {
|
|
|
|
let markdown = test::render(
|
2012-03-09 20:12:15 -06:00
|
|
|
"#[doc = \"desc\"] impl i for int { fn a() { } }");
|
2012-01-31 22:54:46 -06:00
|
|
|
assert str::contains(markdown, "desc");
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn should_write_impl_method_header() {
|
|
|
|
let markdown = test::render(
|
|
|
|
"impl i for int { fn a() { } }");
|
|
|
|
assert str::contains(markdown, "### Method `a`");
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn should_write_impl_method_signature() {
|
|
|
|
let markdown = test::render(
|
|
|
|
"impl i for int { fn a() { } }");
|
|
|
|
assert str::contains(markdown, "\n fn a()");
|
|
|
|
}
|
|
|
|
|
2012-02-02 00:41:41 -06:00
|
|
|
fn write_type(
|
|
|
|
ctxt: ctxt,
|
|
|
|
doc: doc::tydoc
|
|
|
|
) {
|
|
|
|
write_sig(ctxt, doc.sig);
|
2012-03-09 19:40:40 -06:00
|
|
|
write_common(ctxt, doc.desc(), doc.sections());
|
2012-02-02 00:41:41 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn should_write_type_header() {
|
|
|
|
let markdown = test::render("type t = int;");
|
|
|
|
assert str::contains(markdown, "## Type `t`");
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn should_write_type_desc() {
|
|
|
|
let markdown = test::render(
|
2012-03-09 20:12:15 -06:00
|
|
|
"#[doc = \"desc\"] type t = int;");
|
2012-02-02 00:41:41 -06:00
|
|
|
assert str::contains(markdown, "\n\ndesc\n\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn should_write_type_signature() {
|
|
|
|
let markdown = test::render("type t = int;");
|
|
|
|
assert str::contains(markdown, "\n\n type t = int\n\n");
|
|
|
|
}
|
|
|
|
|
2012-01-16 02:33:36 -06:00
|
|
|
#[cfg(test)]
|
2012-01-18 17:02:23 -06:00
|
|
|
mod test {
|
2012-01-18 01:33:11 -06:00
|
|
|
fn render(source: str) -> str {
|
2012-01-29 16:51:09 -06:00
|
|
|
let (srv, doc) = create_doc_srv(source);
|
|
|
|
let markdown = write_markdown_str_srv(srv, doc);
|
2012-01-23 19:58:40 -06:00
|
|
|
#debug("markdown: %s", markdown);
|
|
|
|
markdown
|
|
|
|
}
|
|
|
|
|
2012-03-02 20:33:25 -06:00
|
|
|
fn create_doc_srv(source: str) -> (astsrv::srv, doc::doc) {
|
2012-02-20 23:08:19 -06:00
|
|
|
astsrv::from_str(source) {|srv|
|
2012-03-06 17:57:36 -06:00
|
|
|
|
|
|
|
let config = {
|
|
|
|
output_style: config::doc_per_crate
|
|
|
|
with config::default_config("whatever")
|
|
|
|
};
|
|
|
|
|
2012-02-20 23:08:19 -06:00
|
|
|
let doc = extract::from_srv(srv, "");
|
|
|
|
#debug("doc (extract): %?", doc);
|
2012-02-27 20:07:16 -06:00
|
|
|
let doc = tystr_pass::mk_pass().f(srv, doc);
|
2012-02-20 23:08:19 -06:00
|
|
|
#debug("doc (tystr): %?", doc);
|
2012-02-27 20:07:16 -06:00
|
|
|
let doc = path_pass::mk_pass().f(srv, doc);
|
2012-02-20 23:08:19 -06:00
|
|
|
#debug("doc (path): %?", doc);
|
2012-02-27 20:07:16 -06:00
|
|
|
let doc = attr_pass::mk_pass().f(srv, doc);
|
2012-02-20 23:08:19 -06:00
|
|
|
#debug("doc (attr): %?", doc);
|
2012-03-09 20:12:15 -06:00
|
|
|
let doc = desc_to_brief_pass::mk_pass().f(srv, doc);
|
|
|
|
#debug("doc (desc_to_brief): %?", doc);
|
2012-03-09 18:37:19 -06:00
|
|
|
let doc = unindent_pass::mk_pass().f(srv, doc);
|
|
|
|
#debug("doc (unindent): %?", doc);
|
|
|
|
let doc = sectionalize_pass::mk_pass().f(srv, doc);
|
|
|
|
#debug("doc (trim): %?", doc);
|
|
|
|
let doc = trim_pass::mk_pass().f(srv, doc);
|
|
|
|
#debug("doc (sectionalize): %?", doc);
|
2012-03-06 17:57:36 -06:00
|
|
|
let doc = markdown_index_pass::mk_pass(config).f(srv, doc);
|
2012-03-02 18:20:36 -06:00
|
|
|
#debug("doc (index): %?", doc);
|
2012-02-20 23:08:19 -06:00
|
|
|
(srv, doc)
|
|
|
|
}
|
2012-01-29 16:51:09 -06:00
|
|
|
}
|
|
|
|
|
2012-03-02 20:33:25 -06:00
|
|
|
fn create_doc(source: str) -> doc::doc {
|
2012-01-29 16:51:09 -06:00
|
|
|
let (_, doc) = create_doc_srv(source);
|
2012-01-23 19:58:40 -06:00
|
|
|
doc
|
2012-01-18 01:33:11 -06:00
|
|
|
}
|
|
|
|
|
2012-01-16 02:33:36 -06:00
|
|
|
fn write_markdown_str(
|
2012-03-02 20:33:25 -06:00
|
|
|
doc: doc::doc
|
2012-01-16 02:33:36 -06:00
|
|
|
) -> str {
|
2012-03-04 01:56:38 -06:00
|
|
|
let (writer_factory, po) = markdown_writer::future_writer_factory();
|
|
|
|
write_markdown(doc, writer_factory);
|
|
|
|
ret tuple::second(comm::recv(po));
|
2012-01-16 02:33:36 -06:00
|
|
|
}
|
|
|
|
|
2012-01-29 16:51:09 -06:00
|
|
|
fn write_markdown_str_srv(
|
|
|
|
srv: astsrv::srv,
|
2012-03-02 20:33:25 -06:00
|
|
|
doc: doc::doc
|
2012-01-29 16:51:09 -06:00
|
|
|
) -> str {
|
2012-03-04 01:56:38 -06:00
|
|
|
let (writer_factory, po) = markdown_writer::future_writer_factory();
|
|
|
|
let pass = mk_pass(writer_factory);
|
2012-02-27 20:07:16 -06:00
|
|
|
pass.f(srv, doc);
|
2012-03-04 01:56:38 -06:00
|
|
|
ret tuple::second(comm::recv(po));
|
2012-01-29 16:51:09 -06:00
|
|
|
}
|
|
|
|
|
2012-01-17 01:10:25 -06:00
|
|
|
#[test]
|
|
|
|
fn write_markdown_should_write_mod_headers() {
|
2012-01-18 01:33:11 -06:00
|
|
|
let markdown = render("mod moo { }");
|
2012-01-27 01:16:42 -06:00
|
|
|
assert str::contains(markdown, "# Module `moo`");
|
2012-01-17 01:10:25 -06:00
|
|
|
}
|
2012-01-17 18:30:53 -06:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn should_leave_blank_line_after_header() {
|
2012-01-18 01:33:11 -06:00
|
|
|
let markdown = render("mod morp { }");
|
2012-01-18 01:29:23 -06:00
|
|
|
assert str::contains(markdown, "Module `morp`\n\n");
|
|
|
|
}
|
2012-02-13 00:00:56 -06:00
|
|
|
}
|