rustdoc: Link "Trait Implementations" to sources

All methods listed in "Trait Implementations" now hyperlink to the source trait
instead of themselves, allowing easy browsing of the documentation of a trait
method.

Closes #17476
This commit is contained in:
Alex Crichton 2015-04-06 17:56:35 -07:00
parent fcc89ea500
commit 641bca06c8
4 changed files with 144 additions and 106 deletions

View File

@ -281,48 +281,46 @@ impl fmt::Display for clean::Path {
}
}
/// Used when rendering a `ResolvedPath` structure. This invokes the `path`
/// rendering function with the necessary arguments for linking to a local path.
fn resolved_path(w: &mut fmt::Formatter, did: ast::DefId, p: &clean::Path,
print_all: bool) -> fmt::Result {
path(w, p, print_all,
|cache, loc| {
if ast_util::is_local(did) || cache.inlined.contains(&did) {
Some(repeat("../").take(loc.len()).collect::<String>())
} else {
match cache.extern_locations[&did.krate] {
render::Remote(ref s) => Some(s.to_string()),
render::Local => {
Some(repeat("../").take(loc.len()).collect::<String>())
}
render::Unknown => None,
}
}
},
|cache| {
match cache.paths.get(&did) {
None => None,
Some(&(ref fqp, shortty)) => Some((fqp.clone(), shortty))
}
})
pub fn href(did: ast::DefId) -> Option<(String, ItemType, Vec<String>)> {
let cache = cache();
let loc = CURRENT_LOCATION_KEY.with(|l| l.borrow().clone());
let &(ref fqp, shortty) = match cache.paths.get(&did) {
Some(p) => p,
None => return None,
};
let mut url = if ast_util::is_local(did) || cache.inlined.contains(&did) {
repeat("../").take(loc.len()).collect::<String>()
} else {
match cache.extern_locations[&did.krate] {
render::Remote(ref s) => s.to_string(),
render::Local => repeat("../").take(loc.len()).collect::<String>(),
render::Unknown => return None,
}
};
for component in &fqp[..fqp.len() - 1] {
url.push_str(component);
url.push_str("/");
}
match shortty {
ItemType::Module => {
url.push_str(fqp.last().unwrap());
url.push_str("/index.html");
}
_ => {
url.push_str(shortty.to_static_str());
url.push_str(".");
url.push_str(fqp.last().unwrap());
url.push_str(".html");
}
}
Some((url, shortty, fqp.to_vec()))
}
fn path<F, G>(w: &mut fmt::Formatter,
path: &clean::Path,
print_all: bool,
root: F,
info: G)
-> fmt::Result where
F: FnOnce(&render::Cache, &[String]) -> Option<String>,
G: FnOnce(&render::Cache) -> Option<(Vec<String>, ItemType)>,
{
// The generics will get written to both the title and link
/// Used when rendering a `ResolvedPath` structure. This invokes the `path`
/// rendering function with the necessary arguments for linking to a local path.
fn resolved_path(w: &mut fmt::Formatter, did: ast::DefId, path: &clean::Path,
print_all: bool) -> fmt::Result {
let last = path.segments.last().unwrap();
let generics = format!("{}", last.params);
let loc = CURRENT_LOCATION_KEY.with(|l| l.borrow().clone());
let cache = cache();
let abs_root = root(&*cache, &loc);
let rel_root = match &*path.segments[0].name {
"self" => Some("./".to_string()),
_ => None,
@ -334,8 +332,7 @@ fn path<F, G>(w: &mut fmt::Formatter,
Some(root) => {
let mut root = String::from_str(&root);
for seg in &path.segments[..amt] {
if "super" == seg.name ||
"self" == seg.name {
if "super" == seg.name || "self" == seg.name {
try!(write!(w, "{}::", seg.name));
} else {
root.push_str(&seg.name);
@ -355,37 +352,14 @@ fn path<F, G>(w: &mut fmt::Formatter,
}
}
match info(&*cache) {
// This is a documented path, link to it!
Some((ref fqp, shortty)) if abs_root.is_some() => {
let mut url = String::from_str(&abs_root.unwrap());
let to_link = &fqp[..fqp.len() - 1];
for component in to_link {
url.push_str(component);
url.push_str("/");
}
match shortty {
ItemType::Module => {
url.push_str(fqp.last().unwrap());
url.push_str("/index.html");
}
_ => {
url.push_str(shortty.to_static_str());
url.push_str(".");
url.push_str(fqp.last().unwrap());
url.push_str(".html");
}
}
match href(did) {
Some((url, shortty, fqp)) => {
try!(write!(w, "<a class='{}' href='{}' title='{}'>{}</a>",
shortty, url, fqp.connect("::"), last.name));
}
_ => {
try!(write!(w, "{}", last.name));
}
_ => try!(write!(w, "{}", last.name)),
}
try!(write!(w, "{}", generics));
try!(write!(w, "{}", last.params));
Ok(())
}

View File

@ -62,7 +62,7 @@ use clean;
use doctree;
use fold::DocFolder;
use html::format::{VisSpace, Method, UnsafetySpace, MutableSpace, Stability};
use html::format::{ConciseStability, TyParamBounds, WhereClause};
use html::format::{ConciseStability, TyParamBounds, WhereClause, href};
use html::highlight;
use html::item_type::ItemType;
use html::layout;
@ -137,6 +137,14 @@ pub struct Impl {
pub stability: Option<clean::Stability>,
}
impl Impl {
fn trait_did(&self) -> Option<ast::DefId> {
self.impl_.trait_.as_ref().and_then(|tr| {
if let clean::ResolvedPath { did, .. } = *tr {Some(did)} else {None}
})
}
}
/// This cache is used to store information about the `clean::Crate` being
/// rendered in order to provide more useful documentation. This contains
/// information like all implementors of a trait, all traits a type implements,
@ -277,7 +285,9 @@ impl fmt::Display for IndexItemFunctionType {
return write!(f, "null")
}
let inputs: Vec<String> = self.inputs.iter().map(|ref t| format!("{}", t)).collect();
let inputs: Vec<String> = self.inputs.iter().map(|ref t| {
format!("{}", t)
}).collect();
try!(write!(f, "{{\"inputs\":[{}],\"output\":", inputs.connect(",")));
match self.output {
@ -1780,7 +1790,7 @@ fn item_trait(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item,
try!(write!(w, "{{\n"));
for t in &types {
try!(write!(w, " "));
try!(render_method(w, t));
try!(render_method(w, t, MethodLink::Anchor));
try!(write!(w, ";\n"));
}
if types.len() > 0 && required.len() > 0 {
@ -1788,7 +1798,7 @@ fn item_trait(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item,
}
for m in &required {
try!(write!(w, " "));
try!(render_method(w, m));
try!(render_method(w, m, MethodLink::Anchor));
try!(write!(w, ";\n"));
}
if required.len() > 0 && provided.len() > 0 {
@ -1796,7 +1806,7 @@ fn item_trait(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item,
}
for m in &provided {
try!(write!(w, " "));
try!(render_method(w, m));
try!(render_method(w, m, MethodLink::Anchor));
try!(write!(w, " {{ ... }}\n"));
}
try!(write!(w, "}}"));
@ -1812,7 +1822,7 @@ fn item_trait(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item,
shortty(m),
*m.name.as_ref().unwrap(),
ConciseStability(&m.stability)));
try!(render_method(w, m));
try!(render_method(w, m, MethodLink::Anchor));
try!(write!(w, "</code></h3>"));
try!(document(w, m));
Ok(())
@ -1896,14 +1906,23 @@ fn assoc_type(w: &mut fmt::Formatter, it: &clean::Item,
Ok(())
}
fn render_method(w: &mut fmt::Formatter, meth: &clean::Item) -> fmt::Result {
fn render_method(w: &mut fmt::Formatter, meth: &clean::Item,
link: MethodLink) -> fmt::Result {
fn method(w: &mut fmt::Formatter, it: &clean::Item,
unsafety: ast::Unsafety, abi: abi::Abi,
g: &clean::Generics, selfty: &clean::SelfTy,
d: &clean::FnDecl) -> fmt::Result {
d: &clean::FnDecl, link: MethodLink) -> fmt::Result {
use syntax::abi::Abi;
write!(w, "{}{}fn <a href='#{ty}.{name}' class='fnname'>{name}</a>\
let name = it.name.as_ref().unwrap();
let anchor = format!("#{}.{}", shortty(it), name);
let href = match link {
MethodLink::Anchor => anchor,
MethodLink::GotoSource(did) => {
href(did).map(|p| format!("{}{}", p.0, anchor)).unwrap_or(anchor)
}
};
write!(w, "{}{}fn <a href='{href}' class='fnname'>{name}</a>\
{generics}{decl}{where_clause}",
match unsafety {
ast::Unsafety::Unsafe => "unsafe ",
@ -1913,18 +1932,20 @@ fn render_method(w: &mut fmt::Formatter, meth: &clean::Item) -> fmt::Result {
Abi::Rust => String::new(),
a => format!("extern {} ", a.to_string())
},
ty = shortty(it),
name = it.name.as_ref().unwrap(),
href = href,
name = name,
generics = *g,
decl = Method(selfty, d),
where_clause = WhereClause(g))
}
match meth.inner {
clean::TyMethodItem(ref m) => {
method(w, meth, m.unsafety, m.abi, &m.generics, &m.self_, &m.decl)
method(w, meth, m.unsafety, m.abi, &m.generics, &m.self_, &m.decl,
link)
}
clean::MethodItem(ref m) => {
method(w, meth, m.unsafety, m.abi, &m.generics, &m.self_, &m.decl)
method(w, meth, m.unsafety, m.abi, &m.generics, &m.self_, &m.decl,
link)
}
clean::AssociatedTypeItem(ref bounds, ref default) => {
assoc_type(w, meth, bounds, default)
@ -2151,6 +2172,12 @@ fn render_struct(w: &mut fmt::Formatter, it: &clean::Item,
Ok(())
}
#[derive(Copy, Clone)]
enum MethodLink {
Anchor,
GotoSource(ast::DefId),
}
fn render_methods(w: &mut fmt::Formatter, it: &clean::Item) -> fmt::Result {
match cache().impls.get(&it.def_id) {
Some(v) => {
@ -2159,7 +2186,7 @@ fn render_methods(w: &mut fmt::Formatter, it: &clean::Item) -> fmt::Result {
if non_trait.len() > 0 {
try!(write!(w, "<h2 id='methods'>Methods</h2>"));
for i in &non_trait {
try!(render_impl(w, i));
try!(render_impl(w, i, MethodLink::Anchor));
}
}
if traits.len() > 0 {
@ -2168,13 +2195,16 @@ fn render_methods(w: &mut fmt::Formatter, it: &clean::Item) -> fmt::Result {
let (derived, manual): (Vec<_>, _) = traits.into_iter()
.partition(|i| i.impl_.derived);
for i in &manual {
try!(render_impl(w, i));
let did = i.trait_did().unwrap();
try!(render_impl(w, i, MethodLink::GotoSource(did)));
}
if derived.len() > 0 {
try!(write!(w, "<h3 id='derived_implementations'>Derived Implementations \
</h3>"));
try!(write!(w, "<h3 id='derived_implementations'>\
Derived Implementations \
</h3>"));
for i in &derived {
try!(render_impl(w, i));
let did = i.trait_did().unwrap();
try!(render_impl(w, i, MethodLink::GotoSource(did)));
}
}
}
@ -2184,36 +2214,32 @@ fn render_methods(w: &mut fmt::Formatter, it: &clean::Item) -> fmt::Result {
Ok(())
}
fn render_impl(w: &mut fmt::Formatter, i: &Impl) -> 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),
i.impl_.generics));
match i.impl_.polarity {
Some(clean::ImplPolarity::Negative) => try!(write!(w, "!")),
_ => {}
if let Some(clean::ImplPolarity::Negative) = i.impl_.polarity {
try!(write!(w, "!"));
}
match i.impl_.trait_ {
Some(ref ty) => try!(write!(w, "{} for ", *ty)),
None => {}
if let Some(ref ty) = i.impl_.trait_ {
try!(write!(w, "{} for ", *ty));
}
try!(write!(w, "{}{}</code></h3>", i.impl_.for_, WhereClause(&i.impl_.generics)));
match i.dox {
Some(ref dox) => {
try!(write!(w, "<div class='docblock'>{}</div>",
Markdown(dox)));
}
None => {}
try!(write!(w, "{}{}</code></h3>", i.impl_.for_,
WhereClause(&i.impl_.generics)));
if let Some(ref dox) = i.dox {
try!(write!(w, "<div class='docblock'>{}</div>", Markdown(dox)));
}
fn doctraititem(w: &mut fmt::Formatter, item: &clean::Item, dox: bool)
-> fmt::Result {
fn doctraititem(w: &mut fmt::Formatter, item: &clean::Item,
dox: bool, link: MethodLink) -> fmt::Result {
match item.inner {
clean::MethodItem(..) | clean::TyMethodItem(..) => {
try!(write!(w, "<h4 id='method.{}' class='{}'>{}<code>",
*item.name.as_ref().unwrap(),
shortty(item),
ConciseStability(&item.stability)));
try!(render_method(w, item));
try!(render_method(w, item, link));
try!(write!(w, "</code></h4>\n"));
}
clean::TypedefItem(ref tydef) => {
@ -2247,10 +2273,11 @@ fn render_impl(w: &mut fmt::Formatter, i: &Impl) -> fmt::Result {
try!(write!(w, "<div class='impl-items'>"));
for trait_item in i.impl_.items.iter() {
try!(doctraititem(w, trait_item, true));
try!(doctraititem(w, trait_item, true, link));
}
fn render_default_methods(w: &mut fmt::Formatter,
did: ast::DefId,
t: &clean::Trait,
i: &clean::Impl) -> fmt::Result {
for trait_item in &t.items {
@ -2260,7 +2287,8 @@ fn render_impl(w: &mut fmt::Formatter, i: &Impl) -> fmt::Result {
None => {}
}
try!(doctraititem(w, trait_item, false));
try!(doctraititem(w, trait_item, false,
MethodLink::GotoSource(did)));
}
Ok(())
}
@ -2271,7 +2299,7 @@ fn render_impl(w: &mut fmt::Formatter, i: &Impl) -> fmt::Result {
// for them work.
if let Some(clean::ResolvedPath { did, .. }) = i.impl_.trait_ {
if let Some(t) = cache().traits.get(&did) {
try!(render_default_methods(w, t, &i.impl_));
try!(render_default_methods(w, did, t, &i.impl_));
}
}
try!(write!(w, "</div>"));

View File

@ -0,0 +1,16 @@
// Copyright 2015 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.
#![doc(html_root_url = "http://example.com")]
pub trait Foo {
fn foo(&self) {}
}

View File

@ -0,0 +1,20 @@
// Copyright 2015 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.
// aux-build:issue-17476.rs
extern crate issue_17476;
pub struct Foo;
// @has issue_17476/struct.Foo.html \
// '//*[@href="http://example.com/issue_17476/trait.Foo.html#tymethod.foo"]' \
// 'foo'
impl issue_17476::Foo for Foo {}