Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
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
  • Loading branch information
alexcrichton committed Oct 2, 2013
commit d06043ba0b4f0ed871e5374aa0d6b37fa170dfc6
3 changes: 2 additions & 1 deletion src/libextra/extra.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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"];
Expand Down
111 changes: 79 additions & 32 deletions src/librustdoc/clean.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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()
}
}
}
Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -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::*;
Expand Down Expand Up @@ -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());
Expand All @@ -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 {
Expand Down
Loading