rustdoc: Generate hyperlinks between crates
The general idea of hyperlinking between crates is that it should require as little configuration as possible, if any at all. In this vein, there are two separate ways to generate hyperlinks between crates: 1. When you're generating documentation for a crate 'foo' into folder 'doc', then if foo's external crate dependencies already have documented in the folder 'doc', then hyperlinks will be generated. This will work because all documentation is in the same folder, allowing links to work seamlessly both on the web and on the local filesystem browser. The rationale for this use case is a package with multiple libraries/crates that all want to link to one another, and you don't want to have to deal with going to the web. In theory this could be extended to have a RUST_PATH-style searching situtation, but I'm not sure that it would work seamlessly on the web as it does on the local filesystem, so I'm not attempting to explore this case in this pull request. I believe to fully realize this potential rustdoc would have to be acting as a server instead of a static site generator. 2. One of foo's external dependencies has a #[doc(html_root_url = "...")] attribute. This means that all hyperlinks to the dependency will be rooted at this url. This use case encompasses all packages using libstd/libextra. These two crates now have this attribute encoded (currently at the /doc/master url) and will be read by anything which has a dependency on libstd/libextra. This should also work for arbitrary crates in the wild that have online documentation. I don't like how the version is hard-wired into the url, but I think that this may be a case-by-case thing which doesn't end up being too bad in the long run. Closes #9539
This commit is contained in:
parent
f00d72b78b
commit
d06043ba0b
@ -26,7 +26,8 @@ Rust extras are part of the standard Rust distribution.
|
|||||||
url = "https://github.com/mozilla/rust/tree/master/src/libextra")];
|
url = "https://github.com/mozilla/rust/tree/master/src/libextra")];
|
||||||
|
|
||||||
#[doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk.png",
|
#[doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk.png",
|
||||||
html_favicon_url = "http://www.rust-lang.org/favicon.ico")];
|
html_favicon_url = "http://www.rust-lang.org/favicon.ico",
|
||||||
|
html_root_url = "http://static.rust-lang.org/doc/master")];
|
||||||
|
|
||||||
#[comment = "Rust extras"];
|
#[comment = "Rust extras"];
|
||||||
#[license = "MIT/ASL2"];
|
#[license = "MIT/ASL2"];
|
||||||
|
@ -15,11 +15,18 @@ use its = syntax::parse::token::ident_to_str;
|
|||||||
|
|
||||||
use syntax;
|
use syntax;
|
||||||
use syntax::ast;
|
use syntax::ast;
|
||||||
|
use syntax::ast_map;
|
||||||
use syntax::ast_util;
|
use syntax::ast_util;
|
||||||
use syntax::attr;
|
use syntax::attr;
|
||||||
use syntax::attr::AttributeMethods;
|
use syntax::attr::AttributeMethods;
|
||||||
|
|
||||||
|
use rustc::metadata::cstore;
|
||||||
|
use rustc::metadata::csearch;
|
||||||
|
use rustc::metadata::decoder;
|
||||||
|
|
||||||
use std;
|
use std;
|
||||||
|
use std::hashmap::HashMap;
|
||||||
|
|
||||||
use doctree;
|
use doctree;
|
||||||
use visit_ast;
|
use visit_ast;
|
||||||
use std::local_data;
|
use std::local_data;
|
||||||
@ -61,19 +68,44 @@ impl<T: Clean<U>, U> Clean<~[U]> for syntax::opt_vec::OptVec<T> {
|
|||||||
pub struct Crate {
|
pub struct Crate {
|
||||||
name: ~str,
|
name: ~str,
|
||||||
module: Option<Item>,
|
module: Option<Item>,
|
||||||
|
externs: HashMap<ast::CrateNum, ExternalCrate>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Clean<Crate> for visit_ast::RustdocVisitor {
|
impl Clean<Crate> for visit_ast::RustdocVisitor {
|
||||||
fn clean(&self) -> Crate {
|
fn clean(&self) -> Crate {
|
||||||
use syntax::attr::{find_linkage_metas, last_meta_item_value_str_by_name};
|
use syntax::attr::{find_linkage_metas, last_meta_item_value_str_by_name};
|
||||||
let maybe_meta = last_meta_item_value_str_by_name(find_linkage_metas(self.attrs), "name");
|
let maybe_meta = last_meta_item_value_str_by_name(
|
||||||
|
find_linkage_metas(self.attrs), "name");
|
||||||
|
let cx = local_data::get(super::ctxtkey, |x| *x.unwrap());
|
||||||
|
|
||||||
|
let mut externs = HashMap::new();
|
||||||
|
do cstore::iter_crate_data(cx.sess.cstore) |n, meta| {
|
||||||
|
externs.insert(n, meta.clean());
|
||||||
|
}
|
||||||
|
|
||||||
Crate {
|
Crate {
|
||||||
name: match maybe_meta {
|
name: match maybe_meta {
|
||||||
Some(x) => x.to_owned(),
|
Some(x) => x.to_owned(),
|
||||||
None => fail2!("rustdoc_ng requires a \\#[link(name=\"foo\")] crate attribute"),
|
None => fail2!("rustdoc requires a \\#[link(name=\"foo\")] \
|
||||||
|
crate attribute"),
|
||||||
},
|
},
|
||||||
module: Some(self.module.clean()),
|
module: Some(self.module.clean()),
|
||||||
|
externs: externs,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deriving(Clone, Encodable, Decodable)]
|
||||||
|
pub struct ExternalCrate {
|
||||||
|
name: ~str,
|
||||||
|
attrs: ~[Attribute],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clean<ExternalCrate> for cstore::crate_metadata {
|
||||||
|
fn clean(&self) -> ExternalCrate {
|
||||||
|
ExternalCrate {
|
||||||
|
name: self.name.to_owned(),
|
||||||
|
attrs: decoder::get_crate_attributes(self.data).clean()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -542,7 +574,15 @@ pub enum Type {
|
|||||||
ResolvedPath {
|
ResolvedPath {
|
||||||
path: Path,
|
path: Path,
|
||||||
typarams: Option<~[TyParamBound]>,
|
typarams: Option<~[TyParamBound]>,
|
||||||
did: ast::DefId
|
id: ast::NodeId,
|
||||||
|
},
|
||||||
|
/// Same as above, but only external variants
|
||||||
|
ExternalPath {
|
||||||
|
path: Path,
|
||||||
|
typarams: Option<~[TyParamBound]>,
|
||||||
|
fqn: ~[~str],
|
||||||
|
kind: TypeKind,
|
||||||
|
crate: ast::CrateNum,
|
||||||
},
|
},
|
||||||
// I have no idea how to usefully use this.
|
// I have no idea how to usefully use this.
|
||||||
TyParamBinder(ast::NodeId),
|
TyParamBinder(ast::NodeId),
|
||||||
@ -572,6 +612,14 @@ pub enum Type {
|
|||||||
// region, raw, other boxes, mutable
|
// region, raw, other boxes, mutable
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[deriving(Clone, Encodable, Decodable)]
|
||||||
|
pub enum TypeKind {
|
||||||
|
TypeStruct,
|
||||||
|
TypeEnum,
|
||||||
|
TypeTrait,
|
||||||
|
TypeFunction,
|
||||||
|
}
|
||||||
|
|
||||||
impl Clean<Type> for ast::Ty {
|
impl Clean<Type> for ast::Ty {
|
||||||
fn clean(&self) -> Type {
|
fn clean(&self) -> Type {
|
||||||
use syntax::ast::*;
|
use syntax::ast::*;
|
||||||
@ -1099,26 +1147,12 @@ fn name_from_pat(p: &ast::Pat) -> ~str {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remove_comment_tags(s: &str) -> ~str {
|
|
||||||
if s.starts_with("/") {
|
|
||||||
match s.slice(0,3) {
|
|
||||||
&"///" => return s.slice(3, s.len()).trim().to_owned(),
|
|
||||||
&"/**" | &"/*!" => return s.slice(3, s.len() - 2).trim().to_owned(),
|
|
||||||
_ => return s.trim().to_owned()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return s.to_owned();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Given a Type, resolve it using the def_map
|
/// Given a Type, resolve it using the def_map
|
||||||
fn resolve_type(path: Path, tpbs: Option<~[TyParamBound]>,
|
fn resolve_type(path: Path, tpbs: Option<~[TyParamBound]>,
|
||||||
id: ast::NodeId) -> Type {
|
id: ast::NodeId) -> Type {
|
||||||
use syntax::ast::*;
|
let cx = local_data::get(super::ctxtkey, |x| *x.unwrap());
|
||||||
|
|
||||||
let dm = local_data::get(super::ctxtkey, |x| *x.unwrap()).tycx.def_map;
|
|
||||||
debug2!("searching for {:?} in defmap", id);
|
debug2!("searching for {:?} in defmap", id);
|
||||||
let d = match dm.find(&id) {
|
let d = match cx.tycx.def_map.find(&id) {
|
||||||
Some(k) => k,
|
Some(k) => k,
|
||||||
None => {
|
None => {
|
||||||
let ctxt = local_data::get(super::ctxtkey, |x| *x.unwrap());
|
let ctxt = local_data::get(super::ctxtkey, |x| *x.unwrap());
|
||||||
@ -1128,28 +1162,41 @@ fn resolve_type(path: Path, tpbs: Option<~[TyParamBound]>,
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let def_id = match *d {
|
let (def_id, kind) = match *d {
|
||||||
DefFn(i, _) => i,
|
ast::DefFn(i, _) => (i, TypeFunction),
|
||||||
DefSelf(i) | DefSelfTy(i) => return Self(i),
|
ast::DefSelf(i) | ast::DefSelfTy(i) => return Self(i),
|
||||||
DefTy(i) => i,
|
ast::DefTy(i) => (i, TypeEnum),
|
||||||
DefTrait(i) => {
|
ast::DefTrait(i) => {
|
||||||
debug2!("saw DefTrait in def_to_id");
|
debug2!("saw DefTrait in def_to_id");
|
||||||
i
|
(i, TypeTrait)
|
||||||
},
|
},
|
||||||
DefPrimTy(p) => match p {
|
ast::DefPrimTy(p) => match p {
|
||||||
ty_str => return String,
|
ast::ty_str => return String,
|
||||||
ty_bool => return Bool,
|
ast::ty_bool => return Bool,
|
||||||
_ => return Primitive(p)
|
_ => return Primitive(p)
|
||||||
},
|
},
|
||||||
DefTyParam(i, _) => return Generic(i.node),
|
ast::DefTyParam(i, _) => return Generic(i.node),
|
||||||
DefStruct(i) => i,
|
ast::DefStruct(i) => (i, TypeStruct),
|
||||||
DefTyParamBinder(i) => {
|
ast::DefTyParamBinder(i) => {
|
||||||
debug2!("found a typaram_binder, what is it? {}", i);
|
debug2!("found a typaram_binder, what is it? {}", i);
|
||||||
return TyParamBinder(i);
|
return TyParamBinder(i);
|
||||||
},
|
},
|
||||||
x => fail2!("resolved type maps to a weird def {:?}", x),
|
x => fail2!("resolved type maps to a weird def {:?}", x),
|
||||||
};
|
};
|
||||||
ResolvedPath{ path: path, typarams: tpbs, did: def_id }
|
if ast_util::is_local(def_id) {
|
||||||
|
ResolvedPath{ path: path, typarams: tpbs, id: def_id.node }
|
||||||
|
} else {
|
||||||
|
let fqn = csearch::get_item_path(cx.tycx, def_id);
|
||||||
|
let fqn = fqn.move_iter().map(|i| {
|
||||||
|
match i {
|
||||||
|
ast_map::path_mod(id) |
|
||||||
|
ast_map::path_name(id) |
|
||||||
|
ast_map::path_pretty_name(id, _) => id.clean()
|
||||||
|
}
|
||||||
|
}).to_owned_vec();
|
||||||
|
ExternalPath{ path: path, typarams: tpbs, fqn: fqn, kind: kind,
|
||||||
|
crate: def_id.crate }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_use_source(path: Path, id: ast::NodeId) -> ImportSource {
|
fn resolve_use_source(path: Path, id: ast::NodeId) -> ImportSource {
|
||||||
|
@ -16,6 +16,7 @@ use syntax::ast;
|
|||||||
use syntax::ast_util;
|
use syntax::ast_util;
|
||||||
|
|
||||||
use clean;
|
use clean;
|
||||||
|
use html::render;
|
||||||
use html::render::{cache_key, current_location_key};
|
use html::render::{cache_key, current_location_key};
|
||||||
|
|
||||||
pub struct VisSpace(Option<ast::visibility>);
|
pub struct VisSpace(Option<ast::visibility>);
|
||||||
@ -97,8 +98,46 @@ impl fmt::Default for clean::Path {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolved_path(w: &mut io::Writer, did: ast::DefId,
|
fn resolved_path(w: &mut io::Writer, id: ast::NodeId, p: &clean::Path,
|
||||||
path: &clean::Path, print_all: bool) {
|
print_all: bool) {
|
||||||
|
path(w, p, print_all,
|
||||||
|
|_cache, loc| {
|
||||||
|
match p.segments[0].name.as_slice() {
|
||||||
|
"super" => Some("../".repeat(loc.len() - 1)),
|
||||||
|
_ => Some("../".repeat(loc.len())),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|cache| {
|
||||||
|
match cache.paths.find(&id) {
|
||||||
|
None => None,
|
||||||
|
Some(&(ref fqp, shortty)) => Some((fqp.clone(), shortty))
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn external_path(w: &mut io::Writer, p: &clean::Path, print_all: bool,
|
||||||
|
fqn: &[~str], kind: clean::TypeKind, crate: ast::CrateNum) {
|
||||||
|
path(w, p, print_all,
|
||||||
|
|cache, loc| {
|
||||||
|
match *cache.extern_locations.get(&crate) {
|
||||||
|
render::Remote(ref s) => Some(s.clone()),
|
||||||
|
render::Local => Some("../".repeat(loc.len())),
|
||||||
|
render::Unknown => None,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|_cache| {
|
||||||
|
Some((fqn.to_owned(), match kind {
|
||||||
|
clean::TypeStruct => "struct",
|
||||||
|
clean::TypeEnum => "enum",
|
||||||
|
clean::TypeFunction => "fn",
|
||||||
|
clean::TypeTrait => "trait",
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn path(w: &mut io::Writer, path: &clean::Path, print_all: bool,
|
||||||
|
root: &fn(&render::Cache, &[~str]) -> Option<~str>,
|
||||||
|
info: &fn(&render::Cache) -> Option<(~[~str], &'static str)>) {
|
||||||
// The generics will get written to both the title and link
|
// The generics will get written to both the title and link
|
||||||
let mut generics = ~"";
|
let mut generics = ~"";
|
||||||
let last = path.segments.last();
|
let last = path.segments.last();
|
||||||
@ -121,51 +160,49 @@ fn resolved_path(w: &mut io::Writer, did: ast::DefId,
|
|||||||
do local_data::get(current_location_key) |loc| {
|
do local_data::get(current_location_key) |loc| {
|
||||||
let loc = loc.unwrap();
|
let loc = loc.unwrap();
|
||||||
|
|
||||||
if print_all {
|
|
||||||
let mut root = match path.segments[0].name.as_slice() {
|
|
||||||
"super" => ~"../",
|
|
||||||
"self" => ~"",
|
|
||||||
_ => "../".repeat(loc.len() - 1),
|
|
||||||
};
|
|
||||||
let amt = path.segments.len() - 1;
|
|
||||||
for seg in path.segments.slice_to(amt).iter() {
|
|
||||||
if "super" == seg.name || "self" == seg.name {
|
|
||||||
write!(w, "{}::", seg.name);
|
|
||||||
} else {
|
|
||||||
root.push_str(seg.name);
|
|
||||||
root.push_str("/");
|
|
||||||
write!(w, "<a class='mod'
|
|
||||||
href='{}index.html'>{}</a>::",
|
|
||||||
root,
|
|
||||||
seg.name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
do local_data::get(cache_key) |cache| {
|
do local_data::get(cache_key) |cache| {
|
||||||
do cache.unwrap().read |cache| {
|
do cache.unwrap().read |cache| {
|
||||||
match cache.paths.find(&did.node) {
|
let abs_root = root(cache, loc.as_slice());
|
||||||
// This is a documented path, link to it!
|
let rel_root = match path.segments[0].name.as_slice() {
|
||||||
// FIXME(#9539): this is_local check should not exist
|
"self" => Some(~"./"),
|
||||||
Some(&(ref fqp, shortty)) if ast_util::is_local(did) => {
|
_ => None,
|
||||||
let fqn = fqp.connect("::");
|
};
|
||||||
let same = loc.iter().zip(fqp.iter())
|
|
||||||
.take_while(|&(a, b)| *a == *b).len();
|
|
||||||
|
|
||||||
let mut url = ~"";
|
if print_all {
|
||||||
if "super" == path.segments[0].name {
|
let amt = path.segments.len() - 1;
|
||||||
url.push_str("../");
|
match rel_root {
|
||||||
} else if "self" != path.segments[0].name {
|
Some(root) => {
|
||||||
url.push_str("../".repeat(loc.len() - same));
|
let mut root = root;
|
||||||
}
|
for seg in path.segments.slice_to(amt).iter() {
|
||||||
if same < fqp.len() {
|
if "super" == seg.name || "self" == seg.name {
|
||||||
let remaining = fqp.slice_from(same);
|
write!(w, "{}::", seg.name);
|
||||||
let to_link = remaining.slice_to(remaining.len() - 1);
|
} else {
|
||||||
for component in to_link.iter() {
|
root.push_str(seg.name);
|
||||||
url.push_str(*component);
|
root.push_str("/");
|
||||||
url.push_str("/");
|
write!(w, "<a class='mod'
|
||||||
|
href='{}index.html'>{}</a>::",
|
||||||
|
root,
|
||||||
|
seg.name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
None => {
|
||||||
|
for seg in path.segments.slice_to(amt).iter() {
|
||||||
|
write!(w, "{}::", seg.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match info(cache) {
|
||||||
|
// This is a documented path, link to it!
|
||||||
|
Some((ref fqp, shortty)) if abs_root.is_some() => {
|
||||||
|
let mut url = abs_root.unwrap();
|
||||||
|
let to_link = fqp.slice_to(fqp.len() - 1);
|
||||||
|
for component in to_link.iter() {
|
||||||
|
url.push_str(*component);
|
||||||
|
url.push_str("/");
|
||||||
|
}
|
||||||
match shortty {
|
match shortty {
|
||||||
"mod" => {
|
"mod" => {
|
||||||
url.push_str(*fqp.last());
|
url.push_str(*fqp.last());
|
||||||
@ -178,24 +215,35 @@ fn resolved_path(w: &mut io::Writer, did: ast::DefId,
|
|||||||
url.push_str(".html");
|
url.push_str(".html");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
write!(w, "<a class='{}' href='{}' title='{}'>{}</a>{}",
|
|
||||||
shortty, url, fqn, last.name, generics);
|
write!(w, "<a class='{}' href='{}' title='{}'>{}</a>",
|
||||||
|
shortty, url, fqp.connect("::"), last.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => {
|
_ => {
|
||||||
if print_all {
|
write!(w, "{}", last.name);
|
||||||
let amt = path.segments.len() - 1;
|
|
||||||
for seg in path.segments.iter().take(amt) {
|
|
||||||
write!(w, "{}::", seg.name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
write!(w, "{}{}", last.name, generics);
|
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
write!(w, "{}", generics);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn typarams(w: &mut io::Writer, typarams: &Option<~[clean::TyParamBound]>) {
|
||||||
|
match *typarams {
|
||||||
|
Some(ref params) => {
|
||||||
|
write!(w, "<");
|
||||||
|
for (i, param) in params.iter().enumerate() {
|
||||||
|
if i > 0 { write!(w, ", "); }
|
||||||
|
write!(w, "{}", *param);
|
||||||
|
}
|
||||||
|
write!(w, ">");
|
||||||
|
}
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl fmt::Default for clean::Type {
|
impl fmt::Default for clean::Type {
|
||||||
fn fmt(g: &clean::Type, f: &mut fmt::Formatter) {
|
fn fmt(g: &clean::Type, f: &mut fmt::Formatter) {
|
||||||
match *g {
|
match *g {
|
||||||
@ -206,19 +254,14 @@ impl fmt::Default for clean::Type {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
clean::ResolvedPath{did, typarams: ref typarams, path: ref path} => {
|
clean::ResolvedPath{id, typarams: ref tp, path: ref path} => {
|
||||||
resolved_path(f.buf, did, path, false);
|
resolved_path(f.buf, id, path, false);
|
||||||
match *typarams {
|
typarams(f.buf, tp);
|
||||||
Some(ref params) => {
|
}
|
||||||
f.buf.write("<".as_bytes());
|
clean::ExternalPath{path: ref path, typarams: ref tp,
|
||||||
for (i, param) in params.iter().enumerate() {
|
fqn: ref fqn, kind, crate} => {
|
||||||
if i > 0 { f.buf.write(", ".as_bytes()) }
|
external_path(f.buf, path, false, fqn.as_slice(), kind, crate);
|
||||||
write!(f.buf, "{}", *param);
|
typarams(f.buf, tp);
|
||||||
}
|
|
||||||
f.buf.write(">".as_bytes());
|
|
||||||
}
|
|
||||||
None => {}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
clean::Self(*) => f.buf.write("Self".as_bytes()),
|
clean::Self(*) => f.buf.write("Self".as_bytes()),
|
||||||
clean::Primitive(prim) => {
|
clean::Primitive(prim) => {
|
||||||
@ -417,8 +460,9 @@ impl fmt::Default for clean::ViewPath {
|
|||||||
impl fmt::Default for clean::ImportSource {
|
impl fmt::Default for clean::ImportSource {
|
||||||
fn fmt(v: &clean::ImportSource, f: &mut fmt::Formatter) {
|
fn fmt(v: &clean::ImportSource, f: &mut fmt::Formatter) {
|
||||||
match v.did {
|
match v.did {
|
||||||
Some(did) => {
|
// XXX: shouldn't be restricted to just local imports
|
||||||
resolved_path(f.buf, did, &v.path, true);
|
Some(did) if ast_util::is_local(did) => {
|
||||||
|
resolved_path(f.buf, did.node, &v.path, true);
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
for (i, seg) in v.path.segments.iter().enumerate() {
|
for (i, seg) in v.path.segments.iter().enumerate() {
|
||||||
@ -433,7 +477,8 @@ impl fmt::Default for clean::ImportSource {
|
|||||||
impl fmt::Default for clean::ViewListIdent {
|
impl fmt::Default for clean::ViewListIdent {
|
||||||
fn fmt(v: &clean::ViewListIdent, f: &mut fmt::Formatter) {
|
fn fmt(v: &clean::ViewListIdent, f: &mut fmt::Formatter) {
|
||||||
match v.source {
|
match v.source {
|
||||||
Some(did) => {
|
// XXX: shouldn't be limited to just local imports
|
||||||
|
Some(did) if ast_util::is_local(did) => {
|
||||||
let path = clean::Path {
|
let path = clean::Path {
|
||||||
global: false,
|
global: false,
|
||||||
segments: ~[clean::PathSegment {
|
segments: ~[clean::PathSegment {
|
||||||
@ -442,7 +487,7 @@ impl fmt::Default for clean::ViewListIdent {
|
|||||||
types: ~[],
|
types: ~[],
|
||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
resolved_path(f.buf, did, &path, false);
|
resolved_path(f.buf, did.node, &path, false);
|
||||||
}
|
}
|
||||||
_ => write!(f.buf, "{}", v.name),
|
_ => write!(f.buf, "{}", v.name),
|
||||||
}
|
}
|
||||||
|
@ -28,10 +28,8 @@ use std::vec;
|
|||||||
use extra::arc::RWArc;
|
use extra::arc::RWArc;
|
||||||
use extra::json::ToJson;
|
use extra::json::ToJson;
|
||||||
use extra::sort;
|
use extra::sort;
|
||||||
use extra::time;
|
|
||||||
|
|
||||||
use syntax::ast;
|
use syntax::ast;
|
||||||
use syntax::ast_util::is_local;
|
|
||||||
use syntax::attr;
|
use syntax::attr;
|
||||||
|
|
||||||
use clean;
|
use clean;
|
||||||
@ -52,6 +50,12 @@ pub struct Context {
|
|||||||
include_sources: bool,
|
include_sources: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub enum ExternalLocation {
|
||||||
|
Remote(~str), // remote url root of the documentation
|
||||||
|
Local, // inside local folder
|
||||||
|
Unknown, // unknown where the documentation is
|
||||||
|
}
|
||||||
|
|
||||||
enum Implementor {
|
enum Implementor {
|
||||||
PathType(clean::Type),
|
PathType(clean::Type),
|
||||||
OtherType(clean::Generics, /* trait */ clean::Type, /* for */ clean::Type),
|
OtherType(clean::Generics, /* trait */ clean::Type, /* for */ clean::Type),
|
||||||
@ -68,6 +72,8 @@ struct Cache {
|
|||||||
traits: HashMap<ast::NodeId, HashMap<~str, ~str>>,
|
traits: HashMap<ast::NodeId, HashMap<~str, ~str>>,
|
||||||
// trait id => implementors of the trait
|
// trait id => implementors of the trait
|
||||||
implementors: HashMap<ast::NodeId, ~[Implementor]>,
|
implementors: HashMap<ast::NodeId, ~[Implementor]>,
|
||||||
|
// crate number => where is the crate's dox located at
|
||||||
|
extern_locations: HashMap<ast::CrateNum, ExternalLocation>,
|
||||||
|
|
||||||
priv stack: ~[~str],
|
priv stack: ~[~str],
|
||||||
priv parent_stack: ~[ast::NodeId],
|
priv parent_stack: ~[ast::NodeId],
|
||||||
@ -142,6 +148,7 @@ pub fn run(mut crate: clean::Crate, dst: Path) {
|
|||||||
stack: ~[],
|
stack: ~[],
|
||||||
parent_stack: ~[],
|
parent_stack: ~[],
|
||||||
search_index: ~[],
|
search_index: ~[],
|
||||||
|
extern_locations: HashMap::new(),
|
||||||
};
|
};
|
||||||
cache.stack.push(crate.name.clone());
|
cache.stack.push(crate.name.clone());
|
||||||
crate = cache.fold_crate(crate);
|
crate = cache.fold_crate(crate);
|
||||||
@ -154,6 +161,7 @@ pub fn run(mut crate: clean::Crate, dst: Path) {
|
|||||||
write(dst.push("main.css"), include_str!("static/main.css"));
|
write(dst.push("main.css"), include_str!("static/main.css"));
|
||||||
write(dst.push("normalize.css"), include_str!("static/normalize.css"));
|
write(dst.push("normalize.css"), include_str!("static/normalize.css"));
|
||||||
|
|
||||||
|
// Publish the search index
|
||||||
{
|
{
|
||||||
let dst = dst.push("search-index.js");
|
let dst = dst.push("search-index.js");
|
||||||
let mut w = BufferedWriter::new(dst.open_writer(io::CreateOrTruncate));
|
let mut w = BufferedWriter::new(dst.open_writer(io::CreateOrTruncate));
|
||||||
@ -180,9 +188,9 @@ pub fn run(mut crate: clean::Crate, dst: Path) {
|
|||||||
w.flush();
|
w.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
info2!("emitting source files");
|
// Render all source files (this may turn into a giant no-op)
|
||||||
let started = time::precise_time_ns();
|
|
||||||
{
|
{
|
||||||
|
info2!("emitting source files");
|
||||||
let dst = cx.dst.push("src");
|
let dst = cx.dst.push("src");
|
||||||
mkdir(&dst);
|
mkdir(&dst);
|
||||||
let dst = dst.push(crate.name);
|
let dst = dst.push(crate.name);
|
||||||
@ -194,14 +202,13 @@ pub fn run(mut crate: clean::Crate, dst: Path) {
|
|||||||
};
|
};
|
||||||
crate = folder.fold_crate(crate);
|
crate = folder.fold_crate(crate);
|
||||||
}
|
}
|
||||||
let ended = time::precise_time_ns();
|
|
||||||
info2!("Took {:.03f}s", (ended as f64 - started as f64) / 1e9f64);
|
|
||||||
|
|
||||||
info2!("rendering the whole crate");
|
for (&n, e) in crate.externs.iter() {
|
||||||
let started = time::precise_time_ns();
|
cache.extern_locations.insert(n, extern_location(e, &cx.dst));
|
||||||
|
}
|
||||||
|
|
||||||
|
// And finally render the whole crate's documentation
|
||||||
cx.crate(crate, cache);
|
cx.crate(crate, cache);
|
||||||
let ended = time::precise_time_ns();
|
|
||||||
info2!("Took {:.03f}s", (ended as f64 - started as f64) / 1e9f64);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write(dst: Path, contents: &str) {
|
fn write(dst: Path, contents: &str) {
|
||||||
@ -235,6 +242,38 @@ fn clean_srcpath(src: &str, f: &fn(&str)) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn extern_location(e: &clean::ExternalCrate, dst: &Path) -> ExternalLocation {
|
||||||
|
// See if there's documentation generated into the local directory
|
||||||
|
let local_location = dst.push(e.name);
|
||||||
|
if local_location.is_dir() {
|
||||||
|
return Local;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Failing that, see if there's an attribute specifying where to find this
|
||||||
|
// external crate
|
||||||
|
for attr in e.attrs.iter() {
|
||||||
|
match *attr {
|
||||||
|
clean::List(~"doc", ref list) => {
|
||||||
|
for attr in list.iter() {
|
||||||
|
match *attr {
|
||||||
|
clean::NameValue(~"html_root_url", ref s) => {
|
||||||
|
if s.ends_with("/") {
|
||||||
|
return Remote(s.to_owned());
|
||||||
|
}
|
||||||
|
return Remote(*s + "/");
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Well, at least we tried.
|
||||||
|
return Unknown;
|
||||||
|
}
|
||||||
|
|
||||||
impl<'self> DocFolder for SourceCollector<'self> {
|
impl<'self> DocFolder for SourceCollector<'self> {
|
||||||
fn fold_item(&mut self, item: clean::Item) -> Option<clean::Item> {
|
fn fold_item(&mut self, item: clean::Item) -> Option<clean::Item> {
|
||||||
if self.cx.include_sources && !self.seen.contains(&item.source.filename) {
|
if self.cx.include_sources && !self.seen.contains(&item.source.filename) {
|
||||||
@ -353,8 +392,7 @@ impl DocFolder for Cache {
|
|||||||
match item.inner {
|
match item.inner {
|
||||||
clean::ImplItem(ref i) => {
|
clean::ImplItem(ref i) => {
|
||||||
match i.trait_ {
|
match i.trait_ {
|
||||||
Some(clean::ResolvedPath{ did, _ }) if is_local(did) => {
|
Some(clean::ResolvedPath{ id, _ }) => {
|
||||||
let id = did.node;
|
|
||||||
let v = do self.implementors.find_or_insert_with(id) |_|{
|
let v = do self.implementors.find_or_insert_with(id) |_|{
|
||||||
~[]
|
~[]
|
||||||
};
|
};
|
||||||
@ -441,8 +479,8 @@ impl DocFolder for Cache {
|
|||||||
}
|
}
|
||||||
clean::ImplItem(ref i) => {
|
clean::ImplItem(ref i) => {
|
||||||
match i.for_ {
|
match i.for_ {
|
||||||
clean::ResolvedPath{ did, _ } if is_local(did) => {
|
clean::ResolvedPath{ id, _ } => {
|
||||||
self.parent_stack.push(did.node); true
|
self.parent_stack.push(id); true
|
||||||
}
|
}
|
||||||
_ => false
|
_ => false
|
||||||
}
|
}
|
||||||
@ -457,8 +495,7 @@ impl DocFolder for Cache {
|
|||||||
match item {
|
match item {
|
||||||
clean::Item{ attrs, inner: clean::ImplItem(i), _ } => {
|
clean::Item{ attrs, inner: clean::ImplItem(i), _ } => {
|
||||||
match i.for_ {
|
match i.for_ {
|
||||||
clean::ResolvedPath { did, _ } if is_local(did) => {
|
clean::ResolvedPath { id, _ } => {
|
||||||
let id = did.node;
|
|
||||||
let v = do self.impls.find_or_insert_with(id) |_| {
|
let v = do self.impls.find_or_insert_with(id) |_| {
|
||||||
~[]
|
~[]
|
||||||
};
|
};
|
||||||
@ -1258,7 +1295,7 @@ fn render_impl(w: &mut io::Writer, i: &clean::Impl, dox: &Option<~str>) {
|
|||||||
Some(ref ty) => {
|
Some(ref ty) => {
|
||||||
write!(w, "{} for ", *ty);
|
write!(w, "{} for ", *ty);
|
||||||
match *ty {
|
match *ty {
|
||||||
clean::ResolvedPath { did, _ } => Some(did),
|
clean::ResolvedPath { id, _ } => Some(id),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1289,8 +1326,7 @@ fn render_impl(w: &mut io::Writer, i: &clean::Impl, dox: &Option<~str>) {
|
|||||||
// No documentation? Attempt to slurp in the trait's documentation
|
// No documentation? Attempt to slurp in the trait's documentation
|
||||||
let trait_id = match trait_id {
|
let trait_id = match trait_id {
|
||||||
None => continue,
|
None => continue,
|
||||||
Some(id) if is_local(id) => continue,
|
Some(id) => id,
|
||||||
Some(id) => id.node,
|
|
||||||
};
|
};
|
||||||
do local_data::get(cache_key) |cache| {
|
do local_data::get(cache_key) |cache| {
|
||||||
do cache.unwrap().read |cache| {
|
do cache.unwrap().read |cache| {
|
||||||
|
@ -13,7 +13,6 @@ use std::uint;
|
|||||||
use std::hashmap::HashSet;
|
use std::hashmap::HashSet;
|
||||||
|
|
||||||
use syntax::ast;
|
use syntax::ast;
|
||||||
use syntax::ast_util::is_local;
|
|
||||||
|
|
||||||
use clean;
|
use clean;
|
||||||
use clean::Item;
|
use clean::Item;
|
||||||
@ -131,8 +130,8 @@ pub fn strip_private(mut crate: clean::Crate) -> plugins::PluginResult {
|
|||||||
match i.inner {
|
match i.inner {
|
||||||
clean::ImplItem(ref imp) => {
|
clean::ImplItem(ref imp) => {
|
||||||
match imp.trait_ {
|
match imp.trait_ {
|
||||||
Some(clean::ResolvedPath{ did, _ }) => {
|
Some(clean::ResolvedPath{ id, _ }) => {
|
||||||
if is_local(did) && !self.contains(&did.node) {
|
if !self.contains(&id) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -58,7 +58,8 @@ they contained the following prologue:
|
|||||||
#[crate_type = "lib"];
|
#[crate_type = "lib"];
|
||||||
|
|
||||||
#[doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk.png",
|
#[doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk.png",
|
||||||
html_favicon_url = "http://www.rust-lang.org/favicon.ico")];
|
html_favicon_url = "http://www.rust-lang.org/favicon.ico",
|
||||||
|
html_root_url = "http://static.rust-lang.org/doc/master")];
|
||||||
|
|
||||||
// Don't link to std. We are std.
|
// Don't link to std. We are std.
|
||||||
#[no_std];
|
#[no_std];
|
||||||
|
Loading…
Reference in New Issue
Block a user