rustdoc: Add a pass that extracts brief docs from long docs

If the first paragraph of documentation is short then it will be used as the
brief description.
This commit is contained in:
Brian Anderson 2012-01-24 17:19:27 -08:00
parent 2d84b481de
commit cb44fa2a21
3 changed files with 221 additions and 0 deletions

View File

@ -0,0 +1,219 @@
#[doc = "
Pulls a brief description out of a long description.
If the first paragraph of a long description is short enough then it
is interpreted as the brief description.
"];
export mk_pass;
fn mk_pass() -> pass {
run
}
fn run(
_srv: astsrv::srv,
doc: doc::cratedoc
) -> doc::cratedoc {
let fold = fold::fold({
fold_mod: fold_mod,
fold_const: fold_const,
fold_fn: fold_fn
with *fold::default_seq_fold(())
});
fold.fold_crate(fold, doc)
}
fn fold_mod(fold: fold::fold<()>, doc: doc::moddoc) -> doc::moddoc {
let doc = fold::default_seq_fold_mod(fold, doc);
let (brief, desc) = modify(doc.brief, doc.desc);
~{
brief: brief,
desc: desc
with *doc
}
}
fn fold_const(fold: fold::fold<()>, doc: doc::constdoc) -> doc::constdoc {
let doc = fold::default_seq_fold_const(fold, doc);
let (brief, desc) = modify(doc.brief, doc.desc);
~{
brief: brief,
desc: desc
with *doc
}
}
fn fold_fn(fold: fold::fold<()>, doc: doc::fndoc) -> doc::fndoc {
let doc = fold::default_seq_fold_fn(fold, doc);
let (brief, desc) = modify(doc.brief, doc.desc);
~{
brief: brief,
desc: desc
with *doc
}
}
#[test]
fn should_promote_mod_desc() {
let source = "#[doc(desc = \"desc\")] mod m { }";
let srv = astsrv::mk_srv_from_str(source);
let doc = extract::from_srv(srv, "");
let doc = attr_pass::mk_pass()(srv, doc);
let doc = run(srv, doc);
assert doc.topmod.mods[0].brief == some("desc");
assert doc.topmod.mods[0].desc == none;
}
#[test]
fn should_promote_const_desc() {
let source = "#[doc(desc = \"desc\")] const a: bool = true;";
let srv = astsrv::mk_srv_from_str(source);
let doc = extract::from_srv(srv, "");
let doc = attr_pass::mk_pass()(srv, doc);
let doc = run(srv, doc);
assert doc.topmod.consts[0].brief == some("desc");
assert doc.topmod.consts[0].desc == none;
}
#[test]
fn should_promote_fn_desc() {
let source = "#[doc(desc = \"desc\")] fn a() { }";
let srv = astsrv::mk_srv_from_str(source);
let doc = extract::from_srv(srv, "");
let doc = attr_pass::mk_pass()(srv, doc);
let doc = run(srv, doc);
assert doc.topmod.fns[0].brief == some("desc");
assert doc.topmod.fns[0].desc == none;
}
fn modify(
brief: option<str>,
desc: option<str>
) -> (option<str>, option<str>) {
if option::is_some(brief) || option::is_none(desc) {
ret (brief, desc);
}
parse_desc(option::get(desc))
}
fn parse_desc(desc: str) -> (option<str>, option<str>) {
const max_brief_len: uint = 120u;
let paras = paragraphs(desc);
if check vec::is_not_empty(paras) {
let maybe_brief = vec::head(paras);
if str::char_len(maybe_brief) <= max_brief_len {
let desc_paras = vec::tail(paras);
let desc = if vec::is_not_empty(desc_paras) {
some(str::connect(desc_paras, "\n\n"))
} else {
none
};
(some(maybe_brief), desc)
} else {
(none, some(desc))
}
} else {
(none, none)
}
}
fn paragraphs(s: str) -> [str] {
let lines = str::lines_any(s);
let whitespace_lines = 0;
let accum = "";
let paras = vec::foldl([], lines) {|paras, line|
let res = paras;
if str::is_whitespace(line) {
whitespace_lines += 1;
} else {
if whitespace_lines > 0 {
if str::is_not_empty(accum) {
res += [accum];
accum = "";
}
}
whitespace_lines = 0;
accum = if str::is_empty(accum) {
line
} else {
accum + "\n" + line
}
}
res
};
if str::is_not_empty(accum) {
paras + [accum]
} else {
paras
}
}
#[test]
fn test_paragraphs_1() {
let paras = paragraphs("1\n\n2");
assert paras == ["1", "2"];
}
#[test]
fn test_paragraphs_2() {
let paras = paragraphs("\n\n1\n1\n\n2\n\n");
assert paras == ["1\n1", "2"];
}
#[test]
fn should_promote_short_descs() {
let brief = none;
let desc = some("desc");
let (newbrief, newdesc) = modify(brief, desc);
assert newbrief == desc;
assert newdesc == none;
}
#[test]
fn should_not_promote_long_descs() {
let brief = none;
let desc = some("Warkworth Castle is a ruined medieval building
in the town of the same name in the English county of Northumberland.
The town and castle occupy a loop of the River Coquet, less than a mile
from England's north-east coast. When the castle was founded is uncertain,
but traditionally its construction has been ascribed to Prince Henry of
Scotland in the mid 12th century, although it may have been built by
King Henry II of England when he took control of England'snorthern
counties.");
let (newbrief, _) = modify(brief, desc);
assert newbrief == none;
}
#[test]
fn should_not_promote_descs_over_brief() {
let brief = some("brief");
let desc = some("desc");
let (newbrief, newdesc) = modify(brief, desc);
assert newbrief == brief;
assert newdesc == desc;
}
#[test]
fn should_extract_brief_from_desc() {
let brief = none;
let desc = some("brief\n\ndesc");
let (newbrief, newdesc) = modify(brief, desc);
assert newbrief == some("brief");
assert newdesc == some("desc");
}

View File

@ -23,4 +23,5 @@ mod attr_pass;
mod tystr_pass;
mod prune_undoc_pass;
mod prune_unexported_pass;
mod desc_to_brief_pass;
mod astsrv;

View File

@ -100,6 +100,7 @@ fn run(source_file: str) {
attr_pass::mk_pass(),
// FIXME: This pass should be optional
prune_undoc_pass::mk_pass(),
desc_to_brief_pass::mk_pass(),
gen::mk_pass {|| std::io:: stdout()}
]);
}