rustdoc: Create anchor pages for primitive types
This commit adds support in rustdoc to recognize the `#[doc(primitive = "foo")]` attribute. This attribute indicates that the current module is the "owner" of the primitive type `foo`. For rustdoc, this means that the doc-comment for the module is the doc-comment for the primitive type, plus a signal to all downstream crates that hyperlinks for primitive types will be directed at the crate containing the `#[doc]` directive. Additionally, rustdoc will favor crates closest to the one being documented which "implements the primitive type". For example, documentation of libcore links to libcore for primitive types, but documentation for libstd and beyond all links to libstd for primitive types. This change involves no compiler modifications, it is purely a rustdoc change. The landing pages for the primitive types primarily serve to show a list of implemented traits for the primitive type itself. The primitive types documented includes both strings and slices in a semi-ad-hoc way, but in a way that should provide at least somewhat meaningful documentation. Closes #14474
This commit is contained in:
parent
ba9be0a72b
commit
c2e3aa37da
37 changed files with 444 additions and 88 deletions
|
@ -157,6 +157,9 @@ pub struct Cache {
|
|||
/// Cache of where external crate documentation can be found.
|
||||
pub extern_locations: HashMap<ast::CrateNum, ExternalLocation>,
|
||||
|
||||
/// Cache of where documentation for primitives can be found.
|
||||
pub primitive_locations: HashMap<clean::Primitive, ast::CrateNum>,
|
||||
|
||||
/// Set of definitions which have been inlined from external crates.
|
||||
pub inlined: HashSet<ast::DefId>,
|
||||
|
||||
|
@ -281,6 +284,7 @@ pub fn run(mut krate: clean::Crate, dst: Path) -> io::IoResult<()> {
|
|||
parent_stack: Vec::new(),
|
||||
search_index: Vec::new(),
|
||||
extern_locations: HashMap::new(),
|
||||
primitive_locations: HashMap::new(),
|
||||
privmod: false,
|
||||
public_items: public_items,
|
||||
orphan_methods: Vec::new(),
|
||||
|
@ -297,13 +301,27 @@ pub fn run(mut krate: clean::Crate, dst: Path) -> io::IoResult<()> {
|
|||
cache.stack.push(krate.name.clone());
|
||||
krate = cache.fold_crate(krate);
|
||||
|
||||
|
||||
// Cache where all our extern crates are located
|
||||
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));
|
||||
}
|
||||
|
||||
// Cache where all known primitives have their documentation located.
|
||||
//
|
||||
// Favor linking to as local extern as possible, so iterate all crates in
|
||||
// reverse topological order.
|
||||
for &(n, ref e) in krate.externs.iter().rev() {
|
||||
for &prim in e.primitives.iter() {
|
||||
cache.primitive_locations.insert(prim, n);
|
||||
}
|
||||
}
|
||||
for &prim in krate.primitives.iter() {
|
||||
cache.primitive_locations.insert(prim, ast::LOCAL_CRATE);
|
||||
}
|
||||
|
||||
// Build our search index
|
||||
let index = try!(build_index(&krate, &mut cache));
|
||||
|
||||
// Freeze the cache now that the index has been built. Put an Arc into TLS
|
||||
|
@ -854,30 +872,63 @@ impl DocFolder for Cache {
|
|||
Some(item) => {
|
||||
match item {
|
||||
clean::Item{ attrs, inner: clean::ImplItem(i), .. } => {
|
||||
match i.for_ {
|
||||
clean::ResolvedPath { did, .. } => {
|
||||
use clean::{Primitive, Vector, ResolvedPath, BorrowedRef};
|
||||
use clean::{FixedVector, Slice, Tuple, PrimitiveTuple};
|
||||
|
||||
// extract relevant documentation for this impl
|
||||
let dox = match attrs.move_iter().find(|a| {
|
||||
match *a {
|
||||
clean::NameValue(ref x, _)
|
||||
if "doc" == x.as_slice() => {
|
||||
true
|
||||
}
|
||||
_ => false
|
||||
}
|
||||
}) {
|
||||
Some(clean::NameValue(_, dox)) => Some(dox),
|
||||
Some(..) | None => None,
|
||||
};
|
||||
|
||||
// Figure out the id of this impl. This may map to a
|
||||
// primitive rather than always to a struct/enum.
|
||||
let did = match i.for_ {
|
||||
ResolvedPath { did, .. } => Some(did),
|
||||
|
||||
// References to primitives are picked up as well to
|
||||
// recognize implementations for &str, this may not
|
||||
// be necessary in a DST world.
|
||||
Primitive(p) |
|
||||
BorrowedRef { type_: box Primitive(p), ..} =>
|
||||
{
|
||||
Some(ast_util::local_def(p.to_node_id()))
|
||||
}
|
||||
|
||||
// In a DST world, we may only need
|
||||
// Vector/FixedVector, but for now we also pick up
|
||||
// borrowed references
|
||||
Vector(..) | FixedVector(..) |
|
||||
BorrowedRef{ type_: box Vector(..), .. } |
|
||||
BorrowedRef{ type_: box FixedVector(..), .. } =>
|
||||
{
|
||||
Some(ast_util::local_def(Slice.to_node_id()))
|
||||
}
|
||||
|
||||
Tuple(..) => {
|
||||
let id = PrimitiveTuple.to_node_id();
|
||||
Some(ast_util::local_def(id))
|
||||
}
|
||||
|
||||
_ => None,
|
||||
};
|
||||
|
||||
match did {
|
||||
Some(did) => {
|
||||
let v = self.impls.find_or_insert_with(did, |_| {
|
||||
Vec::new()
|
||||
});
|
||||
// extract relevant documentation for this impl
|
||||
match attrs.move_iter().find(|a| {
|
||||
match *a {
|
||||
clean::NameValue(ref x, _)
|
||||
if "doc" == x.as_slice() => {
|
||||
true
|
||||
}
|
||||
_ => false
|
||||
}
|
||||
}) {
|
||||
Some(clean::NameValue(_, dox)) => {
|
||||
v.push((i, Some(dox)));
|
||||
}
|
||||
Some(..) | None => {
|
||||
v.push((i, None));
|
||||
}
|
||||
}
|
||||
v.push((i, dox));
|
||||
}
|
||||
_ => {}
|
||||
None => {}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
@ -1119,17 +1170,21 @@ impl<'a> fmt::Show for Item<'a> {
|
|||
clean::TraitItem(..) => try!(write!(fmt, "Trait ")),
|
||||
clean::StructItem(..) => try!(write!(fmt, "Struct ")),
|
||||
clean::EnumItem(..) => try!(write!(fmt, "Enum ")),
|
||||
clean::PrimitiveItem(..) => try!(write!(fmt, "Primitive Type ")),
|
||||
_ => {}
|
||||
}
|
||||
let cur = self.cx.current.as_slice();
|
||||
let amt = if self.ismodule() { cur.len() - 1 } else { cur.len() };
|
||||
for (i, component) in cur.iter().enumerate().take(amt) {
|
||||
let mut trail = String::new();
|
||||
for _ in range(0, cur.len() - i - 1) {
|
||||
trail.push_str("../");
|
||||
let is_primitive = match self.item.inner {
|
||||
clean::PrimitiveItem(..) => true,
|
||||
_ => false,
|
||||
};
|
||||
if !is_primitive {
|
||||
let cur = self.cx.current.as_slice();
|
||||
let amt = if self.ismodule() { cur.len() - 1 } else { cur.len() };
|
||||
for (i, component) in cur.iter().enumerate().take(amt) {
|
||||
try!(write!(fmt, "<a href='{}index.html'>{}</a>::",
|
||||
"../".repeat(cur.len() - i - 1),
|
||||
component.as_slice()));
|
||||
}
|
||||
try!(write!(fmt, "<a href='{}index.html'>{}</a>::",
|
||||
trail, component.as_slice()));
|
||||
}
|
||||
try!(write!(fmt, "<a class='{}' href=''>{}</a>",
|
||||
shortty(self.item), self.item.name.get_ref().as_slice()));
|
||||
|
@ -1154,7 +1209,7 @@ impl<'a> fmt::Show for Item<'a> {
|
|||
// [src] link in the downstream documentation will actually come back to
|
||||
// this page, and this link will be auto-clicked. The `id` attribute is
|
||||
// used to find the link to auto-click.
|
||||
if self.cx.include_sources {
|
||||
if self.cx.include_sources && !is_primitive {
|
||||
match self.href() {
|
||||
Some(l) => {
|
||||
try!(write!(fmt,
|
||||
|
@ -1178,6 +1233,7 @@ impl<'a> fmt::Show for Item<'a> {
|
|||
clean::EnumItem(ref e) => item_enum(fmt, self.item, e),
|
||||
clean::TypedefItem(ref t) => item_typedef(fmt, self.item, t),
|
||||
clean::MacroItem(ref m) => item_macro(fmt, self.item, m),
|
||||
clean::PrimitiveItem(ref p) => item_primitive(fmt, self.item, p),
|
||||
_ => Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -1250,6 +1306,8 @@ fn item_module(w: &mut fmt::Formatter, cx: &Context,
|
|||
}
|
||||
(&clean::ViewItemItem(..), _) => Less,
|
||||
(_, &clean::ViewItemItem(..)) => Greater,
|
||||
(&clean::PrimitiveItem(..), _) => Less,
|
||||
(_, &clean::PrimitiveItem(..)) => Greater,
|
||||
(&clean::ModuleItem(..), _) => Less,
|
||||
(_, &clean::ModuleItem(..)) => Greater,
|
||||
(&clean::MacroItem(..), _) => Less,
|
||||
|
@ -1305,6 +1363,7 @@ fn item_module(w: &mut fmt::Formatter, cx: &Context,
|
|||
clean::ForeignFunctionItem(..) => ("ffi-fns", "Foreign Functions"),
|
||||
clean::ForeignStaticItem(..) => ("ffi-statics", "Foreign Statics"),
|
||||
clean::MacroItem(..) => ("macros", "Macros"),
|
||||
clean::PrimitiveItem(..) => ("primitives", "Primitive Types"),
|
||||
};
|
||||
try!(write!(w,
|
||||
"<h2 id='{id}' class='section-header'>\
|
||||
|
@ -1877,8 +1936,11 @@ impl<'a> fmt::Show for Sidebar<'a> {
|
|||
try!(write!(w, "<div class='block {}'><h2>{}</h2>", short, longty));
|
||||
for item in items.iter() {
|
||||
let curty = shortty(cur).to_static_str();
|
||||
let class = if cur.name.get_ref() == item &&
|
||||
short == curty { "current" } else { "" };
|
||||
let class = if cur.name.get_ref() == item && short == curty {
|
||||
"current"
|
||||
} else {
|
||||
""
|
||||
};
|
||||
try!(write!(w, "<a class='{ty} {class}' href='{curty, select,
|
||||
mod{../}
|
||||
other{}
|
||||
|
@ -1949,3 +2011,10 @@ fn item_macro(w: &mut fmt::Formatter, it: &clean::Item,
|
|||
try!(w.write(highlight::highlight(t.source.as_slice(), Some("macro")).as_bytes()));
|
||||
document(w, it)
|
||||
}
|
||||
|
||||
fn item_primitive(w: &mut fmt::Formatter,
|
||||
it: &clean::Item,
|
||||
_p: &clean::Primitive) -> fmt::Result {
|
||||
try!(document(w, it));
|
||||
render_methods(w, it)
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue