Add rustdoc_ng

This commit is contained in:
Corey Richardson 2013-08-15 16:28:54 -04:00
parent d87078be72
commit 268f3f0ff5
14 changed files with 2001 additions and 0 deletions

View File

@ -214,6 +214,7 @@ CFG_LIBRUSTC_$(1) :=$(call CFG_LIB_NAME_$(1),rustc)
CFG_LIBSYNTAX_$(1) :=$(call CFG_LIB_NAME_$(1),syntax)
CFG_LIBRUSTPKG_$(1) :=$(call CFG_LIB_NAME_$(1),rustpkg)
CFG_LIBRUSTDOC_$(1) :=$(call CFG_LIB_NAME_$(1),rustdoc)
CFG_LIBRUSTDOCNG_$(1) :=$(call CFG_LIB_NAME_$(1),rustdoc_ng)
CFG_LIBRUSTI_$(1) :=$(call CFG_LIB_NAME_$(1),rusti)
CFG_LIBRUST_$(1) :=$(call CFG_LIB_NAME_$(1),rust)
@ -223,6 +224,7 @@ LIBRUSTC_GLOB_$(1) :=$(call CFG_LIB_GLOB_$(1),rustc)
LIBSYNTAX_GLOB_$(1) :=$(call CFG_LIB_GLOB_$(1),syntax)
LIBRUSTPKG_GLOB_$(1) :=$(call CFG_LIB_GLOB_$(1),rustpkg)
LIBRUSTDOC_GLOB_$(1) :=$(call CFG_LIB_GLOB_$(1),rustdoc)
LIBRUSTDOCNG_GLOB_$(1) :=$(call CFG_LIB_GLOB_$(1),rustdoc_ng)
LIBRUSTI_GLOB_$(1) :=$(call CFG_LIB_GLOB_$(1),rusti)
LIBRUST_GLOB_$(1) :=$(call CFG_LIB_GLOB_$(1),rust)
EXTRALIB_DSYM_GLOB_$(1) :=$(call CFG_LIB_DSYM_GLOB_$(1),extra)
@ -231,6 +233,7 @@ LIBRUSTC_DSYM_GLOB_$(1) :=$(call CFG_LIB_DSYM_GLOB_$(1),rustc)
LIBSYNTAX_DSYM_GLOB_$(1) :=$(call CFG_LIB_DSYM_GLOB_$(1),syntax)
LIBRUSTPKG_DSYM_GLOB_$(1) :=$(call CFG_LIB_DSYM_GLOB_$(1),rustpkg)
LIBRUSTDOC_DSYM_GLOB_$(1) :=$(call CFG_LIB_DSYM_GLOB_$(1),rustdoc)
LIBRUSTDOCNG_DSYM_GLOB_$(1) :=$(call CFG_LIB_DSYM_GLOB_$(1),rustdoc_ng)
LIBRUSTI_DSYM_GLOB_$(1) :=$(call CFG_LIB_DSYM_GLOB_$(1),rusti)
LIBRUST_DSYM_GLOB_$(1) :=$(call CFG_LIB_DSYM_GLOB_$(1),rust)
@ -443,6 +446,7 @@ CSREQ$(1)_T_$(2)_H_$(3) = \
$$(HBIN$(1)_H_$(3))/rust$$(X_$(3)) \
$$(HLIB$(1)_H_$(3))/$(CFG_LIBRUSTPKG_$(3)) \
$$(HLIB$(1)_H_$(3))/$(CFG_LIBRUSTDOC_$(3)) \
$$(HLIB$(1)_H_$(3))/$(CFG_LIBRUSTDOCNG_$(3)) \
$$(HLIB$(1)_H_$(3))/$(CFG_LIBRUSTI_$(3)) \
$$(HLIB$(1)_H_$(3))/$(CFG_LIBRUST_$(3)) \
$$(TLIB$(1)_T_$(2)_H_$(3))/$(CFG_STDLIB_$(2)) \
@ -451,6 +455,7 @@ CSREQ$(1)_T_$(2)_H_$(3) = \
$$(TLIB$(1)_T_$(2)_H_$(3))/$(CFG_LIBRUSTC_$(2)) \
$$(TLIB$(1)_T_$(2)_H_$(3))/$(CFG_LIBRUSTPKG_$(2)) \
$$(TLIB$(1)_T_$(2)_H_$(3))/$(CFG_LIBRUSTDOC_$(2)) \
$$(TLIB$(1)_T_$(2)_H_$(3))/$(CFG_LIBRUSTDOCNG_$(2)) \
$$(TLIB$(1)_T_$(2)_H_$(3))/$(CFG_LIBRUSTI_$(2)) \
$$(TLIB$(1)_T_$(2)_H_$(3))/$(CFG_LIBRUST_$(2))

View File

