about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2013-09-28 13:46:11 -0700
committerbors <bors@rust-lang.org>2013-09-28 13:46:11 -0700
commit9cc11ca6a31e05c75cd40097b38d6f735254e12c (patch)
treee29b8ff7c90a6888373fe052a888109d64d03297 /src
parentc635fba748ace3ad08b97d9ca53366dadabf2028 (diff)
parent88866a4c20ebe150b9b72dc361653922f7d497dd (diff)
downloadrust-9cc11ca6a31e05c75cd40097b38d6f735254e12c.tar.gz
rust-9cc11ca6a31e05c75cd40097b38d6f735254e12c.zip
auto merge of #9577 : alexcrichton/rust/rustdoc, r=cmr
They're getting smaller each time though!

The highlight of this round is source files in documentation. Still trying to figure out the best syntax-highlighting solution.
Diffstat (limited to 'src')
-rw-r--r--src/librustdoc/clean.rs72
-rw-r--r--src/librustdoc/html/escape.rs44
-rw-r--r--src/librustdoc/html/format.rs25
-rw-r--r--src/librustdoc/html/layout.rs3
-rw-r--r--src/librustdoc/html/render.rs224
-rw-r--r--src/librustdoc/html/static/main.css3
-rw-r--r--src/librustdoc/html/static/main.js63
-rw-r--r--src/librustdoc/passes.rs5
-rw-r--r--src/librustdoc/rustdoc.rs5
9 files changed, 313 insertions, 131 deletions
diff --git a/src/librustdoc/clean.rs b/src/librustdoc/clean.rs
index 70f1815b462..b4b086f3df0 100644
--- a/src/librustdoc/clean.rs
+++ b/src/librustdoc/clean.rs
@@ -84,7 +84,7 @@ impl Clean<Crate> for visit_ast::RustdocVisitor {
 #[deriving(Clone, Encodable, Decodable)]
 pub struct Item {
     /// Stringified span
-    source: ~str,
+    source: Span,
     /// Not everything has a name. E.g., impls
     name: Option<~str>,
     attrs: ~[Attribute],
@@ -539,9 +539,11 @@ impl Clean<TraitMethod> for ast::trait_method {
 #[deriving(Clone, Encodable, Decodable)]
 pub enum Type {
     /// structs/enums/traits (anything that'd be an ast::ty_path)
-    ResolvedPath { path: Path, typarams: Option<~[TyParamBound]>, id: ast::NodeId },
-    /// Reference to an item in an external crate (fully qualified path)
-    External(~str, ~str),
+    ResolvedPath {
+        path: Path,
+        typarams: Option<~[TyParamBound]>,
+        did: ast::DefId
+    },
     // I have no idea how to usefully use this.
     TyParamBinder(ast::NodeId),
     /// For parameterized types, so the consumer of the JSON don't go looking
@@ -736,10 +738,28 @@ impl Clean<VariantKind> for ast::variant_kind {
     }
 }
 
-impl Clean<~str> for syntax::codemap::Span {
-    fn clean(&self) -> ~str {
-        let cm = local_data::get(super::ctxtkey, |x| x.unwrap().clone()).sess.codemap;
-        cm.span_to_str(*self)
+#[deriving(Clone, Encodable, Decodable)]
+pub struct Span {
+    filename: ~str,
+    loline: uint,
+    locol: uint,
+    hiline: uint,
+    hicol: uint,
+}
+
+impl Clean<Span> for syntax::codemap::Span {
+    fn clean(&self) -> Span {
+        let cm = local_data::get(super::ctxtkey, |x| *x.unwrap()).sess.codemap;
+        let filename = cm.span_to_filename(*self);
+        let lo = cm.lookup_char_pos(self.lo);
+        let hi = cm.lookup_char_pos(self.hi);
+        Span {
+            filename: filename.to_owned(),
+            loline: lo.line,
+            locol: *lo.col,
+            hiline: hi.line,
+            hicol: *hi.col,
+        }
     }
 }
 
@@ -1033,7 +1053,7 @@ trait ToSource {
 
 impl ToSource for syntax::codemap::Span {
     fn to_src(&self) -> ~str {
-        debug!("converting span %s to snippet", self.clean());
+        debug!("converting span %? to snippet", self.clean());
         let cm = local_data::get(super::ctxtkey, |x| x.unwrap().clone()).sess.codemap.clone();
         let sn = match cm.span_to_snippet(*self) {
             Some(x) => x,
@@ -1129,39 +1149,7 @@ fn resolve_type(path: Path, tpbs: Option<~[TyParamBound]>,
         },
         x => fail!("resolved type maps to a weird def %?", x),
     };
-
-    if def_id.crate != ast::CRATE_NODE_ID {
-        use rustc::metadata::decoder::*;
-
-        let sess = local_data::get(super::ctxtkey, |x| *x.unwrap()).sess;
-        let cratedata = ::rustc::metadata::cstore::get_crate_data(sess.cstore, def_id.crate);
-        let doc = lookup_item(def_id.node, cratedata.data);
-        let path = syntax::ast_map::path_to_str_with_sep(item_path(doc), "::", sess.intr());
-        let ty = match def_like_to_def(item_to_def_like(doc, def_id, def_id.crate)) {
-            DefFn(*) => ~"fn",
-            DefTy(*) => ~"enum",
-            DefTrait(*) => ~"trait",
-            DefPrimTy(p) => match p {
-                ty_str => ~"str",
-                ty_bool => ~"bool",
-                ty_int(t) => match t.to_str() {
-                    ~"" => ~"i",
-                    s => s
-                },
-                ty_uint(t) => t.to_str(),
-                ty_float(t) => t.to_str(),
-                ty_char => ~"char",
-            },
-            DefTyParam(*) => ~"generic",
-            DefStruct(*) => ~"struct",
-            DefTyParamBinder(*) => ~"typaram_binder",
-            x => fail!("resolved external maps to a weird def %?", x),
-        };
-        let cname = cratedata.name.to_owned();
-        External(cname + "::" + path, ty)
-    } else {
-        ResolvedPath {path: path.clone(), typarams: tpbs, id: def_id.node}
-    }
+    ResolvedPath{ path: path, typarams: tpbs, did: def_id }
 }
 
 fn resolve_use_source(path: Path, id: ast::NodeId) -> ImportSource {
diff --git a/src/librustdoc/html/escape.rs b/src/librustdoc/html/escape.rs
new file mode 100644
index 00000000000..076d43e2c12
--- /dev/null
+++ b/src/librustdoc/html/escape.rs
@@ -0,0 +1,44 @@
+// Copyright 2013 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.
+
+use std::fmt;
+
+pub struct Escape<'self>(&'self str);
+
+impl<'self> fmt::Default for Escape<'self> {
+    fn fmt(s: &Escape<'self>, fmt: &mut fmt::Formatter) {
+        // Because the internet is always right, turns out there's not that many
+        // characters to escape: http://stackoverflow.com/questions/7381974
+        let pile_o_bits = s.as_slice();
+        let mut last = 0;
+        for (i, ch) in s.byte_iter().enumerate() {
+            match ch as char {
+                '<' | '>' | '&' | '\'' | '"' => {
+                    fmt.buf.write(pile_o_bits.slice(last, i).as_bytes());
+                    let s = match ch as char {
+                        '>' => "&gt;",
+                        '<' => "&lt;",
+                        '&' => "&amp;",
+                        '\'' => "&#39;",
+                        '"' => "&quot;",
+                        _ => unreachable!()
+                    };
+                    fmt.buf.write(s.as_bytes());
+                    last = i + 1;
+                }
+                _ => {}
+            }
+        }
+
+        if last < s.len() {
+            fmt.buf.write(pile_o_bits.slice_from(last).as_bytes());
+        }
+    }
+}
diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs
index 7010e7fa4ea..66796252770 100644
--- a/src/librustdoc/html/format.rs
+++ b/src/librustdoc/html/format.rs
@@ -97,7 +97,7 @@ impl fmt::Default for clean::Path {
     }
 }
 
-fn resolved_path(w: &mut io::Writer, id: ast::NodeId,
+fn resolved_path(w: &mut io::Writer, did: ast::DefId,
                  path: &clean::Path, print_all: bool) {
     // The generics will get written to both the title and link
     let mut generics = ~"";
@@ -144,9 +144,10 @@ fn resolved_path(w: &mut io::Writer, id: ast::NodeId,
 
         do local_data::get(cache_key) |cache| {
             do cache.unwrap().read |cache| {
-                match cache.paths.find(&id) {
+                match cache.paths.find(&did.node) {
                     // This is a documented path, link to it!
-                    Some(&(ref fqp, shortty)) => {
+                    // FIXME(#9539): this is_local check should not exist
+                    Some(&(ref fqp, shortty)) if ast_util::is_local(did) => {
                         let fqn = fqp.connect("::");
                         let same = loc.iter().zip(fqp.iter())
                                       .take_while(|&(a, b)| *a == *b).len();
@@ -180,7 +181,7 @@ fn resolved_path(w: &mut io::Writer, id: ast::NodeId,
                         write!(w, "<a class='{}' href='{}' title='{}'>{}</a>{}",
                                shortty, url, fqn, last.name, generics);
                     }
-                    None => {
+                    _ => {
                         if print_all {
                             let amt = path.segments.len() - 1;
                             for seg in path.segments.iter().take(amt) {
@@ -205,8 +206,8 @@ impl fmt::Default for clean::Type {
                     }
                 }
             }
-            clean::ResolvedPath{id, typarams: ref typarams, path: ref path} => {
-                resolved_path(f.buf, id, path, false);
+            clean::ResolvedPath{did, typarams: ref typarams, path: ref path} => {
+                resolved_path(f.buf, did, path, false);
                 match *typarams {
                     Some(ref params) => {
                         f.buf.write("&lt;".as_bytes());
@@ -219,10 +220,6 @@ impl fmt::Default for clean::Type {
                     None => {}
                 }
             }
-            // XXX: this should be a link
-            clean::External(ref a, _) => {
-                write!(f.buf, "{}", *a);
-            }
             clean::Self(*) => f.buf.write("Self".as_bytes()),
             clean::Primitive(prim) => {
                 let s = match prim {
@@ -421,8 +418,8 @@ impl fmt::Default for clean::ViewPath {
 impl fmt::Default for clean::ImportSource {
     fn fmt(v: &clean::ImportSource, f: &mut fmt::Formatter) {
         match v.did {
-            Some(did) if ast_util::is_local(did) => {
-                resolved_path(f.buf, did.node, &v.path, true);
+            Some(did) => {
+                resolved_path(f.buf, did, &v.path, true);
             }
             _ => {
                 for (i, seg) in v.path.segments.iter().enumerate() {
@@ -437,7 +434,7 @@ impl fmt::Default for clean::ImportSource {
 impl fmt::Default for clean::ViewListIdent {
     fn fmt(v: &clean::ViewListIdent, f: &mut fmt::Formatter) {
         match v.source {
-            Some(did) if ast_util::is_local(did) => {
+            Some(did) => {
                 let path = clean::Path {
                     global: false,
                     segments: ~[clean::PathSegment {
@@ -446,7 +443,7 @@ impl fmt::Default for clean::ViewListIdent {
                         types: ~[],
                     }]
                 };
-                resolved_path(f.buf, did.node, &path, false);
+                resolved_path(f.buf, did, &path, false);
             }
             _ => write!(f.buf, "{}", v.name),
         }
diff --git a/src/librustdoc/html/layout.rs b/src/librustdoc/html/layout.rs
index 718bacc24fd..d29bf5ed55b 100644
--- a/src/librustdoc/html/layout.rs
+++ b/src/librustdoc/html/layout.rs
@@ -66,7 +66,8 @@ pub fn render<T: fmt::Default, S: fmt::Default>(
         </form>
     </nav>
 
-    <section class=\"content {ty}\">{content}</section>
+    <section id='main' class=\"content {ty}\">{content}</section>
+    <section id='search' class=\"content hidden\"></section>
 
     <section class=\"footer\"></section>
 
diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs
index 1f720d2e92f..4702cb0f73d 100644
--- a/src/librustdoc/html/render.rs
+++ b/src/librustdoc/html/render.rs
@@ -12,12 +12,14 @@ use std::cell::Cell;
 use std::comm::{SharedPort, SharedChan};
 use std::comm;
 use std::fmt;
-use std::hashmap::HashMap;
+use std::hashmap::{HashMap, HashSet};
 use std::local_data;
 use std::rt::io::buffered::BufferedWriter;
 use std::rt::io::file::{FileInfo, DirectoryInfo};
 use std::rt::io::file;
 use std::rt::io;
+use std::rt::io::Reader;
+use std::str;
 use std::task;
 use std::unstable::finally::Finally;
 use std::util;
@@ -28,11 +30,13 @@ use extra::json::ToJson;
 use extra::sort;
 
 use syntax::ast;
+use syntax::ast_util::is_local;
 use syntax::attr;
 
 use clean;
 use doctree;
 use fold::DocFolder;
+use html::escape::Escape;
 use html::format::{VisSpace, Method, PuritySpace};
 use html::layout;
 use html::markdown::Markdown;
@@ -44,6 +48,7 @@ pub struct Context {
     dst: Path,
     layout: layout::Layout,
     sidebar: HashMap<~str, ~[~str]>,
+    include_sources: bool,
 }
 
 enum Implementor {
@@ -68,6 +73,12 @@ struct Cache {
     priv search_index: ~[IndexItem],
 }
 
+struct SourceCollector<'self> {
+    seen: HashSet<~str>,
+    dst: Path,
+    cx: &'self Context,
+}
+
 struct Item<'self> { cx: &'self Context, item: &'self clean::Item, }
 struct Sidebar<'self> { cx: &'self Context, item: &'self clean::Item, }
 
@@ -79,6 +90,8 @@ struct IndexItem {
     parent: Option<ast::NodeId>,
 }
 
+struct Source<'self>(&'self str);
+
 local_data_key!(pub cache_key: RWArc<Cache>)
 local_data_key!(pub current_location_key: ~[~str])
 
@@ -94,6 +107,7 @@ pub fn run(mut crate: clean::Crate, dst: Path) {
             favicon: ~"",
             crate: crate.name.clone(),
         },
+        include_sources: true,
     };
     mkdir(&cx.dst);
 
@@ -107,6 +121,9 @@ pub fn run(mut crate: clean::Crate, dst: Path) {
                     clean::NameValue(~"html_logo_url", ref s) => {
                         cx.layout.logo = s.to_owned();
                     }
+                    clean::Word(~"html_no_source") => {
+                        cx.include_sources = false;
+                    }
                     _ => {}
                 }
             }
@@ -162,6 +179,19 @@ pub fn run(mut crate: clean::Crate, dst: Path) {
         w.flush();
     }
 
+    if cx.include_sources {
+        let dst = cx.dst.push("src");
+        mkdir(&dst);
+        let dst = dst.push(crate.name);
+        mkdir(&dst);
+        let mut folder = SourceCollector {
+            dst: dst,
+            seen: HashSet::new(),
+            cx: &cx,
+        };
+        crate = folder.fold_crate(crate);
+    }
+
     // Now render the whole crate.
     cx.crate(crate, cache);
 }
@@ -183,7 +213,80 @@ fn mkdir(path: &Path) {
     }
 }
 
-impl<'self> DocFolder for Cache {
+fn clean_srcpath(src: &str, f: &fn(&str)) {
+    let p = Path(src);
+    for c in p.components.iter() {
+        if "." == *c {
+            loop
+        }
+        if ".." == *c {
+            f("up");
+        } else {
+            f(c.as_slice())
+        }
+    }
+}
+
+impl<'self> DocFolder for SourceCollector<'self> {
+    fn fold_item(&mut self, item: clean::Item) -> Option<clean::Item> {
+        if !self.seen.contains(&item.source.filename) {
+            self.emit_source(item.source.filename);
+            self.seen.insert(item.source.filename.clone());
+        }
+        self.fold_item_recur(item)
+    }
+}
+
+impl<'self> SourceCollector<'self> {
+    fn emit_source(&self, filename: &str) {
+        let p = Path(filename);
+
+        // Read the contents of the file
+        let mut contents = ~[];
+        {
+            let mut buf = [0, ..1024];
+            let r = do io::io_error::cond.trap(|_| {}).inside {
+                p.open_reader(io::Open)
+            };
+            // If we couldn't open this file, then just returns because it
+            // probably means that it's some standard library macro thing and we
+            // can't have the source to it anyway.
+            let mut r = match r { Some(r) => r, None => return };
+
+            // read everything
+            loop {
+                match r.read(buf) {
+                    Some(n) => contents.push_all(buf.slice_to(n)),
+                    None => break
+                }
+            }
+        }
+        let contents = str::from_utf8_owned(contents);
+
+        // Create the intermediate directories
+        let mut cur = self.dst.clone();
+        let mut root_path = ~"../../";
+        do clean_srcpath(p.pop().to_str()) |component| {
+            cur = cur.push(component);
+            mkdir(&cur);
+            root_path.push_str("../");
+        }
+
+        let dst = cur.push(*p.components.last() + ".html");
+        let mut w = dst.open_writer(io::CreateOrTruncate);
+
+        let title = format!("{} -- source", *dst.components.last());
+        let page = layout::Page {
+            title: title,
+            ty: "source",
+            root_path: root_path,
+        };
+        layout::render(&mut w as &mut io::Writer, &self.cx.layout,
+                       &page, &(""), &Source(contents.as_slice()));
+    }
+}
+
+impl DocFolder for Cache {
     fn fold_item(&mut self, item: clean::Item) -> Option<clean::Item> {
         // Register any generics to their corresponding string. This is used
         // when pretty-printing types
@@ -223,7 +326,8 @@ impl<'self> DocFolder for Cache {
         match item.inner {
             clean::ImplItem(ref i) => {
                 match i.trait_ {
-                    Some(clean::ResolvedPath{ id, _ }) => {
+                    Some(clean::ResolvedPath{ did, _ }) if is_local(did) => {
+                        let id = did.node;
                         let v = do self.implementors.find_or_insert_with(id) |_|{
                             ~[]
                         };
@@ -248,7 +352,9 @@ impl<'self> DocFolder for Cache {
         match item.name {
             Some(ref s) => {
                 let parent = match item.inner {
-                    clean::TyMethodItem(*) | clean::VariantItem(*) => {
+                    clean::TyMethodItem(*) |
+                    clean::StructFieldItem(*) |
+                    clean::VariantItem(*) => {
                         Some((Some(*self.parent_stack.last()),
                               self.stack.slice_to(self.stack.len() - 1)))
 
@@ -257,8 +363,12 @@ impl<'self> DocFolder for Cache {
                         if self.parent_stack.len() == 0 {
                             None
                         } else {
-                            Some((Some(*self.parent_stack.last()),
-                                  self.stack.as_slice()))
+                            let last = self.parent_stack.last();
+                            let amt = match self.paths.find(last) {
+                                Some(&(_, "trait")) => self.stack.len() - 1,
+                                Some(*) | None => self.stack.len(),
+                            };
+                            Some((Some(*last), self.stack.slice_to(amt)))
                         }
                     }
                     _ => Some((None, self.stack.as_slice()))
@@ -299,13 +409,13 @@ impl<'self> DocFolder for Cache {
 
         // Maintain the parent stack
         let parent_pushed = match item.inner {
-            clean::TraitItem(*) | clean::EnumItem(*) => {
+            clean::TraitItem(*) | clean::EnumItem(*) | clean::StructItem(*) => {
                 self.parent_stack.push(item.id); true
             }
             clean::ImplItem(ref i) => {
                 match i.for_ {
-                    clean::ResolvedPath{ id, _ } => {
-                        self.parent_stack.push(id); true
+                    clean::ResolvedPath{ did, _ } if is_local(did) => {
+                        self.parent_stack.push(did.node); true
                     }
                     _ => false
                 }
@@ -320,7 +430,8 @@ impl<'self> DocFolder for Cache {
                 match item.inner {
                     clean::ImplItem(i) => {
                         match i.for_ {
-                            clean::ResolvedPath { id, _ } => {
+                            clean::ResolvedPath { did, _ } if is_local(did) => {
+                                let id = did.node;
                                 let v = do self.impls.find_or_insert_with(id) |_| {
                                     ~[]
                                 };
@@ -374,7 +485,6 @@ impl Context {
         return ret;
     }
 
-    /// Processes
     fn crate(self, mut crate: clean::Crate, cache: Cache) {
         enum Work {
             Die,
@@ -510,28 +620,6 @@ impl Context {
                 let dst = self.dst.push(item_path(&item));
                 let writer = dst.open_writer(io::CreateOrTruncate);
                 render(writer.unwrap(), self, &item, true);
-
-                // recurse if necessary
-                let name = item.name.get_ref().clone();
-                match item.inner {
-                    clean::EnumItem(e) => {
-                        let mut it = e.variants.move_iter();
-                        do self.recurse(name) |this| {
-                            for item in it {
-                                f(this, item);
-                            }
-                        }
-                    }
-                    clean::StructItem(s) => {
-                        let mut it = s.fields.move_iter();
-                        do self.recurse(name) |this| {
-                            for item in it {
-                                f(this, item);
-                            }
-                        }
-                    }
-                    _ => {}
-                }
             }
 
             _ => {}
@@ -581,6 +669,20 @@ impl<'self> fmt::Default for Item<'self> {
             None => {}
         }
 
+        if it.cx.include_sources {
+            let mut path = ~[];
+            do clean_srcpath(it.item.source.filename) |component| {
+                path.push(component.to_owned());
+            }
+            write!(fmt.buf,
+                   "<a class='source'
+                       href='{root}src/{crate}/{path}.html\\#{line}'>[src]</a>",
+                   root = it.cx.root_path,
+                   crate = it.cx.layout.crate,
+                   path = path.connect("/"),
+                   line = it.item.source.loline);
+        }
+
         // Write the breadcrumb trail header for the top
         write!(fmt.buf, "<h1 class='fqn'>");
         match it.item.inner {
@@ -613,9 +715,6 @@ impl<'self> fmt::Default for Item<'self> {
             clean::StructItem(ref s) => item_struct(fmt.buf, it.item, s),
             clean::EnumItem(ref e) => item_enum(fmt.buf, it.item, e),
             clean::TypedefItem(ref t) => item_typedef(fmt.buf, it.item, t),
-            clean::VariantItem(*) => item_variant(fmt.buf, it.cx, it.item),
-            clean::StructFieldItem(*) => item_struct_field(fmt.buf, it.cx,
-                                                           it.item),
             _ => {}
         }
     }
@@ -862,7 +961,8 @@ fn item_trait(w: &mut io::Writer, it: &clean::Item, t: &clean::Trait) {
     document(w, it);
 
     fn meth(w: &mut io::Writer, m: &clean::TraitMethod) {
-        write!(w, "<h3 id='fn.{}' class='method'><code>",
+        write!(w, "<h3 id='{}.{}' class='method'><code>",
+               shortty(m.item()),
                *m.item().name.get_ref());
         render_method(w, m.item(), false);
         write!(w, "</code></h3>");
@@ -923,13 +1023,15 @@ fn render_method(w: &mut io::Writer, meth: &clean::Item, withlink: bool) {
            g: &clean::Generics, selfty: &clean::SelfTy, d: &clean::FnDecl,
            withlink: bool) {
         write!(w, "{}fn {withlink, select,
-                            true{<a href='\\#fn.{name}' class='fnname'>{name}</a>}
+                            true{<a href='\\#{ty}.{name}'
+                                    class='fnname'>{name}</a>}
                             other{<span class='fnname'>{name}</span>}
                         }{generics}{decl}",
                match purity {
                    ast::unsafe_fn => "unsafe ",
                    _ => "",
                },
+               ty = shortty(it),
                name = it.name.get_ref().as_slice(),
                generics = *g,
                decl = Method(selfty, d),
@@ -1014,7 +1116,7 @@ fn render_struct(w: &mut io::Writer, it: &clean::Item,
             for field in fields.iter() {
                 match field.inner {
                     clean::StructFieldItem(ref ty) => {
-                        write!(w, "    {}<a name='field.{name}'>{name}</a>: \
+                        write!(w, "    {}<a name='structfield.{name}'>{name}</a>: \
                                    {},\n{}",
                                VisSpace(field.visibility),
                                ty.type_,
@@ -1080,7 +1182,7 @@ fn render_impl(w: &mut io::Writer, i: &clean::Impl) {
         Some(ref ty) => {
             write!(w, "{} for ", *ty);
             match *ty {
-                clean::ResolvedPath { id, _ } => Some(id),
+                clean::ResolvedPath { did, _ } => Some(did),
                 _ => None,
             }
         }
@@ -1089,7 +1191,7 @@ fn render_impl(w: &mut io::Writer, i: &clean::Impl) {
     write!(w, "{}</code></h3>", i.for_);
     write!(w, "<div class='methods'>");
     for meth in i.methods.iter() {
-        write!(w, "<h4 id='fn.{}' class='method'><code>",
+        write!(w, "<h4 id='method.{}' class='method'><code>",
                *meth.name.get_ref());
         render_method(w, meth, false);
         write!(w, "</code></h4>\n");
@@ -1102,7 +1204,11 @@ fn render_impl(w: &mut io::Writer, i: &clean::Impl) {
         }
 
         // No documentation? Attempt to slurp in the trait's documentation
-        let trait_id = match trait_id { Some(id) => id, None => loop };
+        let trait_id = match trait_id {
+            None => loop,
+            Some(id) if is_local(id) => loop,
+            Some(id) => id.node,
+        };
         do local_data::get(cache_key) |cache| {
             do cache.unwrap().read |cache| {
                 let name = meth.name.get_ref().as_slice();
@@ -1198,20 +1304,22 @@ fn build_sidebar(m: &clean::Module) -> HashMap<~str, ~[~str]> {
     return map;
 }
 
-fn item_variant(w: &mut io::Writer, cx: &Context, it: &clean::Item) {
-    write!(w, "<DOCTYPE html><html><head>\
-                <meta http-equiv='refresh' content='0; \
-                      url=../enum.{}.html\\#variant.{}'>\
-               </head><body></body></html>",
-           *cx.current.last(),
-           it.name.get_ref().as_slice());
-}
-
-fn item_struct_field(w: &mut io::Writer, cx: &Context, it: &clean::Item) {
-    write!(w, "<DOCTYPE html><html><head>\
-                <meta http-equiv='refresh' content='0; \
-                      url=../struct.{}.html\\#field.{}'>\
-               </head><body></body></html>",
-           *cx.current.last(),
-           it.name.get_ref().as_slice());
+impl<'self> fmt::Default for Source<'self> {
+    fn fmt(s: &Source<'self>, fmt: &mut fmt::Formatter) {
+        let lines = s.line_iter().len();
+        let mut cols = 0;
+        let mut tmp = lines;
+        while tmp > 0 {
+            cols += 1;
+            tmp /= 10;
+        }
+        write!(fmt.buf, "<pre class='line-numbers'>");
+        for i in range(1, lines + 1) {
+            write!(fmt.buf, "<span id='{0:u}'>{0:1$u}</span>\n", i, cols);
+        }
+        write!(fmt.buf, "</pre>");
+        write!(fmt.buf, "<pre class='rust'>");
+        write!(fmt.buf, "{}", Escape(s.as_slice()));
+        write!(fmt.buf, "</pre>");
+    }
 }
diff --git a/src/librustdoc/html/static/main.css b/src/librustdoc/html/static/main.css
index 11ca7a09311..2366a530a87 100644
--- a/src/librustdoc/html/static/main.css
+++ b/src/librustdoc/html/static/main.css
@@ -119,6 +119,9 @@ body {
 .content h1, .content h2 { margin-left: -20px; }
 .content pre { padding: 20px; }
 
+.content pre.line-numbers { float: left; border: none; }
+.line-numbers span { color: #c67e2d; }
+
 .content .highlighted {
     cursor: pointer;
     color: #000 !important;
diff --git a/src/librustdoc/html/static/main.js b/src/librustdoc/html/static/main.js
index b5ae3dadd77..bf9e9ac8027 100644
--- a/src/librustdoc/html/static/main.js
+++ b/src/librustdoc/html/static/main.js
@@ -39,9 +39,15 @@
         if (e.keyCode === 188 && $('#help').hasClass('hidden')) { // question mark
             e.preventDefault();
             $('#help').removeClass('hidden');
-        } else if (e.keyCode === 27 && !$('#help').hasClass('hidden')) { // esc
-            e.preventDefault();
-            $('#help').addClass('hidden');
+        } else if (e.keyCode === 27) { // esc
+            if (!$('#help').hasClass('hidden')) {
+                e.preventDefault();
+                $('#help').addClass('hidden');
+            } else if (!$('#search').hasClass('hidden')) {
+                e.preventDefault();
+                $('#search').addClass('hidden');
+                $('#main').removeClass('hidden');
+            }
         } else if (e.keyCode === 83) { // S
             e.preventDefault();
             $('.search-input').focus();
@@ -202,7 +208,13 @@
             var hoverTimeout, $results = $('.search-results .result');
 
             $results.on('click', function () {
-                document.location.href = $(this).find('a').prop('href');
+                var dst = $(this).find('a')[0];
+                console.log(window.location.pathname, dst.pathname);
+                if (window.location.pathname == dst.pathname) {
+                    $('#search').addClass('hidden');
+                    $('#main').removeClass('hidden');
+                }
+                document.location.href = dst.href;
             }).on('mouseover', function () {
                 var $el = $(this);
                 clearTimeout(hoverTimeout);
@@ -265,25 +277,52 @@
                     output += '<tr class="' + type + ' result"><td>';
 
                     if (type === 'mod') {
-                        output += item.path + '::<a href="' + rootPath + item.path.replace(/::/g, '/') + '/' + name + '/index.html" class="' + type + '">' + name + '</a>';
+                        output += item.path +
+                            '::<a href="' + rootPath +
+                                            item.path.replace(/::/g, '/') + '/' +
+                                            name + '/index.html" class="' +
+                                            type + '">' + name + '</a>';
                     } else if (type === 'static' || type === 'reexport') {
-                        output += item.path + '::<a href="' + rootPath + item.path.replace(/::/g, '/') + '/index.html" class="' + type + '">' + name + '</a>';
+                        output += item.path +
+                            '::<a href="' + rootPath +
+                                            item.path.replace(/::/g, '/') +
+                                            '/index.html" class="' + type +
+                                            '">' + name + '</a>';
                     } else if (item.parent !== undefined) {
                         var myparent = allPaths[item.parent];
-                        output += item.path + '::' + myparent.name + '::<a href="' + rootPath + item.path.replace(/::/g, '/') + '/' + myparent.type + '.' + myparent.name + '.html" class="' + type + '">' + name + '</a>';
+                        var anchor = '#' + type + '.' + name;
+                        output += item.path + '::' + myparent.name +
+                            '::<a href="' + rootPath +
+                                            item.path.replace(/::/g, '/') +
+                                            '/' + myparent.type +
+                                            '.' + myparent.name +
+                                            '.html' + anchor +
+                                            '" class="' + type +
+                                            '">' + name + '</a>';
                     } else {
-                        output += item.path + '::<a href="' + rootPath + item.path.replace(/::/g, '/') + '/' + type + '.' + name + '.html" class="' + type + '">' + name + '</a>';
+                        output += item.path +
+                            '::<a href="' + rootPath +
+                                            item.path.replace(/::/g, '/') +
+                                            '/' + type +
+                                            '.' + name +
+                                            '.html" class="' + type +
+                                            '">' + name + '</a>';
                     }
 
-                    output += '</td><td><span class="desc">' + item.desc + '</span></td></tr>';
+                    output += '</td><td><span class="desc">' + item.desc +
+                                    '</span></td></tr>';
                 });
             } else {
-                output += 'No results :( <a href="https://duckduckgo.com/?q=' + encodeURIComponent('rust ' + query.query) + '">Try on DuckDuckGo?</a>';
+                output += 'No results :( <a href="https://duckduckgo.com/?q=' +
+                            encodeURIComponent('rust ' + query.query) +
+                            '">Try on DuckDuckGo?</a>';
             }
 
             output += "</p>";
-            $('.content').html(output);
-            $('.search-results .desc').width($('.content').width() - 40 - $('.content td:first-child').first().width());
+            $('#main.content').addClass('hidden');
+            $('#search.content').removeClass('hidden').html(output);
+            $('.search-results .desc').width($('.content').width() - 40 -
+                    $('.content td:first-child').first().width());
             initSearchNav();
         }
 
diff --git a/src/librustdoc/passes.rs b/src/librustdoc/passes.rs
index bf35c93caae..bbb20b31272 100644
--- a/src/librustdoc/passes.rs
+++ b/src/librustdoc/passes.rs
@@ -13,6 +13,7 @@ use std::uint;
 use std::hashmap::HashSet;
 
 use syntax::ast;
+use syntax::ast_util::is_local;
 
 use clean;
 use clean::Item;
@@ -130,8 +131,8 @@ pub fn strip_private(mut crate: clean::Crate) -> plugins::PluginResult {
             match i.inner {
                 clean::ImplItem(ref imp) => {
                     match imp.trait_ {
-                        Some(clean::ResolvedPath{ id, _ }) => {
-                            if !self.contains(&id) {
+                        Some(clean::ResolvedPath{ did, _ }) => {
+                            if is_local(did) && !self.contains(&did.node) {
                                 return None;
                             }
                         }
diff --git a/src/librustdoc/rustdoc.rs b/src/librustdoc/rustdoc.rs
index 73a28baab06..2405acd76b6 100644
--- a/src/librustdoc/rustdoc.rs
+++ b/src/librustdoc/rustdoc.rs
@@ -34,10 +34,11 @@ pub mod core;
 pub mod doctree;
 pub mod fold;
 pub mod html {
-    pub mod render;
+    pub mod escape;
+    pub mod format;
     pub mod layout;
     pub mod markdown;
-    pub mod format;
+    pub mod render;
 }
 pub mod passes;
 pub mod plugins;