rustdoc: Get [src] links working for inlined dox

These links work by hyperlinking back to the actual documentation page with a
query parameter which will be recognized and then auto-click the appropriate
[src] link.
This commit is contained in:
Alex Crichton 2014-05-23 20:17:27 -07:00
parent 9f13db2cb2
commit 8dad7f579e
3 changed files with 96 additions and 32 deletions

View File

@ -1575,28 +1575,40 @@ fn try_inline(id: ast::NodeId) -> Option<Vec<Item>> {
};
let did = ast_util::def_id_of_def(def);
if ast_util::is_local(did) { return None }
try_inline_def(tcx, def)
try_inline_def(&**cx, tcx, def)
}
fn try_inline_def(tcx: &ty::ctxt, def: ast::Def) -> Option<Vec<Item>> {
fn try_inline_def(cx: &core::DocContext,
tcx: &ty::ctxt,
def: ast::Def) -> Option<Vec<Item>> {
let mut ret = Vec::new();
let did = ast_util::def_id_of_def(def);
let inner = match def {
ast::DefTrait(did) => TraitItem(build_external_trait(tcx, did)),
ast::DefFn(did, style) =>
FunctionItem(build_external_function(tcx, did, style)),
ast::DefTrait(did) => {
record_extern_fqn(cx, did, TypeTrait);
TraitItem(build_external_trait(tcx, did))
}
ast::DefFn(did, style) => {
record_extern_fqn(cx, did, TypeFunction);
FunctionItem(build_external_function(tcx, did, style))
}
ast::DefStruct(did) => {
record_extern_fqn(cx, did, TypeStruct);
ret.extend(build_impls(tcx, did).move_iter());
StructItem(build_struct(tcx, did))
}
ast::DefTy(did) => {
record_extern_fqn(cx, did, TypeEnum);
ret.extend(build_impls(tcx, did).move_iter());
build_type(tcx, did)
}
// Assume that the enum type is reexported next to the variant, and
// variants don't show up in documentation specially.
ast::DefVariant(..) => return Some(Vec::new()),
ast::DefMod(did) => ModuleItem(build_module(tcx, did)),
ast::DefMod(did) => {
record_extern_fqn(cx, did, TypeModule);
ModuleItem(build_module(cx, tcx, did))
}
_ => return None,
};
let fqn = csearch::get_item_path(tcx, did);
@ -1838,10 +1850,7 @@ fn register_def(cx: &core::DocContext, def: ast::Def) -> ast::DefId {
core::Typed(ref t) => t,
core::NotTyped(_) => return did
};
let fqn = csearch::get_item_path(tcx, did);
let fqn = fqn.move_iter().map(|i| i.to_str().to_strbuf()).collect();
debug!("recording {} => {}", did, fqn);
cx.external_paths.borrow_mut().get_mut_ref().insert(did, (fqn, kind));
record_extern_fqn(cx, did, kind);
match kind {
TypeTrait => {
let t = build_external_trait(tcx, did);
@ -1852,6 +1861,19 @@ fn register_def(cx: &core::DocContext, def: ast::Def) -> ast::DefId {
return did;
}
fn record_extern_fqn(cx: &core::DocContext,
did: ast::DefId,
kind: TypeKind) {
match cx.maybe_typed {
core::Typed(ref tcx) => {
let fqn = csearch::get_item_path(tcx, did);
let fqn = fqn.move_iter().map(|i| i.to_str().to_strbuf()).collect();
cx.external_paths.borrow_mut().get_mut_ref().insert(did, (fqn, kind));
}
core::NotTyped(..) => {}
}
}
fn build_external_trait(tcx: &ty::ctxt, did: ast::DefId) -> Trait {
let def = ty::lookup_trait_def(tcx, did);
let methods = ty::trait_methods(tcx, did);
@ -2000,13 +2022,14 @@ fn build_impl(tcx: &ty::ctxt, did: ast::DefId) -> Item {
}
}
fn build_module(tcx: &ty::ctxt, did: ast::DefId) -> Module {
fn build_module(cx: &core::DocContext, tcx: &ty::ctxt,
did: ast::DefId) -> Module {
let mut items = Vec::new();
csearch::each_child_of_item(&tcx.sess.cstore, did, |def, _, _| {
match def {
decoder::DlDef(def) => {
match try_inline_def(tcx, def) {
match try_inline_def(cx, tcx, def) {
Some(i) => items.extend(i.move_iter()),
None => {}
}

View File

@ -141,6 +141,10 @@ pub struct Cache {
/// necessary.
pub paths: HashMap<ast::DefId, (Vec<String>, ItemType)>,
/// Similar to `paths`, but only holds external paths. This is only used for
/// generating explicit hyperlinks to other crates.
pub external_paths: HashMap<ast::DefId, Vec<StrBuf>>,
/// This map contains information about all known traits of this crate.
/// Implementations of a crate should inherit the documentation of the
/// parent trait if no extra documentation is specified, and default methods
@ -249,7 +253,8 @@ pub fn run(mut krate: clean::Crate, dst: Path) -> io::IoResult<()> {
let analysis = ::analysiskey.get();
let public_items = analysis.as_ref().map(|a| a.public_items.clone());
let public_items = public_items.unwrap_or(NodeSet::new());
let paths = analysis.as_ref().map(|a| {
let paths: HashMap<ast::DefId, (Vec<StrBuf>, ItemType)> =
analysis.as_ref().map(|a| {
let paths = a.external_paths.borrow_mut().take_unwrap();
paths.move_iter().map(|(k, (v, t))| {
(k, (v, match t {
@ -265,6 +270,8 @@ pub fn run(mut krate: clean::Crate, dst: Path) -> io::IoResult<()> {
}).unwrap_or(HashMap::new());
let mut cache = Cache {
impls: HashMap::new(),
external_paths: paths.iter().map(|(&k, &(ref v, _))| (k, v.clone()))
.collect(),
paths: paths,
implementors: HashMap::new(),
stack: Vec::new(),
@ -496,13 +503,15 @@ pub fn run(mut krate: clean::Crate, dst: Path) -> io::IoResult<()> {
seen: HashSet::new(),
cx: &mut cx,
};
// skip all invalid spans
folder.seen.insert("".to_strbuf());
krate = folder.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::new(), item_type::Module));
cache.paths.insert(did, (vec![e.name.to_strbuf()], item_type::Module));
}
// And finally render the whole crate's documentation
@ -1032,23 +1041,38 @@ impl<'a> Item<'a> {
}
}
fn link(&self) -> String {
let mut path = Vec::new();
clean_srcpath(self.item.source.filename.as_bytes(), |component| {
path.push(component.to_owned());
});
let href = if self.item.source.loline == self.item.source.hiline {
format_strbuf!("{}", self.item.source.loline)
fn link(&self) -> Option<String> {
if ast_util::is_local(self.item.def_id) {
let mut path = Vec::new();
clean_srcpath(self.item.source.filename.as_bytes(), |component| {
path.push(component.to_owned());
});
let href = if self.item.source.loline == self.item.source.hiline {
format!("{}", self.item.source.loline)
} else {
format!("{}-{}",
self.item.source.loline,
self.item.source.hiline)
};
Some(format!("{root}src/{krate}/{path}.html\\#{href}",
root = self.cx.root_path,
krate = self.cx.layout.krate,
path = path.connect("/"),
href = href))
} else {
format_strbuf!("{}-{}",
self.item.source.loline,
self.item.source.hiline)
};
format_strbuf!("{root}src/{krate}/{path}.html\\#{href}",
root = self.cx.root_path,
krate = self.cx.layout.krate,
path = path.connect("/"),
href = href)
let cache = cache_key.get().unwrap();
let path = cache.external_paths.get(&self.item.def_id);
let root = match *cache.extern_locations.get(&self.item.def_id.krate) {
Remote(ref s) => s.to_strbuf(),
Local => format!("{}/..", self.cx.root_path),
Unknown => return None,
};
Some(format!("{root}/{path}/{file}?gotosrc={goto}",
root = root,
path = path.slice_to(path.len() - 1).connect("/"),
file = item_path(self.item),
goto = self.item.def_id.node))
}
}
}
@ -1097,8 +1121,15 @@ impl<'a> fmt::Show for Item<'a> {
// Write `src` tag
if self.cx.include_sources {
try!(write!(fmt, "<a class='source' href='{}'>[src]</a>",
self.link()));
match self.link() {
Some(l) => {
try!(write!(fmt,
"<a class='source' id='src-{}' \
href='{}'>[src]</a>",
self.item.def_id.node, l));
}
None => {}
}
}
try!(write!(fmt, "</h1>\n"));

View File

@ -675,4 +675,14 @@
if (window.pending_implementors) {
window.register_implementors(window.pending_implementors);
}
var query = window.location.search.substring(1);
var vars = query.split('&');
for (var i = 0; i < vars.length; i++) {
var pair = vars[i].split('=');
if (pair[0] == 'gotosrc') {
window.location = $('#src-' + pair[1]).attr('href');
}
}
}());