@ -72,6 +72,7 @@ clean$(1)_H_$(2):
$(Q)rm -f $$(HBIN$(1)_H_$(2))/rust$(X_$(2))
$(Q)rm -f $$(HLIB$(1)_H_$(2))/$(CFG_LIBRUSTPKG_$(2))
$(Q)rm -f $$(HLIB$(1)_H_$(2))/$(CFG_LIBRUSTDOC_$(2))
$(Q)rm -f $$(HLIB$(1)_H_$(2))/$(CFG_LIBRUSTDOCNG_$(2))
$(Q)rm -f $$(HLIB$(1)_H_$(2))/$(CFG_RUNTIME_$(2))
$(Q)rm -f $$(HLIB$(1)_H_$(2))/$(CFG_STDLIB_$(2))
$(Q)rm -f $$(HLIB$(1)_H_$(2))/$(CFG_EXTRALIB_$(2))
@ -85,6 +86,7 @@ clean$(1)_H_$(2):
$(Q)rm -f $$(HLIB$(1)_H_$(2))/$(LIBSYNTAX_GLOB_$(2))
$(Q)rm -f $$(HLIB$(1)_H_$(2))/$(LIBRUSTPKG_GLOB_$(2))
$(Q)rm -f $$(HLIB$(1)_H_$(2))/$(LIBRUSTDOC_GLOB_$(2))
$(Q)rm -f $$(HLIB$(1)_H_$(2))/$(LIBRUSTDOCNG_GLOB_$(2))
$(Q)rm -f $$(HLIB$(1)_H_$(2))/$(LIBRUSTI_GLOB_$(2))
$(Q)rm -f $$(HLIB$(1)_H_$(2))/$(LIBRUST_GLOB_$(2))
$(Q)rm -f $$(HLIB$(1)_H_$(2))/$(CFG_RUSTLLVM_$(2))
@ -107,6 +109,7 @@ clean$(1)_T_$(2)_H_$(3):
$(Q)rm -f $$(TBIN$(1)_T_$(2)_H_$(3))/rust$(X_$(2))
$(Q)rm -f $$(TLIB$(1)_T_$(2)_H_$(3))/$(CFG_LIBRUSTPKG_$(2))
$(Q)rm -f $$(TLIB$(1)_T_$(2)_H_$(3))/$(CFG_LIBRUSTDOC_$(2))
$(Q)rm -f $$(TLIB$(1)_T_$(2)_H_$(3))/$(CFG_LIBRUSTDOCNG_$(2))
$(Q)rm -f $$(TLIB$(1)_T_$(2)_H_$(3))/$(CFG_RUNTIME_$(2))
$(Q)rm -f $$(TLIB$(1)_T_$(2)_H_$(3))/$(CFG_STDLIB_$(2))
$(Q)rm -f $$(TLIB$(1)_T_$(2)_H_$(3))/$(CFG_EXTRALIB_$(2))
@ -120,6 +123,7 @@ clean$(1)_T_$(2)_H_$(3):
$(Q)rm -f $$(TLIB$(1)_T_$(2)_H_$(3))/$(LIBSYNTAX_GLOB_$(2))
$(Q)rm -f $$(TLIB$(1)_T_$(2)_H_$(3))/$(LIBRUSTPKG_GLOB_$(2))
$(Q)rm -f $$(TLIB$(1)_T_$(2)_H_$(3))/$(LIBRUSTDOC_GLOB_$(2))
$(Q)rm -f $$(TLIB$(1)_T_$(2)_H_$(3))/$(LIBRUSTDOCNG_GLOB_$(2))
$(Q)rm -f $$(TLIB$(1)_T_$(2)_H_$(3))/$(LIBRUSTI_GLOB_$(2))
$(Q)rm -f $$(TLIB$(1)_T_$(2)_H_$(3))/$(LIBRUST_GLOB_$(2))
$(Q)rm -f $$(TLIB$(1)_T_$(2)_H_$(3))/$(CFG_RUSTLLVM_$(2))

View File

@ -104,6 +104,7 @@ install-target-$(1)-host-$(2): $$(CSREQ$$(ISTAGE)_T_$(1)_H_$(2))
$$(Q)$$(call INSTALL_LIB,$$(LIBSYNTAX_GLOB_$(1)))
$$(Q)$$(call INSTALL_LIB,$$(LIBRUSTPKG_GLOB_$(1)))
$$(Q)$$(call INSTALL_LIB,$$(LIBRUSTDOC_GLOB_$(1)))
$$(Q)$$(call INSTALL_LIB,$$(LIBRUSTDOCNG_GLOB_$(1)))
$$(Q)$$(call INSTALL_LIB,$$(LIBRUSTI_GLOB_$(1)))
$$(Q)$$(call INSTALL_LIB,$$(LIBRUST_GLOB_$(1)))
$$(Q)$$(call INSTALL_LIB,libmorestack.a)
@ -149,6 +150,7 @@ install-host: $(CSREQ$(ISTAGE)_T_$(CFG_BUILD_TRIPLE)_H_$(CFG_BUILD_TRIPLE))
$(Q)$(call INSTALL_LIB,$(LIBRUST_GLOB_$(CFG_BUILD_TRIPLE)))
$(Q)$(call INSTALL_LIB,$(LIBRUSTPKG_GLOB_$(CFG_BUILD_TRIPLE)))
$(Q)$(call INSTALL_LIB,$(LIBRUSTDOC_GLOB_$(CFG_BUILD_TRIPLE)))
$(Q)$(call INSTALL_LIB,$(LIBRUSTDOCNG_GLOB_$(CFG_BUILD_TRIPLE)))
$(Q)$(call INSTALL,$(HL),$(PHL),$(CFG_RUNTIME_$(CFG_BUILD_TRIPLE)))
$(Q)$(call INSTALL,$(HL),$(PHL),$(CFG_RUSTLLVM_$(CFG_BUILD_TRIPLE)))
$(Q)$(call INSTALL,$(S)/man, $(PREFIX_ROOT)/share/man/man1,rust.1)

