rust/src/librustdoc/markdown_pass.rs

927 lines
23 KiB
Rust
Raw Normal View History

// Copyright 2012 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.
//! Generate markdown from a document tree
2012-01-17 19:44:32 -06:00
use core::prelude::*;
use astsrv;
use attr_pass;
use config;
use desc_to_brief_pass;
2012-09-18 18:48:40 -05:00
use doc::ItemUtils;
use doc;
use extract;
use fold;
use markdown_index_pass;
use markdown_pass;
2012-09-18 18:48:40 -05:00
use markdown_writer::Writer;
use markdown_writer::WriterUtils;
use markdown_writer::WriterFactory;
use markdown_writer;
use page_pass;
use pass::Pass;
use path_pass;
use sectionalize_pass;
use sort_pass;
use trim_pass;
use unindent_pass;
use core::iter;
use core::oldcomm;
use core::str;
use core::vec;
use std::par;
use syntax;
2013-01-30 21:32:36 -06:00
pub fn mk_pass(writer_factory: WriterFactory) -> Pass {
2012-09-19 00:43:54 -05:00
let f = fn~(move writer_factory,
2013-01-30 21:32:36 -06:00
srv: astsrv::Srv, doc: doc::Doc) -> doc::Doc {
run(srv, doc, copy writer_factory)
2012-02-27 20:07:16 -06:00
};
Pass {
name: ~"markdown",
2012-09-19 00:43:54 -05:00
f: move f
}
}
2012-02-29 20:10:40 -06:00
fn run(
2012-09-18 18:48:40 -05:00
srv: astsrv::Srv,
2013-01-30 21:32:36 -06:00
doc: doc::Doc,
writer_factory: WriterFactory
2012-09-18 18:48:40 -05:00
) -> doc::Doc {
2012-02-29 20:10:40 -06:00
2012-09-18 18:48:40 -05: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-09-18 18:48:40 -05:00
doc::ModTag(_) => true,
2012-08-03 21:59:04 -05:00
_ => 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
2013-01-30 15:14:35 -06:00
).f)(srv, copy doc);
2012-02-29 20:10:40 -06:00
2012-09-19 00:43:54 -05:00
write_markdown(sorted_doc, move 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() { }"
);
2012-09-21 21:37:57 -05:00
let idx_a = str::find_str(markdown, ~"# Module `a`").get();
let idx_b = str::find_str(markdown, ~"## Function `b`").get();
let idx_c = str::find_str(markdown, ~"# Module `c`").get();
let idx_d = str::find_str(markdown, ~"## Function `d`").get();
assert idx_b < idx_d;
assert idx_d < idx_a;
assert idx_a < idx_c;
}
2013-01-30 21:45:39 -06:00
struct Ctxt {
2012-09-18 18:48:40 -05:00
w: Writer
2013-01-30 21:45:39 -06:00
}
pub fn write_markdown(
2013-01-30 21:32:36 -06:00
doc: doc::Doc,
writer_factory: WriterFactory
2012-01-16 02:33:36 -06:00
) {
2012-10-11 18:42:40 -05:00
// There is easy parallelism to be had here, but
// we don't want to spawn too many pandoc processes.
// (See #2484, which is closed.)
do doc.pages.map |page| {
2013-01-30 21:45:39 -06:00
let ctxt = Ctxt {
2013-01-30 15:14:35 -06:00
w: writer_factory(copy *page)
};
2012-11-21 00:36:32 -06:00
write_page(&ctxt, page)
2012-03-06 17:01:40 -06:00
};
}
2012-01-16 02:33:36 -06:00
2012-11-21 00:36:32 -06:00
fn write_page(ctxt: &Ctxt, page: &doc::Page) {
2013-01-30 15:14:35 -06:00
write_title(ctxt, copy *page);
match copy *page {
2012-09-18 18:48:40 -05:00
doc::CratePage(doc) => {
write_crate(ctxt, doc);
}
2012-09-18 18:48:40 -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::DocPerMod).f)(srv, doc);
2012-09-19 00:43:54 -05:00
write_markdown(doc, move writer_factory);
// We expect two pages to have been written
2012-09-19 00:43:54 -05:00
for iter::repeat(2) {
2012-12-13 16:18:47 -06:00
oldcomm::recv(po);
}
}
2013-01-30 21:32:36 -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
}
2013-01-30 21:32:36 -06:00
fn make_title(page: doc::Page) -> ~str {
2012-08-06 14:34:08 -05:00
let item = match page {
2012-09-18 18:48:40 -05:00
doc::CratePage(CrateDoc) => {
2013-01-30 15:14:35 -06:00
doc::ModTag(copy CrateDoc.topmod)
2012-03-10 18:21:23 -06:00
}
2012-09-18 18:48:40 -05:00
doc::ItemPage(ItemTag) => {
ItemTag
2012-03-10 18:21:23 -06:00
}
};
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 { }");
let doc = (page_pass::mk_pass(config::DocPerMod).f)(srv, doc);
2012-09-19 00:43:54 -05:00
write_markdown(doc, move writer_factory);
for iter::repeat(2) {
2012-12-13 16:18:47 -06:00
let (page, markdown) = oldcomm::recv(po);
2012-08-06 14:34:08 -05:00
match page {
2012-09-18 18:48:40 -05:00
doc::CratePage(_) => {
assert str::contains(markdown, ~"% Crate core");
2012-03-10 18:21:23 -06:00
}
2012-09-18 18:48:40 -05:00
doc::ItemPage(_) => {
assert str::contains(markdown, ~"% Module a");
2012-03-10 18:21:23 -06:00
}
}
}
}
2012-09-18 18:48:40 -05:00
enum Hlvl {
H1 = 1,
H2 = 2,
H3 = 3,
H4 = 4
2012-01-16 02:59:18 -06:00
}
2013-01-30 21:32:36 -06:00
fn write_header(ctxt: &Ctxt, lvl: Hlvl, doc: doc::ItemTag) {
let text = header_text(doc);
write_header_(ctxt, lvl, text);
}
2013-01-30 21:32:36 -06:00
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(~"");
}
2013-01-30 21:32:36 -06:00
pub fn header_kind(doc: doc::ItemTag) -> ~str {
2012-08-06 14:34:08 -05:00
match doc {
2012-09-18 18:48:40 -05:00
doc::ModTag(_) => {
if doc.id() == syntax::ast::crate_node_id {
~"Crate"
} else {
~"Module"
}
}
2012-09-18 18:48:40 -05:00
doc::NmodTag(_) => {
~"Foreign module"
}
2012-09-18 18:48:40 -05:00
doc::FnTag(_) => {
~"Function"
}
2012-09-18 18:48:40 -05:00
doc::ConstTag(_) => {
~"Const"
}
2012-09-18 18:48:40 -05:00
doc::EnumTag(_) => {
~"Enum"
}
2012-09-18 18:48:40 -05:00
doc::TraitTag(_) => {
~"Trait"
2012-03-02 17:17:13 -06:00
}
2012-09-18 18:48:40 -05:00
doc::ImplTag(_) => {
~"Implementation"
2012-03-02 17:17:13 -06:00
}
2012-09-18 18:48:40 -05:00
doc::TyTag(_) => {
~"Type"
2012-03-02 17:17:13 -06:00
}
2012-09-19 16:37:43 -05:00
doc::StructTag(_) => {
~"Struct"
}
2012-03-02 17:17:13 -06:00
}
}
2013-01-30 21:32:36 -06:00
pub fn header_name(doc: doc::ItemTag) -> ~str {
let fullpath = str::connect(doc.path() + ~[doc.name()], ~"::");
2013-01-30 15:14:35 -06:00
match &doc {
&doc::ModTag(_) if doc.id() != syntax::ast::crate_node_id => {
2012-03-02 17:17:13 -06:00
fullpath
}
2013-01-30 15:14:35 -06:00
&doc::NmodTag(_) => {
2012-03-02 17:17:13 -06:00
fullpath
}
2013-01-30 15:14:35 -06:00
&doc::ImplTag(ref doc) => {
2012-09-21 21:37:57 -05:00
assert doc.self_ty.is_some();
2013-01-30 15:14:35 -06:00
let self_ty = (&doc.self_ty).get();
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;
}
fmt!("%s for %s", trait_part, self_ty)
}
2012-08-03 21:59:04 -05:00
_ => {
2012-03-02 17:17:13 -06:00
doc.name()
}
}
}
2013-01-30 21:32:36 -06:00
pub fn header_text(doc: doc::ItemTag) -> ~str {
2013-01-30 15:14:35 -06:00
match &doc {
&doc::ImplTag(ref ImplDoc) => {
let header_kind = header_kind(copy doc);
2012-09-18 18:48:40 -05:00
let desc = if ImplDoc.trait_types.is_empty() {
2013-01-30 15:14:35 -06:00
fmt!("for `%s`", (&ImplDoc.self_ty).get())
2012-08-08 19:19:06 -05:00
} else {
2012-09-18 18:48:40 -05:00
fmt!("of `%s` for `%s`", ImplDoc.trait_types[0],
2013-01-30 15:14:35 -06:00
(&ImplDoc.self_ty).get())
2012-08-08 19:19:06 -05:00
};
return fmt!("%s %s", header_kind, desc);
2012-08-08 19:19:06 -05:00
}
_ => {}
2012-08-08 19:19:06 -05:00
}
header_text_(header_kind(copy doc),
header_name(doc))
2012-03-02 17:17:13 -06:00
}
2012-11-21 00:36:32 -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(
2012-11-21 00:36:32 -06:00
ctxt: &Ctxt,
2013-01-30 21:32:36 -06:00
doc: doc::CrateDoc
2012-01-16 23:37:29 -06:00
) {
2013-01-30 15:14:35 -06:00
write_top_module(ctxt, copy doc.topmod);
2012-01-16 17:44:10 -06:00
}
2012-01-16 02:59:18 -06:00
fn write_top_module(
2012-11-21 00:36:32 -06:00
ctxt: &Ctxt,
2013-01-30 21:32:36 -06:00
ModDoc: doc::ModDoc
2012-01-16 02:59:18 -06:00
) {
2012-09-18 18:48:40 -05:00
write_mod_contents(ctxt, ModDoc);
2012-01-16 02:59:18 -06:00
}
fn write_mod(
2012-11-21 00:36:32 -06:00
ctxt: &Ctxt,
2013-01-30 21:32:36 -06:00
ModDoc: doc::ModDoc
2012-01-16 02:59:18 -06:00
) {
2012-09-18 18:48:40 -05:00
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-12-13 16:18:47 -06:00
fn write_oldcommon(
2012-11-21 00:36:32 -06:00
ctxt: &Ctxt,
2013-01-30 21:32:36 -06:00
desc: Option<~str>,
2012-11-21 00:36:32 -06:00
sections: &[doc::Section]
2012-03-09 18:37:19 -06:00
) {
write_desc(ctxt, desc);
write_sections(ctxt, sections);
}
fn write_desc(
2012-11-21 00:36:32 -06:00
ctxt: &Ctxt,
2013-01-30 21:32:36 -06: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
}
}
2012-11-21 00:36:32 -06:00
fn write_sections(ctxt: &Ctxt, sections: &[doc::Section]) {
for vec::each(sections) |section| {
2013-01-30 15:14:35 -06:00
write_section(ctxt, copy *section);
2012-03-09 18:37:19 -06:00
}
}
2013-01-30 21:32:36 -06:00
fn write_section(ctxt: &Ctxt, section: doc::Section) {
2013-01-30 15:14:35 -06:00
write_header_(ctxt, H4, copy section.header);
ctxt.w.write_line(copy 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(
2012-11-21 00:36:32 -06:00
ctxt: &Ctxt,
2013-01-30 21:32:36 -06:00
doc: doc::ModDoc
2012-01-16 02:59:18 -06:00
) {
2012-12-13 16:18:47 -06:00
write_oldcommon(ctxt, doc.desc(), doc.sections());
2012-09-21 21:37:57 -05:00
if doc.index.is_some() {
2013-01-30 15:14:35 -06:00
write_index(ctxt, (&doc.index).get());
2012-03-02 18:20:36 -06:00
}
for doc.items.each |itemTag| {
2013-01-30 15:14:35 -06:00
write_item(ctxt, copy *itemTag);
}
}
2013-01-30 21:32:36 -06:00
fn write_item(ctxt: &Ctxt, doc: doc::ItemTag) {
2012-03-10 18:21:23 -06:00
write_item_(ctxt, doc, true);
}
2013-01-30 21:32:36 -06:00
fn write_item_no_header(ctxt: &Ctxt, doc: doc::ItemTag) {
2012-03-10 18:21:23 -06:00
write_item_(ctxt, doc, false);
}
2013-01-30 21:32:36 -06:00
fn write_item_(ctxt: &Ctxt, doc: doc::ItemTag, write_header: bool) {
2012-03-10 18:21:23 -06:00
if write_header {
2013-01-30 15:14:35 -06:00
write_item_header(ctxt, copy doc);
2012-03-10 18:21:23 -06:00
}
2012-08-06 14:34:08 -05:00
match doc {
2012-09-18 18:48:40 -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),
2012-09-19 16:37:43 -05:00
doc::TyTag(TyDoc) => write_type(ctxt, TyDoc),
doc::StructTag(StructDoc) => write_struct(ctxt, StructDoc),
}
2012-01-16 02:59:18 -06:00
}
2013-01-30 21:32:36 -06:00
fn write_item_header(ctxt: &Ctxt, doc: doc::ItemTag) {
write_header(ctxt, item_header_lvl(&doc), doc);
2012-03-10 18:21:23 -06:00
}
2013-01-30 21:32:36 -06:00
fn item_header_lvl(doc: &doc::ItemTag) -> Hlvl {
2012-08-06 14:34:08 -05:00
match doc {
2013-01-30 21:32:36 -06:00
&doc::ModTag(_) | &doc::NmodTag(_) => H1,
2012-09-18 18:48:40 -05:00
_ => 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");
}
2013-01-30 21:32:36 -06:00
fn write_index(ctxt: &Ctxt, index: doc::Index) {
2012-03-02 18:20:36 -06:00
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);
2013-01-30 15:14:35 -06:00
let id = copy entry.link;
2012-09-21 21:37:57 -05:00
if entry.brief.is_some() {
2012-08-22 19:24:52 -05:00
ctxt.w.write_line(fmt!("* [%s](%s) - %s",
2013-01-30 15:14:35 -06:00
header, id, (&entry.brief).get()));
} 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"
);
}
2013-01-30 21:32:36 -06:00
fn write_nmod(ctxt: &Ctxt, doc: doc::NmodDoc) {
2012-12-13 16:18:47 -06:00
write_oldcommon(ctxt, doc.desc(), doc.sections());
2012-09-21 21:37:57 -05:00
if doc.index.is_some() {
2013-01-30 15:14:35 -06:00
write_index(ctxt, (&doc.index).get());
}
2012-09-18 18:48:40 -05:00
for doc.fns.each |FnDoc| {
2013-01-30 15:14:35 -06:00
write_item_header(ctxt, doc::FnTag(copy *FnDoc));
write_fn(ctxt, copy *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(
2012-11-21 00:36:32 -06:00
ctxt: &Ctxt,
2013-01-30 21:32:36 -06:00
doc: doc::FnDoc
2012-01-16 02:59:18 -06:00
) {
2012-01-30 22:53:52 -06:00
write_fnlike(
ctxt,
2013-01-30 15:14:35 -06:00
copy doc.sig,
doc.desc(),
doc.sections()
2012-01-30 22:53:52 -06:00
);
}
fn write_fnlike(
2012-11-21 00:36:32 -06:00
ctxt: &Ctxt,
2013-01-30 21:32:36 -06:00
sig: Option<~str>,
desc: Option<~str>,
2012-11-21 00:36:32 -06:00
sections: &[doc::Section]
2012-01-30 22:53:52 -06:00
) {
write_sig(ctxt, sig);
2012-12-13 16:18:47 -06:00
write_oldcommon(ctxt, desc, sections);
2012-01-18 01:39:22 -06:00
}
2013-01-30 21:32:36 -06: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(~"");
}
None => die!(~"unimplemented")
}
}
2013-01-30 21:32:36 -06:00
fn code_block_indent(s: ~str) -> ~str {
let lines = str::lines_any(s);
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(doc::CrateDoc{
topmod: doc::ModDoc{
items: ~[doc::FnTag(doc::SimpleItemDoc{
2012-09-04 15:29:32 -05:00
sig: Some(~"line 1\nline 2"),
2013-01-30 15:14:35 -06:00
.. copy doc.cratemod().fns()[0]
2012-09-04 15:29:32 -05:00
})],
.. doc.cratemod()
},
2012-09-18 18:48:40 -05:00
.. 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(
2012-11-21 00:36:32 -06:00
ctxt: &Ctxt,
2013-01-30 21:32:36 -06:00
doc: doc::ConstDoc
2012-01-24 02:51:19 -06:00
) {
2013-01-30 15:14:35 -06:00
write_sig(ctxt, copy doc.sig);
2012-12-13 16:18:47 -06:00
write_oldcommon(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(
2012-11-21 00:36:32 -06:00
ctxt: &Ctxt,
2013-01-30 21:32:36 -06:00
doc: doc::EnumDoc
2012-01-25 22:55:55 -06:00
) {
2012-12-13 16:18:47 -06:00
write_oldcommon(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(
2012-11-21 00:36:32 -06:00
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
}
2012-09-18 18:48:40 -05:00
write_header_(ctxt, H4, ~"Variants");
2012-01-25 22:55:55 -06:00
for vec::each(docs) |variant| {
2013-01-30 15:14:35 -06:00
write_variant(ctxt, copy *variant);
}
2012-01-25 22:55:55 -06:00
ctxt.w.write_line(~"");
2012-01-25 22:55:55 -06:00
}
2013-01-30 21:32:36 -06:00
fn write_variant(ctxt: &Ctxt, doc: doc::VariantDoc) {
2012-09-21 21:37:57 -05:00
assert doc.sig.is_some();
2013-01-30 15:14:35 -06:00
let sig = (&doc.sig).get();
match copy 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");
}
2013-01-30 21:32:36 -06:00
fn write_trait(ctxt: &Ctxt, doc: doc::TraitDoc) {
2012-12-13 16:18:47 -06:00
write_oldcommon(ctxt, doc.desc(), doc.sections());
2012-01-30 22:53:52 -06:00
write_methods(ctxt, doc.methods);
}
2012-11-21 00:36:32 -06:00
fn write_methods(ctxt: &Ctxt, docs: &[doc::MethodDoc]) {
for vec::each(docs) |doc| {
2013-01-30 15:14:35 -06:00
write_method(ctxt, copy *doc);
}
2012-01-30 22:53:52 -06:00
}
2013-01-30 21:32:36 -06:00
fn write_method(ctxt: &Ctxt, doc: doc::MethodDoc) {
2012-09-18 18:48:40 -05:00
write_header_(ctxt, H3, header_text_(~"Method", doc.name));
2012-01-30 22:53:52 -06:00
write_fnlike(
ctxt,
2013-01-30 15:14:35 -06:00
copy doc.sig,
copy 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, ~"## Trait `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
}
2013-01-30 21:32:36 -06:00
fn write_impl(ctxt: &Ctxt, doc: doc::ImplDoc) {
2012-12-13 16:18:47 -06:00
write_oldcommon(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(
2012-11-21 00:36:32 -06:00
ctxt: &Ctxt,
2013-01-30 21:32:36 -06:00
doc: doc::TyDoc
2012-02-02 00:41:41 -06:00
) {
2013-01-30 15:14:35 -06:00
write_sig(ctxt, copy doc.sig);
2012-12-13 16:18:47 -06:00
write_oldcommon(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-09-19 16:37:43 -05:00
fn write_struct(
2012-11-21 00:36:32 -06:00
ctxt: &Ctxt,
2013-01-30 21:32:36 -06:00
doc: doc::StructDoc
2012-09-19 16:37:43 -05:00
) {
2013-01-30 15:14:35 -06:00
write_sig(ctxt, copy doc.sig);
2012-12-13 16:18:47 -06:00
write_oldcommon(ctxt, doc.desc(), doc.sections());
2012-09-19 16:37:43 -05:00
}
#[test]
fn should_write_struct_header() {
let markdown = test::render(~"struct S { field: () }");
assert str::contains(markdown, ~"## Struct `S`\n\n");
}
2012-01-16 02:33:36 -06:00
#[cfg(test)]
mod test {
use astsrv;
use attr_pass;
use config;
use desc_to_brief_pass;
use doc;
use extract;
use markdown_index_pass;
use markdown_pass::{mk_pass, write_markdown};
use markdown_writer;
use path_pass;
use sectionalize_pass;
use trim_pass;
use tystr_pass;
use unindent_pass;
use core::oldcomm;
use core::path::Path;
use core::str;
2013-01-30 21:32:36 -06:00
pub 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
}
2013-01-30 21:32:36 -06:00
pub 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 = config::Config {
2012-09-18 18:48:40 -05:00
output_style: config::DocPerCrate,
2012-09-04 15:29:32 -05:00
.. config::default_config(&Path("whatever"))
};
let doc = extract::from_srv(srv, ~"");
2012-08-22 19:24:52 -05:00
debug!("doc (extract): %?", doc);
let doc = (tystr_pass::mk_pass().f)(srv, doc);
2012-08-22 19:24:52 -05:00
debug!("doc (tystr): %?", doc);
let doc = (path_pass::mk_pass().f)(srv, doc);
2012-08-22 19:24:52 -05:00
debug!("doc (path): %?", doc);
let doc = (attr_pass::mk_pass().f)(srv, doc);
2012-08-22 19:24:52 -05:00
debug!("doc (attr): %?", doc);
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);
let doc = (unindent_pass::mk_pass().f)(srv, doc);
2012-08-22 19:24:52 -05:00
debug!("doc (unindent): %?", doc);
let doc = (sectionalize_pass::mk_pass().f)(srv, doc);
2012-08-22 19:24:52 -05:00
debug!("doc (trim): %?", doc);
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)
}
}
2013-01-30 21:32:36 -06:00
pub fn create_doc(source: ~str) -> doc::Doc {
let (_, doc) = create_doc_srv(source);
doc
}
pub fn write_markdown_str(
2013-01-30 21:32:36 -06:00
doc: doc::Doc
) -> ~str {
let (writer_factory, po) = markdown_writer::future_writer_factory();
2012-09-19 00:43:54 -05:00
write_markdown(doc, move writer_factory);
2012-12-13 16:18:47 -06:00
return oldcomm::recv(po).second();
2012-01-16 02:33:36 -06:00
}
pub fn write_markdown_str_srv(
2012-09-18 18:48:40 -05:00
srv: astsrv::Srv,
2013-01-30 21:32:36 -06:00
doc: doc::Doc
) -> ~str {
let (writer_factory, po) = markdown_writer::future_writer_factory();
2012-09-19 00:43:54 -05:00
let pass = mk_pass(move writer_factory);
(pass.f)(srv, doc);
2012-12-13 16:18:47 -06:00
return oldcomm::recv(po).second();
}
2012-01-17 01:10:25 -06:00
#[test]
pub 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]
pub fn should_leave_blank_line_after_header() {
let markdown = render(~"mod morp { }");
assert str::contains(markdown, ~"Module `morp`\n\n");
}
}