mirror of
https://github.com/rust-lang/rust.git
synced 2024-10-31 06:22:00 +00:00
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:
commit
db78fdc10c
@ -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)
|
||||
-----------------------
|
||||
|
15
mk/docs.mk
15
mk/docs.mk
@ -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
|
||||
|
||||
|
||||
|
@ -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"];
|
||||
|
@ -9,6 +9,7 @@
|
||||
// except according to those terms.
|
||||
|
||||
#[macro_escape];
|
||||
#[doc(hidden)];
|
||||
|
||||
macro_rules! rterrln (
|
||||
($( $arg:expr),+) => ( {
|
||||
|
@ -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 {
|
||||
|
||||
|
@ -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 {
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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];
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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("/*") {
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
364
src/rustdoc_ng/html/format.rs
Normal file
364
src/rustdoc_ng/html/format.rs
Normal 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("<".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(">".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("<".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(">".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("<");
|
||||
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(">");
|
||||
}
|
||||
|
||||
// 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("<".as_bytes());
|
||||
for (i, param) in params.iter().enumerate() {
|
||||
if i > 0 { f.buf.write(", ".as_bytes()) }
|
||||
write!(f.buf, "{}", *param);
|
||||
}
|
||||
f.buf.write(">".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 => "&",
|
||||
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, "&{}{}{}",
|
||||
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{ -> {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!("&{} self", *lt));
|
||||
}
|
||||
clean::SelfBorrowed(Some(ref lt), clean::Mutable) => {
|
||||
args.push_str(format!("&{} mut self", *lt));
|
||||
}
|
||||
clean::SelfBorrowed(None, clean::Mutable) => {
|
||||
args.push_str("&mut self");
|
||||
}
|
||||
clean::SelfBorrowed(None, clean::Immutable) => {
|
||||
args.push_str("&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{ -> {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 => {}
|
||||
}
|
||||
}
|
||||
}
|
130
src/rustdoc_ng/html/layout.rs
Normal file
130
src/rustdoc_ng/html/layout.rs
Normal 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>↑</dt>
|
||||
<dd>Move up in search results</dd>
|
||||
<dt>↓</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 }
|
||||
}
|
54
src/rustdoc_ng/html/markdown.rs
Normal file
54
src/rustdoc_ng/html/markdown.rs
Normal 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
1108
src/rustdoc_ng/html/render.rs
Normal file
1108
src/rustdoc_ng/html/render.rs
Normal file
File diff suppressed because it is too large
Load Diff
6
src/rustdoc_ng/html/static/jquery-2.0.3.min.js
vendored
Normal file
6
src/rustdoc_ng/html/static/jquery-2.0.3.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
270
src/rustdoc_ng/html/static/main.css
Normal file
270
src/rustdoc_ng/html/static/main.css
Normal 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;
|
||||
}
|
420
src/rustdoc_ng/html/static/main.js
Normal file
420
src/rustdoc_ng/html/static/main.js
Normal 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
396
src/rustdoc_ng/html/static/normalize.css
vendored
Normal 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;
|
||||
}
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
@ -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) }
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user