diff --git a/src/rustdoc/rustdoc.rs b/src/rustdoc/rustdoc.rs new file mode 100755 index 00000000000..9d119d208f4 --- /dev/null +++ b/src/rustdoc/rustdoc.rs @@ -0,0 +1,205 @@ +/* rustdoc - rust->markdown translator + * + * Copyright 2011 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use std; +use rustc; + +import option; +import option::{some, none}; +import rustc::syntax::ast; +import rustc::syntax::codemap; +import rustc::syntax::parse::parser; +import rustc::syntax::print::pprust; +import rustc::syntax::visit; +import std::io; +import std::map; + +type rustdoc = { + ps: pprust::ps, + w: io::writer +}; + +type fndoc = { + brief: str, + desc: option::t, + return: option::t, + args: map::hashmap +}; + +#[doc( + brief = "Documents a single function.", + args(rd = "Rustdoc context", + ident = "Identifier for this function", + doc = "Function docs extracted from attributes", + _fn = "AST object representing this function") +)] +fn doc_fn(rd: rustdoc, ident: str, doc: fndoc, _fn: ast::_fn) { + rd.w.write_line("## Function `" + ident + "`"); + rd.w.write_line(doc.brief); + alt doc.desc { + some(_d) { + rd.w.write_line(""); + rd.w.write_line(_d); + rd.w.write_line(""); + } + none. { } + } + for arg: ast::arg in _fn.decl.inputs { + rd.w.write_str("### Argument `" + arg.ident + "`: "); + rd.w.write_line("`" + pprust::ty_to_str(arg.ty) + "`"); + alt doc.args.find(arg.ident) { + some(_d) { + rd.w.write_line(_d); + } + none. { } + }; + } + rd.w.write_line("### Returns `" + pprust::ty_to_str(_fn.decl.output) + "`"); + alt doc.return { + some(_r) { rd.w.write_line(_r); } + none. { } + } +} + +#[doc( + brief = "Parses function docs from a complex #[doc] attribute.", + desc = "Supported attributes: + +* `brief`: Brief description +* `desc`: Long description +* `return`: Description of return value +* `args`: List of argname = argdesc pairs +", + args(items = "Doc attribute contents"), + return = "Parsed function docs." +)] +fn parse_compound_fndoc(items: [@ast::meta_item]) -> fndoc { + let brief = none; + let desc = none; + let return = none; + let argdocs = map::new_str_hash::(); + let argdocsfound = none; + for item: @ast::meta_item in items { + alt item.node { + ast::meta_name_value("brief", {node: ast::lit_str(value), + span: _}) { + brief = some(value); + } + ast::meta_name_value("desc", {node: ast::lit_str(value), + span: _}) { + desc = some(value); + } + ast::meta_name_value("return", {node: ast::lit_str(value), + span: _}) { + return = some(value); + } + ast::meta_list("args", args) { + argdocsfound = some(args); + } + _ { } + } + } + + alt argdocsfound { + none. { } + some(ds) { + for d: @ast::meta_item in ds { + alt d.node { + ast::meta_name_value(key, {node: ast::lit_str(value), + span: _}) { + argdocs.insert(key, value); + } + } + } + } + } + + let _brief = alt brief { + some(_b) { _b } + none. { "_undocumented_" } + }; + + { brief: _brief, desc: desc, return: return, args: argdocs } +} + +#[doc( + brief = "Documents a single crate item.", + args(rd = "Rustdoc context", + item = "AST item to document") +)] +fn doc_item(rd: rustdoc, item: @ast::item) { + let _fndoc = none; + let noargdocs = map::new_str_hash::(); + for attr: ast::attribute in item.attrs { + alt attr.node.value.node { + ast::meta_name_value("doc", {node: ast::lit_str(value), span: _}) { + _fndoc = some({ brief: value, + desc: none, + return: none, + args: noargdocs }); + } + ast::meta_list("doc", docs) { + _fndoc = some(parse_compound_fndoc(docs)); + } + } + } + + let _fndoc0 = alt _fndoc { + some(_d) { _d } + none. { { brief: "_undocumented_", desc: none, return: none, args: noargdocs } } + }; + + alt item.node { + ast::item_const(ty, expr) { } + ast::item_fn(_fn, _) { + doc_fn(rd, item.ident, _fndoc0, _fn); + } + ast::item_mod(_mod) { } + ast::item_ty(ty, typarams) { } + ast::item_tag(variant, typarams) { } + ast::item_obj(_obj, typarams, node_id) { } + ast::item_res(dtor, dtorid, typarams, ctorid) { } + }; +} + +#[doc( + brief = "Generate a crate document header.", + args(rd = "Rustdoc context", + name = "Crate name") +)] +fn doc_header(rd: rustdoc, name: str) { + rd.w.write_line("# Crate " + name); +} + +#[doc( + brief = "Main function.", + desc = "Command-line arguments: + +* argv[1]: crate file name", + args(argv = "Command-line arguments.") +)] +fn main(argv: [str]) { + let sess = @{cm: codemap::new_codemap(), mutable next_id: 0}; + let w = io::stdout(); + let rd = { ps: pprust::rust_printer(w), w: w }; + doc_header(rd, argv[1]); + let p = parser::parse_crate_from_source_file(argv[1], [], sess); + let v = visit::mk_simple_visitor(@{ + visit_item: bind doc_item(rd, _) + with *visit::default_simple_visitor()}); + visit::visit_crate(*p, (), v); +}