auto merge of #9691 : alexcrichton/rust/rustdoc, r=cmr

This slurps in the commits from #9684 as well as closing #9539.
This commit is contained in:
bors 2013-10-03 00:56:34 -07:00
commit 8f40641e01
8 changed files with 288 additions and 128 deletions

View file

@ -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"];

View file

@ -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 {

View file

@ -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, "&lt;");
for (i, param) in params.iter().enumerate() {
if i > 0 { write!(w, ", "); }
write!(w, "{}", *param);
}
write!(w, "&gt;");
}
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("&lt;".as_bytes());
for (i, param) in params.iter().enumerate() {
if i > 0 { f.buf.write(", ".as_bytes()) }
write!(f.buf, "{}", *param);
}
f.buf.write("&gt;".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),
}

View file

@ -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) |_| {
~[]
};
@ -714,13 +751,18 @@ impl<'self> fmt::Default for Item<'self> {
do clean_srcpath(it.item.source.filename) |component| {
path.push(component.to_owned());
}
let href = if it.item.source.loline == it.item.source.hiline {
format!("{}", it.item.source.loline)
} else {
format!("{}-{}", it.item.source.loline, it.item.source.hiline)
};
write!(fmt.buf,
"<a class='source'
href='{root}src/{crate}/{path}.html\\#{line}'>[src]</a>",
href='{root}src/{crate}/{path}.html\\#{href}'>[src]</a>",
root = it.cx.root_path,
crate = it.cx.layout.crate,
path = path.connect("/"),
line = it.item.source.loline);
href = href);
}
// Write the breadcrumb trail header for the top
@ -1253,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,
}
}
@ -1284,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| {

View file

@ -118,9 +118,16 @@ body {
.content h1 { margin-top: 0; }
.content h1, .content h2 { margin-left: -20px; }
.content pre { padding: 20px; }
.content.source pre.rust {
white-space: pre;
overflow: auto;
padding-left: 0;
}
.content pre.line-numbers { float: left; border: none; }
.line-numbers span { color: #c67e2d; }
.line-numbers .line-highlighted {
background-color: #fff871;
}
.content .highlighted {
cursor: pointer;

View file

@ -31,6 +31,25 @@
resizeShortBlocks();
$(window).on('resize', resizeShortBlocks);
function highlightSourceLines() {
var i, from, to, match = window.location.hash.match(/^#?(\d+)(?:-(\d+))?$/);
if (match) {
from = parseInt(match[1], 10);
to = Math.min(50000, parseInt(match[2] || match[1], 10));
from = Math.min(from, to);
if ($('#' + from).length === 0) {
return;
}
$('#' + from)[0].scrollIntoView();
$('.line-numbers span').removeClass('line-highlighted');
for (i = from; i <= to; i += 1) {
$('#' + i).addClass('line-highlighted');
}
}
}
highlightSourceLines();
$(window).on('hashchange', highlightSourceLines);
$(document).on('keyup', function (e) {
if (document.activeElement.tagName === 'INPUT') {
return;

View file

@ -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;
}
}

View file

@ -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];