Skip to content

Commit 06eb976

Browse files
committed
rustdoc: Show types for traits across crates
Right now, when you look in the "Implementors" section for traits, you only see implementors within that crate. This commit modifies that section to include implementors from neighboring crates as well. For example, the Container trait currently says that it is only implemented by strings and slices, but it is in fact implemented by nearly all containers. Implementation-wise, this change generates an "implementors cache" similarly to the search index where each crate will append implementors to the files. When the page for a trait is loaded, it will load its specific cache file, rendering links for all upstream types which implement the trait.
1 parent 1edb0e5 commit 06eb976

File tree

2 files changed

+113
-22
lines changed

2 files changed

+113
-22
lines changed

src/librustdoc/html/render.rs

+91-22
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,10 @@ pub struct Cache {
150150
/// When rendering traits, it's often useful to be able to list all
151151
/// implementors of the trait, and this mapping is exactly, that: a mapping
152152
/// of trait ids to the list of known implementors of the trait
153-
pub implementors: HashMap<ast::NodeId, Vec<Implementor> >,
153+
pub implementors: HashMap<ast::NodeId, Vec<Implementor>>,
154+
155+
/// Implementations of external traits, keyed by the external trait def id.
156+
pub foreign_implementors: HashMap<ast::DefId, Vec<Implementor>>,
154157

155158
/// Cache of where external crate documentation can be found.
156159
pub extern_locations: HashMap<ast::CrateNum, ExternalLocation>,
@@ -268,6 +271,7 @@ pub fn run(mut krate: clean::Crate, dst: Path) -> io::IoResult<()> {
268271
paths: paths,
269272
traits: HashMap::new(),
270273
implementors: HashMap::new(),
274+
foreign_implementors: HashMap::new(),
271275
stack: Vec::new(),
272276
parent_stack: Vec::new(),
273277
search_index: Vec::new(),
@@ -396,26 +400,84 @@ pub fn run(mut krate: clean::Crate, dst: Path) -> io::IoResult<()> {
396400
try!(write(cx.dst.join("Heuristica-Bold.woff"),
397401
include_bin!("static/Heuristica-Bold.woff")));
398402

399-
// Update the search index
400-
let dst = cx.dst.join("search-index.js");
401-
let mut all_indexes = Vec::new();
402-
all_indexes.push(index);
403-
if dst.exists() {
404-
for line in BufferedReader::new(File::open(&dst)).lines() {
405-
let line = try!(line);
406-
if !line.starts_with("searchIndex") { continue }
407-
if line.starts_with(format!("searchIndex['{}']", krate.name)) {
408-
continue
403+
fn collect(path: &Path, krate: &str,
404+
key: &str) -> io::IoResult<Vec<StrBuf>> {
405+
let mut ret = Vec::new();
406+
if path.exists() {
407+
for line in BufferedReader::new(File::open(path)).lines() {
408+
let line = try!(line);
409+
if !line.starts_with(key) { continue }
410+
if line.starts_with(format!("{}['{}']", key, krate)) {
411+
continue
412+
}
413+
ret.push(line.to_strbuf());
409414
}
410-
all_indexes.push(line);
411415
}
416+
return Ok(ret);
412417
}
418+
419+
// Update the search index
420+
let dst = cx.dst.join("search-index.js");
421+
let all_indexes = try!(collect(&dst, krate.name.as_slice(),
422+
"searchIndex"));
413423
let mut w = try!(File::create(&dst));
414424
try!(writeln!(&mut w, r"var searchIndex = \{\};"));
425+
try!(writeln!(&mut w, "{}", index));
415426
for index in all_indexes.iter() {
416427
try!(writeln!(&mut w, "{}", *index));
417428
}
418429
try!(writeln!(&mut w, "initSearch(searchIndex);"));
430+
431+
// Update the list of all implementors for traits
432+
let dst = cx.dst.join("implementors");
433+
try!(mkdir(&dst));
434+
for (&did, imps) in cache.foreign_implementors.iter() {
435+
let &(ref remote_path, remote_item_type) = cache.paths.get(&did);
436+
437+
let mut mydst = dst.clone();
438+
for part in remote_path.slice_to(remote_path.len() - 1).iter() {
439+
mydst.push(part.as_slice());
440+
try!(mkdir(&mydst));
441+
}
442+
mydst.push(format!("{}.{}.js",
443+
remote_item_type.to_static_str(),
444+
*remote_path.get(remote_path.len() - 1)));
445+
let all_implementors = try!(collect(&mydst, krate.name.as_slice(),
446+
"implementors"));
447+
448+
try!(mkdir(&mydst.dir_path()));
449+
let mut f = BufferedWriter::new(try!(File::create(&mydst)));
450+
try!(writeln!(&mut f, r"(function() \{var implementors = \{\};"));
451+
452+
for implementor in all_implementors.iter() {
453+
try!(writeln!(&mut f, "{}", *implementor));
454+
}
455+
456+
try!(write!(&mut f, r"implementors['{}'] = \{", krate.name));
457+
for imp in imps.iter() {
458+
let &(ref path, item_type) = match *imp {
459+
PathType(clean::ResolvedPath { did, .. }) => {
460+
cache.paths.get(&did)
461+
}
462+
PathType(..) | OtherType(..) => continue,
463+
};
464+
try!(write!(&mut f, r#"{}:"#, *path.get(path.len() - 1)));
465+
try!(write!(&mut f, r#""{}"#,
466+
path.slice_to(path.len() - 1).connect("/")));
467+
try!(write!(&mut f, r#"/{}.{}.html","#,
468+
item_type.to_static_str(),
469+
*path.get(path.len() - 1)));
470+
}
471+
try!(writeln!(&mut f, r"\};"));
472+
try!(writeln!(&mut f, "{}", r"
473+
if (window.register_implementors) {
474+
window.register_implementors(implementors);
475+
} else {
476+
window.pending_implementors = implementors;
477+
}
478+
"));
479+
try!(writeln!(&mut f, r"\})()"));
480+
}
419481
}
420482

421483
// Render all source files (this may turn into a giant no-op)
@@ -635,13 +697,13 @@ impl DocFolder for Cache {
635697
match i.trait_ {
636698
// FIXME: this is_local() check seems to be losing
637699
// information
638-
Some(clean::ResolvedPath{ did, .. })
639-
if ast_util::is_local(did) =>
640-
{
641-
let id = did.node;
642-
let v = self.implementors.find_or_insert_with(id, |_|{
643-
Vec::new()
644-
});
700+
Some(clean::ResolvedPath{ did, .. }) => {
701+
let v = if ast_util::is_local(did) {
702+
self.implementors.find_or_insert(did.node, Vec::new())
703+
} else {
704+
self.foreign_implementors.find_or_insert(did,
705+
Vec::new())
706+
};
645707
match i.for_ {
646708
clean::ResolvedPath{..} => {
647709
v.unshift(PathType(i.for_.clone()));
@@ -1050,7 +1112,7 @@ impl<'a> fmt::Show for Item<'a> {
10501112
}
10511113
clean::FunctionItem(ref f) | clean::ForeignFunctionItem(ref f) =>
10521114
item_function(fmt, self.item, f),
1053-
clean::TraitItem(ref t) => item_trait(fmt, self.item, t),
1115+
clean::TraitItem(ref t) => item_trait(fmt, self.cx, self.item, t),
10541116
clean::StructItem(ref s) => item_struct(fmt, self.item, s),
10551117
clean::EnumItem(ref e) => item_enum(fmt, self.item, e),
10561118
clean::TypedefItem(ref t) => item_typedef(fmt, self.item, t),
@@ -1273,7 +1335,7 @@ fn item_function(w: &mut fmt::Formatter, it: &clean::Item,
12731335
document(w, it)
12741336
}
12751337

1276-
fn item_trait(w: &mut fmt::Formatter, it: &clean::Item,
1338+
fn item_trait(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item,
12771339
t: &clean::Trait) -> fmt::Result {
12781340
let mut parents = StrBuf::new();
12791341
if t.parents.len() > 0 {
@@ -1353,7 +1415,7 @@ fn item_trait(w: &mut fmt::Formatter, it: &clean::Item,
13531415
Some(implementors) => {
13541416
try!(write!(w, "
13551417
<h2 id='implementors'>Implementors</h2>
1356-
<ul class='item-list'>
1418+
<ul class='item-list' id='implementors-list'>
13571419
"));
13581420
for i in implementors.iter() {
13591421
match *i {
@@ -1367,6 +1429,13 @@ fn item_trait(w: &mut fmt::Formatter, it: &clean::Item,
13671429
}
13681430
}
13691431
try!(write!(w, "</ul>"));
1432+
try!(write!(w, r#"<script type="text/javascript" async
1433+
src="{}/implementors/{}/{}.{}.js"></script>"#,
1434+
cx.current.iter().map(|_| "..")
1435+
.collect::<Vec<&str>>().connect("/"),
1436+
cx.current.connect("/"),
1437+
shortty(it).to_static_str(),
1438+
*it.name.get_ref()));
13701439
}
13711440
None => {}
13721441
}

src/librustdoc/html/static/main.js

+22
Original file line numberDiff line numberDiff line change
@@ -653,4 +653,26 @@
653653
}
654654

655655
window.initSearch = initSearch;
656+
657+
window.register_implementors = function(imp) {
658+
var list = $('#implementors-list');
659+
var libs = Object.getOwnPropertyNames(imp);
660+
for (var i = 0; i < libs.length; i++) {
661+
var structs = Object.getOwnPropertyNames(imp[libs[i]]);
662+
for (var j = 0; j < structs.length; j++) {
663+
console.log(i, structs[j]);
664+
var path = rootPath + imp[libs[i]][structs[j]];
665+
var klass = path.contains("type.") ? "type" : "struct";
666+
var link = $('<a>').text(structs[j])
667+
.attr('href', path)
668+
.attr('class', klass);
669+
var code = $('<code>').append(link);
670+
var li = $('<li>').append(code);
671+
list.append(li);
672+
}
673+
}
674+
};
675+
if (window.pending_implementors) {
676+
window.register_implementors(window.pending_implementors);
677+
}
656678
}());

0 commit comments

Comments
 (0)