about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorAlex Crichton <alex@alexcrichton.com>2015-04-06 17:56:35 -0700
committerAlex Crichton <alex@alexcrichton.com>2015-04-07 17:54:34 -0700
commit641bca06c82e2fa744e7b14bc45cfa501baf57e6 (patch)
treea058733e05332ad362f2f993b830277600c9db3b /src
parentfcc89ea5006441350f0275946ff6db84a1118e49 (diff)
downloadrust-641bca06c82e2fa744e7b14bc45cfa501baf57e6.tar.gz
rust-641bca06c82e2fa744e7b14bc45cfa501baf57e6.zip
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
Diffstat (limited to 'src')
-rw-r--r--src/librustdoc/html/format.rs108
-rw-r--r--src/librustdoc/html/render.rs104
-rw-r--r--src/test/auxiliary/issue-17476.rs16
-rw-r--r--src/test/rustdoc/issue-17476.rs20
4 files changed, 143 insertions, 105 deletions
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 {
     }
 }
 
+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()))
+}
+
 /// 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,
+fn resolved_path(w: &mut fmt::Formatter, did: ast::DefId, path: &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))
-            }
-        })
-}
-
-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
     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(())
 }
 
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<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>"));
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 <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) {}
+}
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 <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 {}