diff --git a/src/libextra/extra.rs b/src/libextra/extra.rs index 796dd9c7c7e..74787e66fec 100644 --- a/src/libextra/extra.rs +++ b/src/libextra/extra.rs @@ -26,7 +26,8 @@ Rust extras are part of the standard Rust distribution. 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", - 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"]; #[license = "MIT/ASL2"]; diff --git a/src/librustdoc/clean.rs b/src/librustdoc/clean.rs index b0f2ba286e6..b9100d6e365 100644 --- a/src/librustdoc/clean.rs +++ b/src/librustdoc/clean.rs @@ -15,11 +15,18 @@ use its = syntax::parse::token::ident_to_str; use syntax; use syntax::ast; +use syntax::ast_map; use syntax::ast_util; use syntax::attr; use syntax::attr::AttributeMethods; +use rustc::metadata::cstore; +use rustc::metadata::csearch; +use rustc::metadata::decoder; + use std; +use std::hashmap::HashMap; + use doctree; use visit_ast; use std::local_data; @@ -61,19 +68,44 @@ impl<T: Clean<U>, U> Clean<~[U]> for syntax::opt_vec::OptVec<T> { pub struct Crate { name: ~str, module: Option<Item>, + externs: HashMap<ast::CrateNum, ExternalCrate>, } impl Clean<Crate> for visit_ast::RustdocVisitor { fn clean(&self) -> Crate { 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 { name: match maybe_meta { 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()), + 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 { path: Path, 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. TyParamBinder(ast::NodeId), @@ -572,6 +612,14 @@ pub enum Type { // region, raw, other boxes, mutable } +#[deriving(Clone, Encodable, Decodable)] +pub enum TypeKind { + TypeStruct, + TypeEnum, + TypeTrait, + TypeFunction, +} + impl Clean<Type> for ast::Ty { fn clean(&self) -> Type { 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 fn resolve_type(path: Path, tpbs: Option<~[TyParamBound]>, id: ast::NodeId) -> Type { - use syntax::ast::*; - - let dm = local_data::get(super::ctxtkey, |x| *x.unwrap()).tycx.def_map; + let cx = local_data::get(super::ctxtkey, |x| *x.unwrap()); debug2!("searching for {:?} in defmap", id); - let d = match dm.find(&id) { + let d = match cx.tycx.def_map.find(&id) { Some(k) => k, None => { 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 { - DefFn(i, _) => i, - DefSelf(i) | DefSelfTy(i) => return Self(i), - DefTy(i) => i, - DefTrait(i) => { + let (def_id, kind) = match *d { + ast::DefFn(i, _) => (i, TypeFunction), + ast::DefSelf(i) | ast::DefSelfTy(i) => return Self(i), + ast::DefTy(i) => (i, TypeEnum), + ast::DefTrait(i) => { debug2!("saw DefTrait in def_to_id"); - i + (i, TypeTrait) }, - DefPrimTy(p) => match p { - ty_str => return String, - ty_bool => return Bool, + ast::DefPrimTy(p) => match p { + ast::ty_str => return String, + ast::ty_bool => return Bool, _ => return Primitive(p) }, - DefTyParam(i, _) => return Generic(i.node), - DefStruct(i) => i, - DefTyParamBinder(i) => { + ast::DefTyParam(i, _) => return Generic(i.node), + ast::DefStruct(i) => (i, TypeStruct), + ast::DefTyParamBinder(i) => { debug2!("found a typaram_binder, what is it? {}", i); return TyParamBinder(i); }, 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 { diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index acbed7f4120..c5f6d7c44fd 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -16,6 +16,7 @@ use syntax::ast; use syntax::ast_util; use clean; +use html::render; use html::render::{cache_key, current_location_key}; 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, - path: &clean::Path, print_all: bool) { +fn resolved_path(w: &mut io::Writer, id: ast::NodeId, p: &clean::Path, + 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 let mut generics = ~""; 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| { 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 cache.unwrap().read |cache| { - match cache.paths.find(&did.node) { - // This is a documented path, link to it! - // 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(); + let abs_root = root(cache, loc.as_slice()); + let rel_root = match path.segments[0].name.as_slice() { + "self" => Some(~"./"), + _ => None, + }; - let mut url = ~""; - if "super" == path.segments[0].name { - url.push_str("../"); - } else if "self" != path.segments[0].name { - url.push_str("../".repeat(loc.len() - same)); - } - if same < fqp.len() { - let remaining = fqp.slice_from(same); - let to_link = remaining.slice_to(remaining.len() - 1); - for component in to_link.iter() { - url.push_str(*component); - url.push_str("/"); + if print_all { + let amt = path.segments.len() - 1; + match rel_root { + Some(root) => { + let mut root = root; + 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); + } } } + 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 { "mod" => { url.push_str(*fqp.last()); @@ -178,24 +215,35 @@ fn resolved_path(w: &mut io::Writer, did: ast::DefId, 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 { - 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, "{}", last.name); } - }; + } + 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 { fn fmt(g: &clean::Type, f: &mut fmt::Formatter) { match *g { @@ -206,19 +254,14 @@ impl fmt::Default for clean::Type { } } } - clean::ResolvedPath{did, typarams: ref typarams, path: ref path} => { - resolved_path(f.buf, did, path, false); - match *typarams { - Some(ref params) => { - f.buf.write("<".as_bytes()); - for (i, param) in params.iter().enumerate() { - if i > 0 { f.buf.write(", ".as_bytes()) } - write!(f.buf, "{}", *param); - } - f.buf.write(">".as_bytes()); - } - None => {} - } + clean::ResolvedPath{id, typarams: ref tp, path: ref path} => { + resolved_path(f.buf, id, path, false); + typarams(f.buf, tp); + } + clean::ExternalPath{path: ref path, typarams: ref tp, + fqn: ref fqn, kind, crate} => { + external_path(f.buf, path, false, fqn.as_slice(), kind, crate); + typarams(f.buf, tp); } clean::Self(*) => f.buf.write("Self".as_bytes()), clean::Primitive(prim) => { @@ -417,8 +460,9 @@ 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) => { - resolved_path(f.buf, did, &v.path, true); + // XXX: shouldn't be restricted to just local imports + 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() { @@ -433,7 +477,8 @@ 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) => { + // XXX: shouldn't be limited to just local imports + Some(did) if ast_util::is_local(did) => { let path = clean::Path { global: false, segments: ~[clean::PathSegment { @@ -442,7 +487,7 @@ impl fmt::Default for clean::ViewListIdent { types: ~[], }] }; - resolved_path(f.buf, did, &path, false); + resolved_path(f.buf, did.node, &path, false); } _ => write!(f.buf, "{}", v.name), } diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index b39a2bcbfb5..6f3595ac2d9 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -28,10 +28,8 @@ use std::vec; use extra::arc::RWArc; use extra::json::ToJson; use extra::sort; -use extra::time; use syntax::ast; -use syntax::ast_util::is_local; use syntax::attr; use clean; @@ -52,6 +50,12 @@ pub struct Context { 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 { PathType(clean::Type), OtherType(clean::Generics, /* trait */ clean::Type, /* for */ clean::Type), @@ -68,6 +72,8 @@ struct Cache { traits: HashMap<ast::NodeId, HashMap<~str, ~str>>, // trait id => implementors of the trait 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 parent_stack: ~[ast::NodeId], @@ -142,6 +148,7 @@ pub fn run(mut crate: clean::Crate, dst: Path) { stack: ~[], parent_stack: ~[], search_index: ~[], + extern_locations: HashMap::new(), }; cache.stack.push(crate.name.clone()); 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("normalize.css"), include_str!("static/normalize.css")); + // Publish the search index { let dst = dst.push("search-index.js"); 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(); } - info2!("emitting source files"); - let started = time::precise_time_ns(); + // Render all source files (this may turn into a giant no-op) { + info2!("emitting source files"); let dst = cx.dst.push("src"); mkdir(&dst); let dst = dst.push(crate.name); @@ -194,14 +202,13 @@ pub fn run(mut crate: clean::Crate, dst: Path) { }; 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"); - let started = time::precise_time_ns(); + for (&n, e) in crate.externs.iter() { + cache.extern_locations.insert(n, extern_location(e, &cx.dst)); + } + + // And finally render the whole crate's documentation 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) { @@ -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> { fn fold_item(&mut self, item: clean::Item) -> Option<clean::Item> { if self.cx.include_sources && !self.seen.contains(&item.source.filename) { @@ -353,8 +392,7 @@ impl DocFolder for Cache { match item.inner { clean::ImplItem(ref i) => { match i.trait_ { - Some(clean::ResolvedPath{ did, _ }) if is_local(did) => { - let id = did.node; + Some(clean::ResolvedPath{ id, _ }) => { let v = do self.implementors.find_or_insert_with(id) |_|{ ~[] }; @@ -441,8 +479,8 @@ impl DocFolder for Cache { } clean::ImplItem(ref i) => { match i.for_ { - clean::ResolvedPath{ did, _ } if is_local(did) => { - self.parent_stack.push(did.node); true + clean::ResolvedPath{ id, _ } => { + self.parent_stack.push(id); true } _ => false } @@ -457,8 +495,7 @@ impl DocFolder for Cache { match item { clean::Item{ attrs, inner: clean::ImplItem(i), _ } => { match i.for_ { - clean::ResolvedPath { did, _ } if is_local(did) => { - let id = did.node; + clean::ResolvedPath { 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) => { write!(w, "{} for ", *ty); match *ty { - clean::ResolvedPath { did, _ } => Some(did), + clean::ResolvedPath { id, _ } => Some(id), _ => 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 let trait_id = match trait_id { None => continue, - Some(id) if is_local(id) => continue, - Some(id) => id.node, + Some(id) => id, }; do local_data::get(cache_key) |cache| { do cache.unwrap().read |cache| { diff --git a/src/librustdoc/passes.rs b/src/librustdoc/passes.rs index 8f1955fb423..e7b4ce9e056 100644 --- a/src/librustdoc/passes.rs +++ b/src/librustdoc/passes.rs @@ -13,7 +13,6 @@ use std::uint; use std::hashmap::HashSet; use syntax::ast; -use syntax::ast_util::is_local; use clean; use clean::Item; @@ -131,8 +130,8 @@ pub fn strip_private(mut crate: clean::Crate) -> plugins::PluginResult { match i.inner { clean::ImplItem(ref imp) => { match imp.trait_ { - Some(clean::ResolvedPath{ did, _ }) => { - if is_local(did) && !self.contains(&did.node) { + Some(clean::ResolvedPath{ id, _ }) => { + if !self.contains(&id) { return None; } } diff --git a/src/libstd/std.rs b/src/libstd/std.rs index ece623fab24..5501cdfdcd5 100644 --- a/src/libstd/std.rs +++ b/src/libstd/std.rs @@ -58,7 +58,8 @@ they contained the following prologue: #[crate_type = "lib"]; #[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. #[no_std];