rust/src/rustdoc/markdown_pass.rs

848 lines
21 KiB
Rust
Raw Normal View History

//! Generate markdown from a document tree
2012-01-17 19:44:32 -06:00
2012-09-05 12:41:47 -05:00
use doc::item_utils;
use markdown_writer::writer;
use markdown_writer::writer_utils;
use markdown_writer::writer_factory;
export mk_pass;
2012-03-02 17:17:13 -06:00
export header_kind, header_name, header_text;
fn mk_pass(+writer_factory: writer_factory) -> pass {
let f = fn~(srv: astsrv::srv, doc: doc::doc) -> doc::doc {
run(srv, doc, copy writer_factory)
2012-02-27 20:07:16 -06:00
};
{
name: ~"markdown",
2012-02-27 20:07:16 -06:00
f: f
}
}
2012-02-29 20:10:40 -06:00
fn run(
srv: astsrv::srv,
doc: doc::doc,
+writer_factory: writer_factory
) -> doc::doc {
2012-02-29 20:10:40 -06:00
pure fn mods_last(item1: &doc::itemtag, item2: &doc::itemtag) -> bool {
pure fn is_mod(item: &doc::itemtag) -> bool {
2012-08-06 14:34:08 -05:00
match *item {
2012-08-03 21:59:04 -05:00
doc::modtag(_) => true,
_ => false
2012-02-29 20:10:40 -06:00
}
}
let lteq = !is_mod(item1) || is_mod(item2);
lteq
}
// 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
write_markdown(sorted_doc, writer_factory);
2012-08-01 19:30:05 -05:00
return doc;
2012-02-29 20:10:40 -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() { }"
);
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`"));
assert idx_b < idx_d;
assert idx_d < idx_a;
assert idx_a < idx_c;
}
type ctxt = {
w: writer
};
2012-01-16 02:33:36 -06:00
fn write_markdown(
doc: doc::doc,
+writer_factory: writer_factory
2012-01-16 02:33:36 -06:00
) {
// FIXME #2484: There is easy parallelism to be had here but
// we don't want to spawn too many pandoc processes
do doc.pages.map |page| {
let ctxt = {
w: writer_factory(page)
};
2012-03-06 17:01:40 -06:00
write_page(ctxt, page)
};
}
2012-01-16 02:33:36 -06:00
fn write_page(ctxt: ctxt, page: doc::page) {
2012-03-10 18:21:23 -06:00
write_title(ctxt, page);
2012-08-06 14:34:08 -05:00
match page {
2012-08-03 21:59:04 -05:00
doc::cratepage(doc) => {
write_crate(ctxt, doc);
}
2012-08-03 21:59:04 -05:00
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);
}
}
ctxt.w.write_done();
2012-01-16 23:37:29 -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
for iter::repeat(2u) {
comm::recv(po);
}
}
2012-03-10 18:21:23 -06:00
fn write_title(ctxt: ctxt, page: doc::page) {
2012-08-22 19:24:52 -05:00
ctxt.w.write_line(fmt!("%% %s", make_title(page)));
ctxt.w.write_line(~"");
2012-03-10 18:21:23 -06:00
}
fn make_title(page: doc::page) -> ~str {
2012-08-06 14:34:08 -05:00
let item = match page {
2012-08-03 21:59:04 -05:00
doc::cratepage(cratedoc) => {
2012-03-10 18:21:23 -06:00
doc::modtag(cratedoc.topmod)
}
2012-08-03 21:59:04 -05:00
doc::itempage(itemtag) => {
2012-03-10 18:21:23 -06:00
itemtag
}
};
let title = markdown_pass::header_text(item);
let title = str::replace(title, ~"`", ~"");
2012-08-01 19:30:05 -05:00
return title;
2012-03-10 18:21:23 -06:00
}
#[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 { }");
2012-03-10 18:21:23 -06:00
let doc = page_pass::mk_pass(config::doc_per_mod).f(srv, doc);
write_markdown(doc, writer_factory);
for iter::repeat(2u) {
2012-03-10 18:21:23 -06:00
let (page, markdown) = comm::recv(po);
2012-08-06 14:34:08 -05:00
match page {
2012-08-03 21:59:04 -05:00
doc::cratepage(_) => {
assert str::contains(markdown, ~"% Crate core");
2012-03-10 18:21:23 -06:00
}
2012-08-03 21:59:04 -05:00
doc::itempage(_) => {
assert str::contains(markdown, ~"% Module a");
2012-03-10 18:21:23 -06:00
}
}
}
}
enum hlvl {
h1 = 1,
h2 = 2,
2012-03-09 18:37:19 -06:00
h3 = 3,
h4 = 4
2012-01-16 02:59:18 -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-08-22 19:24:52 -05:00
ctxt.w.write_line(fmt!("%s %s", hashes, title));
ctxt.w.write_line(~"");
}
fn header_kind(doc: doc::itemtag) -> ~str {
2012-08-06 14:34:08 -05:00
match doc {
2012-08-03 21:59:04 -05:00
doc::modtag(_) => {
if doc.id() == syntax::ast::crate_node_id {
~"Crate"
} else {
~"Module"
}
}
2012-08-03 21:59:04 -05:00
doc::nmodtag(_) => {
~"Foreign module"
}
2012-08-03 21:59:04 -05:00
doc::fntag(_) => {
~"Function"
}
2012-08-03 21:59:04 -05:00
doc::consttag(_) => {
~"Const"
}
2012-08-03 21:59:04 -05:00
doc::enumtag(_) => {
~"Enum"
}
2012-08-03 21:59:04 -05:00
doc::traittag(_) => {
~"Interface"
2012-03-02 17:17:13 -06:00
}
2012-08-25 20:19:54 -05:00
doc::impltag(_) => {
~"Implementation"
2012-03-02 17:17:13 -06:00
}
2012-08-03 21:59:04 -05:00
doc::tytag(_) => {
~"Type"
2012-03-02 17:17:13 -06:00
}
}
}
fn header_name(doc: doc::itemtag) -> ~str {
let fullpath = str::connect(doc.path() + ~[doc.name()], ~"::");
2012-08-06 14:34:08 -05:00
match doc {
2012-08-03 21:59:04 -05:00
doc::modtag(_) if doc.id() != syntax::ast::crate_node_id => {
2012-03-02 17:17:13 -06:00
fullpath
}
2012-08-03 21:59:04 -05:00
doc::nmodtag(_) => {
2012-03-02 17:17:13 -06:00
fullpath
}
2012-08-03 21:59:04 -05:00
doc::impltag(doc) => {
assert option::is_some(doc.self_ty);
let self_ty = option::get(doc.self_ty);
let mut trait_part = ~"";
for doc.trait_types.eachi |i, trait_type| {
if i == 0 {
trait_part += ~" of ";
} else {
trait_part += ~", ";
}
trait_part += trait_type;
}
2012-08-22 19:24:52 -05:00
fmt!("%s%s for %s", doc.name(), trait_part, self_ty)
}
2012-08-03 21:59:04 -05:00
_ => {
2012-03-02 17:17:13 -06:00
doc.name()
}
}
}
fn header_text(doc: doc::itemtag) -> ~str {
2012-08-08 19:19:06 -05:00
match doc {
doc::impltag(impldoc) => {
let header_kind = header_kind(doc);
let desc = if impldoc.trait_types.is_empty() {
2012-08-22 19:24:52 -05:00
fmt!("for `%s`", impldoc.self_ty.get())
2012-08-08 19:19:06 -05:00
} else {
2012-08-22 19:24:52 -05:00
fmt!("of `%s` for `%s`", impldoc.trait_types[0],
impldoc.self_ty.get())
2012-08-08 19:19:06 -05:00
};
2012-08-22 19:24:52 -05:00
fmt!("%s %s", header_kind, desc)
2012-08-08 19:19:06 -05:00
}
_ => {
header_text_(header_kind(doc), header_name(doc))
}
}
2012-03-02 17:17:13 -06:00
}
fn header_text_(kind: ~str, name: ~str) -> ~str {
2012-08-22 19:24:52 -05:00
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
) {
write_mod_contents(ctxt, moddoc);
2012-01-16 02:59:18 -06:00
}
fn write_mod(
ctxt: ctxt,
moddoc: doc::moddoc
) {
write_mod_contents(ctxt, moddoc);
2012-01-16 02:59:18 -06:00
}
#[test]
fn should_write_full_path_to_mod() {
let markdown = test::render(~"mod a { mod b { mod c { } } }");
assert str::contains(markdown, ~"# Module `a::b::c`");
}
2012-03-09 18:37:19 -06:00
fn write_common(
ctxt: ctxt,
2012-08-20 14:23:37 -05:00
desc: Option<~str>,
sections: ~[doc::section]
2012-03-09 18:37:19 -06:00
) {
write_desc(ctxt, desc);
write_sections(ctxt, sections);
}
fn write_desc(
ctxt: ctxt,
2012-08-20 14:23:37 -05:00
desc: Option<~str>
2012-03-09 18:37:19 -06:00
) {
2012-08-06 14:34:08 -05:00
match desc {
2012-08-20 14:23:37 -05:00
Some(desc) => {
2012-03-09 18:37:19 -06:00
ctxt.w.write_line(desc);
ctxt.w.write_line(~"");
2012-03-09 18:37:19 -06:00
}
2012-08-20 14:23:37 -05:00
None => ()
2012-03-09 18:37:19 -06:00
}
}
fn write_sections(ctxt: ctxt, sections: ~[doc::section]) {
2012-06-30 18:19:07 -05:00
do vec::iter(sections) |section| {
2012-03-09 18:37:19 -06:00
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(~"");
2012-03-09 18:37:19 -06:00
}
#[test]
fn should_write_sections() {
let markdown = test::render(
~"#[doc = \"\
2012-03-09 18:37:19 -06:00
# Header\n\
Body\"]\
mod a { }");
assert str::contains(markdown, ~"#### Header\n\nBody\n\n");
2012-03-09 18:37:19 -06:00
}
2012-01-16 02:59:18 -06:00
fn write_mod_contents(
ctxt: ctxt,
doc: doc::moddoc
2012-01-16 02:59:18 -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-06-30 18:19:07 -05:00
for doc.items.each |itemtag| {
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-08-06 14:34:08 -05:00
match doc {
2012-08-03 21:59:04 -05:00
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::traittag(traitdoc) => write_trait(ctxt, traitdoc),
doc::impltag(impldoc) => write_impl(ctxt, impldoc),
doc::tytag(tydoc) => write_type(ctxt, tydoc)
}
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 {
2012-08-06 14:34:08 -05:00
match doc {
2012-08-03 21:59:04 -05:00
doc::modtag(_) | doc::nmodtag(_) => h1,
_ => h2
2012-03-10 18:21:23 -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) {
2012-08-01 19:30:05 -05:00
return;
2012-03-02 18:20:36 -06:00
}
2012-03-02 18:27:31 -06:00
2012-06-30 18:19:07 -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;
if option::is_some(entry.brief) {
2012-08-22 19:24:52 -05:00
ctxt.w.write_line(fmt!("* [%s](%s) - %s",
header, id, option::get(entry.brief)));
} else {
2012-08-22 19:24:52 -05:00
ctxt.w.write_line(fmt!("* [%s](%s)", header, id));
}
2012-03-02 18:20:36 -06:00
}
ctxt.w.write_line(~"");
2012-03-02 18:20:36 -06:00
}
#[test]
fn should_write_index() {
let markdown = test::render(~"mod a { } mod b { }");
2012-03-02 18:20:36 -06:00
assert str::contains(
markdown,
~"\n\n* [Module `a`](#module-a)\n\
* [Module `b`](#module-b)\n\n"
2012-03-02 18:20:36 -06:00
);
}
#[test]
fn should_write_index_brief() {
let markdown = test::render(~"#[doc = \"test\"] mod a { }");
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-02 18:20:36 -06:00
}
#[test]
fn should_write_index_for_foreign_mods() {
let markdown = test::render(~"extern mod a { fn a(); }");
assert str::contains(
markdown,
~"\n\n* [Function `a`](#function-a)\n\n"
);
}
fn write_nmod(ctxt: ctxt, doc: doc::nmoddoc) {
write_common(ctxt, doc.desc(), doc.sections());
if option::is_some(doc.index) {
write_index(ctxt, option::get(doc.index));
}
2012-06-30 18:19:07 -05:00
for doc.fns.each |fndoc| {
2012-03-10 18:43:38 -06:00
write_item_header(ctxt, doc::fntag(fndoc));
write_fn(ctxt, fndoc);
}
}
#[test]
fn should_write_foreign_mods() {
let markdown = test::render(~"#[doc = \"test\"] extern mod a { }");
assert str::contains(markdown, ~"Foreign module `a`");
assert str::contains(markdown, ~"test");
}
#[test]
fn should_write_foreign_fns() {
let markdown = test::render(
~"extern mod a { #[doc = \"test\"] fn a(); }");
assert str::contains(markdown, ~"test");
}
2012-03-10 18:43:38 -06:00
#[test]
fn should_write_foreign_fn_headers() {
let markdown = test::render(
~"extern mod a { #[doc = \"test\"] fn a(); }");
assert str::contains(markdown, ~"## Function `a`");
2012-03-10 18:43:38 -06:00
}
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,
doc.desc(),
doc.sections()
2012-01-30 22:53:52 -06:00
);
}
fn write_fnlike(
ctxt: ctxt,
2012-08-20 14:23:37 -05:00
sig: Option<~str>,
desc: Option<~str>,
sections: ~[doc::section]
2012-01-30 22:53:52 -06:00
) {
write_sig(ctxt, sig);
write_common(ctxt, desc, sections);
2012-01-18 01:39:22 -06:00
}
2012-08-20 14:23:37 -05:00
fn write_sig(ctxt: ctxt, sig: Option<~str>) {
2012-08-06 14:34:08 -05:00
match sig {
2012-08-20 14:23:37 -05:00
Some(sig) => {
ctxt.w.write_line(code_block_indent(sig));
ctxt.w.write_line(~"");
}
2012-08-20 14:23:37 -05:00
None => fail ~"unimplemented"
}
}
fn code_block_indent(s: ~str) -> ~str {
let lines = str::lines_any(s);
2012-08-22 19:24:52 -05:00
let indented = vec::map(lines, |line| fmt!(" %s", line) );
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() { }");
assert str::contains(markdown, ~"## Function `func`");
2012-01-27 00:45:37 -06:00
}
#[test]
fn should_write_the_function_signature() {
let markdown = test::render(~"#[doc = \"f\"] fn a() { }");
assert str::contains(markdown, ~"\n fn a()\n");
}
#[test]
fn should_insert_blank_line_after_fn_signature() {
let markdown = test::render(~"#[doc = \"f\"] fn a() { }");
assert str::contains(markdown, ~"fn a()\n\n");
}
#[test]
fn should_correctly_indent_fn_signature() {
let doc = test::create_doc(~"fn a() { }");
let doc = doc::doc_({
pages: ~[
doc::cratepage({
topmod: doc::moddoc_({
items: ~[doc::fntag({
2012-09-04 15:29:32 -05:00
sig: Some(~"line 1\nline 2"),
.. doc.cratemod().fns()[0]
})],
.. *doc.cratemod()
}),
.. doc.cratedoc()
})
]
});
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() {
let markdown = test::render(~"fn a() { }");
assert str::contains(markdown, ~"Function `a`\n\n fn a()");
2012-01-27 00:45:37 -06:00
}
2012-01-24 02:51:19 -06:00
fn write_const(
ctxt: ctxt,
doc: doc::constdoc
) {
write_sig(ctxt, doc.sig);
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;");
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(
~"#[doc = \"b\"]\
2012-01-24 02:51:19 -06:00
const a: bool = true;");
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
) {
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 }");
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(
~"#[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,
docs: ~[doc::variantdoc]
2012-01-25 22:55:55 -06:00
) {
if vec::is_empty(docs) {
2012-08-01 19:30:05 -05:00
return;
2012-01-25 22:55:55 -06:00
}
write_header_(ctxt, h4, ~"Variants");
2012-01-25 22:55:55 -06:00
2012-06-30 18:19:07 -05:00
vec::iter(docs, |variant| write_variant(ctxt, variant) );
2012-01-25 22:55:55 -06:00
ctxt.w.write_line(~"");
2012-01-25 22:55:55 -06:00
}
fn write_variant(ctxt: ctxt, doc: doc::variantdoc) {
assert option::is_some(doc.sig);
let sig = option::get(doc.sig);
2012-08-06 14:34:08 -05:00
match doc.desc {
2012-08-20 14:23:37 -05:00
Some(desc) => {
2012-08-22 19:24:52 -05:00
ctxt.w.write_line(fmt!("* `%s` - %s", sig, desc));
2012-01-25 22:55:55 -06:00
}
2012-08-20 14:23:37 -05:00
None => {
2012-08-22 19:24:52 -05:00
ctxt.w.write_line(fmt!("* `%s`", sig));
2012-01-25 22:55:55 -06:00
}
}
}
#[test]
fn should_write_variant_list() {
let markdown = test::render(
~"enum a { \
2012-01-25 22:55:55 -06:00
#[doc = \"test\"] b, \
#[doc = \"test\"] c }");
assert str::contains(
markdown,
~"\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 }");
2012-01-25 22:55:55 -06:00
assert str::contains(
markdown,
~"\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) }");
2012-01-25 22:55:55 -06:00
assert str::contains(
markdown,
~"\n\n#### Variants\n\
2012-01-25 22:55:55 -06:00
\n* `b(int)`\
\n* `c(int)` - a\n\n");
}
fn write_trait(ctxt: ctxt, doc: doc::traitdoc) {
write_common(ctxt, doc.desc(), doc.sections());
2012-01-30 22:53:52 -06:00
write_methods(ctxt, doc.methods);
}
fn write_methods(ctxt: ctxt, docs: ~[doc::methoddoc]) {
2012-06-30 18:19:07 -05:00
do vec::iter(docs) |doc| { write_method(ctxt, doc) }
2012-01-30 22:53:52 -06:00
}
fn write_method(ctxt: ctxt, doc: doc::methoddoc) {
write_header_(ctxt, h3, header_text_(~"Method", doc.name));
2012-01-30 22:53:52 -06:00
write_fnlike(
ctxt,
doc.sig,
doc.desc,
doc.sections
2012-01-30 22:53:52 -06:00
);
}
#[test]
fn should_write_trait_header() {
let markdown = test::render(~"trait i { fn a(); }");
assert str::contains(markdown, ~"## Interface `i`");
2012-01-30 22:53:52 -06:00
}
#[test]
fn should_write_trait_desc() {
2012-01-30 22:53:52 -06:00
let markdown = test::render(
~"#[doc = \"desc\"] trait i { fn a(); }");
assert str::contains(markdown, ~"desc");
2012-01-30 22:53:52 -06:00
}
#[test]
fn should_write_trait_method_header() {
2012-01-30 22:53:52 -06:00
let markdown = test::render(
~"trait i { fn a(); }");
assert str::contains(markdown, ~"### Method `a`");
2012-01-30 22:53:52 -06:00
}
#[test]
fn should_write_trait_method_signature() {
2012-01-30 22:53:52 -06:00
let markdown = test::render(
~"trait i { fn a(); }");
assert str::contains(markdown, ~"\n fn a()");
2012-01-30 22:53:52 -06:00
}
2012-01-31 22:54:46 -06:00
fn write_impl(ctxt: ctxt, doc: doc::impldoc) {
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() {
2012-08-08 19:19:06 -05:00
let markdown = test::render(~"impl int { fn a() { } }");
assert str::contains(markdown, ~"## Implementation for `int`");
2012-01-31 22:54:46 -06:00
}
#[test]
fn should_write_impl_header_with_trait() {
2012-08-08 19:19:06 -05:00
let markdown = test::render(~"impl int: j { fn a() { } }");
assert str::contains(markdown, ~"## Implementation of `j` for `int`");
2012-01-31 22:54:46 -06:00
}
#[test]
fn should_write_impl_desc() {
let markdown = test::render(
2012-08-08 19:19:06 -05:00
~"#[doc = \"desc\"] impl int { fn a() { } }");
assert str::contains(markdown, ~"desc");
2012-01-31 22:54:46 -06:00
}
#[test]
fn should_write_impl_method_header() {
let markdown = test::render(
2012-08-08 19:19:06 -05:00
~"impl int { fn a() { } }");
assert str::contains(markdown, ~"### Method `a`");
2012-01-31 22:54:46 -06:00
}
#[test]
fn should_write_impl_method_signature() {
let markdown = test::render(
2012-08-08 19:19:06 -05:00
~"impl int { fn a() { } }");
assert str::contains(markdown, ~"\n fn a()");
2012-01-31 22:54:46 -06:00
}
2012-02-02 00:41:41 -06:00
fn write_type(
ctxt: ctxt,
doc: doc::tydoc
) {
write_sig(ctxt, doc.sig);
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`");
2012-02-02 00:41:41 -06:00
}
#[test]
fn should_write_type_desc() {
let markdown = test::render(
~"#[doc = \"desc\"] type t = int;");
assert str::contains(markdown, ~"\n\ndesc\n\n");
2012-02-02 00:41:41 -06:00
}
#[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-02-02 00:41:41 -06:00
}
2012-01-16 02:33:36 -06:00
#[cfg(test)]
mod test {
fn render(source: ~str) -> ~str {
let (srv, doc) = create_doc_srv(source);
let markdown = write_markdown_str_srv(srv, doc);
2012-08-22 19:24:52 -05:00
debug!("markdown: %s", markdown);
markdown
}
fn create_doc_srv(source: ~str) -> (astsrv::srv, doc::doc) {
2012-06-30 18:19:07 -05:00
do astsrv::from_str(source) |srv| {
let config = {
2012-09-04 15:29:32 -05:00
output_style: config::doc_per_crate,
.. config::default_config(&Path("whatever"))
};
let doc = extract::from_srv(srv, ~"");
2012-08-22 19:24:52 -05:00
debug!("doc (extract): %?", doc);
2012-02-27 20:07:16 -06:00
let doc = tystr_pass::mk_pass().f(srv, doc);
2012-08-22 19:24:52 -05:00
debug!("doc (tystr): %?", doc);
2012-02-27 20:07:16 -06:00
let doc = path_pass::mk_pass().f(srv, doc);
2012-08-22 19:24:52 -05:00
debug!("doc (path): %?", doc);
2012-02-27 20:07:16 -06:00
let doc = attr_pass::mk_pass().f(srv, doc);
2012-08-22 19:24:52 -05:00
debug!("doc (attr): %?", doc);
2012-03-09 20:12:15 -06:00
let doc = desc_to_brief_pass::mk_pass().f(srv, doc);
2012-08-22 19:24:52 -05:00
debug!("doc (desc_to_brief): %?", doc);
2012-03-09 18:37:19 -06:00
let doc = unindent_pass::mk_pass().f(srv, doc);
2012-08-22 19:24:52 -05:00
debug!("doc (unindent): %?", doc);
2012-03-09 18:37:19 -06:00
let doc = sectionalize_pass::mk_pass().f(srv, doc);
2012-08-22 19:24:52 -05:00
debug!("doc (trim): %?", doc);
2012-03-09 18:37:19 -06:00
let doc = trim_pass::mk_pass().f(srv, doc);
2012-08-22 19:24:52 -05:00
debug!("doc (sectionalize): %?", doc);
let doc = markdown_index_pass::mk_pass(config).f(srv, doc);
2012-08-22 19:24:52 -05:00
debug!("doc (index): %?", doc);
(srv, doc)
}
}
fn create_doc(source: ~str) -> doc::doc {
let (_, doc) = create_doc_srv(source);
doc
}
2012-01-16 02:33:36 -06:00
fn write_markdown_str(
doc: doc::doc
) -> ~str {
let (writer_factory, po) = markdown_writer::future_writer_factory();
write_markdown(doc, writer_factory);
2012-08-01 19:30:05 -05:00
return comm::recv(po).second();
2012-01-16 02:33:36 -06:00
}
fn write_markdown_str_srv(
srv: astsrv::srv,
doc: doc::doc
) -> ~str {
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-08-01 19:30:05 -05:00
return comm::recv(po).second();
}
2012-01-17 01:10:25 -06:00
#[test]
fn write_markdown_should_write_mod_headers() {
let markdown = render(~"mod moo { }");
assert str::contains(markdown, ~"# Module `moo`");
2012-01-17 01:10:25 -06:00
}
#[test]
fn should_leave_blank_line_after_header() {
let markdown = render(~"mod morp { }");
assert str::contains(markdown, ~"Module `morp`\n\n");
}
}