auto merge of #9374 : alexcrichton/rust/rustdoc-web, r=cmr

This large commit implements and `html` output option for rustdoc_ng. The
executable has been altered to be invoked as "rustdoc_ng html <crate>" and
it will dump everything into the local "doc" directory. JSON can still be
generated by changing 'html' to 'json'.

This also fixes a number of bugs in rustdoc_ng relating to comment stripping,
along with some other various issues that I found along the way.

The `make doc` command has been altered to generate the new documentation into
the `doc/ng/$(CRATE)` directories.

Previews
* http://www.contrib.andrew.cmu.edu/~acrichto/doc/std/
* http://www.contrib.andrew.cmu.edu/~acrichto/doc/extra/

Missing features
* Different versions of documentation on the same page (all possibly indexed as well?) I think that this needs to be thought out before action is taken. It's an awesome idea, but it should be done carefully.
* Source links are missing. This is a little dependent on getting versions working. In theory we should link back to github, but we should always link back to the exact version the documentation was generated from.
* Integration with other tools. It would be awesome to have rustpkg-style inference of the package name and version so they don't have to be specified anywhere. Additionally, I should be able to build documentation for a pkgid, not necessarily a crate file.

cc @cmr/@Seldaek
This commit is contained in:
bors 2013-09-21 00:16:02 -07:00
commit db78fdc10c
22 changed files with 3067 additions and 139 deletions

View File

@ -143,6 +143,7 @@ Version 0.8 (October 2013)
* The runtime uses jemalloc for allocations.
* Segmented stacks are temporarily disabled as part of the transition to
the new runtime. Stack overflows are possible!
* A new documentation backend, rustdoc_ng, is available for use
Version 0.7 (July 2013)
-----------------------

View File

@ -213,6 +213,7 @@ else
# The rustdoc executable
RUSTDOC = $(HBIN2_H_$(CFG_BUILD_TRIPLE))/rustdoc$(X_$(CFG_BUILD_TRIPLE))
RUSTDOC_NG = $(HBIN2_H_$(CFG_BUILD_TRIPLE))/rustdoc_ng$(X_$(CFG_BUILD_TRIPLE))
# The library documenting macro
# $(1) - The output directory
@ -230,8 +231,22 @@ doc/$(1)/rust.css: rust.css
DOCS += doc/$(1)/index.html
endef
# The library documenting macro
# $(1) - The output directory
# $(2) - The crate file
# $(3) - The crate soruce files
define libdocng
doc/ng/$(1)/index.html: $(2) $(3) $$(RUSTDOC_NG)
@$$(call E, rustdoc_ng: $$@)
$(Q)$(RUSTDOC_NG) html $(2) -o doc/ng
DOCS += doc/ng/$(1)/index.html
endef
$(eval $(call libdoc,std,$(STDLIB_CRATE),$(STDLIB_INPUTS)))
$(eval $(call libdoc,extra,$(EXTRALIB_CRATE),$(EXTRALIB_INPUTS)))
$(eval $(call libdocng,std,$(STDLIB_CRATE),$(STDLIB_INPUTS)))
$(eval $(call libdocng,extra,$(EXTRALIB_CRATE),$(EXTRALIB_INPUTS)))
endif

View File

@ -25,6 +25,10 @@ Rust extras are part of the standard Rust distribution.
uuid = "122bed0b-c19b-4b82-b0b7-7ae8aead7297",
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",
passes = "strip-hidden")];
#[comment = "Rust extras"];
#[license = "MIT/ASL2"];
#[crate_type = "lib"];

View File