View File

@ -23,6 +23,11 @@ RUSTPKG_INPUTS := $(wildcard $(S)src/librustpkg/*.rs)
RUSTDOC_LIB := $(S)src/librustdoc/rustdoc.rs
RUSTDOC_INPUTS := $(wildcard $(S)src/librustdoc/*.rs)
# rustdoc_ng, the next generation documentation tool
RUSTDOCNG_LIB := $(S)src/rustdoc/lib.rs
RUSTDOCNG_INPUTS := $(wildcard $(S)src/rustdoc/*.rs)
# Rusti, the JIT REPL
RUSTI_LIB := $(S)src/librusti/rusti.rs
RUSTI_INPUTS := $(wildcard $(S)src/librusti/*.rs)
@ -78,6 +83,14 @@ $$(TBIN$(1)_T_$(4)_H_$(3))/rustdoc$$(X_$(4)): \
@$$(call E, compile_and_link: $$@)
$$(STAGE$(1)_T_$(4)_H_$(3)) --cfg rustdoc -o $$@ $$<
$$(TBIN$(1)_T_$(4)_H_$(3))/rustdoc_ng$$(X_$(4)): \
$$(DRIVER_CRATE) \
$$(TSREQ$(1)_T_$(4)_H_$(3)) \
$$(TLIB$(1)_T_$(4)_H_$(3))/$(CFG_LIBRUSTDOCNG_$(4)) \
| $$(TBIN$(1)_T_$(4)_H_$(3))/
@$$(call E, compile_and_link: $$@)
$$(STAGE$(1)_T_$(4)_H_$(3)) --cfg rustdoc_ng -o $$@ $$<
$$(TLIB$(1)_T_$(4)_H_$(3))/$(CFG_LIBRUSTI_$(4)): \
$$(RUSTI_LIB) $$(RUSTI_INPUTS) \
$$(SREQ$(1)_T_$(4)_H_$(3)) \

2
src/rustdoc_ng/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*.swp
main

1081
src/rustdoc_ng/clean.rs Normal file

File diff suppressed because it is too large Load Diff

68
src/rustdoc_ng/core.rs Normal file
View File

@ -0,0 +1,68 @@
use rustc;
use rustc::{driver, middle};
use syntax;
use syntax::parse;
use syntax::ast;
use std::os;
use std::local_data;
use visit_ast::RustdocVisitor;
use clean;
use clean::Clean;
pub struct DocContext {
crate: @ast::Crate,
tycx: middle::ty::ctxt,
sess: driver::session::Session
}
/// Parses, resolves, and typechecks the given crate
fn get_ast_and_resolve(cpath: &Path, libs: ~[Path]) -> DocContext {
use syntax::codemap::dummy_spanned;
use rustc::driver::driver::*;
let parsesess = parse::new_parse_sess(None);
let input = file_input(cpath.clone());
let sessopts = @driver::session::options {
binary: @"rustdoc",
maybe_sysroot: Some(@os::self_exe_path().unwrap().pop()),
addl_lib_search_paths: @mut libs,
.. (*rustc::driver::session::basic_options()).clone()
};
let diagnostic_handler = syntax::diagnostic::mk_handler(None);
let span_diagnostic_handler =
syntax::diagnostic::mk_span_handler(diagnostic_handler, parsesess.cm);
let sess = driver::driver::build_session_(sessopts, parsesess.cm,
syntax::diagnostic::emit,
span_diagnostic_handler);
let mut cfg = build_configuration(sess, @"rustdoc_ng", &input);
cfg.push(@dummy_spanned(ast::MetaWord(@"stage2")));
let mut crate = phase_1_parse_input(sess, cfg.clone(), &input);
crate = phase_2_configure_and_expand(sess, cfg, crate);
let analysis = phase_3_run_analysis_passes(sess, crate);
debug!("crate: %?", crate);
DocContext { crate: crate, tycx: analysis.ty_cx, sess: sess }
}
pub fn run_core (libs: ~[Path], path: &Path) -> clean::Crate {
let ctxt = @get_ast_and_resolve(path, libs);
debug!("defmap:");
for (k, v) in ctxt.tycx.def_map.iter() {
debug!("%?: %?", k, v);
}
local_data::set(super::ctxtkey, ctxt);
let v = @mut RustdocVisitor::new();
v.visit(ctxt.crate);
v.clean()
}

158
src/rustdoc_ng/doctree.rs Normal file
View File

@ -0,0 +1,158 @@
//! This module is used to store stuff from Rust's AST in a more convenient
//! manner (and with prettier names) before cleaning.
use syntax;
use syntax::codemap::span;
use syntax::ast;
use syntax::ast::{ident, NodeId};
pub struct Module {
name: Option<ident>,
attrs: ~[ast::Attribute],
where: span,
structs: ~[Struct],
enums: ~[Enum],
fns: ~[Function],
mods: ~[Module],
id: NodeId,
typedefs: ~[Typedef],
statics: ~[Static],
traits: ~[Trait],
vis: ast::visibility,
impls: ~[Impl],
view_items: ~[ast::view_item],
}
impl Module {
pub fn new(name: Option<ident>) -> Module {
Module {
name : name,
id: 0,
vis: ast::private,
where: syntax::codemap::dummy_sp(),
attrs : ~[],
structs : ~[],
enums : ~[],
fns : ~[],
mods : ~[],
typedefs : ~[],
statics : ~[],
traits : ~[],
impls : ~[],
view_items : ~[],
}
}
}
#[deriving(ToStr, Clone, Encodable, Decodable)]
pub enum StructType {
/// A normal struct
Plain,
/// A tuple struct
Tuple,
/// A newtype struct (tuple struct with one element)
Newtype,
/// A unit struct
Unit
}
pub enum TypeBound {
RegionBound,
TraitBound(ast::trait_ref)
}
pub struct Struct {
vis: ast::visibility,
id: NodeId,
struct_type: StructType,
name: ident,
generics: ast::Generics,
attrs: ~[ast::Attribute],
fields: ~[@ast::struct_field],
where: span,
}
pub struct Enum {
vis: ast::visibility,
variants: ~[Variant],
generics: ast::Generics,
attrs: ~[ast::Attribute],
id: NodeId,
where: span,
name: ident,
}
pub struct Variant {
name: ident,
attrs: ~[ast::Attribute],
kind: ast::variant_kind,
id: ast::NodeId,
vis: ast::visibility,
where: span,
}
pub struct Function {
decl: ast::fn_decl,
attrs: ~[ast::Attribute],
id: NodeId,
name: ident,
vis: ast::visibility,
where: span,
generics: ast::Generics,
}
pub struct Typedef {
ty: ast::Ty,
gen: ast::Generics,
name: ast::ident,
id: ast::NodeId,
attrs: ~[ast::Attribute],
where: span,
vis: ast::visibility,
}
pub struct Static {
type_: ast::Ty,
mutability: ast::mutability,
expr: @ast::expr,
name: ast::ident,
attrs: ~[ast::Attribute],
vis: ast::visibility,
id: ast::NodeId,
where: span,
}
pub struct Trait {
name: ast::ident,
methods: ~[ast::trait_method], //should be TraitMethod
generics: ast::Generics,
parents: ~[ast::trait_ref],
attrs: ~[ast::Attribute],
id: ast::NodeId,
where: span,
vis: ast::visibility,
}
pub struct Impl {
generics: ast::Generics,
trait_: Option<ast::trait_ref>,
for_: ast::Ty,
methods: ~[@ast::method],
attrs: ~[ast::Attribute],
where: span,
vis: ast::visibility,
id: ast::NodeId,
}
pub fn struct_type_from_def(sd: &ast::struct_def) -> StructType {
if sd.ctor_id.is_some() {
// We are in a tuple-struct
match sd.fields.len() {
0 => Unit,
1 => Newtype,
_ => Tuple
}
} else {
Plain
}
}

101
src/rustdoc_ng/fold.rs Normal file
View File

@ -0,0 +1,101 @@
use std;
use clean::*;
use std::iterator::Extendable;
pub trait DocFolder {
fn fold_item(&mut self, item: Item) -> Option<Item> {
self.fold_item_recur(item)
}
/// don't override!
fn fold_item_recur(&mut self, item: Item) -> Option<Item> {
use std::util::swap;
let Item { attrs, name, source, visibility, id, inner } = item;
let inner = inner;
let c = |x| self.fold_item(x);
let inner = match inner {
StructItem(i) => {
let mut i = i;
let mut foo = ~[]; swap(&mut foo, &mut i.fields);
i.fields.extend(&mut foo.move_iter().filter_map(|x| self.fold_item(x)));
StructItem(i)
},
ModuleItem(i) => {
ModuleItem(self.fold_mod(i))
},
EnumItem(i) => {
let mut i = i;
let mut foo = ~[]; swap(&mut foo, &mut i.variants);
i.variants.extend(&mut foo.move_iter().filter_map(|x| self.fold_item(x)));
EnumItem(i)
},
TraitItem(i) => {
fn vtrm<T: DocFolder>(this: &mut T, trm: TraitMethod) -> Option<TraitMethod> {
match trm {
Required(it) => {
match this.fold_item(it) {
Some(x) => return Some(Required(x)),
None => return None,
}
},
Provided(it) => {
match this.fold_item(it) {
Some(x) => return Some(Provided(x)),
None => return None,
}
},
}
}
let mut i = i;
let mut foo = ~[]; swap(&mut foo, &mut i.methods);
i.methods.extend(&mut foo.move_iter().filter_map(|x| vtrm(self, x)));
TraitItem(i)
},
ImplItem(i) => {
let mut i = i;
let mut foo = ~[]; swap(&mut foo, &mut i.methods);
i.methods.extend(&mut foo.move_iter().filter_map(|x| self.fold_item(x)));
ImplItem(i)
},
VariantItem(i) => {
let i2 = i.clone(); // this clone is small
match i.kind {
StructVariant(j) => {
let mut j = j;
let mut foo = ~[]; swap(&mut foo, &mut j.fields);
j.fields.extend(&mut foo.move_iter().filter_map(c));
VariantItem(Variant {kind: StructVariant(j), ..i2})
},
_ => VariantItem(i2)
}
},
x => x
};
Some(Item { attrs: attrs, name: name, source: source, inner: inner,
visibility: visibility, id: id })
}
fn fold_mod(&mut self, m: Module) -> Module {
Module { items: m.items.move_iter().filter_map(|i| self.fold_item(i)).collect() }
}
fn fold_crate(&mut self, mut c: Crate) -> Crate {
let mut mod_ = None;
std::util::swap(&mut mod_, &mut c.module);
let mod_ = mod_.unwrap();
c.module = self.fold_item(mod_);
let Crate { name, module } = c;
match module {
Some(Item { inner: ModuleItem(m), name: name_, attrs: attrs_,
source, visibility: vis, id }) => {
return Crate { module: Some(Item { inner:
ModuleItem(self.fold_mod(m)),
name: name_, attrs: attrs_,
source: source, id: id, visibility: vis }), name: name};
},
Some(_) => fail!("non-module item set as module of crate"),
None => return Crate { module: None, name: name},
}
}
}

27
src/rustdoc_ng/lib.rs Normal file
View File

@ -0,0 +1,27 @@
#[link(name = "rustdoc_ng",
vers = "0.1.0",
uuid = "8c6e4598-1596-4aa5-a24c-b811914bbbc6")];
#[desc = "rustdoc, the Rust documentation extractor"];
#[license = "MIT/ASL2"];
#[crate_type = "lib"];
#[deny(warnings)];
extern mod syntax;
extern mod rustc;
extern mod extra;
use extra::serialize::Encodable;
pub mod core;
pub mod doctree;
pub mod clean;
pub mod visit_ast;
pub mod fold;
pub mod plugins;
pub mod passes;
pub static SCHEMA_VERSION: &'static str = "0.8.0";
pub static ctxtkey: std::local_data::Key<@core::DocContext> = &std::local_data::Key;

94
src/rustdoc_ng/main.rs Normal file
View File

@ -0,0 +1,94 @@
#[link(name = "rustdoc_ng",
vers = "0.1.0",
uuid = "8c6e4598-1596-4aa5-a24c-b811914bbbc6")];
#[desc = "rustdoc, the Rust documentation extractor"];
#[license = "MIT/ASL2"];
#[crate_type = "bin"];
extern mod extra;
extern mod rustdoc_ng;
use rustdoc_ng::*;
use std::cell::Cell;
use extra::serialize::Encodable;
fn main() {
use extra::getopts::*;
use extra::getopts::groups::*;
let args = std::os::args();
let opts = ~[
optmulti("L", "library-path", "directory to add to crate search path", "DIR"),
optmulti("p", "plugin", "plugin to load and run", "NAME"),
optmulti("", "plugin-path", "directory to load plugins from", "DIR"),
// auxillary pass (defaults to hidden_strip
optmulti("a", "pass", "auxillary pass to run", "NAME"),
optflag("n", "no-defult-passes", "do not run the default passes"),
optflag("h", "help", "show this help message"),
];
let matches = getopts(args.tail(), opts).unwrap();
if opt_present(&matches, "h") || opt_present(&matches, "help") {
println(usage(args[0], opts));
return;
}
let libs = Cell::new(opt_strs(&matches, "L").map(|s| Path(*s)));
let mut passes = if opt_present(&matches, "n") {
~[]
} else {
~[~"collapse-docs", ~"clean-comments", ~"collapse-privacy" ]
};
opt_strs(&matches, "a").map(|x| passes.push(x.clone()));
if matches.free.len() != 1 {
println(usage(args[0], opts));
return;
}
let cr = Cell::new(Path(matches.free[0]));
let crate = std::task::try(|| {let cr = cr.take(); core::run_core(libs.take(), &cr)}).unwrap();
// { "schema": version, "crate": { parsed crate ... }, "plugins": { output of plugins ... }}
let mut json = ~extra::treemap::TreeMap::new();
json.insert(~"schema", extra::json::String(SCHEMA_VERSION.to_owned()));
let mut pm = plugins::PluginManager::new(Path("/tmp/rustdoc_ng/plugins"));
for pass in passes.iter() {
pm.add_plugin(match pass.as_slice() {
"strip-hidden" => passes::strip_hidden,
"clean-comments" => passes::clean_comments,
"collapse-docs" => passes::collapse_docs,
"collapse-privacy" => passes::collapse_privacy,
s => { error!("unknown pass %s, skipping", s); passes::noop },
})
}
for pname in opt_strs(&matches, "p").move_iter() {
pm.load_plugin(pname);
}
let (crate, res) = pm.run_plugins(crate);
let plugins_json = ~res.move_iter().filter_map(|opt| opt).collect();
// FIXME: yuck, Rust -> str -> JSON round trip! No way to .encode
// straight to the Rust JSON representation.
let crate_json_str = do std::io::with_str_writer |w| {
crate.encode(&mut extra::json::Encoder(w));
};
let crate_json = match extra::json::from_str(crate_json_str) {
Ok(j) => j,
Err(_) => fail!("Rust generated JSON is invalid??")
};
json.insert(~"crate", crate_json);
json.insert(~"plugins", extra::json::Object(plugins_json));
println(extra::json::Object(json).to_str());
}

193
src/rustdoc_ng/passes.rs Normal file
View File

@ -0,0 +1,193 @@
use std;
use clean;
use syntax::ast;
use clean::Item;
use plugins;
use fold;
use fold::DocFolder;
/// A sample pass showing the minimum required work for a plugin.
pub fn noop(crate: clean::Crate) -> plugins::PluginResult {
(crate, None)
}
/// Strip items marked `#[doc(hidden)]`
pub fn strip_hidden(crate: clean::Crate) -> plugins::PluginResult {
struct Stripper;
impl fold::DocFolder for Stripper {
fn fold_item(&mut self, i: Item) -> Option<Item> {
for attr in i.attrs.iter() {
match attr {
&clean::List(~"doc", ref l) => {
for innerattr in l.iter() {
match innerattr {
&clean::Word(ref s) if "hidden" == *s => {
info!("found one in strip_hidden; removing");
return None;
},
_ => (),
}
}
},
_ => ()
}
}
self.fold_item_recur(i)
}
}
let mut stripper = Stripper;
let crate = stripper.fold_crate(crate);
(crate, None)
}
pub fn clean_comments(crate: clean::Crate) -> plugins::PluginResult {
struct CommentCleaner;
impl fold::DocFolder for CommentCleaner {
fn fold_item(&mut self, i: Item) -> Option<Item> {
let mut i = i;
let mut avec: ~[clean::Attribute] = ~[];
for attr in i.attrs.iter() {
match attr {
&clean::NameValue(~"doc", ref s) => avec.push(
clean::NameValue(~"doc", clean_comment_body(s.clone()))),
x => avec.push(x.clone())
}
}
i.attrs = avec;
self.fold_item_recur(i)
}
}
let mut cleaner = CommentCleaner;
let crate = cleaner.fold_crate(crate);
(crate, None)
}
pub fn collapse_privacy(crate: clean::Crate) -> plugins::PluginResult {
struct PrivacyCollapser {
stack: ~[clean::Visibility]
}
impl fold::DocFolder for PrivacyCollapser {
fn fold_item(&mut self, mut i: Item) -> Option<Item> {
if i.visibility.is_some() {
if i.visibility == Some(ast::inherited) {
i.visibility = Some(self.stack.last().clone());
} else {
self.stack.push(i.visibility.clone().unwrap());
}
}
self.fold_item_recur(i)
}
}
let mut privacy = PrivacyCollapser { stack: ~[] };
let crate = privacy.fold_crate(crate);
(crate, None)
}
pub fn collapse_docs(crate: clean::Crate) -> plugins::PluginResult {
struct Collapser;
impl fold::DocFolder for Collapser {
fn fold_item(&mut self, i: Item) -> Option<Item> {
let mut docstr = ~"";
let mut i = i;
for attr in i.attrs.iter() {
match *attr {
clean::NameValue(~"doc", ref s) => {
docstr.push_str(s.clone());
docstr.push_char('\n');
},
_ => ()
}
}
let mut a: ~[clean::Attribute] = i.attrs.iter().filter(|&a| match a {
&clean::NameValue(~"doc", _) => false,
_ => true
}).map(|x| x.clone()).collect();
if "" != docstr {
a.push(clean::NameValue(~"doc", docstr.trim().to_owned()));
}
i.attrs = a;
self.fold_item_recur(i)
}
}
let mut collapser = Collapser;
let crate = collapser.fold_crate(crate);
(crate, None)
}
//Utility
enum CleanCommentStates {
Collect,
Strip,
Stripped,
}
/// Returns the index of the last character all strings have common in their
/// prefix.
fn longest_common_prefix(s: ~[~str]) -> uint {
// find the longest common prefix
debug!("lcp: looking into %?", s);
// index of the last character all the strings share
let mut index = 0u;
if s.len() <= 1 {
return 0;
}
// whether one of the strings has been exhausted of characters yet
let mut exhausted = false;
// character iterators for all the lines
let mut lines = s.iter().filter(|x| x.len() != 0).map(|x| x.iter()).to_owned_vec();
'outer: loop {
// because you can't label a while loop
if exhausted == true {
break;
}
debug!("lcp: index %u", index);
let mut lines = lines.mut_iter();
let ch = match lines.next().unwrap().next() {
Some(c) => c,
None => { exhausted = true; loop },
};
debug!("looking for char %c", ch);
for line in lines {
match line.next() {
Some(c) => if c == ch { loop } else { exhausted = true; loop 'outer },
None => { exhausted = true; loop 'outer }
}
}
index += 1;
}
debug!("lcp: last index %u", index);
index
}
fn clean_comment_body(s: ~str) -> ~str {
// FIXME #31: lots of copies in here.
let lines = s.line_iter().to_owned_vec();
match lines.len() {
0 => return ~"",
1 => return lines[0].slice_from(2).trim().to_owned(),
_ => (),
}
let mut ol = std::vec::with_capacity(lines.len());
for line in lines.clone().move_iter() {
// replace meaningless things with a single newline
match line {
x if ["/**", "/*!", "///", "//!", "*/"].contains(&x.trim()) => ol.push(~""),
x if x.trim() == "" => ol.push(~""),
x => ol.push(x.to_owned())
}
}
let li = longest_common_prefix(ol.clone());
let x = ol.iter()
.filter(|x| { debug!("cleaning line: %s", **x); true })
.map(|x| if x.len() == 0 { ~"" } else { x.slice_chars(li, x.char_len()).to_owned() })
.to_owned_vec().connect("\n");
x.trim().to_owned()
}

