about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorAaron Turon <aturon@mozilla.com>2014-07-04 00:51:46 -0700
committerAaron Turon <aturon@mozilla.com>2014-07-10 20:51:35 -0700
commit4d16de01d0beb84dc4a351022ea5cb587b4ab557 (patch)
treef51d913f9828ac455dee055e58f2f9846f8c8868 /src
parentb57d272e9908e164a72bd1a688141031705e1208 (diff)
downloadrust-4d16de01d0beb84dc4a351022ea5cb587b4ab557.tar.gz
rust-4d16de01d0beb84dc4a351022ea5cb587b4ab557.zip
rustdoc: Add stability dashboard
This commit adds a crate-level dashboard summarizing the stability
levels of all items for all submodules of the crate.

The information is also written as a json file, intended for consumption
by pages like http://huonw.github.io/isrustfastyet/

Closes #13541
Diffstat (limited to 'src')
-rw-r--r--src/librustdoc/html/format.rs70
-rw-r--r--src/librustdoc/html/render.rs56
-rw-r--r--src/librustdoc/html/static/main.css11
-rw-r--r--src/librustdoc/lib.rs1
-rw-r--r--src/librustdoc/stability_summary.rs174
5 files changed, 306 insertions, 6 deletions
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, "<tr>"));
+            try!(write!(f, "<td class='summary'>\
+                            <a class='summary' href='{}'>{}</a></td>",
+                        Vec::from_slice(context.slice_from(1))
+                            .append_one("index.html").connect("/"),
+                        path));
+            try!(write!(f, "<td>"));
+            try!(write!(f, "<span class='summary Stable' \
+                            style='width: {}px; display: inline-block'>&nbsp</span>",
+                        (width * cnt.stable)/tot));
+            try!(write!(f, "<span class='summary Unstable' \
+                            style='width: {}px; display: inline-block'>&nbsp</span>",
+                        (width * cnt.unstable)/tot));
+            try!(write!(f, "<span class='summary Experimental' \
+                            style='width: {}px; display: inline-block'>&nbsp</span>",
+                        (width * cnt.experimental)/tot));
+            try!(write!(f, "<span class='summary Deprecated' \
+                            style='width: {}px; display: inline-block'>&nbsp</span>",
+                        (width * cnt.deprecated)/tot));
+            try!(write!(f, "<span class='summary Unmarked' \
+                            style='width: {}px; display: inline-block'>&nbsp</span>",
+                        (width * cnt.unmarked)/tot));
+            try!(write!(f, "</td></tr>"));
+
+            for submodule in m.submodules.iter() {
+                try!(fmt_inner(f, context, submodule));
+            }
+            context.pop();
+            Ok(())
+        }
+
+        let mut context = Vec::new();
+
+        try!(write!(f,
+r"<h1 class='fqn'>Stability dashboard: crate <a class='mod' href='index.html'>{}</a></h1>
+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:
+<blockquote>
+<a class='stability Stable'></a> stable,<br/>
+<a class='stability Unstable'></a> unstable,<br/>
+<a class='stability Experimental'></a> experimental,<br/>
+<a class='stability Deprecated'></a> deprecated,<br/>
+<a class='stability Unmarked'></a> unmarked
+</blockquote>
+The counts do not include methods or trait
+implementations that are visible only through a re-exported type.",
+self.name));
+        try!(write!(f, "<table>"))
+        try!(fmt_inner(f, &mut context, self));
+        write!(f, "</table>")
+    }
+}
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<String> {
@@ -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, "<span class='out-of-band'>"));
+
+        // Write stability dashboard link
+        match self.item.inner {
+            clean::ModuleItem(ref m) if m.is_crate => {
+                try!(write!(fmt, "<a href='stability.html'>[stability dashboard]</a> "));
+            }
+            _ => {}
+        };
+
         // 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,
-                                "<a class='source' id='src-{}' \
-                                    href='{}'>[src]</a>",
+                    try!(write!(fmt, "<a id='src-{}' href='{}'>[src]</a>",
                                 self.item.def_id.node, l));
                 }
                 None => {}
             }
         }
+
+        try!(write!(fmt, "</span>"));
+
         try!(write!(fmt, "</h1>\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::<Vec<uint>>();
@@ -1514,6 +1559,7 @@ fn item_module(w: &mut fmt::Formatter, cx: &Context,
             }
         }
     }
+
     write!(w, "</table>")
 }
 
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 <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.
+
+//! 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<Counts, Counts> 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<ModuleSummary>,
+}
+
+impl PartialOrd for ModuleSummary {
+    fn partial_cmp(&self, other: &ModuleSummary) -> Option<Ordering> {
+        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<ModuleSummary>) {
+    // 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()
+        }
+    }
+}