diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs
index 382e299d28d..84c0f0b97a1 100644
--- a/src/librustdoc/html/format.rs
+++ b/src/librustdoc/html/format.rs
@@ -22,6 +22,7 @@ use syntax::ast;
use syntax::ast_util;
use clean;
+use stability_summary::ModuleSummary;
use html::item_type;
use html::item_type::ItemType;
use html::render;
@@ -631,3 +632,72 @@ impl<'a> fmt::Show for ConciseStability<'a> {
}
}
}
+
+impl fmt::Show for ModuleSummary {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ fn fmt_inner<'a>(f: &mut fmt::Formatter,
+ context: &mut Vec<&'a str>,
+ m: &'a ModuleSummary)
+ -> fmt::Result {
+ let cnt = m.counts;
+ let tot = cnt.total();
+ if tot == 0 { return Ok(()) }
+
+ context.push(m.name.as_slice());
+ let path = context.connect("::");
+
+ // the total width of each row's stability summary, in pixels
+ let width = 500;
+
+ try!(write!(f, "
"));
+ try!(write!(f, "\
+ {} | ",
+ Vec::from_slice(context.slice_from(1))
+ .append_one("index.html").connect("/"),
+ path));
+ try!(write!(f, ""));
+ try!(write!(f, " ",
+ (width * cnt.stable)/tot));
+ try!(write!(f, " ",
+ (width * cnt.unstable)/tot));
+ try!(write!(f, " ",
+ (width * cnt.experimental)/tot));
+ try!(write!(f, " ",
+ (width * cnt.deprecated)/tot));
+ try!(write!(f, " ",
+ (width * cnt.unmarked)/tot));
+ try!(write!(f, " |
"));
+
+ for submodule in m.submodules.iter() {
+ try!(fmt_inner(f, context, submodule));
+ }
+ context.pop();
+ Ok(())
+ }
+
+ let mut context = Vec::new();
+
+ try!(write!(f,
+r"Stability dashboard: crate {}
+This dashboard summarizes the stability levels for all of the public modules of
+the crate, according to the total number of items at each level in the module and its children:
+
+ stable,
+ unstable,
+ experimental,
+ deprecated,
+ unmarked
+
+The counts do not include methods or trait
+implementations that are visible only through a re-exported type.",
+self.name));
+ try!(write!(f, ""))
+ try!(fmt_inner(f, &mut context, self));
+ write!(f, "
")
+ }
+}
diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs
index 70edbcf86e1..3761f918332 100644
--- a/src/librustdoc/html/render.rs
+++ b/src/librustdoc/html/render.rs
@@ -43,6 +43,8 @@ use std::sync::Arc;
use externalfiles::ExternalHtml;
+use serialize::json;
+use serialize::Encodable;
use serialize::json::ToJson;
use syntax::ast;
use syntax::ast_util;
@@ -59,6 +61,7 @@ use html::item_type;
use html::layout;
use html::markdown::Markdown;
use html::markdown;
+use stability_summary;
/// Major driving force in all rustdoc rendering. This contains information
/// about where in the tree-like hierarchy rendering is occurring and controls
@@ -249,6 +252,11 @@ pub fn run(mut krate: clean::Crate, external_html: &ExternalHtml, dst: Path) ->
try!(mkdir(&cx.dst));
+ // Crawl the crate, building a summary of the stability levels. NOTE: this
+ // summary *must* be computed with the original `krate`; the folding below
+ // removes the impls from their modules.
+ let summary = stability_summary::build(&krate);
+
// Crawl the crate attributes looking for attributes which control how we're
// going to emit HTML
match krate.module.as_ref().map(|m| m.doc_list().unwrap_or(&[])) {
@@ -361,7 +369,7 @@ pub fn run(mut krate: clean::Crate, external_html: &ExternalHtml, dst: Path) ->
let krate = try!(render_sources(&mut cx, krate));
// And finally render the whole crate's documentation
- cx.krate(krate)
+ cx.krate(krate, summary)
}
fn build_index(krate: &clean::Crate, cache: &mut Cache) -> io::IoResult {
@@ -1045,13 +1053,34 @@ impl Context {
///
/// This currently isn't parallelized, but it'd be pretty easy to add
/// parallelization to this function.
- fn krate(self, mut krate: clean::Crate) -> io::IoResult<()> {
+ fn krate(mut self, mut krate: clean::Crate,
+ stability: stability_summary::ModuleSummary) -> io::IoResult<()> {
let mut item = match krate.module.take() {
Some(i) => i,
None => return Ok(())
};
item.name = Some(krate.name);
+ // render stability dashboard
+ try!(self.recurse(stability.name.clone(), |this| {
+ let json_dst = &this.dst.join("stability.json");
+ let mut json_out = BufferedWriter::new(try!(File::create(json_dst)));
+ try!(stability.encode(&mut json::Encoder::new(&mut json_out)));
+
+ let title = stability.name.clone().append(" - Stability dashboard");
+ let page = layout::Page {
+ ty: "mod",
+ root_path: this.root_path.as_slice(),
+ title: title.as_slice(),
+ };
+ let html_dst = &this.dst.join("stability.html");
+ let mut html_out = BufferedWriter::new(try!(File::create(html_dst)));
+ layout::render(&mut html_out, &this.layout, &page,
+ &Sidebar{ cx: this, item: &item },
+ &stability)
+ }));
+
+ // render the crate documentation
let mut work = vec!((self, item));
loop {
match work.pop() {
@@ -1061,6 +1090,7 @@ impl Context {
None => break,
}
}
+
Ok(())
}
@@ -1233,6 +1263,8 @@ impl<'a> Item<'a> {
}
}
+
+
impl<'a> fmt::Show for Item<'a> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
// Write the breadcrumb trail header for the top
@@ -1269,6 +1301,17 @@ impl<'a> fmt::Show for Item<'a> {
// Write stability level
try!(write!(fmt, "{}", Stability(&self.item.stability)));
+ // Links to out-of-band information, i.e. src and stability dashboard
+ try!(write!(fmt, ""));
+
+ // Write stability dashboard link
+ match self.item.inner {
+ clean::ModuleItem(ref m) if m.is_crate => {
+ try!(write!(fmt, "[stability dashboard] "));
+ }
+ _ => {}
+ };
+
// Write `src` tag
//
// When this item is part of a `pub use` in a downstream crate, the
@@ -1278,14 +1321,15 @@ impl<'a> fmt::Show for Item<'a> {
if self.cx.include_sources && !is_primitive {
match self.href() {
Some(l) => {
- try!(write!(fmt,
- "[src]",
+ try!(write!(fmt, "[src]",
self.item.def_id.node, l));
}
None => {}
}
}
+
+ try!(write!(fmt, ""));
+
try!(write!(fmt, "\n"));
match self.item.inner {
@@ -1355,6 +1399,7 @@ fn document(w: &mut fmt::Formatter, item: &clean::Item) -> fmt::Result {
fn item_module(w: &mut fmt::Formatter, cx: &Context,
item: &clean::Item, items: &[clean::Item]) -> fmt::Result {
try!(document(w, item));
+
let mut indices = range(0, items.len()).filter(|i| {
!ignore_private_item(&items[*i])
}).collect::>();
@@ -1514,6 +1559,7 @@ fn item_module(w: &mut fmt::Formatter, cx: &Context,
}
}
}
+
write!(w, "")
}
diff --git a/src/librustdoc/html/static/main.css b/src/librustdoc/html/static/main.css
index f65198fcfe2..4f790f96750 100644
--- a/src/librustdoc/html/static/main.css
+++ b/src/librustdoc/html/static/main.css
@@ -238,7 +238,7 @@ nav.sub {
.docblock h2 { font-size: 1.15em; }
.docblock h3, .docblock h4, .docblock h5 { font-size: 1em; }
-.content .source {
+.content .out-of-band {
float: right;
font-size: 23px;
}
@@ -409,6 +409,15 @@ h1 .stability {
.stability.Locked { border-color: #0084B6; color: #00668c; }
.stability.Unmarked { border-color: #FFFFFF; }
+.summary {
+ padding-right: 0px;
+}
+.summary.Deprecated { background-color: #A071A8; }
+.summary.Experimental { background-color: #D46D6A; }
+.summary.Unstable { background-color: #D4B16A; }
+.summary.Stable { background-color: #54A759; }
+.summary.Unmarked { background-color: #FFFFFF; }
+
:target { background: #FDFFD3; }
/* Code highlighting */
diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs
index fc2fe00afbc..76b9f11089f 100644
--- a/src/librustdoc/lib.rs
+++ b/src/librustdoc/lib.rs
@@ -56,6 +56,7 @@ pub mod html {
pub mod markdown;
pub mod passes;
pub mod plugins;
+pub mod stability_summary;
pub mod visit_ast;
pub mod test;
mod flock;
diff --git a/src/librustdoc/stability_summary.rs b/src/librustdoc/stability_summary.rs
new file mode 100644
index 00000000000..18e90d5d621
--- /dev/null
+++ b/src/librustdoc/stability_summary.rs
@@ -0,0 +1,174 @@
+// Copyright 2014 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 or the MIT license
+// , at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! This module crawls a `clean::Crate` and produces a summarization of the
+//! stability levels within the crate. The summary contains the module
+//! hierarchy, with item counts for every stability level per module. A parent
+//! module's count includes its childrens's.
+
+use std::ops::Add;
+use std::num::Zero;
+use std::iter::AdditiveIterator;
+
+use syntax::attr::{Deprecated, Experimental, Unstable, Stable, Frozen, Locked};
+use syntax::ast::Public;
+
+use clean::{Crate, Item, ModuleItem, Module, StructItem, Struct, EnumItem, Enum};
+use clean::{ImplItem, Impl, TraitItem, Trait, TraitMethod, Provided, Required};
+use clean::{ViewItemItem, PrimitiveItem};
+
+#[deriving(Zero, Encodable, Decodable, PartialEq, Eq)]
+/// The counts for each stability level.
+pub struct Counts {
+ pub deprecated: uint,
+ pub experimental: uint,
+ pub unstable: uint,
+ pub stable: uint,
+ pub frozen: uint,
+ pub locked: uint,
+
+ /// No stability level, inherited or otherwise.
+ pub unmarked: uint,
+}
+
+impl Add for Counts {
+ fn add(&self, other: &Counts) -> Counts {
+ Counts {
+ deprecated: self.deprecated + other.deprecated,
+ experimental: self.experimental + other.experimental,
+ unstable: self.unstable + other.unstable,
+ stable: self.stable + other.stable,
+ frozen: self.frozen + other.frozen,
+ locked: self.locked + other.locked,
+ unmarked: self.unmarked + other.unmarked,
+ }
+ }
+}
+
+impl Counts {
+ pub fn total(&self) -> uint {
+ self.deprecated + self.experimental + self.unstable + self.stable +
+ self.frozen + self.locked + self.unmarked
+ }
+}
+
+#[deriving(Encodable, Decodable, PartialEq, Eq)]
+/// A summarized module, which includes total counts and summarized chilcren
+/// modules.
+pub struct ModuleSummary {
+ pub name: String,
+ pub counts: Counts,
+ pub submodules: Vec,
+}
+
+impl PartialOrd for ModuleSummary {
+ fn partial_cmp(&self, other: &ModuleSummary) -> Option {
+ self.name.partial_cmp(&other.name)
+ }
+}
+
+impl Ord for ModuleSummary {
+ fn cmp(&self, other: &ModuleSummary) -> Ordering {
+ self.name.cmp(&other.name)
+ }
+}
+
+// is the item considered publically visible?
+fn visible(item: &Item) -> bool {
+ match item.inner {
+ ImplItem(_) => true,
+ _ => item.visibility == Some(Public)
+ }
+}
+
+// Produce the summary for an arbitrary item. If the item is a module, include a
+// module summary. The counts for items with nested items (e.g. modules, traits,
+// impls) include all children counts.
+fn summarize_item(item: &Item) -> (Counts, Option) {
+ // count this item
+ let item_counts = match item.stability {
+ None => Counts { unmarked: 1, .. Zero::zero() },
+ Some(ref stab) => match stab.level {
+ Deprecated => Counts { deprecated: 1, .. Zero::zero() },
+ Experimental => Counts { experimental: 1, .. Zero::zero() },
+ Unstable => Counts { unstable: 1, .. Zero::zero() },
+ Stable => Counts { stable: 1, .. Zero::zero() },
+ Frozen => Counts { frozen: 1, .. Zero::zero() },
+ Locked => Counts { locked: 1, .. Zero::zero() },
+ }
+ };
+
+ // Count this item's children, if any. Note that a trait impl is
+ // considered to have no children.
+ match item.inner {
+ // Require explicit `pub` to be visible
+ StructItem(Struct { fields: ref subitems, .. }) |
+ ImplItem(Impl { methods: ref subitems, trait_: None, .. }) => {
+ let subcounts = subitems.iter().filter(|i| visible(*i))
+ .map(summarize_item)
+ .map(|s| s.val0())
+ .sum();
+ (item_counts + subcounts, None)
+ }
+ // `pub` automatically
+ EnumItem(Enum { variants: ref subitems, .. }) => {
+ let subcounts = subitems.iter().map(summarize_item)
+ .map(|s| s.val0())
+ .sum();
+ (item_counts + subcounts, None)
+ }
+ TraitItem(Trait { methods: ref methods, .. }) => {
+ fn extract_item<'a>(meth: &'a TraitMethod) -> &'a Item {
+ match *meth {
+ Provided(ref item) | Required(ref item) => item
+ }
+ }
+ let subcounts = methods.iter().map(extract_item)
+ .map(summarize_item)
+ .map(|s| s.val0())
+ .sum();
+ (item_counts + subcounts, None)
+ }
+ ModuleItem(Module { items: ref items, .. }) => {
+ let mut counts = item_counts;
+ let mut submodules = Vec::new();
+
+ for (subcounts, submodule) in items.iter().filter(|i| visible(*i))
+ .map(summarize_item) {
+ counts = counts + subcounts;
+ submodule.map(|m| submodules.push(m));
+ }
+ submodules.sort();
+
+ (counts, Some(ModuleSummary {
+ name: item.name.as_ref().map_or("".to_string(), |n| n.clone()),
+ counts: counts,
+ submodules: submodules,
+ }))
+ }
+ // no stability information for the following items:
+ ViewItemItem(_) | PrimitiveItem(_) => (Zero::zero(), None),
+ _ => (item_counts, None)
+ }
+}
+
+/// Summarizes the stability levels in a crate.
+pub fn build(krate: &Crate) -> ModuleSummary {
+ match krate.module {
+ None => ModuleSummary {
+ name: krate.name.clone(),
+ counts: Zero::zero(),
+ submodules: Vec::new(),
+ },
+ Some(ref item) => ModuleSummary {
+ name: krate.name.clone(), .. summarize_item(item).val1().unwrap()
+ }
+ }
+}