80
src/rustdoc_ng/plugins.rs Normal file
View File

@ -0,0 +1,80 @@
use clean;
use extra;
use dl = std::unstable::dynamic_lib;
pub type PluginJson = Option<(~str, extra::json::Json)>;
pub type PluginResult = (clean::Crate, PluginJson);
pub type plugin_callback = extern fn (clean::Crate) -> PluginResult;
/// Manages loading and running of plugins
pub struct PluginManager {
priv dylibs: ~[dl::DynamicLibrary],
priv callbacks: ~[plugin_callback],
/// The directory plugins will be loaded from
prefix: Path,
}
impl PluginManager {
/// Create a new plugin manager
pub fn new(prefix: Path) -> PluginManager {
PluginManager {
dylibs: ~[],
callbacks: ~[],
prefix: prefix,
}
}
/// Load a plugin with the given name.
///
/// Turns `name` into the proper dynamic library filename for the given
/// platform. On windows, it turns into name.dll, on OS X, name.dylib, and
/// elsewhere, libname.so.
pub fn load_plugin(&mut self, name: ~str) {
let x = self.prefix.push(libname(name));
let lib_result = dl::DynamicLibrary::open(Some(&x));
let lib = lib_result.unwrap();
let plugin = unsafe { lib.symbol("rustdoc_plugin_entrypoint") }.unwrap();
self.dylibs.push(lib);
self.callbacks.push(plugin);
}
/// Load a normal Rust function as a plugin.
///
/// This is to run passes over the cleaned crate. Plugins run this way
/// correspond to the A-aux tag on Github.
pub fn add_plugin(&mut self, plugin: plugin_callback) {
self.callbacks.push(plugin);
}
/// Run all the loaded plugins over the crate, returning their results
pub fn run_plugins(&self, crate: clean::Crate) -> (clean::Crate, ~[PluginJson]) {
let mut out_json = ~[];
let mut crate = crate;
for &callback in self.callbacks.iter() {
let (c, res) = callback(crate);
crate = c;
out_json.push(res);
}
(crate, out_json)
}
}
#[cfg(target_os="win32")]
fn libname(mut n: ~str) -> ~str {
n.push_str(".dll");
n
}
#[cfg(target_os="macos")]
fn libname(mut n: ~str) -> ~str {
n.push_str(".dylib");
n
}
#[cfg(not(target_os="win32"), not(target_os="macos"))]
fn libname(n: ~str) -> ~str {
let mut i = ~"lib";
i.push_str(n);
i.push_str(".so");
i
}