@ -9,6 +9,7 @@
// except according to those terms.
#[macro_escape];
#[doc(hidden)];
macro_rules! rterrln (
($( $arg:expr),+) => ( {

View File

@ -11,6 +11,7 @@
// FIXME(#4375): this shouldn't have to be a nested module named 'generated'
#[macro_escape];
#[doc(hidden)];
macro_rules! int_module (($T:ty, $bits:expr) => (mod generated {

View File

@ -11,6 +11,7 @@
// FIXME(#4375): this shouldn't have to be a nested module named 'generated'
#[macro_escape];
#[doc(hidden)];
macro_rules! uint_module (($T:ty, $T_SIGNED:ty, $bits:expr) => (mod generated {

View File

@ -54,7 +54,8 @@ Several modules in `core` are clients of `rt`:
*/
#[doc(hidden)];
// XXX: this should not be here.
#[allow(missing_doc)];
use cell::Cell;
use clone::Clone;

View File

@ -57,6 +57,10 @@ they contained the following prologue:
#[license = "MIT/ASL2"];
#[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",
passes = "strip-hidden")];
// Don't link to std. We are std.
#[no_std];

View File

@ -262,7 +262,7 @@ impl Context {
let span = match self.names.find(&name) {
Some(e) => e.span,
None => {
let msg = fmt!("There is no argument named `%s`", name);
let msg = fmt!("there is no argument named `%s`", name);
self.ecx.span_err(self.fmtsp, msg);
return;
}

View File

@ -107,9 +107,7 @@ pub fn strip_doc_comment_decoration(comment: &str) -> ~str {
}
if comment.starts_with("//") {
// FIXME #5475:
// return comment.slice(3u, comment.len()).to_owned();
let r = comment.slice(3u, comment.len()); return r.to_owned();
return comment.slice(3u, comment.len()).to_owned();
}
if comment.starts_with("/*") {

View File

@ -15,6 +15,7 @@ use its = syntax::parse::token::ident_to_str;
use syntax;
use syntax::ast;
use syntax::attr::AttributeMethods;
use std;
use doctree;
@ -90,6 +91,48 @@ pub struct Item {
id: ast::NodeId,
}
impl Item {
/// Finds the `doc` attribute as a List and returns the list of attributes
/// nested inside.
pub fn doc_list<'a>(&'a self) -> Option<&'a [Attribute]> {
for attr in self.attrs.iter() {
match *attr {
List(~"doc", ref list) => { return Some(list.as_slice()); }
_ => {}
}
}
return None;
}
/// Finds the `doc` attribute as a NameValue and returns the corresponding
/// value found.
pub fn doc_value<'a>(&'a self) -> Option<&'a str> {
for attr in self.attrs.iter() {
match *attr {
NameValue(~"doc", ref v) => { return Some(v.as_slice()); }
_ => {}
}
}
return None;
}
pub fn is_mod(&self) -> bool {
match self.inner { ModuleItem(*) => true, _ => false }
}
pub fn is_trait(&self) -> bool {
match self.inner { TraitItem(*) => true, _ => false }
}
pub fn is_struct(&self) -> bool {
match self.inner { StructItem(*) => true, _ => false }
}
pub fn is_enum(&self) -> bool {
match self.inner { EnumItem(*) => true, _ => false }
}
pub fn is_fn(&self) -> bool {
match self.inner { FunctionItem(*) => true, _ => false }
}
}
#[deriving(Clone, Encodable, Decodable)]
pub enum ItemEnum {
StructItem(Struct),
@ -155,7 +198,7 @@ impl Clean<Attribute> for ast::MetaItem {
impl Clean<Attribute> for ast::Attribute {
fn clean(&self) -> Attribute {
self.node.value.clean()
self.desugar_doc().node.value.clean()
}
}
@ -437,18 +480,24 @@ pub enum TraitMethod {
}
impl TraitMethod {
fn is_req(&self) -> bool {
pub fn is_req(&self) -> bool {
match self {
&Required(*) => true,
_ => false,
}
}
fn is_def(&self) -> bool {
pub fn is_def(&self) -> bool {
match self {
&Provided(*) => true,
_ => false,
}
}
pub fn item<'a>(&'a self) -> &'a Item {
match *self {
Required(ref item) => item,
Provided(ref item) => item,
}
}
}
impl Clean<TraitMethod> for ast::trait_method {

View File

@ -91,21 +91,9 @@ pub trait DocFolder {
}
fn fold_crate(&mut self, mut c: Crate) -> Crate {
let mut mod_ = None;
std::util::swap(&mut mod_, &mut c.module);
let mod_ = mod_.unwrap();
c.module = self.fold_item(mod_);
let Crate { name, module } = c;
match module {
Some(Item { inner: ModuleItem(m), name: name_, attrs: attrs_,
source, visibility: vis, id }) => {
return Crate { module: Some(Item { inner:
ModuleItem(self.fold_mod(m)),
name: name_, attrs: attrs_,
source: source, id: id, visibility: vis }), name: name};
},
Some(_) => fail!("non-module item set as module of crate"),
None => return Crate { module: None, name: name},
}
c.module = match std::util::replace(&mut c.module, None) {
Some(module) => self.fold_item(module), None => None
};
return c;
}
}

View File

@ -0,0 +1,364 @@
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use std::fmt;
use std::local_data;
use std::rt::io;
use syntax::ast;
use clean;
use html::render::{cache_key, current_location_key};
pub struct VisSpace(Option<ast::visibility>);
pub struct Method<'self>(&'self clean::SelfTy, &'self clean::FnDecl);
impl fmt::Default for clean::Generics {
fn fmt(g: &clean::Generics, f: &mut fmt::Formatter) {
if g.lifetimes.len() == 0 && g.type_params.len() == 0 { return }
f.buf.write("&lt;".as_bytes());
for (i, life) in g.lifetimes.iter().enumerate() {
if i > 0 { f.buf.write(", ".as_bytes()); }
write!(f.buf, "{}", *life);
}
if g.type_params.len() > 0 {
if g.lifetimes.len() > 0 { f.buf.write(", ".as_bytes()); }
for (i, tp) in g.type_params.iter().enumerate() {
if i > 0 { f.buf.write(", ".as_bytes()) }
f.buf.write(tp.name.as_bytes());
if tp.bounds.len() > 0 {
f.buf.write(": ".as_bytes());
for (i, bound) in tp.bounds.iter().enumerate() {
if i > 0 { f.buf.write(" + ".as_bytes()); }
write!(f.buf, "{}", *bound);
}
}
}
}
f.buf.write("&gt;".as_bytes());
}
}
impl fmt::Default for clean::Lifetime {
fn fmt(l: &clean::Lifetime, f: &mut fmt::Formatter) {
f.buf.write("'".as_bytes());
f.buf.write(l.as_bytes());
}
}
impl fmt::Default for clean::TyParamBound {
fn fmt(bound: &clean::TyParamBound, f: &mut fmt::Formatter) {
match *bound {
clean::RegionBound => {
f.buf.write("'static".as_bytes())
}
clean::TraitBound(ref ty) => {
write!(f.buf, "{}", *ty);
}
}
}
}
impl fmt::Default for clean::Path {
fn fmt(path: &clean::Path, f: &mut fmt::Formatter) {
if path.global { f.buf.write("::".as_bytes()) }
for (i, seg) in path.segments.iter().enumerate() {
if i > 0 { f.buf.write("::".as_bytes()) }
f.buf.write(seg.name.as_bytes());
if seg.lifetime.is_some() || seg.types.len() > 0 {
f.buf.write("&lt;".as_bytes());
match seg.lifetime {
Some(ref lifetime) => write!(f.buf, "{}", *lifetime),
None => {}
}
for (i, ty) in seg.types.iter().enumerate() {
if i > 0 || seg.lifetime.is_some() {
f.buf.write(", ".as_bytes());
}
write!(f.buf, "{}", *ty);
}
f.buf.write("&gt;".as_bytes());
}
}
}
}
fn resolved_path(w: &mut io::Writer, id: ast::NodeId, path: &clean::Path) {
// The generics will get written to both the title and link
let mut generics = ~"";
let last = path.segments.last();
if last.lifetime.is_some() || last.types.len() > 0 {
generics.push_str("&lt;");
match last.lifetime {
Some(ref lifetime) => generics.push_str(format!("{}", *lifetime)),
None => {}
}
for (i, ty) in last.types.iter().enumerate() {
if i > 0 || last.lifetime.is_some() {
generics.push_str(", ");
}
generics.push_str(format!("{}", *ty));
}
generics.push_str("&gt;");
}
// Did someone say rightward-drift?
do local_data::get(current_location_key) |loc| {
let loc = loc.unwrap();
do local_data::get(cache_key) |cache| {
do cache.unwrap().read |cache| {
match cache.paths.find(&id) {
// This is a documented path, link to it!
Some(&(ref fqp, shortty)) => {
let fqn = fqp.connect("::");
let mut same = 0;
for (a, b) in loc.iter().zip(fqp.iter()) {
if *a == *b {
same += 1;
} else {
break;
}
}
let mut url = ~"";
for _ in range(same, loc.len()) {
url.push_str("../");
}
if same == fqp.len() {
url.push_str(shortty);
url.push_str(".");
url.push_str(*fqp.last());
url.push_str(".html");
} else {
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("/");
}
url.push_str(shortty);
url.push_str(".");
url.push_str(*remaining.last());
url.push_str(".html");
}
write!(w, "<a class='{}' href='{}' title='{}'>{}</a>{}",
shortty, url, fqn, last.name, generics);
}
None => {
write!(w, "{}{}", last.name, generics);
}
};
}
}
}
}
impl fmt::Default for clean::Type {
fn fmt(g: &clean::Type, f: &mut fmt::Formatter) {
match *g {
clean::TyParamBinder(id) | clean::Generic(id) => {
do local_data::get(cache_key) |cache| {
do cache.unwrap().read |m| {
f.buf.write(m.typarams.get(&id).as_bytes());
}
}
}
clean::Unresolved(*) => unreachable!(),
clean::ResolvedPath{id, typarams: ref typarams, path: ref path} => {
resolved_path(f.buf, id, path);
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 => {}
}
}
// XXX: this should be a link
clean::External(ref a, _) => {
write!(f.buf, "{}", *a);
}
clean::Self(*) => f.buf.write("Self".as_bytes()),
clean::Primitive(prim) => {
let s = match prim {
ast::ty_int(ast::ty_i) => "int",
ast::ty_int(ast::ty_i8) => "i8",
ast::ty_int(ast::ty_i16) => "i16",
ast::ty_int(ast::ty_i32) => "i32",
ast::ty_int(ast::ty_i64) => "i64",
ast::ty_uint(ast::ty_u) => "uint",
ast::ty_uint(ast::ty_u8) => "u8",
ast::ty_uint(ast::ty_u16) => "u16",
ast::ty_uint(ast::ty_u32) => "u32",
ast::ty_uint(ast::ty_u64) => "u64",
ast::ty_float(ast::ty_f) => "float",
ast::ty_float(ast::ty_f32) => "f32",
ast::ty_float(ast::ty_f64) => "f64",
ast::ty_str => "str",
ast::ty_bool => "bool",
ast::ty_char => "char",
};
f.buf.write(s.as_bytes());
}
clean::Closure(ref decl) => {
f.buf.write(match decl.sigil {
ast::BorrowedSigil => "&amp;",
ast::ManagedSigil => "@",
ast::OwnedSigil => "~",
}.as_bytes());
match decl.region {
Some(ref region) => write!(f.buf, "{} ", *region),
None => {}
}
write!(f.buf, "{}{}fn{}",
match decl.purity {
ast::unsafe_fn => "unsafe ",
ast::extern_fn => "extern ",
ast::impure_fn => ""
},
match decl.onceness {
ast::Once => "once ",
ast::Many => "",
},
decl.decl);
// XXX: where are bounds and lifetimes printed?!
}
clean::BareFunction(ref decl) => {
write!(f.buf, "{}{}fn{}{}",
match decl.purity {
ast::unsafe_fn => "unsafe ",
ast::extern_fn => "extern ",
ast::impure_fn => ""
},
match decl.abi {
~"" | ~"\"Rust\"" => ~"",
ref s => " " + *s + " ",
},
decl.generics,
decl.decl);
}
clean::Tuple(ref typs) => {
f.buf.write("(".as_bytes());
for (i, typ) in typs.iter().enumerate() {
if i > 0 { f.buf.write(", ".as_bytes()) }
write!(f.buf, "{}", *typ);
}
f.buf.write(")".as_bytes());
}
clean::Vector(ref t) => write!(f.buf, "[{}]", **t),
clean::FixedVector(ref t, ref s) => {
write!(f.buf, "[{}, ..{}]", **t, *s);
}
clean::String => f.buf.write("str".as_bytes()),
clean::Bool => f.buf.write("bool".as_bytes()),
clean::Unit => f.buf.write("()".as_bytes()),
clean::Bottom => f.buf.write("!".as_bytes()),
clean::Unique(ref t) => write!(f.buf, "~{}", **t),
clean::Managed(m, ref t) => {
write!(f.buf, "@{}{}",
match m {
clean::Mutable => "mut ",
clean::Immutable => "",
}, **t)
}
clean::RawPointer(m, ref t) => {
write!(f.buf, "*{}{}",
match m {
clean::Mutable => "mut ",
clean::Immutable => "",
}, **t)
}
clean::BorrowedRef{ lifetime: ref l, mutability, type_: ref ty} => {
let lt = match *l { Some(ref l) => format!("{} ", *l), _ => ~"" };
write!(f.buf, "&amp;{}{}{}",
lt,
match mutability {
clean::Mutable => "mut ",
clean::Immutable => "",
},
**ty);
}
}
}
}
impl fmt::Default for clean::FnDecl {
fn fmt(d: &clean::FnDecl, f: &mut fmt::Formatter) {
let mut args = ~"";
for (i, input) in d.inputs.iter().enumerate() {
if i > 0 { args.push_str(", "); }
if input.name.len() > 0 {
args.push_str(format!("{}: ", input.name));
}
args.push_str(format!("{}", input.type_));
}
write!(f.buf, "({args}){arrow, select, yes{ -&gt; {ret}} other{}}",
args = args,
arrow = match d.output { clean::Unit => "no", _ => "yes" },
ret = d.output);
}
}
impl<'self> fmt::Default for Method<'self> {
fn fmt(m: &Method<'self>, f: &mut fmt::Formatter) {
let Method(selfty, d) = *m;
let mut args = ~"";
match *selfty {
clean::SelfStatic => {},
clean::SelfValue => args.push_str("self"),
clean::SelfOwned => args.push_str("~self"),
clean::SelfManaged(clean::Mutable) => args.push_str("@mut self"),
clean::SelfManaged(clean::Immutable) => args.push_str("@self"),
clean::SelfBorrowed(Some(ref lt), clean::Immutable) => {
args.push_str(format!("&amp;{} self", *lt));
}
clean::SelfBorrowed(Some(ref lt), clean::Mutable) => {
args.push_str(format!("&amp;{} mut self", *lt));
}
clean::SelfBorrowed(None, clean::Mutable) => {
args.push_str("&amp;mut self");
}
clean::SelfBorrowed(None, clean::Immutable) => {
args.push_str("&amp;self");
}
}
for (i, input) in d.inputs.iter().enumerate() {
if i > 0 || args.len() > 0 { args.push_str(", "); }
if input.name.len() > 0 {
args.push_str(format!("{}: ", input.name));
}
args.push_str(format!("{}", input.type_));
}
write!(f.buf, "({args}){arrow, select, yes{ -&gt; {ret}} other{}}",
args = args,
arrow = match d.output { clean::Unit => "no", _ => "yes" },
ret = d.output);
}
}
impl fmt::Default for VisSpace {
fn fmt(v: &VisSpace, f: &mut fmt::Formatter) {
match **v {
Some(ast::public) => { write!(f.buf, "pub "); }
Some(ast::private) => { write!(f.buf, "priv "); }
Some(ast::inherited) | None => {}
}
}
}

View File

@ -0,0 +1,130 @@
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use std::fmt;
use std::rt::io;
#[deriving(Clone)]
pub struct Layout {
logo: ~str,
favicon: ~str,
crate: ~str,
}
pub struct Page<'self> {
title: &'self str,
ty: &'self str,
root_path: &'self str,
}
pub fn render<T: fmt::Default, S: fmt::Default>(
dst: &mut io::Writer, layout: &Layout, page: &Page, sidebar: &S, t: &T)
{
write!(dst, "
<!DOCTYPE html>
<html lang=\"en\">
<head>
<meta charset=\"utf-8\" />
<title>{title}</title>
<link href='http://fonts.googleapis.com/css?family=Oswald:700|Inconsolata:400'
rel='stylesheet' type='text/css'>
<link rel=\"stylesheet\" type=\"text/css\" href=\"{root_path}main.css\">
{favicon, select, none{} other{
<link rel=\"icon\" href=\"#\" sizes=\"16x16\"
type=\"image/vnd.microsoft.icon\" />}}
</head>
<body>
<!--[if lte IE 8]>
<div class=\"warning\">
This old browser is unsupported and will most likely display funky
things
</div>
<![endif]-->
<section class=\"sidebar\">
{logo, select, none{} other{
<a href='{root_path}index.html'><img src='#' alt=''/></a>
}}
{sidebar}
</section>
<nav class=\"sub\">
<form class=\"search-form js-only\">
<input class=\"search-input\" name=\"search\"
autocomplete=\"off\" />
<button class=\"do-search\">Search</button>
</form>
</nav>
<section class=\"content {ty}\">{content}</section>
<section class=\"footer\"></section>
<script>
var rootPath = \"{root_path}\";
</script>
<script src=\"{root_path}jquery.js\"></script>
<script src=\"{root_path}{crate}/search-index.js\"></script>
<script src=\"{root_path}main.js\"></script>
<div id=\"help\" class=\"hidden\">
<div class=\"shortcuts\">
<h1>Keyboard shortcuts</h1>
<dl>
<dt>?</dt>
<dd>Show this help dialog</dd>
<dt>S</dt>
<dd>Focus the search field</dd>
<dt>&uarr;</dt>
<dd>Move up in search results</dd>
<dt>&darr;</dt>
<dd>Move down in search results</dd>
<dt>&\\#9166;</dt>
<dd>Go to active search result</dd>
</dl>
</div>
<div class=\"infos\">
<h1>Search tricks</h1>
<p>
Prefix searches with a type followed by a colon (e.g.
<code>fn:</code>) to restrict the search to a given type.
</p>
<p>
Accepted types are: <code>fn</code>, <code>mod</code>,
<code>struct</code> (or <code>str</code>), <code>enum</code>,
<code>trait</code>, <code>typedef</code> (or
<code>tdef</code>).
</p>
</div>
</div>
</body>
</html>
",
content = *t,
root_path = page.root_path,
ty = page.ty,
logo = nonestr(layout.logo),
title = page.title,
favicon = nonestr(layout.favicon),
sidebar = *sidebar,
crate = layout.crate,
);
}
fn boolstr(b: bool) -> &'static str {
if b { "true" } else { "false" }
}
fn nonestr<'a>(s: &'a str) -> &'a str {
if s == "" { "none" } else { s }
}

View File

@ -0,0 +1,54 @@
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use std::fmt;
use std::rt::io::Reader;
use std::rt::io::pipe::PipeStream;
use std::rt::io::process::{ProcessConfig, Process, CreatePipe};
use std::rt::io;
pub struct Markdown<'self>(&'self str);
impl<'self> fmt::Default for Markdown<'self> {
fn fmt(md: &Markdown<'self>, fmt: &mut fmt::Formatter) {
if md.len() == 0 { return; }
// Create the pandoc process
do io::io_error::cond.trap(|err| {
fail2!("Error executing `pandoc`: {}", err.desc);
}).inside {
let io = ~[CreatePipe(PipeStream::new().unwrap(), true, false),
CreatePipe(PipeStream::new().unwrap(), false, true)];
let args = ProcessConfig {
program: "pandoc",
args: [],
env: None,
cwd: None,
io: io,
};
let mut p = Process::new(args).expect("couldn't fork for pandoc");
// Write the markdown to stdin and close it.
p.io[0].get_mut_ref().write(md.as_bytes());
p.io[0] = None;
// Ferry the output from pandoc over to the destination buffer.
let mut buf = [0, ..1024];
loop {
match p.io[1].get_mut_ref().read(buf) {
None | Some(0) => { break }
Some(n) => {
fmt.buf.write(buf.slice_to(n));
}
}
}
}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,270 @@
/**
* Copyright 2013 The Rust Project Developers. See the COPYRIGHT
* file at the top-level directory of this distribution and at
* http://rust-lang.org/COPYRIGHT.
*
* Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
* http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
* <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
* option. This file may not be copied, modified, or distributed
* except according to those terms.
*/
@import "normalize.css";
* {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
/* Fonts */
body {
font: 13px Arial, sans-serif;
line-height: 165%;
}
h1, .sidebar .location {
font: 700 22px "Oswald", Arial, sans-serif;
}
h2, h3, h4 {
font: 700 16px "Oswald", Arial, sans-serif;
text-transform: uppercase;
}
h2 code, h3 code, h4 code {
text-transform: none;
font-size: 1.2em;
}
code, pre, h1.fqn {
font: 15px "Inconsolata", "Consolas", "Courier New", monospace;
}
h1.fqn {
font-size: 26px;
font-weight: normal;
}
nav {
font: 700 26px "Oswald", Arial, sans-serif;
text-transform: uppercase;
}
nav.sub {
padding-top: 20px;
font: 700 16px "Oswald", Arial, sans-serif;
text-transform: uppercase;
text-align: right;
}
/* General structure */
html, body {
min-height: 100%;
height: 100%;
}
body {
position: relative;
height: auto;
padding-bottom: 20px;
}
.sidebar {
width: 200px;
position: absolute;
left: 0;
top: 0;
min-height: 100%;
}
.content, nav { max-width: 960px; }
/* Everything else */
.js-only, .hidden { display: none; }
.sidebar {
background: #e9e9e9;
padding: 10px;
}
.sidebar img {
margin: 20px auto;
display: block;
}
.sidebar .location { margin-bottom: 10px; }
.sidebar .block, pre { background: #fff; }
.sidebar .block, pre, .content { border-bottom: 2px solid black; }
.trait { border-color: #fcae2b !important; }
.mod { border-color: #809fc7 !important; }
.enum { border-color: #93bc99 !important; }
.struct { border-color: #e53700 !important; }
.fn { border-color: #a2777f !important; }
.block {
padding: 10px;
margin-bottom: 10px;
}
.block h2 { margin-top: 0; }
.content {
background: #f3f3f3;
padding: 20px 20px 20px 40px;
}
.content h1 { margin-top: 0; }
.content h1, .content h2 { margin-left: -20px; }
.content pre { padding: 20px; }
.content .highlighted {
cursor: pointer;
color: #000 !important;
background-color: #ccc;
}
.content .highlighted a { color: #000 !important; }
.content .highlighted.trait { background-color: #fece7e; }
.content .highlighted.mod { background-color: #afc6e4; }
.content .highlighted.enum { background-color: #b4d1b9; }
.content .highlighted.struct { background-color: #e7b1a0; }
.content .highlighted.fn { background-color: #c6afb3; }
.docblock.short.nowrap {
display: block;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.docblock.short p {
overflow: hidden;
text-overflow: ellipsis;
margin: 0;
}
.docblock h1, .docblock h2, .docblock h3, .docblock h4, .docblock h5 {
margin-left: 0;
}
.docblock h1 { font-size: 1.1em; }
.docblock h2 { font-size: 1.05em; }
.docblock h3, .docblock h4, .docblock h5 { font-size: 1em; }
.content .source { float: right; }
.content table {
border-spacing: 0 5px;
border-collapse: separate;
}
.content td { vertical-align: top; }
.content td:first-child { padding-right: 20px; }
.content td p:first-child { margin-top: 0; }
.content td h1, .content td h2 { margin-left: 0; font-size: 1.1em; }
.content .item-list {
list-style-type: none;
padding: 0;
}
.content .item-list li { margin-bottom: 3px; }
.content .multi-column {
-moz-column-count: 5;
-moz-column-gap: 2.5em;
-webkit-column-count: 5;
-webkit-column-gap: 2.5em;
column-count: 5;
column-gap: 2.5em;
}
.content .multi-column li { width: 100%; display: inline-block; }
.content .method { font-size: 1em; }
.content .methods { margin-left: 20px; }
.content .methods .docblock { margin-left: 20px; }
nav {
border-bottom: 1px solid #e0e0e0;
padding-bottom: 10px;
margin-bottom: 10px;
}
nav.main {
padding: 20px 0;
text-align: center;
}
nav.main .current {
border-top: 1px solid #000;
border-bottom: 1px solid #000;
}
nav.main .separator {
border: 1px solid #000;
display: inline-block;
height: 23px;
margin: 0 20px;
}
nav.sum { text-align: right; }
nav.sub form { display: inline; }
nav, .content {
margin-left: 220px;
margin-right: 20px;
}
a {
text-decoration: none;
color: #000;
}
.content a, .block a.current { font-weight: bold; }
.content a.trait, .block a.current.trait { color: #ed9603; }
.content a.mod, .block a.current.mod { color: #4d76ae; }
.content a.enum, .block a.current.enum { color: #5e9766; }
.content a.struct, .block a.current.struct { color: #e53700; }
.content a.fn, .block a.current.fn { color: #8c6067; }
.search-input {
border: 2px solid #f2f2f2;
border-radius: 2px;
width: 350px;
}
.search-results .desc {
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
display: block;
}
#help {
background: #e9e9e9;
border-radius: 4px;
box-shadow: 0 0 6px rgba(0,0,0,.2);
position: absolute;
top: 300px;
left: 50%;
margin-top: -125px;
margin-left: -275px;
width: 550px;
height: 250px;
border: 1px solid #bfbfbf;
}
#help dt {
float: left;
border-radius: 3px;
border: 1px solid #bfbfbf;
background: #fff;
width: 23px;
text-align: center;
clear: left;
display: block;
margin-top: -1px;
}
#help dd { margin: 5px 33px; }
#help .infos { padding-left: 0; }
#help h1 { margin-top: 0; }
#help div {
width: 50%;
float: left;
padding: 20px;
}

View File

@ -0,0 +1,420 @@
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/*jslint browser: true, es5: true */
/*globals $: true, searchIndex: true, rootPath: true, allPaths: true */
(function () {
"use strict";
var resizeTimeout, interval;
$('.js-only').removeClass('js-only');
function resizeShortBlocks() {
if (resizeTimeout) {
clearTimeout(resizeTimeout);
}
resizeTimeout = setTimeout(function () {
var contentWidth = $('.content').width();
$('.docblock.short').width(function () {
return contentWidth - 40 - $(this).prev().width();
}).addClass('nowrap');
}, 150);
}
resizeShortBlocks();
$(window).on('resize', resizeShortBlocks);
$(document).on('keyup', function (e) {
if (document.activeElement.tagName === 'INPUT') {
return;
}
if (e.keyCode === 188 && $('#help').hasClass('hidden')) { // question mark
e.preventDefault();
$('#help').removeClass('hidden');
} else if (e.keyCode === 27 && !$('#help').hasClass('hidden')) { // esc
e.preventDefault();
$('#help').addClass('hidden');
} else if (e.keyCode === 83) { // S
e.preventDefault();
$('.search-input').focus();
}
}).on('click', function (e) {
if (!$(e.target).closest('#help').length) {
$('#help').addClass('hidden');
}
});
$('.version-selector').on('change', function () {
var i, match,
url = document.location.href,
stripped = '',
len = rootPath.match(/\.\.\//g).length + 1;
for (i = 0; i < len; i += 1) {
match = url.match(/\/[^\/]*$/);
if (i < len - 1) {
stripped = match[0] + stripped;
}
url = url.substring(0, url.length - match[0].length);
}
url += '/' + $('.version-selector').val() + stripped;
document.location.href = url;
});
function initSearch(searchIndex) {
var currentResults, index;
// clear cached values from the search bar
$(".search-input")[0].value = '';
function execQuery(query, max, searchWords) {
var valLower = query.query.toLowerCase(),
val = valLower,
typeFilter = query.type,
results = [],
aa = 0,
bb = 0;
// quoted values mean literal search
bb = searchWords.length;
if ((val.charAt(0) === "\"" || val.charAt(0) === "'") && val.charAt(val.length - 1) === val.charAt(0)) {
val = val.substr(1, val.length - 2);
for (aa = 0; aa < bb; aa += 1) {
if (searchWords[aa] === val) {
// filter type: ... queries
if (!typeFilter || typeFilter === searchIndex[aa].ty) {
results.push([aa, -1]);
}
}
if (results.length === max) {
break;
}
}
} else {
// gather matching search results up to a certain maximum
val = val.replace(/\_/g, "");
for (aa = 0; aa < bb; aa += 1) {
if (searchWords[aa].indexOf(val) > -1 || searchWords[aa].replace(/_/g, "").indexOf(val) > -1) {
// filter type: ... queries
if (!typeFilter || typeFilter === searchIndex[aa].ty) {
results.push([aa, searchWords[aa].replace(/_/g, "").indexOf(val)]);
}
}
if (results.length === max) {
break;
}
}
}
bb = results.length;
for (aa = 0; aa < bb; aa += 1) {
results[aa].push(searchIndex[results[aa][0]].ty);
}
for (aa = 0; aa < bb; aa += 1) {
results[aa].push(searchIndex[results[aa][0]].path);
}
// if there are no results then return to default and fail
if (results.length === 0) {
return [];
}
// sort by exact match
results.sort(function search_complete_sort0(aaa, bbb) {
if (searchWords[aaa[0]] === valLower && searchWords[bbb[0]] !== valLower) {
return 1;
}
});
// first sorting attempt
// sort by item name length
results.sort(function search_complete_sort1(aaa, bbb) {
if (searchWords[aaa[0]].length > searchWords[bbb[0]].length) {
return 1;
}
});
// second sorting attempt
// sort by item name
results.sort(function search_complete_sort1(aaa, bbb) {
if (searchWords[aaa[0]].length === searchWords[bbb[0]].length && searchWords[aaa[0]] > searchWords[bbb[0]]) {
return 1;
}
});
// third sorting attempt
// sort by index of keyword in item name
if (results[0][1] !== -1) {
results.sort(function search_complete_sort1(aaa, bbb) {
if (aaa[1] > bbb[1] && bbb[1] === 0) {
return 1;
}
});
}
// fourth sorting attempt
// sort by type
results.sort(function search_complete_sort3(aaa, bbb) {
if (searchWords[aaa[0]] === searchWords[bbb[0]] && aaa[2] > bbb[2]) {
return 1;
}
});
// fifth sorting attempt
// sort by path
results.sort(function search_complete_sort4(aaa, bbb) {
if (searchWords[aaa[0]] === searchWords[bbb[0]] && aaa[2] === bbb[2] && aaa[3] > bbb[3]) {
return 1;
}
});
// sixth sorting attempt
// remove duplicates, according to the data provided
for (aa = results.length - 1; aa > 0; aa -= 1) {
if (searchWords[results[aa][0]] === searchWords[results[aa - 1][0]] && results[aa][2] === results[aa - 1][2] && results[aa][3] === results[aa - 1][3]) {
results[aa][0] = -1;
}
}
return results;
}
function getQuery() {
var matches, type, query = $('.search-input').val();
matches = query.match(/^(fn|mod|str(uct)?|enum|trait|t(ype)?d(ef)?)\s*:\s*/i);
if (matches) {
type = matches[1].replace(/^td$/, 'typedef').replace(/^str$/, 'struct').replace(/^tdef$/, 'typedef').replace(/^typed$/, 'typedef');
query = query.substring(matches[0].length);
}
return {
query: query,
type: type,
id: query + type,
};
}
function initSearchNav() {
var hoverTimeout, $results = $('.search-results .result');
$results.on('click', function () {
document.location.href = $(this).find('a').prop('href');
}).on('mouseover', function () {
var $el = $(this);
clearTimeout(hoverTimeout);
hoverTimeout = setTimeout(function () {
$results.removeClass('highlighted');
$el.addClass('highlighted');
}, 20);
});
$(document).off('keyup.searchnav');
$(document).on('keyup.searchnav', function (e) {
var $active = $results.filter('.highlighted');
if (e.keyCode === 38) { // up
e.preventDefault();
if (!$active.length || !$active.prev()) {
return;
}
$active.prev().addClass('highlighted');
$active.removeClass('highlighted');
} else if (e.keyCode === 40) { // down
e.preventDefault();
if (!$active.length) {
$results.first().addClass('highlighted');
} else if ($active.next().length) {
$active.next().addClass('highlighted');
$active.removeClass('highlighted');
}
} else if (e.keyCode === 13) { // return
e.preventDefault();
if ($active.length) {
document.location.href = $active.find('a').prop('href');
}
}
});
}
function showResults(results) {
var output, shown, query = getQuery();
currentResults = query.id;
output = '<h1>Results for ' + query.query + (query.type ? ' (type: ' + query.type + ')' : '') + '</h1>';
output += '<table class="search-results">';
if (results.length > 0) {
shown = [];
results.forEach(function (item) {
var name, type;
if (shown.indexOf(item) !== -1) {
return;
}
shown.push(item);
name = item.name;
type = item.ty;
output += '<tr class="' + type + ' result"><td>';
if (type === 'mod') {
output += item.path + '::<a href="' + rootPath + item.path.replace(/::/g, '/') + '/' + name + '/index.html" class="' + type + '">' + name + '</a>';
} else if (type === 'static' || type === 'reexport') {
output += item.path + '::<a href="' + rootPath + item.path.replace(/::/g, '/') + '/index.html" class="' + type + '">' + name + '</a>';
} else if (item.parent !== undefined) {
var myparent = allPaths[item.parent];
output += item.path + '::' + myparent.name + '::<a href="' + rootPath + item.path.replace(/::/g, '/') + '/' + myparent.type + '.' + myparent.name + '.html" class="' + type + '">' + name + '</a>';
} else {
output += item.path + '::<a href="' + rootPath + item.path.replace(/::/g, '/') + '/' + type + '.' + name + '.html" class="' + type + '">' + name + '</a>';
}
output += '</td><td><span class="desc">' + item.desc + '</span></td></tr>';
});
} else {
output += 'No results :( <a href="https://duckduckgo.com/?q=' + encodeURIComponent('rust ' + query.query) + '">Try on DuckDuckGo?</a>';
}
output += "</p>";
$('.content').html(output);
$('.search-results .desc').width($('.content').width() - 40 - $('.content td:first-child').first().width());
initSearchNav();
}
function search(e) {
var query, filterdata = [], obj, i, len,
results = [],
maxResults = 200,
resultIndex;
query = getQuery();
if (e) {
e.preventDefault();
}
if (!query.query || query.id === currentResults) {
return;
}
resultIndex = execQuery(query, 20000, index);
len = resultIndex.length;
for (i = 0; i < len; i += 1) {
if (resultIndex[i][0] > -1) {
obj = searchIndex[resultIndex[i][0]];
filterdata.push([obj.name, obj.ty, obj.path, obj.desc]);
results.push(obj);
}
if (results.length >= maxResults) {
break;
}
}
// TODO add sorting capability through this function?
//
// // the handler for the table heading filtering
// filterdraw = function search_complete_filterdraw(node) {
// var name = "",
// arrow = "",
// op = 0,
// tbody = node.parentNode.parentNode.nextSibling,
// anchora = {},
// tra = {},
// tha = {},
// td1a = {},
// td2a = {},
// td3a = {},
// aaa = 0,
// bbb = 0;
//
// // the 4 following conditions set the rules for each
// // table heading
// if (node === ths[0]) {
// op = 0;
// name = "name";
// ths[1].innerHTML = ths[1].innerHTML.split(" ")[0];
// ths[2].innerHTML = ths[2].innerHTML.split(" ")[0];
// ths[3].innerHTML = ths[3].innerHTML.split(" ")[0];
// }
// if (node === ths[1]) {
// op = 1;
// name = "type";
// ths[0].innerHTML = ths[0].innerHTML.split(" ")[0];
// ths[2].innerHTML = ths[2].innerHTML.split(" ")[0];
// ths[3].innerHTML = ths[3].innerHTML.split(" ")[0];
// }
// if (node === ths[2]) {
// op = 2;
// name = "path";
// ths[0].innerHTML = ths[0].innerHTML.split(" ")[0];
// ths[1].innerHTML = ths[1].innerHTML.split(" ")[0];
// ths[3].innerHTML = ths[3].innerHTML.split(" ")[0];
// }
// if (node === ths[3]) {
// op = 3;
// name = "description";
// ths[0].innerHTML = ths[0].innerHTML.split(" ")[0];
// ths[1].innerHTML = ths[1].innerHTML.split(" ")[0];
// ths[2].innerHTML = ths[2].innerHTML.split(" ")[0];
// }
//
// // ascending or descending search
// arrow = node.innerHTML.split(" ")[1];
// if (arrow === undefined || arrow === "\u25b2") {
// arrow = "\u25bc";
// } else {
// arrow = "\u25b2";
// }
//
// // filter the data
// filterdata.sort(function search_complete_filterDraw_sort(xx, yy) {
// if ((arrow === "\u25b2" && xx[op].toLowerCase() < yy[op].toLowerCase()) || (arrow === "\u25bc" && xx[op].toLowerCase() > yy[op].toLowerCase())) {
// return 1;
// }
// });
// };
showResults(results);
}
function buildIndex(searchIndex) {
var len = searchIndex.length,
i = 0,
searchWords = [];
// before any analysis is performed lets gather the search terms to
// search against apart from the rest of the data. This is a quick
// operation that is cached for the life of the page state so that
// all other search operations have access to this cached data for
// faster analysis operations
for (i = 0; i < len; i += 1) {
if (typeof searchIndex[i].name === "string") {
searchWords.push(searchIndex[i].name.toLowerCase());
} else {
searchWords.push("");
}
}
return searchWords;
}
function startSearch() {
var keyUpTimeout;
$('.do-search').on('click', search);
$('.search-input').on('keyup', function () {
clearTimeout(keyUpTimeout);
keyUpTimeout = setTimeout(search, 100);
});
}
index = buildIndex(searchIndex);
startSearch();
}
initSearch(searchIndex);
}());

396
src/rustdoc_ng/html/static/normalize.css vendored Normal file
View File

@ -0,0 +1,396 @@
/*! normalize.css v2.1.2 | MIT License | git.io/normalize */
/* ==========================================================================
HTML5 display definitions
========================================================================== */
/**
* Correct `block` display not defined in IE 8/9.
*/
article,
aside,
details,
figcaption,
figure,
footer,
header,
hgroup,
main,
nav,
section,
summary {
display: block;
}
/**
* Correct `inline-block` display not defined in IE 8/9.
*/
audio,
canvas,
video {
display: inline-block;
}
/**
* Prevent modern browsers from displaying `audio` without controls.
* Remove excess height in iOS 5 devices.
*/
audio:not([controls]) {
display: none;
height: 0;
}
/**
* Address styling not present in IE 8/9.
*/
[hidden] {
display: none;
}
/* ==========================================================================
Base
========================================================================== */
/**
* 1. Set default font family to sans-serif.
* 2. Prevent iOS text size adjust after orientation change, without disabling
* user zoom.
*/
html {
font-family: sans-serif; /* 1 */
-ms-text-size-adjust: 100%; /* 2 */
-webkit-text-size-adjust: 100%; /* 2 */
}
/**
* Remove default margin.
*/
body {
margin: 0;
}
/* ==========================================================================
Links
========================================================================== */
/**
* Address `outline` inconsistency between Chrome and other browsers.
*/
a:focus {
outline: thin dotted;
}
/**
* Improve readability when focused and also mouse hovered in all browsers.
*/
a:active,
a:hover {
outline: 0;
}
/* ==========================================================================
Typography
========================================================================== */
/**
* Address variable `h1` font-size and margin within `section` and `article`
* contexts in Firefox 4+, Safari 5, and Chrome.
*/
h1 {
font-size: 2em;
margin: 0.67em 0;
}
/**
* Address styling not present in IE 8/9, Safari 5, and Chrome.
*/
abbr[title] {
border-bottom: 1px dotted;
}
/**
* Address style set to `bolder` in Firefox 4+, Safari 5, and Chrome.
*/
b,
strong {
font-weight: bold;
}
/**
* Address styling not present in Safari 5 and Chrome.
*/
dfn {
font-style: italic;
}
/**
* Address differences between Firefox and other browsers.
*/
hr {
-moz-box-sizing: content-box;
box-sizing: content-box;
height: 0;
}
/**
* Address styling not present in IE 8/9.
*/
mark {
background: #ff0;
color: #000;
}
/**
* Correct font family set oddly in Safari 5 and Chrome.
*/
code,
kbd,
pre,
samp {
font-family: monospace, serif;
font-size: 1em;
}
/**
* Improve readability of pre-formatted text in all browsers.
*/
pre {
white-space: pre-wrap;
}
/**
* Set consistent quote types.
*/
q {
quotes: "\201C" "\201D" "\2018" "\2019";
}
/**
* Address inconsistent and variable font size in all browsers.
*/
small {
font-size: 80%;
}
/**
* Prevent `sub` and `sup` affecting `line-height` in all browsers.
*/
sub,
sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sup {
top: -0.5em;
}
sub {
bottom: -0.25em;
}
/* ==========================================================================
Embedded content
========================================================================== */
/**
* Remove border when inside `a` element in IE 8/9.
*/
img {
border: 0;
}
/**
* Correct overflow displayed oddly in IE 9.
*/
svg:not(:root) {
overflow: hidden;
}
/* ==========================================================================
Figures
========================================================================== */
/**
* Address margin not present in IE 8/9 and Safari 5.
*/
figure {
margin: 0;
}
/* ==========================================================================
Forms
========================================================================== */
/**
* Define consistent border, margin, and padding.
*/
fieldset {
border: 1px solid #c0c0c0;
margin: 0 2px;
padding: 0.35em 0.625em 0.75em;
}
/**
* 1. Correct `color` not being inherited in IE 8/9.
* 2. Remove padding so people aren't caught out if they zero out fieldsets.
*/
legend {
border: 0; /* 1 */
padding: 0; /* 2 */
}
/**
* 1. Correct font family not being inherited in all browsers.
* 2. Correct font size not being inherited in all browsers.
* 3. Address margins set differently in Firefox 4+, Safari 5, and Chrome.
*/
button,
input,
select,
textarea {
font-family: inherit; /* 1 */
font-size: 100%; /* 2 */
margin: 0; /* 3 */
}
/**
* Address Firefox 4+ setting `line-height` on `input` using `!important` in
* the UA stylesheet.
*/
button,
input {
line-height: normal;
}
/**
* Address inconsistent `text-transform` inheritance for `button` and `select`.
* All other form control elements do not inherit `text-transform` values.
* Correct `button` style inheritance in Chrome, Safari 5+, and IE 8+.
* Correct `select` style inheritance in Firefox 4+ and Opera.
*/
button,
select {
text-transform: none;
}
/**
* 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`
* and `video` controls.
* 2. Correct inability to style clickable `input` types in iOS.
* 3. Improve usability and consistency of cursor style between image-type
* `input` and others.
*/
button,
html input[type="button"], /* 1 */
input[type="reset"],
input[type="submit"] {
-webkit-appearance: button; /* 2 */
cursor: pointer; /* 3 */
}
/**
* Re-set default cursor for disabled elements.
*/
button[disabled],
html input[disabled] {
cursor: default;
}
/**
* 1. Address box sizing set to `content-box` in IE 8/9.
* 2. Remove excess padding in IE 8/9.
*/
input[type="checkbox"],
input[type="radio"] {
box-sizing: border-box; /* 1 */
padding: 0; /* 2 */
}
/**
* 1. Address `appearance` set to `searchfield` in Safari 5 and Chrome.
* 2. Address `box-sizing` set to `border-box` in Safari 5 and Chrome
* (include `-moz` to future-proof).
*/
input[type="search"] {
-webkit-appearance: textfield; /* 1 */
-moz-box-sizing: content-box;
-webkit-box-sizing: content-box; /* 2 */
box-sizing: content-box;
}
/**
* Remove inner padding and search cancel button in Safari 5 and Chrome
* on OS X.
*/
input[type="search"]::-webkit-search-cancel-button,
input[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}
/**
* Remove inner padding and border in Firefox 4+.
*/
button::-moz-focus-inner,
input::-moz-focus-inner {
border: 0;
padding: 0;
}
/**
* 1. Remove default vertical scrollbar in IE 8/9.
* 2. Improve readability and alignment in all browsers.
*/
textarea {
overflow: auto; /* 1 */
vertical-align: top; /* 2 */
}
/* ==========================================================================
Tables
========================================================================== */
/**
* Remove most spacing between table cells.
*/
table {
border-collapse: collapse;
border-spacing: 0;
}

View File

@ -8,7 +8,9 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use std;
use std::num;
use std::uint;
use clean;
use syntax::ast;
use clean::Item;
@ -16,11 +18,6 @@ use plugins;
use fold;
use fold::DocFolder;
/// A sample pass showing the minimum required work for a plugin.
pub fn noop(crate: clean::Crate) -> plugins::PluginResult {
(crate, None)
}
/// Strip items marked `#[doc(hidden)]`
pub fn strip_hidden(crate: clean::Crate) -> plugins::PluginResult {
struct Stripper;
@ -32,7 +29,7 @@ pub fn strip_hidden(crate: clean::Crate) -> plugins::PluginResult {
for innerattr in l.iter() {
match innerattr {
&clean::Word(ref s) if "hidden" == *s => {
info!("found one in strip_hidden; removing");
debug!("found one in strip_hidden; removing");
return None;
},
_ => (),
@ -50,7 +47,7 @@ pub fn strip_hidden(crate: clean::Crate) -> plugins::PluginResult {
(crate, None)
}
pub fn clean_comments(crate: clean::Crate) -> plugins::PluginResult {
pub fn unindent_comments(crate: clean::Crate) -> plugins::PluginResult {
struct CommentCleaner;
impl fold::DocFolder for CommentCleaner {
fn fold_item(&mut self, i: Item) -> Option<Item> {
@ -59,7 +56,7 @@ pub fn clean_comments(crate: clean::Crate) -> plugins::PluginResult {
for attr in i.attrs.iter() {
match attr {
&clean::NameValue(~"doc", ref s) => avec.push(
clean::NameValue(~"doc", clean_comment_body(s.clone()))),
clean::NameValue(~"doc", unindent(*s))),
x => avec.push(x.clone())
}
}
@ -124,80 +121,111 @@ pub fn collapse_docs(crate: clean::Crate) -> plugins::PluginResult {
(crate, None)
}
//Utility
enum CleanCommentStates {
Collect,
Strip,
Stripped,
}
// n.b. this is copied from src/librustdoc/unindent_pass.rs
pub fn unindent(s: &str) -> ~str {
let lines = s.any_line_iter().collect::<~[&str]>();
let mut saw_first_line = false;
let mut saw_second_line = false;
let min_indent = do lines.iter().fold(uint::max_value) |min_indent, line| {
/// Returns the index of the last character all strings have common in their
/// prefix.
fn longest_common_prefix(s: ~[~str]) -> uint {
// find the longest common prefix
// After we see the first non-whitespace line, look at
// the line we have. If it is not whitespace, and therefore
// part of the first paragraph, then ignore the indentation
// level of the first line
let ignore_previous_indents =
saw_first_line &&
!saw_second_line &&
!line.is_whitespace();
debug!("lcp: looking into %?", s);
// index of the last character all the strings share
let mut index = 0u;
if s.len() <= 1 {
return 0;
}
// whether one of the strings has been exhausted of characters yet
let mut exhausted = false;
// character iterators for all the lines
let mut lines = s.iter().filter(|x| x.len() != 0).map(|x| x.iter()).to_owned_vec();
'outer: loop {
// because you can't label a while loop
if exhausted == true {
break;
}
debug!("lcp: index %u", index);
let mut lines = lines.mut_iter();
let ch = match lines.next().unwrap().next() {
Some(c) => c,
None => { exhausted = true; loop },
let min_indent = if ignore_previous_indents {
uint::max_value
} else {
min_indent
};
debug!("looking for char %c", ch);
for line in lines {
match line.next() {
Some(c) => if c == ch { loop } else { exhausted = true; loop 'outer },
None => { exhausted = true; loop 'outer }
}
}
index += 1;
}
debug!("lcp: last index %u", index);
index
if saw_first_line {
saw_second_line = true;
}
if line.is_whitespace() {
min_indent
} else {
saw_first_line = true;
let mut spaces = 0;
do line.iter().all |char| {
// Only comparing against space because I wouldn't
// know what to do with mixed whitespace chars
if char == ' ' {
spaces += 1;
true
} else {
false
}
};
num::min(min_indent, spaces)
}
};
match lines {
[head, .. tail] => {
let mut unindented = ~[ head.trim() ];
unindented.push_all(do tail.map |&line| {
if line.is_whitespace() {
line
} else {
assert!(line.len() >= min_indent);
line.slice_from(min_indent)
}
});
unindented.connect("\n")
}
[] => s.to_owned()
}
}
fn clean_comment_body(s: ~str) -> ~str {
// FIXME #31: lots of copies in here.
let lines = s.line_iter().to_owned_vec();
match lines.len() {
0 => return ~"",
1 => return lines[0].slice_from(2).trim().to_owned(),
_ => (),
#[cfg(test)]
mod unindent_tests {
use super::unindent;
#[test]
fn should_unindent() {
let s = ~" line1\n line2";
let r = unindent(s);
assert_eq!(r, ~"line1\nline2");
}
let mut ol = std::vec::with_capacity(lines.len());
for line in lines.clone().move_iter() {
// replace meaningless things with a single newline
match line {
x if ["/**", "/*!", "///", "//!", "*/"].contains(&x.trim()) => ol.push(~""),
x if x.trim() == "" => ol.push(~""),
x => ol.push(x.to_owned())
}
#[test]
fn should_unindent_multiple_paragraphs() {
let s = ~" line1\n\n line2";
let r = unindent(s);
assert_eq!(r, ~"line1\n\nline2");
}
let li = longest_common_prefix(ol.clone());
let x = ol.iter()
.filter(|x| { debug!("cleaning line: %s", **x); true })
.map(|x| if x.len() == 0 { ~"" } else { x.slice_chars(li, x.char_len()).to_owned() })
.to_owned_vec().connect("\n");
x.trim().to_owned()
#[test]
fn should_leave_multiple_indent_levels() {
// Line 2 is indented another level beyond the
// base indentation and should be preserved
let s = ~" line1\n\n line2";
let r = unindent(s);
assert_eq!(r, ~"line1\n\n line2");
}
#[test]
fn should_ignore_first_line_indent() {
// Thi first line of the first paragraph may not be indented as
// far due to the way the doc string was written:
//
// #[doc = "Start way over here
// and continue here"]
let s = ~"line1\n line2";
let r = unindent(s);
assert_eq!(r, ~"line1\nline2");
}
#[test]
fn should_not_ignore_first_line_indent_in_a_single_line_para() {
let s = ~"line1\n\n line2";
let r = unindent(s);
assert_eq!(r, ~"line1\n\n line2");
}
}

View File

@ -22,20 +22,34 @@ extern mod rustc;
extern mod extra;
use extra::serialize::Encodable;
use extra::time;
use std::cell::Cell;
use std::rt::io;
use std::rt::io::Writer;
use std::rt::io::file::FileInfo;
pub mod clean;
pub mod core;
pub mod doctree;
pub mod clean;
pub mod visit_ast;
pub mod fold;
pub mod plugins;
pub mod html {
pub mod render;
pub mod layout;
pub mod markdown;
pub mod format;
}
pub mod passes;
pub mod plugins;
pub mod visit_ast;
pub static SCHEMA_VERSION: &'static str = "0.8.0";
local_data_key!(pub ctxtkey: @core::DocContext)
enum OutputFormat {
HTML, JSON
}
pub fn main() {
main_args(std::os::args());
}
@ -44,65 +58,132 @@ pub fn main_args(args: &[~str]) {
use extra::getopts::groups::*;
let opts = ~[
optmulti("L", "library-path", "directory to add to crate search path", "DIR"),
optmulti("p", "plugin", "plugin to load and run", "NAME"),
optmulti("L", "library-path", "directory to add to crate search path",
"DIR"),
optmulti("", "plugin-path", "directory to load plugins from", "DIR"),
// auxillary pass (defaults to hidden_strip
optmulti("a", "pass", "auxillary pass to run", "NAME"),
optflag("n", "no-defult-passes", "do not run the default passes"),
optmulti("", "passes", "space separated list of passes to also run",
"PASSES"),
optmulti("", "plugins", "space separated list of plugins to also load",
"PLUGINS"),
optflag("h", "help", "show this help message"),
optflag("", "nodefaults", "don't run the default passes"),
optopt("o", "output", "where to place the output", "PATH"),
];
let matches = getopts(args.tail(), opts).unwrap();
if matches.opt_present("h") || matches.opt_present("help") {
println(usage(args[0], opts));
return;
}
let libs = Cell::new(matches.opt_strs("L").map(|s| Path(*s)));
let mut passes = if matches.opt_present("n") {
~[]
} else {
~[~"collapse-docs", ~"clean-comments", ~"collapse-privacy" ]
let myusage = || {
println(usage(format!("{} [options] [html|json] <crate>", args[0]), opts));
};
matches.opt_strs("a").map(|x| passes.push(x.clone()));
if matches.free.len() != 1 {
println(usage(args[0], opts));
if matches.opt_present("h") || matches.opt_present("help") {
myusage();
return;
}
let cr = Cell::new(Path(matches.free[0]));
let (format, cratefile) = match matches.free.clone() {
[~"json", crate] => (JSON, crate),
[~"html", crate] => (HTML, crate),
[s, _] => {
println!("Unknown output format: `{}`", s);
myusage();
exit(1);
}
[_, .._] => {
println!("Expected exactly one crate to process");
myusage();
exit(1);
}
_ => {
println!("Expected an output format and then one crate");
myusage();
exit(1);
}
};
// First, parse the crate and extract all relevant information.
let libs = Cell::new(matches.opt_strs("L").map(|s| Path(*s)));
let cr = Cell::new(Path(cratefile));
info2!("starting to run rustc");
let crate = do std::task::try {
let cr = cr.take();
core::run_core(libs.take(), &cr)
}.unwrap();
info2!("finished with rustc");
// { "schema": version, "crate": { parsed crate ... }, "plugins": { output of plugins ... }}
let mut json = ~extra::treemap::TreeMap::new();
json.insert(~"schema", extra::json::String(SCHEMA_VERSION.to_owned()));
let mut pm = plugins::PluginManager::new(Path("/tmp/rustdoc_ng/plugins"));
for pass in passes.iter() {
pm.add_plugin(match pass.as_slice() {
"strip-hidden" => passes::strip_hidden,
"clean-comments" => passes::clean_comments,
"collapse-docs" => passes::collapse_docs,
"collapse-privacy" => passes::collapse_privacy,
s => { error!("unknown pass %s, skipping", s); passes::noop },
})
// Process all of the crate attributes, extracting plugin metadata along
// with the passes which we are supposed to run.
let mut default_passes = !matches.opt_present("nodefaults");
let mut passes = matches.opt_strs("passes");
let mut plugins = matches.opt_strs("plugins");
match crate.module.get_ref().doc_list() {
Some(nested) => {
for inner in nested.iter() {
match *inner {
clean::Word(~"no_default_passes") => {
default_passes = false;
}
clean::NameValue(~"passes", ref value) => {
for pass in value.word_iter() {
passes.push(pass.to_owned());
}
}
clean::NameValue(~"plugins", ref value) => {
for p in value.word_iter() {
plugins.push(p.to_owned());
}
}
_ => {}
}
}
}
None => {}
}
if default_passes {
passes.unshift(~"collapse-docs");
passes.unshift(~"unindent-comments");
}
for pname in matches.opt_strs("p").move_iter() {
// Load all plugins/passes into a PluginManager
let mut pm = plugins::PluginManager::new(Path("/tmp/rustdoc_ng/plugins"));
for pass in passes.iter() {
let plugin = match pass.as_slice() {
"strip-hidden" => passes::strip_hidden,
"unindent-comments" => passes::unindent_comments,
"collapse-docs" => passes::collapse_docs,
"collapse-privacy" => passes::collapse_privacy,
s => { error!("unknown pass %s, skipping", s); loop },
};
pm.add_plugin(plugin);
}
info2!("loading plugins...");
for pname in plugins.move_iter() {
pm.load_plugin(pname);
}
// Run everything!
info2!("Executing passes/plugins");
let (crate, res) = pm.run_plugins(crate);
info2!("going to format");
let started = time::precise_time_ns();
let output = matches.opt_str("o").map(|s| Path(*s));
match format {
HTML => { html::render::run(crate, output.unwrap_or(Path("doc"))) }
JSON => { jsonify(crate, res, output.unwrap_or(Path("doc.json"))) }
}
let ended = time::precise_time_ns();
info2!("Took {:.03f}s", (ended as f64 - started as f64) / 1000000000f64);
}
fn jsonify(crate: clean::Crate, res: ~[plugins::PluginJson], dst: Path) {
// {
// "schema": version,
// "crate": { parsed crate ... },
// "plugins": { output of plugins ... }
// }
let mut json = ~extra::treemap::TreeMap::new();
json.insert(~"schema", extra::json::String(SCHEMA_VERSION.to_owned()));
let plugins_json = ~res.move_iter().filter_map(|opt| opt).collect();
// FIXME #8335: yuck, Rust -> str -> JSON round trip! No way to .encode
@ -118,5 +199,13 @@ pub fn main_args(args: &[~str]) {
json.insert(~"crate", crate_json);
json.insert(~"plugins", extra::json::Object(plugins_json));
println(extra::json::Object(json).to_str());
let mut file = dst.open_writer(io::Create).unwrap();
let output = extra::json::Object(json).to_str();
file.write(output.as_bytes());
}
fn exit(status: int) -> ! {
#[fixed_stack_segment]; #[inline(never)];
use std::libc;
unsafe { libc::exit(status as libc::c_int) }
}