Auto merge of #49504 - GuillaumeGomez:doc-all-types, r=QuietMisdreavus

Add page to list all crate's items

r? @QuietMisdreavus
This commit is contained in:
bors 2018-04-10 06:23:52 +00:00
commit a1c21ed7e2
5 changed files with 267 additions and 36 deletions

View File

@ -1087,7 +1087,8 @@ impl<'a> SourceCollector<'a> {
href.push_str(component);
href.push('/');
});
let mut fname = p.file_name().expect("source has no filename")
let mut fname = p.file_name()
.expect("source has no filename")
.to_os_string();
fname.push(".html");
cur.push(&fname);
@ -1373,6 +1374,135 @@ impl<'a> Cache {
}
}
#[derive(Debug, Eq, PartialEq, Hash)]
struct ItemEntry {
url: String,
name: String,
}
impl ItemEntry {
fn new(mut url: String, name: String) -> ItemEntry {
while url.starts_with('/') {
url.remove(0);
}
ItemEntry {
url,
name,
}
}
}
impl fmt::Display for ItemEntry {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "<a href='{}'>{}</a>", self.url, Escape(&self.name))
}
}
impl PartialOrd for ItemEntry {
fn partial_cmp(&self, other: &ItemEntry) -> Option<::std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for ItemEntry {
fn cmp(&self, other: &ItemEntry) -> ::std::cmp::Ordering {
self.name.cmp(&other.name)
}
}
#[derive(Debug)]
struct AllTypes {
structs: HashSet<ItemEntry>,
enums: HashSet<ItemEntry>,
unions: HashSet<ItemEntry>,
primitives: HashSet<ItemEntry>,
traits: HashSet<ItemEntry>,
macros: HashSet<ItemEntry>,
functions: HashSet<ItemEntry>,
typedefs: HashSet<ItemEntry>,
statics: HashSet<ItemEntry>,
constants: HashSet<ItemEntry>,
}
impl AllTypes {
fn new() -> AllTypes {
AllTypes {
structs: HashSet::with_capacity(100),
enums: HashSet::with_capacity(100),
unions: HashSet::with_capacity(100),
primitives: HashSet::with_capacity(26),
traits: HashSet::with_capacity(100),
macros: HashSet::with_capacity(100),
functions: HashSet::with_capacity(100),
typedefs: HashSet::with_capacity(100),
statics: HashSet::with_capacity(100),
constants: HashSet::with_capacity(100),
}
}
fn append(&mut self, item_name: String, item_type: &ItemType) {
let mut url: Vec<_> = item_name.split("::").skip(1).collect();
if let Some(name) = url.pop() {
let new_url = format!("{}/{}.{}.html", url.join("/"), item_type, name);
url.push(name);
let name = url.join("::");
match *item_type {
ItemType::Struct => self.structs.insert(ItemEntry::new(new_url, name)),
ItemType::Enum => self.enums.insert(ItemEntry::new(new_url, name)),
ItemType::Union => self.unions.insert(ItemEntry::new(new_url, name)),
ItemType::Primitive => self.primitives.insert(ItemEntry::new(new_url, name)),
ItemType::Trait => self.traits.insert(ItemEntry::new(new_url, name)),
ItemType::Macro => self.macros.insert(ItemEntry::new(new_url, name)),
ItemType::Function => self.functions.insert(ItemEntry::new(new_url, name)),
ItemType::Typedef => self.typedefs.insert(ItemEntry::new(new_url, name)),
ItemType::Static => self.statics.insert(ItemEntry::new(new_url, name)),
ItemType::Constant => self.constants.insert(ItemEntry::new(new_url, name)),
_ => true,
};
}
}
}
fn print_entries(f: &mut fmt::Formatter, e: &HashSet<ItemEntry>, title: &str,
class: &str) -> fmt::Result {
if !e.is_empty() {
let mut e: Vec<&ItemEntry> = e.iter().collect();
e.sort();
write!(f, "<h3 id='{}'>{}</h3><ul class='{} docblock'>{}</ul>",
title,
Escape(title),
class,
e.iter().map(|s| format!("<li>{}</li>", s)).collect::<String>())?;
}
Ok(())
}
impl fmt::Display for AllTypes {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f,
"<h1 class='fqn'>\
<span class='in-band'>List of all items</span>\
<span class='out-of-band'>\
<span id='render-detail'>\
<a id=\"toggle-all-docs\" href=\"javascript:void(0)\" title=\"collapse all docs\">\
[<span class='inner'>&#x2212;</span>]\
</a>\
</span>
</span>
</h1>")?;
print_entries(f, &self.structs, "Structs", "structs")?;
print_entries(f, &self.enums, "Enums", "enums")?;
print_entries(f, &self.unions, "Unions", "unions")?;
print_entries(f, &self.primitives, "Primitives", "primitives")?;
print_entries(f, &self.traits, "Traits", "traits")?;
print_entries(f, &self.macros, "Macros", "macros")?;
print_entries(f, &self.functions, "Functions", "functions")?;
print_entries(f, &self.typedefs, "Typedefs", "typedefs")?;
print_entries(f, &self.statics, "Statics", "statics")?;
print_entries(f, &self.constants, "Constants", "constants")
}
}
impl Context {
/// String representation of how to get back to the root path of the 'doc/'
/// folder in terms of a relative URL.
@ -1414,16 +1544,52 @@ impl Context {
Some(i) => i,
None => return Ok(()),
};
let final_file = self.dst.join(&krate.name)
.join("all.html");
let crate_name = krate.name.clone();
item.name = Some(krate.name);
// Render the crate documentation
let mut work = vec![(self, item)];
let mut all = AllTypes::new();
while let Some((mut cx, item)) = work.pop() {
cx.item(item, |cx, item| {
work.push((cx.clone(), item))
})?
{
// Render the crate documentation
let mut work = vec![(self.clone(), item)];
while let Some((mut cx, item)) = work.pop() {
cx.item(item, &mut all, |cx, item| {
work.push((cx.clone(), item))
})?
}
}
let mut w = BufWriter::new(try_err!(File::create(&final_file), &final_file));
let mut root_path = self.dst.to_str().expect("invalid path").to_owned();
if !root_path.ends_with('/') {
root_path.push('/');
}
let page = layout::Page {
title: "List of all items in this crate",
css_class: "mod",
root_path: "../",
description: "List of all items in this crate",
keywords: BASIC_KEYWORDS,
resource_suffix: &self.shared.resource_suffix,
};
let sidebar = if let Some(ref version) = cache().crate_version {
format!("<p class='location'>Crate {}</p>\
<div class='block version'>\
<p>Version {}</p>\
</div>\
<a id='all-types' href='index.html'><p>Back to index</p></a>",
crate_name, version)
} else {
String::new()
};
try_err!(layout::render(&mut w, &self.shared.layout,
&page, &sidebar, &all,
self.shared.css_file_extension.is_some(),
&self.shared.themes),
&final_file);
Ok(())
}
@ -1496,8 +1662,8 @@ impl Context {
/// all sub-items which need to be rendered.
///
/// The rendering driver uses this closure to queue up more work.
fn item<F>(&mut self, item: clean::Item, mut f: F) -> Result<(), Error> where
F: FnMut(&mut Context, clean::Item),
fn item<F>(&mut self, item: clean::Item, all: &mut AllTypes, mut f: F) -> Result<(), Error>
where F: FnMut(&mut Context, clean::Item),
{
// Stripped modules survive the rustdoc passes (i.e. `strip-private`)
// if they contain impls for public types. These modules can also
@ -1544,7 +1710,7 @@ impl Context {
}
for item in m.items {
f(this,item);
f(this, item);
}
Ok(())
@ -1562,13 +1728,14 @@ impl Context {
let mut dst = try_err!(File::create(&joint_dst), &joint_dst);
try_err!(dst.write_all(&buf), &joint_dst);
all.append(full_path(self, &item), &item_type);
// Redirect from a sane URL using the namespace to Rustdoc's
// URL for the page.
let redir_name = format!("{}.{}.html", name, item_type.name_space());
let redir_dst = self.dst.join(redir_name);
if let Ok(redirect_out) = OpenOptions::new().create_new(true)
.write(true)
.open(&redir_dst) {
.write(true)
.open(&redir_dst) {
let mut redirect_out = BufWriter::new(redirect_out);
try_err!(layout::redirect(&mut redirect_out, file_name), &redir_dst);
}
@ -1730,11 +1897,12 @@ impl<'a> fmt::Display for Item<'a> {
version)?;
}
write!(fmt,
r##"<span id='render-detail'>
<a id="toggle-all-docs" href="javascript:void(0)" title="collapse all docs">
[<span class='inner'>&#x2212;</span>]
</a>
</span>"##)?;
"<span id='render-detail'>\
<a id=\"toggle-all-docs\" href=\"javascript:void(0)\" \
title=\"collapse all docs\">\
[<span class='inner'>&#x2212;</span>]\
</a>\
</span>")?;
// Write `src` tag
//
@ -3567,24 +3735,23 @@ impl<'a> fmt::Display for Sidebar<'a> {
if it.is_struct() || it.is_trait() || it.is_primitive() || it.is_union()
|| it.is_enum() || it.is_mod() || it.is_typedef() {
write!(fmt, "<p class='location'>")?;
match it.inner {
clean::StructItem(..) => write!(fmt, "Struct ")?,
clean::TraitItem(..) => write!(fmt, "Trait ")?,
clean::PrimitiveItem(..) => write!(fmt, "Primitive Type ")?,
clean::UnionItem(..) => write!(fmt, "Union ")?,
clean::EnumItem(..) => write!(fmt, "Enum ")?,
clean::TypedefItem(..) => write!(fmt, "Type Definition ")?,
clean::ForeignTypeItem => write!(fmt, "Foreign Type ")?,
clean::ModuleItem(..) => if it.is_crate() {
write!(fmt, "Crate ")?;
} else {
write!(fmt, "Module ")?;
write!(fmt, "<p class='location'>{}{}</p>",
match it.inner {
clean::StructItem(..) => "Struct ",
clean::TraitItem(..) => "Trait ",
clean::PrimitiveItem(..) => "Primitive Type ",
clean::UnionItem(..) => "Union ",
clean::EnumItem(..) => "Enum ",
clean::TypedefItem(..) => "Type Definition ",
clean::ForeignTypeItem => "Foreign Type ",
clean::ModuleItem(..) => if it.is_crate() {
"Crate "
} else {
"Module "
},
_ => "",
},
_ => (),
}
write!(fmt, "{}", it.name.as_ref().unwrap())?;
write!(fmt, "</p>")?;
it.name.as_ref().unwrap())?;
}
if it.is_crate() {
@ -3592,8 +3759,10 @@ impl<'a> fmt::Display for Sidebar<'a> {
write!(fmt,
"<div class='block version'>\
<p>Version {}</p>\
</div>",
version)?;
</div>
<a id='all-types' href='all.html'><p>See all {}'s items</p></a>",
version,
it.name.as_ref().unwrap())?;
}
}

View File

@ -1294,3 +1294,21 @@ kbd {
font-size: 19px;
display: block;
}
#main > ul {
padding-left: 10px;
}
#main > ul > li {
list-style: none;
}
#all-types {
text-align: center;
border: 1px solid;
margin: 0 10px;
margin-bottom: 10px;
display: block;
border-radius: 7px;
}
#all-types > p {
margin: 5px 0;
}

View File

@ -389,3 +389,10 @@ kbd {
background: #f0f0f0;
}
}
#all-types {
background-color: #505050;
}
#all-types:hover {
background-color: #606060;
}

View File

@ -383,3 +383,10 @@ kbd {
background: #fff;
}
}
#all-types {
background-color: #fff;
}
#all-types:hover {
background-color: #f9f9f9;
}

30
src/test/rustdoc/all.rs Normal file
View File

@ -0,0 +1,30 @@
// Copyright 2018 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.
#![crate_name = "foo"]
// @has foo/all.html '//a[@href="struct.Struct.html"]' 'Struct'
// @has foo/all.html '//a[@href="enum.Enum.html"]' 'Enum'
// @has foo/all.html '//a[@href="union.Union.html"]' 'Union'
// @has foo/all.html '//a[@href="constant.CONST.html"]' 'CONST'
// @has foo/all.html '//a[@href="static.STATIC.html"]' 'STATIC'
// @has foo/all.html '//a[@href="fn.function.html"]' 'function'
pub struct Struct;
pub enum Enum {
X,
Y,
}
pub union Union {
x: u32,
}
pub const CONST: u32 = 0;
pub static STATIC: &str = "baguette";
pub fn function() {}