1
Fork 0

rustdoc: Overhaul stability displays

This commit is an overhaul to how rustdoc deals with stability of the standard
library. The handling has all been revisited with respect to Rust's current
approach to stability in terms of implementation as well as the state of the
standard library today. The high level changes made were:

* Stable items now have no marker by default
* Color-based small stability markers have been removed
* Module listings now fade out unstable/deprecated items slightly
* Trait methods have a separate background color based on stability and also
  list the reason that they are unstable.
* `impl` blocks with stability no longer render at all. This may be re-added
  once the compiler recognizes stability on `impl` blocks.
* `impl` blocks no longer have stability of the methods implemente indicated
* The stability summary has been removed

Closes #15468
Closes #21674
Closes #24201
This commit is contained in:
Alex Crichton 2015-04-13 11:55:00 -07:00
parent 5576b0558c
commit 0a46933c4d
6 changed files with 132 additions and 447 deletions

View file

@ -56,19 +56,20 @@ use serialize::json::ToJson;
use syntax::abi;
use syntax::ast;
use syntax::ast_util;
use syntax::attr;
use rustc::util::nodemap::NodeSet;
use clean;
use doctree;
use fold::DocFolder;
use html::format::{VisSpace, Method, UnsafetySpace, MutableSpace, Stability};
use html::format::{ConciseStability, TyParamBounds, WhereClause, href, AbiSpace};
use html::escape::Escape;
use html::format::{TyParamBounds, WhereClause, href, AbiSpace};
use html::format::{VisSpace, Method, UnsafetySpace, MutableSpace};
use html::highlight;
use html::item_type::ItemType;
use html::layout;
use html::markdown::Markdown;
use html::markdown;
use stability_summary;
/// A pair of name and its optional document.
pub type NameDoc = (String, Option<String>);
@ -437,11 +438,8 @@ pub fn run(mut krate: clean::Crate,
try!(write_shared(&cx, &krate, &*cache, index));
let krate = try!(render_sources(&mut cx, krate));
// Crawl the crate, building a summary of the stability levels.
let summary = stability_summary::build(&krate);
// And finally render the whole crate's documentation
cx.krate(krate, summary)
cx.krate(krate)
}
fn build_index(krate: &clean::Crate, cache: &mut Cache) -> io::Result<String> {
@ -645,8 +643,7 @@ fn write_shared(cx: &Context,
// going on). If they're in different crates then the crate defining
// the trait will be interested in our implementation.
if imp.def_id.krate == did.krate { continue }
try!(write!(&mut f, r#""{}impl{} {}{} for {}","#,
ConciseStability(&imp.stability),
try!(write!(&mut f, r#""impl{} {}{} for {}","#,
imp.generics,
if imp.polarity == Some(clean::ImplPolarity::Negative) { "!" } else { "" },
imp.trait_, imp.for_));
@ -1143,38 +1140,13 @@ impl Context {
///
/// This currently isn't parallelized, but it'd be pretty easy to add
/// parallelization to this function.
fn krate(mut self, mut krate: clean::Crate,
stability: stability_summary::ModuleSummary) -> io::Result<()> {
fn krate(self, mut krate: clean::Crate) -> io::Result<()> {
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 = BufWriter::new(try!(File::create(json_dst)));
try!(write!(&mut json_out, "{}", json::as_json(&stability)));
let mut title = stability.name.clone();
title.push_str(" - Stability dashboard");
let desc = format!("API stability overview for the Rust `{}` crate.",
this.layout.krate);
let page = layout::Page {
ty: "mod",
root_path: &this.root_path,
title: &title,
description: &desc,
keywords: get_basic_keywords(),
};
let html_dst = &this.dst.join("stability.html");
let mut html_out = BufWriter::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 {
@ -1456,21 +1428,8 @@ impl<'a> fmt::Display for Item<'a> {
try!(write!(fmt, "<a class='{}' href=''>{}</a>",
shortty(self.item), self.item.name.as_ref().unwrap()));
// Write stability level
try!(write!(fmt, "<wbr>{}", Stability(&self.item.stability)));
try!(write!(fmt, "</span>")); // in-band
// 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]</a> "));
}
_ => {}
};
try!(write!(fmt,
r##"<span id='render-detail'>
<a id="collapse-all" href="#">[-]</a>&nbsp;<a id="expand-all" href="#">[+]</a>
@ -1554,11 +1513,11 @@ fn plain_summary_line(s: Option<&str>) -> String {
}
fn document(w: &mut fmt::Formatter, item: &clean::Item) -> fmt::Result {
match item.doc_value() {
Some(s) => {
try!(write!(w, "<div class='docblock'>{}</div>", Markdown(s)));
}
None => {}
if let Some(s) = short_stability(item, true) {
try!(write!(w, "<div class='stability'>{}</div>", s));
}
if let Some(s) = item.doc_value() {
try!(write!(w, "<div class='docblock'>{}</div>", Markdown(s)));
}
Ok(())
}
@ -1593,10 +1552,17 @@ fn item_module(w: &mut fmt::Formatter, cx: &Context,
fn cmp(i1: &clean::Item, i2: &clean::Item, idx1: usize, idx2: usize) -> Ordering {
let ty1 = shortty(i1);
let ty2 = shortty(i2);
if ty1 == ty2 {
return i1.name.cmp(&i2.name);
if ty1 != ty2 {
return (reorder(ty1), idx1).cmp(&(reorder(ty2), idx2))
}
(reorder(ty1), idx1).cmp(&(reorder(ty2), idx2))
let s1 = i1.stability.as_ref().map(|s| s.level);
let s2 = i2.stability.as_ref().map(|s| s.level);
match (s1, s2) {
(Some(attr::Unstable), Some(attr::Stable)) => return Ordering::Greater,
(Some(attr::Stable), Some(attr::Unstable)) => return Ordering::Less,
_ => {}
}
i1.name.cmp(&i2.name)
}
indices.sort_by(|&i1, &i2| cmp(&items[i1], &items[i2], i1, i2));
@ -1665,19 +1631,27 @@ fn item_module(w: &mut fmt::Formatter, cx: &Context,
_ => {
if myitem.name.is_none() { continue }
let stab_docs = if let Some(s) = short_stability(myitem, false) {
format!("[{}]", s)
} else {
String::new()
};
try!(write!(w, "
<tr>
<td>{stab}<a class='{class}' href='{href}'
title='{title}'>{}</a></td>
<td class='docblock short'>{}</td>
<tr class='{stab} module-item'>
<td><a class='{class}' href='{href}'
title='{title}'>{name}</a></td>
<td class='docblock short'>
{stab_docs} {docs}
</td>
</tr>
",
*myitem.name.as_ref().unwrap(),
Markdown(&shorter(myitem.doc_value())[..]),
name = *myitem.name.as_ref().unwrap(),
stab_docs = stab_docs,
docs = Markdown(&shorter(myitem.doc_value())),
class = shortty(myitem),
stab = myitem.stability_class(),
href = item_path(myitem),
title = full_path(cx, myitem),
stab = ConciseStability(&myitem.stability)));
title = full_path(cx, myitem)));
}
}
}
@ -1685,6 +1659,30 @@ fn item_module(w: &mut fmt::Formatter, cx: &Context,
write!(w, "</table>")
}
fn short_stability(item: &clean::Item, show_reason: bool) -> Option<String> {
item.stability.as_ref().and_then(|stab| {
let reason = if show_reason && stab.reason.len() > 0 {
format!(": {}", stab.reason)
} else {
String::new()
};
let text = if stab.deprecated_since.len() > 0 {
let since = if show_reason {
format!(" since {}", Escape(&stab.deprecated_since))
} else {
String::new()
};
format!("Deprecated{}{}", since, Markdown(&reason))
} else if stab.level == attr::Unstable {
format!("Unstable{}", Markdown(&reason))
} else {
return None
};
Some(format!("<em class='stab {}'>{}</em>",
item.stability_class(), text))
})
}
struct Initializer<'a>(&'a str);
impl<'a> fmt::Display for Initializer<'a> {
@ -1800,10 +1798,10 @@ fn item_trait(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item,
fn trait_item(w: &mut fmt::Formatter, m: &clean::Item)
-> fmt::Result {
try!(write!(w, "<h3 id='{}.{}' class='method'>{}<code>",
shortty(m),
*m.name.as_ref().unwrap(),
ConciseStability(&m.stability)));
try!(write!(w, "<h3 id='{ty}.{name}' class='method stab {stab}'><code>",
ty = shortty(m),
name = *m.name.as_ref().unwrap(),
stab = m.stability_class()));
try!(render_method(w, m, MethodLink::Anchor));
try!(write!(w, "</code></h3>"));
try!(document(w, m));
@ -1854,8 +1852,7 @@ fn item_trait(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item,
match cache.implementors.get(&it.def_id) {
Some(implementors) => {
for i in implementors {
try!(writeln!(w, "<li>{}<code>impl{} {} for {}{}</code></li>",
ConciseStability(&i.stability),
try!(writeln!(w, "<li><code>impl{} {} for {}{}</code></li>",
i.generics, i.trait_, i.for_, WhereClause(&i.generics)));
}
}
@ -1964,9 +1961,10 @@ fn item_struct(w: &mut fmt::Formatter, it: &clean::Item,
if fields.peek().is_some() {
try!(write!(w, "<h2 class='fields'>Fields</h2>\n<table>"));
for field in fields {
try!(write!(w, "<tr><td id='structfield.{name}'>\
{stab}<code>{name}</code></td><td>",
stab = ConciseStability(&field.stability),
try!(write!(w, "<tr class='stab {stab}'>
<td id='structfield.{name}'>\
<code>{name}</code></td><td>",
stab = field.stability_class(),
name = field.name.as_ref().unwrap()));
try!(document(w, field));
try!(write!(w, "</td></tr>"));
@ -2034,8 +2032,7 @@ fn item_enum(w: &mut fmt::Formatter, it: &clean::Item,
if !e.variants.is_empty() {
try!(write!(w, "<h2 class='variants'>Variants</h2>\n<table>"));
for variant in &e.variants {
try!(write!(w, "<tr><td id='variant.{name}'>{stab}<code>{name}</code></td><td>",
stab = ConciseStability(&variant.stability),
try!(write!(w, "<tr><td id='variant.{name}'><code>{name}</code></td><td>",
name = variant.name.as_ref().unwrap()));
try!(document(w, variant));
match variant.inner {
@ -2200,8 +2197,7 @@ fn render_methods(w: &mut fmt::Formatter, it: &clean::Item) -> fmt::Result {
fn render_impl(w: &mut fmt::Formatter, i: &Impl, link: MethodLink)
-> fmt::Result {
try!(write!(w, "<h3 class='impl'>{}<code>impl{} ",
ConciseStability(&i.stability),
try!(write!(w, "<h3 class='impl'><code>impl{} ",
i.impl_.generics));
if let Some(clean::ImplPolarity::Negative) = i.impl_.polarity {
try!(write!(w, "!"));
@ -2216,48 +2212,43 @@ fn render_impl(w: &mut fmt::Formatter, i: &Impl, link: MethodLink)
}
fn doctraititem(w: &mut fmt::Formatter, item: &clean::Item,
dox: bool, link: MethodLink) -> fmt::Result {
link: MethodLink) -> fmt::Result {
match item.inner {
clean::MethodItem(..) | clean::TyMethodItem(..) => {
try!(write!(w, "<h4 id='method.{}' class='{}'>{}<code>",
try!(write!(w, "<h4 id='method.{}' class='{}'><code>",
*item.name.as_ref().unwrap(),
shortty(item),
ConciseStability(&item.stability)));
shortty(item)));
try!(render_method(w, item, link));
try!(write!(w, "</code></h4>\n"));
}
clean::TypedefItem(ref tydef) => {
let name = item.name.as_ref().unwrap();
try!(write!(w, "<h4 id='assoc_type.{}' class='{}'>{}<code>",
try!(write!(w, "<h4 id='assoc_type.{}' class='{}'><code>",
*name,
shortty(item),
ConciseStability(&item.stability)));
shortty(item)));
try!(write!(w, "type {} = {}", name, tydef.type_));
try!(write!(w, "</code></h4>\n"));
}
clean::AssociatedTypeItem(ref bounds, ref default) => {
let name = item.name.as_ref().unwrap();
try!(write!(w, "<h4 id='assoc_type.{}' class='{}'>{}<code>",
try!(write!(w, "<h4 id='assoc_type.{}' class='{}'><code>",
*name,
shortty(item),
ConciseStability(&item.stability)));
shortty(item)));
try!(assoc_type(w, item, bounds, default));
try!(write!(w, "</code></h4>\n"));
}
_ => panic!("can't make docs for trait item with name {:?}", item.name)
}
match item.doc_value() {
Some(s) if dox => {
try!(write!(w, "<div class='docblock'>{}</div>", Markdown(s)));
Ok(())
}
Some(..) | None => Ok(())
if let MethodLink::Anchor = link {
document(w, item)
} else {
Ok(())
}
}
try!(write!(w, "<div class='impl-items'>"));
for trait_item in i.impl_.items.iter() {
try!(doctraititem(w, trait_item, true, link));
try!(doctraititem(w, trait_item, link));
}
fn render_default_methods(w: &mut fmt::Formatter,
@ -2271,8 +2262,7 @@ fn render_impl(w: &mut fmt::Formatter, i: &Impl, link: MethodLink)
None => {}
}
try!(doctraititem(w, trait_item, false,
MethodLink::GotoSource(did)));
try!(doctraititem(w, trait_item, MethodLink::GotoSource(did)));
}
Ok(())
}