auto merge of #9577 : alexcrichton/rust/rustdoc, r=cmr
They're getting smaller each time though! The highlight of this round is source files in documentation. Still trying to figure out the best syntax-highlighting solution.
This commit is contained in:
@@ -12,12 +12,14 @@ use std::cell::Cell;
|
||||
use std::comm::{SharedPort, SharedChan};
|
||||
use std::comm;
|
||||
use std::fmt;
|
||||
use std::hashmap::HashMap;
|
||||
use std::hashmap::{HashMap, HashSet};
|
||||
use std::local_data;
|
||||
use std::rt::io::buffered::BufferedWriter;
|
||||
use std::rt::io::file::{FileInfo, DirectoryInfo};
|
||||
use std::rt::io::file;
|
||||
use std::rt::io;
|
||||
use std::rt::io::Reader;
|
||||
use std::str;
|
||||
use std::task;
|
||||
use std::unstable::finally::Finally;
|
||||
use std::util;
|
||||
@@ -28,11 +30,13 @@ use extra::json::ToJson;
|
||||
use extra::sort;
|
||||
|
||||
use syntax::ast;
|
||||
use syntax::ast_util::is_local;
|
||||
use syntax::attr;
|
||||
|
||||
use clean;
|
||||
use doctree;
|
||||
use fold::DocFolder;
|
||||
use html::escape::Escape;
|
||||
use html::format::{VisSpace, Method, PuritySpace};
|
||||
use html::layout;
|
||||
use html::markdown::Markdown;
|
||||
@@ -44,6 +48,7 @@ pub struct Context {
|
||||
dst: Path,
|
||||
layout: layout::Layout,
|
||||
sidebar: HashMap<~str, ~[~str]>,
|
||||
include_sources: bool,
|
||||
}
|
||||
|
||||
enum Implementor {
|
||||
@@ -68,6 +73,12 @@ struct Cache {
|
||||
priv search_index: ~[IndexItem],
|
||||
}
|
||||
|
||||
struct SourceCollector<'self> {
|
||||
seen: HashSet<~str>,
|
||||
dst: Path,
|
||||
cx: &'self Context,
|
||||
}
|
||||
|
||||
struct Item<'self> { cx: &'self Context, item: &'self clean::Item, }
|
||||
struct Sidebar<'self> { cx: &'self Context, item: &'self clean::Item, }
|
||||
|
||||
@@ -79,6 +90,8 @@ struct IndexItem {
|
||||
parent: Option<ast::NodeId>,
|
||||
}
|
||||
|
||||
struct Source<'self>(&'self str);
|
||||
|
||||
local_data_key!(pub cache_key: RWArc<Cache>)
|
||||
local_data_key!(pub current_location_key: ~[~str])
|
||||
|
||||
@@ -94,6 +107,7 @@ pub fn run(mut crate: clean::Crate, dst: Path) {
|
||||
favicon: ~"",
|
||||
crate: crate.name.clone(),
|
||||
},
|
||||
include_sources: true,
|
||||
};
|
||||
mkdir(&cx.dst);
|
||||
|
||||
@@ -107,6 +121,9 @@ pub fn run(mut crate: clean::Crate, dst: Path) {
|
||||
clean::NameValue(~"html_logo_url", ref s) => {
|
||||
cx.layout.logo = s.to_owned();
|
||||
}
|
||||
clean::Word(~"html_no_source") => {
|
||||
cx.include_sources = false;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
@@ -162,6 +179,19 @@ pub fn run(mut crate: clean::Crate, dst: Path) {
|
||||
w.flush();
|
||||
}
|
||||
|
||||
if cx.include_sources {
|
||||
let dst = cx.dst.push("src");
|
||||
mkdir(&dst);
|
||||
let dst = dst.push(crate.name);
|
||||
mkdir(&dst);
|
||||
let mut folder = SourceCollector {
|
||||
dst: dst,
|
||||
seen: HashSet::new(),
|
||||
cx: &cx,
|
||||
};
|
||||
crate = folder.fold_crate(crate);
|
||||
}
|
||||
|
||||
// Now render the whole crate.
|
||||
cx.crate(crate, cache);
|
||||
}
|
||||
@@ -183,7 +213,80 @@ fn mkdir(path: &Path) {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'self> DocFolder for Cache {
|
||||
fn clean_srcpath(src: &str, f: &fn(&str)) {
|
||||
let p = Path(src);
|
||||
for c in p.components.iter() {
|
||||
if "." == *c {
|
||||
loop
|
||||
}
|
||||
if ".." == *c {
|
||||
f("up");
|
||||
} else {
|
||||
f(c.as_slice())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'self> DocFolder for SourceCollector<'self> {
|
||||
fn fold_item(&mut self, item: clean::Item) -> Option<clean::Item> {
|
||||
if !self.seen.contains(&item.source.filename) {
|
||||
self.emit_source(item.source.filename);
|
||||
self.seen.insert(item.source.filename.clone());
|
||||
}
|
||||
self.fold_item_recur(item)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'self> SourceCollector<'self> {
|
||||
fn emit_source(&self, filename: &str) {
|
||||
let p = Path(filename);
|
||||
|
||||
// Read the contents of the file
|
||||
let mut contents = ~[];
|
||||
{
|
||||
let mut buf = [0, ..1024];
|
||||
let r = do io::io_error::cond.trap(|_| {}).inside {
|
||||
p.open_reader(io::Open)
|
||||
};
|
||||
// If we couldn't open this file, then just returns because it
|
||||
// probably means that it's some standard library macro thing and we
|
||||
// can't have the source to it anyway.
|
||||
let mut r = match r { Some(r) => r, None => return };
|
||||
|
||||
// read everything
|
||||
loop {
|
||||
match r.read(buf) {
|
||||
Some(n) => contents.push_all(buf.slice_to(n)),
|
||||
None => break
|
||||
}
|
||||
}
|
||||
}
|
||||
let contents = str::from_utf8_owned(contents);
|
||||
|
||||
// Create the intermediate directories
|
||||
let mut cur = self.dst.clone();
|
||||
let mut root_path = ~"../../";
|
||||
do clean_srcpath(p.pop().to_str()) |component| {
|
||||
cur = cur.push(component);
|
||||
mkdir(&cur);
|
||||
root_path.push_str("../");
|
||||
}
|
||||
|
||||
let dst = cur.push(*p.components.last() + ".html");
|
||||
let mut w = dst.open_writer(io::CreateOrTruncate);
|
||||
|
||||
let title = format!("{} -- source", *dst.components.last());
|
||||
let page = layout::Page {
|
||||
title: title,
|
||||
ty: "source",
|
||||
root_path: root_path,
|
||||
};
|
||||
layout::render(&mut w as &mut io::Writer, &self.cx.layout,
|
||||
&page, &(""), &Source(contents.as_slice()));
|
||||
}
|
||||
}
|
||||
|
||||
impl DocFolder for Cache {
|
||||
fn fold_item(&mut self, item: clean::Item) -> Option<clean::Item> {
|
||||
// Register any generics to their corresponding string. This is used
|
||||
// when pretty-printing types
|
||||
@@ -223,7 +326,8 @@ impl<'self> DocFolder for Cache {
|
||||
match item.inner {
|
||||
clean::ImplItem(ref i) => {
|
||||
match i.trait_ {
|
||||
Some(clean::ResolvedPath{ id, _ }) => {
|
||||
Some(clean::ResolvedPath{ did, _ }) if is_local(did) => {
|
||||
let id = did.node;
|
||||
let v = do self.implementors.find_or_insert_with(id) |_|{
|
||||
~[]
|
||||
};
|
||||
@@ -248,7 +352,9 @@ impl<'self> DocFolder for Cache {
|
||||
match item.name {
|
||||
Some(ref s) => {
|
||||
let parent = match item.inner {
|
||||
clean::TyMethodItem(*) | clean::VariantItem(*) => {
|
||||
clean::TyMethodItem(*) |
|
||||
clean::StructFieldItem(*) |
|
||||
clean::VariantItem(*) => {
|
||||
Some((Some(*self.parent_stack.last()),
|
||||
self.stack.slice_to(self.stack.len() - 1)))
|
||||
|
||||
@@ -257,8 +363,12 @@ impl<'self> DocFolder for Cache {
|
||||
if self.parent_stack.len() == 0 {
|
||||
None
|
||||
} else {
|
||||
Some((Some(*self.parent_stack.last()),
|
||||
self.stack.as_slice()))
|
||||
let last = self.parent_stack.last();
|
||||
let amt = match self.paths.find(last) {
|
||||
Some(&(_, "trait")) => self.stack.len() - 1,
|
||||
Some(*) | None => self.stack.len(),
|
||||
};
|
||||
Some((Some(*last), self.stack.slice_to(amt)))
|
||||
}
|
||||
}
|
||||
_ => Some((None, self.stack.as_slice()))
|
||||
@@ -299,13 +409,13 @@ impl<'self> DocFolder for Cache {
|
||||
|
||||
// Maintain the parent stack
|
||||
let parent_pushed = match item.inner {
|
||||
clean::TraitItem(*) | clean::EnumItem(*) => {
|
||||
clean::TraitItem(*) | clean::EnumItem(*) | clean::StructItem(*) => {
|
||||
self.parent_stack.push(item.id); true
|
||||
}
|
||||
clean::ImplItem(ref i) => {
|
||||
match i.for_ {
|
||||
clean::ResolvedPath{ id, _ } => {
|
||||
self.parent_stack.push(id); true
|
||||
clean::ResolvedPath{ did, _ } if is_local(did) => {
|
||||
self.parent_stack.push(did.node); true
|
||||
}
|
||||
_ => false
|
||||
}
|
||||
@@ -320,7 +430,8 @@ impl<'self> DocFolder for Cache {
|
||||
match item.inner {
|
||||
clean::ImplItem(i) => {
|
||||
match i.for_ {
|
||||
clean::ResolvedPath { id, _ } => {
|
||||
clean::ResolvedPath { did, _ } if is_local(did) => {
|
||||
let id = did.node;
|
||||
let v = do self.impls.find_or_insert_with(id) |_| {
|
||||
~[]
|
||||
};
|
||||
@@ -374,7 +485,6 @@ impl Context {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// Processes
|
||||
fn crate(self, mut crate: clean::Crate, cache: Cache) {
|
||||
enum Work {
|
||||
Die,
|
||||
@@ -510,28 +620,6 @@ impl Context {
|
||||
let dst = self.dst.push(item_path(&item));
|
||||
let writer = dst.open_writer(io::CreateOrTruncate);
|
||||
render(writer.unwrap(), self, &item, true);
|
||||
|
||||
// recurse if necessary
|
||||
let name = item.name.get_ref().clone();
|
||||
match item.inner {
|
||||
clean::EnumItem(e) => {
|
||||
let mut it = e.variants.move_iter();
|
||||
do self.recurse(name) |this| {
|
||||
for item in it {
|
||||
f(this, item);
|
||||
}
|
||||
}
|
||||
}
|
||||
clean::StructItem(s) => {
|
||||
let mut it = s.fields.move_iter();
|
||||
do self.recurse(name) |this| {
|
||||
for item in it {
|
||||
f(this, item);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
_ => {}
|
||||
@@ -581,6 +669,20 @@ impl<'self> fmt::Default for Item<'self> {
|
||||
None => {}
|
||||
}
|
||||
|
||||
if it.cx.include_sources {
|
||||
let mut path = ~[];
|
||||
do clean_srcpath(it.item.source.filename) |component| {
|
||||
path.push(component.to_owned());
|
||||
}
|
||||
write!(fmt.buf,
|
||||
"<a class='source'
|
||||
href='{root}src/{crate}/{path}.html\\#{line}'>[src]</a>",
|
||||
root = it.cx.root_path,
|
||||
crate = it.cx.layout.crate,
|
||||
path = path.connect("/"),
|
||||
line = it.item.source.loline);
|
||||
}
|
||||
|
||||
// Write the breadcrumb trail header for the top
|
||||
write!(fmt.buf, "<h1 class='fqn'>");
|
||||
match it.item.inner {
|
||||
@@ -613,9 +715,6 @@ impl<'self> fmt::Default for Item<'self> {
|
||||
clean::StructItem(ref s) => item_struct(fmt.buf, it.item, s),
|
||||
clean::EnumItem(ref e) => item_enum(fmt.buf, it.item, e),
|
||||
clean::TypedefItem(ref t) => item_typedef(fmt.buf, it.item, t),
|
||||
clean::VariantItem(*) => item_variant(fmt.buf, it.cx, it.item),
|
||||
clean::StructFieldItem(*) => item_struct_field(fmt.buf, it.cx,
|
||||
it.item),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
@@ -862,7 +961,8 @@ fn item_trait(w: &mut io::Writer, it: &clean::Item, t: &clean::Trait) {
|
||||
document(w, it);
|
||||
|
||||
fn meth(w: &mut io::Writer, m: &clean::TraitMethod) {
|
||||
write!(w, "<h3 id='fn.{}' class='method'><code>",
|
||||
write!(w, "<h3 id='{}.{}' class='method'><code>",
|
||||
shortty(m.item()),
|
||||
*m.item().name.get_ref());
|
||||
render_method(w, m.item(), false);
|
||||
write!(w, "</code></h3>");
|
||||
@@ -923,13 +1023,15 @@ fn render_method(w: &mut io::Writer, meth: &clean::Item, withlink: bool) {
|
||||
g: &clean::Generics, selfty: &clean::SelfTy, d: &clean::FnDecl,
|
||||
withlink: bool) {
|
||||
write!(w, "{}fn {withlink, select,
|
||||
true{<a href='\\#fn.{name}' class='fnname'>{name}</a>}
|
||||
true{<a href='\\#{ty}.{name}'
|
||||
class='fnname'>{name}</a>}
|
||||
other{<span class='fnname'>{name}</span>}
|
||||
}{generics}{decl}",
|
||||
match purity {
|
||||
ast::unsafe_fn => "unsafe ",
|
||||
_ => "",
|
||||
},
|
||||
ty = shortty(it),
|
||||
name = it.name.get_ref().as_slice(),
|
||||
generics = *g,
|
||||
decl = Method(selfty, d),
|
||||
@@ -1014,7 +1116,7 @@ fn render_struct(w: &mut io::Writer, it: &clean::Item,
|
||||
for field in fields.iter() {
|
||||
match field.inner {
|
||||
clean::StructFieldItem(ref ty) => {
|
||||
write!(w, " {}<a name='field.{name}'>{name}</a>: \
|
||||
write!(w, " {}<a name='structfield.{name}'>{name}</a>: \
|
||||
{},\n{}",
|
||||
VisSpace(field.visibility),
|
||||
ty.type_,
|
||||
@@ -1080,7 +1182,7 @@ fn render_impl(w: &mut io::Writer, i: &clean::Impl) {
|
||||
Some(ref ty) => {
|
||||
write!(w, "{} for ", *ty);
|
||||
match *ty {
|
||||
clean::ResolvedPath { id, _ } => Some(id),
|
||||
clean::ResolvedPath { did, _ } => Some(did),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@@ -1089,7 +1191,7 @@ fn render_impl(w: &mut io::Writer, i: &clean::Impl) {
|
||||
write!(w, "{}</code></h3>", i.for_);
|
||||
write!(w, "<div class='methods'>");
|
||||
for meth in i.methods.iter() {
|
||||
write!(w, "<h4 id='fn.{}' class='method'><code>",
|
||||
write!(w, "<h4 id='method.{}' class='method'><code>",
|
||||
*meth.name.get_ref());
|
||||
render_method(w, meth, false);
|
||||
write!(w, "</code></h4>\n");
|
||||
@@ -1102,7 +1204,11 @@ fn render_impl(w: &mut io::Writer, i: &clean::Impl) {
|
||||
}
|
||||
|
||||
// No documentation? Attempt to slurp in the trait's documentation
|
||||
let trait_id = match trait_id { Some(id) => id, None => loop };
|
||||
let trait_id = match trait_id {
|
||||
None => loop,
|
||||
Some(id) if is_local(id) => loop,
|
||||
Some(id) => id.node,
|
||||
};
|
||||
do local_data::get(cache_key) |cache| {
|
||||
do cache.unwrap().read |cache| {
|
||||
let name = meth.name.get_ref().as_slice();
|
||||
@@ -1198,20 +1304,22 @@ fn build_sidebar(m: &clean::Module) -> HashMap<~str, ~[~str]> {
|
||||
return map;
|
||||
}
|
||||
|
||||
fn item_variant(w: &mut io::Writer, cx: &Context, it: &clean::Item) {
|
||||
write!(w, "<DOCTYPE html><html><head>\
|
||||
<meta http-equiv='refresh' content='0; \
|
||||
url=../enum.{}.html\\#variant.{}'>\
|
||||
</head><body></body></html>",
|
||||
*cx.current.last(),
|
||||
it.name.get_ref().as_slice());
|
||||
}
|
||||
|
||||
fn item_struct_field(w: &mut io::Writer, cx: &Context, it: &clean::Item) {
|
||||
write!(w, "<DOCTYPE html><html><head>\
|
||||
<meta http-equiv='refresh' content='0; \
|
||||
url=../struct.{}.html\\#field.{}'>\
|
||||
</head><body></body></html>",
|
||||
*cx.current.last(),
|
||||
it.name.get_ref().as_slice());
|
||||
impl<'self> fmt::Default for Source<'self> {
|
||||
fn fmt(s: &Source<'self>, fmt: &mut fmt::Formatter) {
|
||||
let lines = s.line_iter().len();
|
||||
let mut cols = 0;
|
||||
let mut tmp = lines;
|
||||
while tmp > 0 {
|
||||
cols += 1;
|
||||
tmp /= 10;
|
||||
}
|
||||
write!(fmt.buf, "<pre class='line-numbers'>");
|
||||
for i in range(1, lines + 1) {
|
||||
write!(fmt.buf, "<span id='{0:u}'>{0:1$u}</span>\n", i, cols);
|
||||
}
|
||||
write!(fmt.buf, "</pre>");
|
||||
write!(fmt.buf, "<pre class='rust'>");
|
||||
write!(fmt.buf, "{}", Escape(s.as_slice()));
|
||||
write!(fmt.buf, "</pre>");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user