From 641bca06c82e2fa744e7b14bc45cfa501baf57e6 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 6 Apr 2015 17:56:35 -0700 Subject: [PATCH] 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 --- src/librustdoc/html/format.rs | 110 ++++++++++++------------------ src/librustdoc/html/render.rs | 104 +++++++++++++++++----------- src/test/auxiliary/issue-17476.rs | 16 +++++ src/test/rustdoc/issue-17476.rs | 20 ++++++ 4 files changed, 144 insertions(+), 106 deletions(-) create mode 100644 src/test/auxiliary/issue-17476.rs create mode 100644 src/test/rustdoc/issue-17476.rs diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index a52c996bdb7..365e34476aa 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -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::()) - } else { - match cache.extern_locations[&did.krate] { - render::Remote(ref s) => Some(s.to_string()), - render::Local => { - Some(repeat("../").take(loc.len()).collect::()) - } - 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)> { + 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::() + } else { + match cache.extern_locations[&did.krate] { + render::Remote(ref s) => s.to_string(), + render::Local => repeat("../").take(loc.len()).collect::(), + 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(w: &mut fmt::Formatter, - path: &clean::Path, - print_all: bool, - root: F, - info: G) - -> fmt::Result where - F: FnOnce(&render::Cache, &[String]) -> Option, - G: FnOnce(&render::Cache) -> Option<(Vec, 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(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(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, "{}", 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(()) } diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index 3796614bcf5..418256d4723 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -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, } +impl Impl { + fn trait_did(&self) -> Option { + 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 = self.inputs.iter().map(|ref t| format!("{}", t)).collect(); + let inputs: Vec = 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, "")); 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 {name}\ + 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 {name}\ {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, "

Methods

")); 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, "

Derived Implementations \ -

")); + try!(write!(w, "

\ + Derived Implementations \ +

")); 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, "

{}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, "{}{}

", i.impl_.for_, WhereClause(&i.impl_.generics))); - match i.dox { - Some(ref dox) => { - try!(write!(w, "
{}
", - Markdown(dox))); - } - None => {} + try!(write!(w, "{}{}", i.impl_.for_, + WhereClause(&i.impl_.generics))); + if let Some(ref dox) = i.dox { + try!(write!(w, "
{}
", 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, "

{}", *item.name.as_ref().unwrap(), shortty(item), ConciseStability(&item.stability))); - try!(render_method(w, item)); + try!(render_method(w, item, link)); try!(write!(w, "

\n")); } clean::TypedefItem(ref tydef) => { @@ -2247,10 +2273,11 @@ fn render_impl(w: &mut fmt::Formatter, i: &Impl) -> fmt::Result { try!(write!(w, "
")); 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, "
")); diff --git a/src/test/auxiliary/issue-17476.rs b/src/test/auxiliary/issue-17476.rs new file mode 100644 index 00000000000..d3a86035742 --- /dev/null +++ b/src/test/auxiliary/issue-17476.rs @@ -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 or the MIT license +// , 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) {} +} diff --git a/src/test/rustdoc/issue-17476.rs b/src/test/rustdoc/issue-17476.rs new file mode 100644 index 00000000000..cb224d66b44 --- /dev/null +++ b/src/test/rustdoc/issue-17476.rs @@ -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 or the MIT license +// , 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 {}