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 {}

    \ +
    \ +

    Version {}

    \ +
    \ +

    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 {}

    \ -
    ", - 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> { "
    \

    Version {}

    \
    -

    See all {}'s items

    ", +

    See all {}'s items

    ", version, - cx.shared.resource_suffix, it.name.as_ref().unwrap())?; } }