173
src/rustdoc_ng/visit_ast.rs Normal file
View File

@ -0,0 +1,173 @@
//! Rust AST Visitor. Extracts useful information and massages it into a form
//! usable for clean
use syntax::abi::AbiSet;
use syntax::{ast, ast_map};
use syntax::codemap::span;
use doctree::*;
use std::local_data;
pub struct RustdocVisitor {
module: Module,
attrs: ~[ast::Attribute],
}
impl RustdocVisitor {
pub fn new() -> RustdocVisitor {
RustdocVisitor {
module: Module::new(None),
attrs: ~[],
}
}
}
impl RustdocVisitor {
pub fn visit(@mut self, crate: &ast::Crate) {
self.attrs = crate.attrs.clone();
fn visit_struct_def(item: &ast::item, sd: @ast::struct_def, generics:
&ast::Generics) -> Struct {
debug!("Visiting struct");
let struct_type = struct_type_from_def(sd);
Struct {
id: item.id,
struct_type: struct_type,
name: item.ident,
vis: item.vis,
attrs: item.attrs.clone(),
generics: generics.clone(),
fields: sd.fields.iter().map(|x| (*x).clone()).to_owned_vec(),
where: item.span
}
}
fn visit_enum_def(it: &ast::item, def: &ast::enum_def, params: &ast::Generics) -> Enum {
debug!("Visiting enum");
let mut vars: ~[Variant] = ~[];
for x in def.variants.iter() {
vars.push(Variant {
name: x.node.name,
attrs: x.node.attrs.clone(),
vis: x.node.vis,
id: x.node.id,
kind: x.node.kind.clone(),
where: x.span,
});
}
Enum {
name: it.ident,
variants: vars,
vis: it.vis,
generics: params.clone(),
attrs: it.attrs.clone(),
id: it.id,
where: it.span,
}
}
fn visit_fn(item: &ast::item, fd: &ast::fn_decl, _purity: &ast::purity,
_abi: &AbiSet, gen: &ast::Generics) -> Function {
debug!("Visiting fn");
Function {
id: item.id,
vis: item.vis,
attrs: item.attrs.clone(),
decl: fd.clone(),
name: item.ident,
where: item.span,
generics: gen.clone(),
}
}
fn visit_mod_contents(span: span, attrs: ~[ast::Attribute], vis:
ast::visibility, id: ast::NodeId, m: &ast::_mod) -> Module {
let am = local_data::get(super::ctxtkey, |x| *x.unwrap()).tycx.items;
let name = match am.find(&id) {
Some(m) => match m {
&ast_map::node_item(ref it, _) => Some(it.ident),
_ => fail!("mod id mapped to non-item in the ast map")
},
None => None
};
let mut om = Module::new(name);
om.view_items = m.view_items.clone();
om.where = span;
om.attrs = attrs;
om.vis = vis;
om.id = id;
for i in m.items.iter() {
visit_item(*i, &mut om);
}
om
}
fn visit_item(item: &ast::item, om: &mut Module) {
debug!("Visiting item %?", item);
match item.node {
ast::item_mod(ref m) => {
om.mods.push(visit_mod_contents(item.span, item.attrs.clone(),
item.vis, item.id, m));
},
ast::item_enum(ref ed, ref gen) => om.enums.push(visit_enum_def(item, ed, gen)),
ast::item_struct(sd, ref gen) => om.structs.push(visit_struct_def(item, sd, gen)),
ast::item_fn(ref fd, ref pur, ref abi, ref gen, _) =>
om.fns.push(visit_fn(item, fd, pur, abi, gen)),
ast::item_ty(ref ty, ref gen) => {
let t = Typedef {
ty: ty.clone(),
gen: gen.clone(),
name: item.ident,
id: item.id,
attrs: item.attrs.clone(),
where: item.span,
vis: item.vis,
};
om.typedefs.push(t);
},
ast::item_static(ref ty, ref mut_, ref exp) => {
let s = Static {
type_: ty.clone(),
mutability: mut_.clone(),
expr: exp.clone(),
id: item.id,
name: item.ident,
attrs: item.attrs.clone(),
where: item.span,
vis: item.vis,
};
om.statics.push(s);
},
ast::item_trait(ref gen, ref tr, ref met) => {
let t = Trait {
name: item.ident,
methods: met.clone(),
generics: gen.clone(),
parents: tr.clone(),
id: item.id,
attrs: item.attrs.clone(),
where: item.span,
vis: item.vis,
};
om.traits.push(t);
},
ast::item_impl(ref gen, ref tr, ref ty, ref meths) => {
let i = Impl {
generics: gen.clone(),
trait_: tr.clone(),
for_: ty.clone(),
methods: meths.clone(),
attrs: item.attrs.clone(),
id: item.id,
where: item.span,
vis: item.vis,
};
om.impls.push(i);
},
_ => (),
}
}
self.module = visit_mod_contents(crate.span, crate.attrs.clone(),
ast::public, ast::CRATE_NODE_ID, &crate.module);
}
}