From 760e389fa64b3f36ab7d8c4941aedc4f9cbc4fc9 Mon Sep 17 00:00:00 2001
From: Guillaume Gomez
Date: Fri, 30 Mar 2018 11:19:49 +0200
Subject: [PATCH 1/3] Add page to list all crate's items
---
src/librustdoc/html/render.rs | 242 +++++++++++++++++---
src/librustdoc/html/static/rustdoc.css | 18 ++
src/librustdoc/html/static/themes/dark.css | 7 +
src/librustdoc/html/static/themes/light.css | 7 +
4 files changed, 238 insertions(+), 36 deletions(-)
diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs
index ff6cc56e5b4..0828f324a49 100644
--- a/src/librustdoc/html/render.rs
+++ b/src/librustdoc/html/render.rs
@@ -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, "{}", 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,
+ enums: HashSet,
+ unions: HashSet,
+ primitives: HashSet,
+ traits: HashSet,
+ macros: HashSet,
+ functions: HashSet,
+ typedefs: HashSet,
+ statics: HashSet,
+ constants: HashSet,
+}
+
+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: &str) {
+ 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 {
+ "struct" => self.structs.insert(ItemEntry::new(new_url, name)),
+ "enum" => self.enums.insert(ItemEntry::new(new_url, name)),
+ "union" => self.unions.insert(ItemEntry::new(new_url, name)),
+ "primitive" => self.primitives.insert(ItemEntry::new(new_url, name)),
+ "trait" => self.traits.insert(ItemEntry::new(new_url, name)),
+ "macro" => self.macros.insert(ItemEntry::new(new_url, name)),
+ "fn" => self.functions.insert(ItemEntry::new(new_url, name)),
+ "typedef" => self.typedefs.insert(ItemEntry::new(new_url, name)),
+ "static" => self.statics.insert(ItemEntry::new(new_url, name)),
+ "constant" => self.constants.insert(ItemEntry::new(new_url, name)),
+ _ => true,
+ };
+ }
+ }
+}
+
+fn print_entries(f: &mut fmt::Formatter, e: &HashSet, title: &str,
+ class: &str) -> fmt::Result {
+ if !e.is_empty() {
+ let mut e: Vec<&ItemEntry> = e.iter().collect();
+ e.sort();
+ write!(f, "{}
",
+ title,
+ Escape(title),
+ class,
+ e.iter().map(|s| format!("{}", s)).collect::())?;
+ }
+ Ok(())
+}
+
+impl fmt::Display for AllTypes {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f,
+"\
+ List of all items\
+ \
+ \
+ \
+ [−]\
+ \
+
+
+
")?;
+ 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(File::create(&final_file)
+ .expect("failed to create all.html"));
+ 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!("Crate {}
\
+ \
+ Back to index
",
+ crate_name, version)
+ } else {
+ String::new()
+ };
+ layout::render(&mut w, &self.shared.layout,
+ &page, &sidebar, &all,
+ self.shared.css_file_extension.is_some(),
+ &self.shared.themes).expect("layout rendering failed");
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(&mut self, item: clean::Item, mut f: F) -> Result<(), Error> where
- F: FnMut(&mut Context, clean::Item),
+ fn item(&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.css_class());
// 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##"
-
- [−]
-
- "##)?;
+ "\
+ \
+ [−]\
+ \
+ ")?;
// Write `src` tag
//
@@ -3540,24 +3708,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, "")?;
- 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, "
{}{}
",
+ 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, "
")?;
+ it.name.as_ref().unwrap())?;
}
if it.is_crate() {
@@ -3565,8 +3732,11 @@ impl<'a> fmt::Display for Sidebar<'a> {
write!(fmt,
"",
- version)?;
+
+ See all {}'s items
",
+ version,
+ cx.shared.resource_suffix,
+ it.name.as_ref().unwrap())?;
}
}
diff --git a/src/librustdoc/html/static/rustdoc.css b/src/librustdoc/html/static/rustdoc.css
index 31a501d24e7..cea1e893632 100644
--- a/src/librustdoc/html/static/rustdoc.css
+++ b/src/librustdoc/html/static/rustdoc.css
@@ -1291,3 +1291,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;
+}
\ No newline at end of file
diff --git a/src/librustdoc/html/static/themes/dark.css b/src/librustdoc/html/static/themes/dark.css
index 2d0fe55f70d..f43a8598f33 100644
--- a/src/librustdoc/html/static/themes/dark.css
+++ b/src/librustdoc/html/static/themes/dark.css
@@ -389,3 +389,10 @@ kbd {
background: #f0f0f0;
}
}
+
+#all-types {
+ background-color: #505050;
+}
+#all-types:hover {
+ background-color: #606060;
+}
diff --git a/src/librustdoc/html/static/themes/light.css b/src/librustdoc/html/static/themes/light.css
index 2334a272855..e13818b4bd2 100644
--- a/src/librustdoc/html/static/themes/light.css
+++ b/src/librustdoc/html/static/themes/light.css
@@ -383,3 +383,10 @@ kbd {
background: #fff;
}
}
+
+#all-types {
+ background-color: #fff;
+}
+#all-types:hover {
+ background-color: #f9f9f9;
+}
\ No newline at end of file
From 43815a508c2ed924e1616138f8a7915caa79d4ba Mon Sep 17 00:00:00 2001
From: Guillaume Gomez
Date: Tue, 3 Apr 2018 00:56:33 +0200
Subject: [PATCH 2/3] Add test for all.html
---
src/test/rustdoc/all.rs | 30 ++++++++++++++++++++++++++++++
1 file changed, 30 insertions(+)
create mode 100644 src/test/rustdoc/all.rs
diff --git a/src/test/rustdoc/all.rs b/src/test/rustdoc/all.rs
new file mode 100644
index 00000000000..ec391319b18
--- /dev/null
+++ b/src/test/rustdoc/all.rs
@@ -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 or the MIT license
+// , 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() {}
From 1292e51e73586f6d8297a3987ab3d3de425b83e1 Mon Sep 17 00:00:00 2001
From: Guillaume Gomez
Date: Sat, 7 Apr 2018 13:10:49 +0200
Subject: [PATCH 3/3] Improve code
---
src/librustdoc/html/render.rs | 41 +++++++++++++++++------------------
1 file changed, 20 insertions(+), 21 deletions(-)
diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs
index 0828f324a49..59395648221 100644
--- a/src/librustdoc/html/render.rs
+++ b/src/librustdoc/html/render.rs
@@ -1440,23 +1440,23 @@ impl AllTypes {
}
}
- fn append(&mut self, item_name: String, item_type: &str) {
+ 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 {
- "struct" => self.structs.insert(ItemEntry::new(new_url, name)),
- "enum" => self.enums.insert(ItemEntry::new(new_url, name)),
- "union" => self.unions.insert(ItemEntry::new(new_url, name)),
- "primitive" => self.primitives.insert(ItemEntry::new(new_url, name)),
- "trait" => self.traits.insert(ItemEntry::new(new_url, name)),
- "macro" => self.macros.insert(ItemEntry::new(new_url, name)),
- "fn" => self.functions.insert(ItemEntry::new(new_url, name)),
- "typedef" => self.typedefs.insert(ItemEntry::new(new_url, name)),
- "static" => self.statics.insert(ItemEntry::new(new_url, name)),
- "constant" => self.constants.insert(ItemEntry::new(new_url, name)),
+ 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,
};
}
@@ -1562,8 +1562,7 @@ impl Context {
}
}
- let mut w = BufWriter::new(File::create(&final_file)
- .expect("failed to create all.html"));
+ 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('/');
@@ -1586,10 +1585,11 @@ impl Context {
} else {
String::new()
};
- layout::render(&mut w, &self.shared.layout,
- &page, &sidebar, &all,
- self.shared.css_file_extension.is_some(),
- &self.shared.themes).expect("layout rendering failed");
+ 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(())
}
@@ -1728,7 +1728,7 @@ 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.css_class());
+ 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());
@@ -3733,9 +3733,8 @@ impl<'a> fmt::Display for Sidebar<'a> {
"
- See all {}'s items
",
+ See all {}'s items
",
version,
- cx.shared.resource_suffix,
it.name.as_ref().unwrap())?;
}
}