diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 2e41b2c2f49d3..0a2de70147d85 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -519,9 +519,9 @@ impl Clean> for ty::Region { ty::ReStatic => Some(Lifetime("static".to_string())), ty::ReLateBound(_, ty::BrNamed(_, name)) => Some(Lifetime(token::get_name(name).get().to_string())), + ty::ReEarlyBound(_, _, name) => Some(Lifetime(name.clean())), ty::ReLateBound(..) | - ty::ReEarlyBound(..) | ty::ReFree(..) | ty::ReScope(..) | ty::ReInfer(..) | diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index 22619bdbf8517..c5e6c73e38fc4 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -102,13 +102,11 @@ pub enum ExternalLocation { Unknown, } -/// Different ways an implementor of a trait can be rendered. -pub enum Implementor { - /// Paths are displayed specially by omitting the `impl XX for` cruft - PathType(clean::Type), - /// This is the generic representation of a trait implementor, used for - /// primitive types and otherwise non-path types. - OtherType(clean::Generics, /* trait */ clean::Type, /* for */ clean::Type), +/// Metadata about an implementor of a trait. +pub struct Implementor { + generics: clean::Generics, + trait_: clean::Type, + for_: clean::Type, } /// This cache is used to store information about the `clean::Crate` being @@ -229,6 +227,8 @@ pub fn run(mut krate: clean::Crate, dst: Path) -> io::IoResult<()> { }; try!(mkdir(&cx.dst)); + // Crawl the crate attributes looking for attributes which control how we're + // going to emit HTML match krate.module.as_ref().map(|m| m.doc_list().unwrap_or(&[])) { Some(attrs) => { for attr in attrs.iter() { @@ -297,19 +297,44 @@ pub fn run(mut krate: clean::Crate, dst: Path) -> io::IoResult<()> { cache.stack.push(krate.name.clone()); krate = cache.fold_crate(krate); + + for &(n, ref e) in krate.externs.iter() { + cache.extern_locations.insert(n, extern_location(e, &cx.dst)); + let did = ast::DefId { krate: n, node: ast::CRATE_NODE_ID }; + cache.paths.insert(did, (vec![e.name.to_string()], item_type::Module)); + } + + let index = try!(build_index(&krate, &mut cache)); + + // Freeze the cache now that the index has been built. Put an Arc into TLS + // for future parallelization opportunities + let cache = Arc::new(cache); + cache_key.replace(Some(cache.clone())); + current_location_key.replace(Some(Vec::new())); + + try!(write_shared(&cx, &krate, &*cache, index)); + let krate = try!(render_sources(&mut cx, krate)); + + // And finally render the whole crate's documentation + cx.krate(krate) +} + +fn build_index(krate: &clean::Crate, cache: &mut Cache) -> io::IoResult { + // Build the search index from the collected metadata let mut nodeid_to_pathid = HashMap::new(); let mut pathid_to_nodeid = Vec::new(); { - let Cache { search_index: ref mut index, - orphan_methods: ref meths, paths: ref mut paths, ..} = cache; + let Cache { ref mut search_index, + ref orphan_methods, + ref mut paths, .. } = *cache; // Attach all orphan methods to the type's definition if the type // has since been learned. - for &(pid, ref item) in meths.iter() { + for &(pid, ref item) in orphan_methods.iter() { let did = ast_util::local_def(pid); match paths.find(&did) { Some(&(ref fqp, _)) => { - index.push(IndexItem { + search_index.push(IndexItem { ty: shortty(item), name: item.name.clone().unwrap(), path: fqp.slice_to(fqp.len() - 1).connect("::") @@ -324,7 +349,7 @@ pub fn run(mut krate: clean::Crate, dst: Path) -> io::IoResult<()> { // Reduce `NodeId` in paths into smaller sequential numbers, // and prune the paths that do not appear in the index. - for item in index.iter() { + for item in search_index.iter() { match item.parent { Some(nodeid) => { if !nodeid_to_pathid.contains_key(&nodeid) { @@ -339,189 +364,171 @@ pub fn run(mut krate: clean::Crate, dst: Path) -> io::IoResult<()> { assert_eq!(nodeid_to_pathid.len(), pathid_to_nodeid.len()); } - // Publish the search index - let index = { - let mut w = MemWriter::new(); - try!(write!(&mut w, r#"searchIndex['{}'] = \{"items":["#, krate.name)); - - let mut lastpath = "".to_string(); - for (i, item) in cache.search_index.iter().enumerate() { - // Omit the path if it is same to that of the prior item. - let path; - if lastpath.as_slice() == item.path.as_slice() { - path = ""; - } else { - lastpath = item.path.to_string(); - path = item.path.as_slice(); - }; + // Collect the index into a string + let mut w = MemWriter::new(); + try!(write!(&mut w, r#"searchIndex['{}'] = \{"items":["#, krate.name)); - if i > 0 { - try!(write!(&mut w, ",")); - } - try!(write!(&mut w, r#"[{:u},"{}","{}",{}"#, - item.ty, item.name, path, - item.desc.to_json().to_str())); - match item.parent { - Some(nodeid) => { - let pathid = *nodeid_to_pathid.find(&nodeid).unwrap(); - try!(write!(&mut w, ",{}", pathid)); - } - None => {} + let mut lastpath = "".to_string(); + for (i, item) in cache.search_index.iter().enumerate() { + // Omit the path if it is same to that of the prior item. + let path; + if lastpath.as_slice() == item.path.as_slice() { + path = ""; + } else { + lastpath = item.path.to_string(); + path = item.path.as_slice(); + }; + + if i > 0 { + try!(write!(&mut w, ",")); + } + try!(write!(&mut w, r#"[{:u},"{}","{}",{}"#, + item.ty, item.name, path, + item.desc.to_json().to_str())); + match item.parent { + Some(nodeid) => { + let pathid = *nodeid_to_pathid.find(&nodeid).unwrap(); + try!(write!(&mut w, ",{}", pathid)); } - try!(write!(&mut w, "]")); + None => {} } + try!(write!(&mut w, "]")); + } - try!(write!(&mut w, r#"],"paths":["#)); + try!(write!(&mut w, r#"],"paths":["#)); - for (i, &did) in pathid_to_nodeid.iter().enumerate() { - let &(ref fqp, short) = cache.paths.find(&did).unwrap(); - if i > 0 { - try!(write!(&mut w, ",")); - } - try!(write!(&mut w, r#"[{:u},"{}"]"#, - short, *fqp.last().unwrap())); + for (i, &did) in pathid_to_nodeid.iter().enumerate() { + let &(ref fqp, short) = cache.paths.find(&did).unwrap(); + if i > 0 { + try!(write!(&mut w, ",")); } + try!(write!(&mut w, r#"[{:u},"{}"]"#, + short, *fqp.last().unwrap())); + } - try!(write!(&mut w, r"]\};")); + try!(write!(&mut w, r"]\};")); - str::from_utf8(w.unwrap().as_slice()).unwrap().to_string() - }; + Ok(str::from_utf8(w.unwrap().as_slice()).unwrap().to_string()) +} +fn write_shared(cx: &Context, + krate: &clean::Crate, + cache: &Cache, + search_index: String) -> io::IoResult<()> { // Write out the shared files. Note that these are shared among all rustdoc // docs placed in the output directory, so this needs to be a synchronized // operation with respect to all other rustdocs running around. - { - try!(mkdir(&cx.dst)); - let _lock = ::flock::Lock::new(&cx.dst.join(".lock")); - - // Add all the static files. These may already exist, but we just - // overwrite them anyway to make sure that they're fresh and up-to-date. - try!(write(cx.dst.join("jquery.js"), - include_bin!("static/jquery-2.1.0.min.js"))); - try!(write(cx.dst.join("main.js"), include_bin!("static/main.js"))); - try!(write(cx.dst.join("main.css"), include_bin!("static/main.css"))); - try!(write(cx.dst.join("normalize.css"), - include_bin!("static/normalize.css"))); - try!(write(cx.dst.join("FiraSans-Regular.woff"), - include_bin!("static/FiraSans-Regular.woff"))); - try!(write(cx.dst.join("FiraSans-Medium.woff"), - include_bin!("static/FiraSans-Medium.woff"))); - try!(write(cx.dst.join("Heuristica-Regular.woff"), - include_bin!("static/Heuristica-Regular.woff"))); - try!(write(cx.dst.join("Heuristica-Italic.woff"), - include_bin!("static/Heuristica-Italic.woff"))); - try!(write(cx.dst.join("Heuristica-Bold.woff"), - include_bin!("static/Heuristica-Bold.woff"))); - - fn collect(path: &Path, krate: &str, - key: &str) -> io::IoResult> { - let mut ret = Vec::new(); - if path.exists() { - for line in BufferedReader::new(File::open(path)).lines() { - let line = try!(line); - if !line.as_slice().starts_with(key) { - continue - } - if line.as_slice().starts_with( - format!("{}['{}']", key, krate).as_slice()) { - continue - } - ret.push(line.to_string()); + try!(mkdir(&cx.dst)); + let _lock = ::flock::Lock::new(&cx.dst.join(".lock")); + + // Add all the static files. These may already exist, but we just + // overwrite them anyway to make sure that they're fresh and up-to-date. + try!(write(cx.dst.join("jquery.js"), + include_bin!("static/jquery-2.1.0.min.js"))); + try!(write(cx.dst.join("main.js"), include_bin!("static/main.js"))); + try!(write(cx.dst.join("main.css"), include_bin!("static/main.css"))); + try!(write(cx.dst.join("normalize.css"), + include_bin!("static/normalize.css"))); + try!(write(cx.dst.join("FiraSans-Regular.woff"), + include_bin!("static/FiraSans-Regular.woff"))); + try!(write(cx.dst.join("FiraSans-Medium.woff"), + include_bin!("static/FiraSans-Medium.woff"))); + try!(write(cx.dst.join("Heuristica-Regular.woff"), + include_bin!("static/Heuristica-Regular.woff"))); + try!(write(cx.dst.join("Heuristica-Italic.woff"), + include_bin!("static/Heuristica-Italic.woff"))); + try!(write(cx.dst.join("Heuristica-Bold.woff"), + include_bin!("static/Heuristica-Bold.woff"))); + + fn collect(path: &Path, krate: &str, + key: &str) -> io::IoResult> { + let mut ret = Vec::new(); + if path.exists() { + for line in BufferedReader::new(File::open(path)).lines() { + let line = try!(line); + if !line.as_slice().starts_with(key) { + continue } - } - return Ok(ret); - } - - // Update the search index - let dst = cx.dst.join("search-index.js"); - let all_indexes = try!(collect(&dst, krate.name.as_slice(), - "searchIndex")); - let mut w = try!(File::create(&dst)); - try!(writeln!(&mut w, r"var searchIndex = \{\};")); - try!(writeln!(&mut w, "{}", index)); - for index in all_indexes.iter() { - try!(writeln!(&mut w, "{}", *index)); - } - try!(writeln!(&mut w, "initSearch(searchIndex);")); - - // Update the list of all implementors for traits - let dst = cx.dst.join("implementors"); - try!(mkdir(&dst)); - for (&did, imps) in cache.implementors.iter() { - if ast_util::is_local(did) { continue } - let &(ref remote_path, remote_item_type) = cache.paths.get(&did); - - let mut mydst = dst.clone(); - for part in remote_path.slice_to(remote_path.len() - 1).iter() { - mydst.push(part.as_slice()); - try!(mkdir(&mydst)); - } - mydst.push(format!("{}.{}.js", - remote_item_type.to_static_str(), - *remote_path.get(remote_path.len() - 1))); - let all_implementors = try!(collect(&mydst, krate.name.as_slice(), - "implementors")); - - try!(mkdir(&mydst.dir_path())); - let mut f = BufferedWriter::new(try!(File::create(&mydst))); - try!(writeln!(&mut f, r"(function() \{var implementors = \{\};")); - - for implementor in all_implementors.iter() { - try!(writeln!(&mut f, "{}", *implementor)); - } - - try!(write!(&mut f, r"implementors['{}'] = \{", krate.name)); - for imp in imps.iter() { - let &(ref path, item_type) = match *imp { - PathType(clean::ResolvedPath { did, .. }) => { - cache.paths.get(&did) - } - PathType(..) | OtherType(..) => continue, - }; - try!(write!(&mut f, r#"{}:"#, *path.get(path.len() - 1))); - try!(write!(&mut f, r#""{}"#, - path.slice_to(path.len() - 1).connect("/"))); - try!(write!(&mut f, r#"/{}.{}.html","#, - item_type.to_static_str(), - *path.get(path.len() - 1))); - } - try!(writeln!(&mut f, r"\};")); - try!(writeln!(&mut f, "{}", r" - if (window.register_implementors) { - window.register_implementors(implementors); - } else { - window.pending_implementors = implementors; + if line.as_slice().starts_with( + format!("{}['{}']", key, krate).as_slice()) { + continue } - ")); - try!(writeln!(&mut f, r"\})()")); + ret.push(line.to_string()); + } } + return Ok(ret); } - // Render all source files (this may turn into a giant no-op) - { - info!("emitting source files"); - let dst = cx.dst.join("src"); - try!(mkdir(&dst)); - let dst = dst.join(krate.name.as_slice()); - try!(mkdir(&dst)); - let mut folder = SourceCollector { - dst: dst, - seen: HashSet::new(), - cx: &mut cx, - }; - // skip all invalid spans - folder.seen.insert("".to_string()); - krate = folder.fold_crate(krate); + // Update the search index + let dst = cx.dst.join("search-index.js"); + let all_indexes = try!(collect(&dst, krate.name.as_slice(), + "searchIndex")); + let mut w = try!(File::create(&dst)); + try!(writeln!(&mut w, r"var searchIndex = \{\};")); + try!(writeln!(&mut w, "{}", search_index)); + for index in all_indexes.iter() { + try!(writeln!(&mut w, "{}", *index)); } + try!(writeln!(&mut w, "initSearch(searchIndex);")); + + // Update the list of all implementors for traits + let dst = cx.dst.join("implementors"); + try!(mkdir(&dst)); + for (&did, imps) in cache.implementors.iter() { + let &(ref remote_path, remote_item_type) = cache.paths.get(&did); + + let mut mydst = dst.clone(); + for part in remote_path.slice_to(remote_path.len() - 1).iter() { + mydst.push(part.as_slice()); + try!(mkdir(&mydst)); + } + mydst.push(format!("{}.{}.js", + remote_item_type.to_static_str(), + *remote_path.get(remote_path.len() - 1))); + let all_implementors = try!(collect(&mydst, krate.name.as_slice(), + "implementors")); + + try!(mkdir(&mydst.dir_path())); + let mut f = BufferedWriter::new(try!(File::create(&mydst))); + try!(writeln!(&mut f, r"(function() \{var implementors = \{\};")); + + for implementor in all_implementors.iter() { + try!(write!(&mut f, "{}", *implementor)); + } - for &(n, ref e) in krate.externs.iter() { - cache.extern_locations.insert(n, extern_location(e, &cx.dst)); - let did = ast::DefId { krate: n, node: ast::CRATE_NODE_ID }; - cache.paths.insert(did, (vec![e.name.to_string()], item_type::Module)); + try!(write!(&mut f, r"implementors['{}'] = [", krate.name)); + for imp in imps.iter() { + try!(write!(&mut f, r#""impl{} {} for {}","#, + imp.generics, imp.trait_, imp.for_)); + } + try!(writeln!(&mut f, r"];")); + try!(writeln!(&mut f, "{}", r" + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } + ")); + try!(writeln!(&mut f, r"\})()")); } + Ok(()) +} - // And finally render the whole crate's documentation - cx.krate(krate, cache) +fn render_sources(cx: &mut Context, + krate: clean::Crate) -> io::IoResult { + info!("emitting source files"); + let dst = cx.dst.join("src"); + try!(mkdir(&dst)); + let dst = dst.join(krate.name.as_slice()); + try!(mkdir(&dst)); + let mut folder = SourceCollector { + dst: dst, + seen: HashSet::new(), + cx: cx, + }; + // skip all invalid spans + folder.seen.insert("".to_string()); + Ok(folder.fold_crate(krate)) } /// Writes the entire contents of a string to a destination, not attempting to @@ -718,16 +725,11 @@ impl DocFolder for Cache { let v = self.implementors.find_or_insert_with(did, |_| { Vec::new() }); - match i.for_ { - clean::ResolvedPath{..} => { - v.unshift(PathType(i.for_.clone())); - } - _ => { - v.push(OtherType(i.generics.clone(), - i.trait_.get_ref().clone(), - i.for_.clone())); - } - } + v.push(Implementor { + generics: i.generics.clone(), + trait_: i.trait_.get_ref().clone(), + for_: i.for_.clone(), + }); } Some(..) | None => {} } @@ -941,16 +943,13 @@ impl Context { /// /// This currently isn't parallelized, but it'd be pretty easy to add /// parallelization to this function. - fn krate(self, mut krate: clean::Crate, cache: Cache) -> io::IoResult<()> { + fn krate(self, mut krate: clean::Crate) -> io::IoResult<()> { let mut item = match krate.module.take() { Some(i) => i, None => return Ok(()) }; item.name = Some(krate.name); - // using a rwarc makes this parallelizable in the future - cache_key.replace(Some(Arc::new(cache))); - let mut work = vec!((self, item)); loop { match work.pop() { @@ -1473,34 +1472,33 @@ fn item_trait(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item, try!(write!(w, "")); } - match cache_key.get().unwrap().implementors.find(&it.def_id) { + let cache = cache_key.get().unwrap(); + try!(write!(w, " +

Implementors

+
    + ")); + match cache.implementors.find(&it.def_id) { Some(implementors) => { - try!(write!(w, " -

    Implementors

    -
      - ")); for i in implementors.iter() { - match *i { - PathType(ref ty) => { - try!(write!(w, "
    • {}
    • ", *ty)); - } - OtherType(ref generics, ref trait_, ref for_) => { - try!(write!(w, "
    • impl{} {} for {}
    • ", - *generics, *trait_, *for_)); - } - } + try!(writeln!(w, "
    • impl{} {} for {}
    • ", + i.generics, i.trait_, i.for_)); } - try!(write!(w, "
    ")); - try!(write!(w, r#""#, - cx.current.iter().map(|_| "..") - .collect::>().connect("/"), - cx.current.connect("/"), - shortty(it).to_static_str(), - *it.name.get_ref())); } None => {} } + try!(write!(w, "
")); + try!(write!(w, r#""#, + root_path = Vec::from_elem(cx.current.len(), "..").connect("/"), + path = if ast_util::is_local(it.def_id) { + cx.current.connect("/") + } else { + let path = cache.external_paths.get(&it.def_id); + path.slice_to(path.len() - 1).connect("/") + }, + ty = shortty(it).to_static_str(), + name = *it.name.get_ref())); Ok(()) } diff --git a/src/librustdoc/html/static/main.js b/src/librustdoc/html/static/main.js index 2fb824653d3db..d153728ccce54 100644 --- a/src/librustdoc/html/static/main.js +++ b/src/librustdoc/html/static/main.js @@ -658,15 +658,13 @@ var list = $('#implementors-list'); var libs = Object.getOwnPropertyNames(imp); for (var i = 0; i < libs.length; i++) { - var structs = Object.getOwnPropertyNames(imp[libs[i]]); + if (libs[i] == currentCrate) continue; + var structs = imp[libs[i]]; for (var j = 0; j < structs.length; j++) { - console.log(i, structs[j]); - var path = rootPath + imp[libs[i]][structs[j]]; - var klass = path.contains("type.") ? "type" : "struct"; - var link = $('').text(structs[j]) - .attr('href', path) - .attr('class', klass); - var code = $('').append(link); + var code = $('').append(structs[j]); + $.each(code.find('a'), function(idx, a) { + $(a).attr('href', rootPath + $(a).attr('href')); + }); var li = $('
  • ').append(code); list.append(li); }