Skip to content

Commit a1c21ed

Browse files
committed
Auto merge of #49504 - GuillaumeGomez:doc-all-types, r=QuietMisdreavus
Add page to list all crate's items r? @QuietMisdreavus
2 parents 5d7f892 + 1292e51 commit a1c21ed

File tree

5 files changed

+267
-36
lines changed

5 files changed

+267
-36
lines changed

src/librustdoc/html/render.rs

+205-36
Original file line numberDiff line numberDiff line change
@@ -1087,7 +1087,8 @@ impl<'a> SourceCollector<'a> {
10871087
href.push_str(component);
10881088
href.push('/');
10891089
});
1090-
let mut fname = p.file_name().expect("source has no filename")
1090+
let mut fname = p.file_name()
1091+
.expect("source has no filename")
10911092
.to_os_string();
10921093
fname.push(".html");
10931094
cur.push(&fname);
@@ -1373,6 +1374,135 @@ impl<'a> Cache {
13731374
}
13741375
}
13751376

1377+
#[derive(Debug, Eq, PartialEq, Hash)]
1378+
struct ItemEntry {
1379+
url: String,
1380+
name: String,
1381+
}
1382+
1383+
impl ItemEntry {
1384+
fn new(mut url: String, name: String) -> ItemEntry {
1385+
while url.starts_with('/') {
1386+
url.remove(0);
1387+
}
1388+
ItemEntry {
1389+
url,
1390+
name,
1391+
}
1392+
}
1393+
}
1394+
1395+
impl fmt::Display for ItemEntry {
1396+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1397+
write!(f, "<a href='{}'>{}</a>", self.url, Escape(&self.name))
1398+
}
1399+
}
1400+
1401+
impl PartialOrd for ItemEntry {
1402+
fn partial_cmp(&self, other: &ItemEntry) -> Option<::std::cmp::Ordering> {
1403+
Some(self.cmp(other))
1404+
}
1405+
}
1406+
1407+
impl Ord for ItemEntry {
1408+
fn cmp(&self, other: &ItemEntry) -> ::std::cmp::Ordering {
1409+
self.name.cmp(&other.name)
1410+
}
1411+
}
1412+
1413+
#[derive(Debug)]
1414+
struct AllTypes {
1415+
structs: HashSet<ItemEntry>,
1416+
enums: HashSet<ItemEntry>,
1417+
unions: HashSet<ItemEntry>,
1418+
primitives: HashSet<ItemEntry>,
1419+
traits: HashSet<ItemEntry>,
1420+
macros: HashSet<ItemEntry>,
1421+
functions: HashSet<ItemEntry>,
1422+
typedefs: HashSet<ItemEntry>,
1423+
statics: HashSet<ItemEntry>,
1424+
constants: HashSet<ItemEntry>,
1425+
}
1426+
1427+
impl AllTypes {
1428+
fn new() -> AllTypes {
1429+
AllTypes {
1430+
structs: HashSet::with_capacity(100),
1431+
enums: HashSet::with_capacity(100),
1432+
unions: HashSet::with_capacity(100),
1433+
primitives: HashSet::with_capacity(26),
1434+
traits: HashSet::with_capacity(100),
1435+
macros: HashSet::with_capacity(100),
1436+
functions: HashSet::with_capacity(100),
1437+
typedefs: HashSet::with_capacity(100),
1438+
statics: HashSet::with_capacity(100),
1439+
constants: HashSet::with_capacity(100),
1440+
}
1441+
}
1442+
1443+
fn append(&mut self, item_name: String, item_type: &ItemType) {
1444+
let mut url: Vec<_> = item_name.split("::").skip(1).collect();
1445+
if let Some(name) = url.pop() {
1446+
let new_url = format!("{}/{}.{}.html", url.join("/"), item_type, name);
1447+
url.push(name);
1448+
let name = url.join("::");
1449+
match *item_type {
1450+
ItemType::Struct => self.structs.insert(ItemEntry::new(new_url, name)),
1451+
ItemType::Enum => self.enums.insert(ItemEntry::new(new_url, name)),
1452+
ItemType::Union => self.unions.insert(ItemEntry::new(new_url, name)),
1453+
ItemType::Primitive => self.primitives.insert(ItemEntry::new(new_url, name)),
1454+
ItemType::Trait => self.traits.insert(ItemEntry::new(new_url, name)),
1455+
ItemType::Macro => self.macros.insert(ItemEntry::new(new_url, name)),
1456+
ItemType::Function => self.functions.insert(ItemEntry::new(new_url, name)),
1457+
ItemType::Typedef => self.typedefs.insert(ItemEntry::new(new_url, name)),
1458+
ItemType::Static => self.statics.insert(ItemEntry::new(new_url, name)),
1459+
ItemType::Constant => self.constants.insert(ItemEntry::new(new_url, name)),
1460+
_ => true,
1461+
};
1462+
}
1463+
}
1464+
}
1465+
1466+
fn print_entries(f: &mut fmt::Formatter, e: &HashSet<ItemEntry>, title: &str,
1467+
class: &str) -> fmt::Result {
1468+
if !e.is_empty() {
1469+
let mut e: Vec<&ItemEntry> = e.iter().collect();
1470+
e.sort();
1471+
write!(f, "<h3 id='{}'>{}</h3><ul class='{} docblock'>{}</ul>",
1472+
title,
1473+
Escape(title),
1474+
class,
1475+
e.iter().map(|s| format!("<li>{}</li>", s)).collect::<String>())?;
1476+
}
1477+
Ok(())
1478+
}
1479+
1480+
impl fmt::Display for AllTypes {
1481+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1482+
write!(f,
1483+
"<h1 class='fqn'>\
1484+
<span class='in-band'>List of all items</span>\
1485+
<span class='out-of-band'>\
1486+
<span id='render-detail'>\
1487+
<a id=\"toggle-all-docs\" href=\"javascript:void(0)\" title=\"collapse all docs\">\
1488+
[<span class='inner'>&#x2212;</span>]\
1489+
</a>\
1490+
</span>
1491+
</span>
1492+
</h1>")?;
1493+
print_entries(f, &self.structs, "Structs", "structs")?;
1494+
print_entries(f, &self.enums, "Enums", "enums")?;
1495+
print_entries(f, &self.unions, "Unions", "unions")?;
1496+
print_entries(f, &self.primitives, "Primitives", "primitives")?;
1497+
print_entries(f, &self.traits, "Traits", "traits")?;
1498+
print_entries(f, &self.macros, "Macros", "macros")?;
1499+
print_entries(f, &self.functions, "Functions", "functions")?;
1500+
print_entries(f, &self.typedefs, "Typedefs", "typedefs")?;
1501+
print_entries(f, &self.statics, "Statics", "statics")?;
1502+
print_entries(f, &self.constants, "Constants", "constants")
1503+
}
1504+
}
1505+
13761506
impl Context {
13771507
/// String representation of how to get back to the root path of the 'doc/'
13781508
/// folder in terms of a relative URL.
@@ -1414,16 +1544,52 @@ impl Context {
14141544
Some(i) => i,
14151545
None => return Ok(()),
14161546
};
1547+
let final_file = self.dst.join(&krate.name)
1548+
.join("all.html");
1549+
let crate_name = krate.name.clone();
14171550
item.name = Some(krate.name);
14181551

1419-
// Render the crate documentation
1420-
let mut work = vec![(self, item)];
1552+
let mut all = AllTypes::new();
14211553

1422-
while let Some((mut cx, item)) = work.pop() {
1423-
cx.item(item, |cx, item| {
1424-
work.push((cx.clone(), item))
1425-
})?
1554+
{
1555+
// Render the crate documentation
1556+
let mut work = vec![(self.clone(), item)];
1557+
1558+
while let Some((mut cx, item)) = work.pop() {
1559+
cx.item(item, &mut all, |cx, item| {
1560+
work.push((cx.clone(), item))
1561+
})?
1562+
}
14261563
}
1564+
1565+
let mut w = BufWriter::new(try_err!(File::create(&final_file), &final_file));
1566+
let mut root_path = self.dst.to_str().expect("invalid path").to_owned();
1567+
if !root_path.ends_with('/') {
1568+
root_path.push('/');
1569+
}
1570+
let page = layout::Page {
1571+
title: "List of all items in this crate",
1572+
css_class: "mod",
1573+
root_path: "../",
1574+
description: "List of all items in this crate",
1575+
keywords: BASIC_KEYWORDS,
1576+
resource_suffix: &self.shared.resource_suffix,
1577+
};
1578+
let sidebar = if let Some(ref version) = cache().crate_version {
1579+
format!("<p class='location'>Crate {}</p>\
1580+
<div class='block version'>\
1581+
<p>Version {}</p>\
1582+
</div>\
1583+
<a id='all-types' href='index.html'><p>Back to index</p></a>",
1584+
crate_name, version)
1585+
} else {
1586+
String::new()
1587+
};
1588+
try_err!(layout::render(&mut w, &self.shared.layout,
1589+
&page, &sidebar, &all,
1590+
self.shared.css_file_extension.is_some(),
1591+
&self.shared.themes),
1592+
&final_file);
14271593
Ok(())
14281594
}
14291595

@@ -1496,8 +1662,8 @@ impl Context {
14961662
/// all sub-items which need to be rendered.
14971663
///
14981664
/// The rendering driver uses this closure to queue up more work.
1499-
fn item<F>(&mut self, item: clean::Item, mut f: F) -> Result<(), Error> where
1500-
F: FnMut(&mut Context, clean::Item),
1665+
fn item<F>(&mut self, item: clean::Item, all: &mut AllTypes, mut f: F) -> Result<(), Error>
1666+
where F: FnMut(&mut Context, clean::Item),
15011667
{
15021668
// Stripped modules survive the rustdoc passes (i.e. `strip-private`)
15031669
// if they contain impls for public types. These modules can also
@@ -1544,7 +1710,7 @@ impl Context {
15441710
}
15451711

15461712
for item in m.items {
1547-
f(this,item);
1713+
f(this, item);
15481714
}
15491715

15501716
Ok(())
@@ -1562,13 +1728,14 @@ impl Context {
15621728
let mut dst = try_err!(File::create(&joint_dst), &joint_dst);
15631729
try_err!(dst.write_all(&buf), &joint_dst);
15641730

1731+
all.append(full_path(self, &item), &item_type);
15651732
// Redirect from a sane URL using the namespace to Rustdoc's
15661733
// URL for the page.
15671734
let redir_name = format!("{}.{}.html", name, item_type.name_space());
15681735
let redir_dst = self.dst.join(redir_name);
15691736
if let Ok(redirect_out) = OpenOptions::new().create_new(true)
1570-
.write(true)
1571-
.open(&redir_dst) {
1737+
.write(true)
1738+
.open(&redir_dst) {
15721739
let mut redirect_out = BufWriter::new(redirect_out);
15731740
try_err!(layout::redirect(&mut redirect_out, file_name), &redir_dst);
15741741
}
@@ -1730,11 +1897,12 @@ impl<'a> fmt::Display for Item<'a> {
17301897
version)?;
17311898
}
17321899
write!(fmt,
1733-
r##"<span id='render-detail'>
1734-
<a id="toggle-all-docs" href="javascript:void(0)" title="collapse all docs">
1735-
[<span class='inner'>&#x2212;</span>]
1736-
</a>
1737-
</span>"##)?;
1900+
"<span id='render-detail'>\
1901+
<a id=\"toggle-all-docs\" href=\"javascript:void(0)\" \
1902+
title=\"collapse all docs\">\
1903+
[<span class='inner'>&#x2212;</span>]\
1904+
</a>\
1905+
</span>")?;
17381906

17391907
// Write `src` tag
17401908
//
@@ -3567,33 +3735,34 @@ impl<'a> fmt::Display for Sidebar<'a> {
35673735

35683736
if it.is_struct() || it.is_trait() || it.is_primitive() || it.is_union()
35693737
|| it.is_enum() || it.is_mod() || it.is_typedef() {
3570-
write!(fmt, "<p class='location'>")?;
3571-
match it.inner {
3572-
clean::StructItem(..) => write!(fmt, "Struct ")?,
3573-
clean::TraitItem(..) => write!(fmt, "Trait ")?,
3574-
clean::PrimitiveItem(..) => write!(fmt, "Primitive Type ")?,
3575-
clean::UnionItem(..) => write!(fmt, "Union ")?,
3576-
clean::EnumItem(..) => write!(fmt, "Enum ")?,
3577-
clean::TypedefItem(..) => write!(fmt, "Type Definition ")?,
3578-
clean::ForeignTypeItem => write!(fmt, "Foreign Type ")?,
3579-
clean::ModuleItem(..) => if it.is_crate() {
3580-
write!(fmt, "Crate ")?;
3581-
} else {
3582-
write!(fmt, "Module ")?;
3738+
write!(fmt, "<p class='location'>{}{}</p>",
3739+
match it.inner {
3740+
clean::StructItem(..) => "Struct ",
3741+
clean::TraitItem(..) => "Trait ",
3742+
clean::PrimitiveItem(..) => "Primitive Type ",
3743+
clean::UnionItem(..) => "Union ",
3744+
clean::EnumItem(..) => "Enum ",
3745+
clean::TypedefItem(..) => "Type Definition ",
3746+
clean::ForeignTypeItem => "Foreign Type ",
3747+
clean::ModuleItem(..) => if it.is_crate() {
3748+
"Crate "
3749+
} else {
3750+
"Module "
3751+
},
3752+
_ => "",
35833753
},
3584-
_ => (),
3585-
}
3586-
write!(fmt, "{}", it.name.as_ref().unwrap())?;
3587-
write!(fmt, "</p>")?;
3754+
it.name.as_ref().unwrap())?;
35883755
}
35893756

35903757
if it.is_crate() {
35913758
if let Some(ref version) = cache().crate_version {
35923759
write!(fmt,
35933760
"<div class='block version'>\
35943761
<p>Version {}</p>\
3595-
</div>",
3596-
version)?;
3762+
</div>
3763+
<a id='all-types' href='all.html'><p>See all {}'s items</p></a>",
3764+
version,
3765+
it.name.as_ref().unwrap())?;
35973766
}
35983767
}
35993768

src/librustdoc/html/static/rustdoc.css

+18
Original file line numberDiff line numberDiff line change
@@ -1294,3 +1294,21 @@ kbd {
12941294
font-size: 19px;
12951295
display: block;
12961296
}
1297+
1298+
#main > ul {
1299+
padding-left: 10px;
1300+
}
1301+
#main > ul > li {
1302+
list-style: none;
1303+
}
1304+
#all-types {
1305+
text-align: center;
1306+
border: 1px solid;
1307+
margin: 0 10px;
1308+
margin-bottom: 10px;
1309+
display: block;
1310+
border-radius: 7px;
1311+
}
1312+
#all-types > p {
1313+
margin: 5px 0;
1314+
}

src/librustdoc/html/static/themes/dark.css

+7
Original file line numberDiff line numberDiff line change
@@ -389,3 +389,10 @@ kbd {
389389
background: #f0f0f0;
390390
}
391391
}
392+
393+
#all-types {
394+
background-color: #505050;
395+
}
396+
#all-types:hover {
397+
background-color: #606060;
398+
}

src/librustdoc/html/static/themes/light.css

+7
Original file line numberDiff line numberDiff line change
@@ -383,3 +383,10 @@ kbd {
383383
background: #fff;
384384
}
385385
}
386+
387+
#all-types {
388+
background-color: #fff;
389+
}
390+
#all-types:hover {
391+
background-color: #f9f9f9;
392+
}

src/test/rustdoc/all.rs

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![crate_name = "foo"]
12+
13+
// @has foo/all.html '//a[@href="struct.Struct.html"]' 'Struct'
14+
// @has foo/all.html '//a[@href="enum.Enum.html"]' 'Enum'
15+
// @has foo/all.html '//a[@href="union.Union.html"]' 'Union'
16+
// @has foo/all.html '//a[@href="constant.CONST.html"]' 'CONST'
17+
// @has foo/all.html '//a[@href="static.STATIC.html"]' 'STATIC'
18+
// @has foo/all.html '//a[@href="fn.function.html"]' 'function'
19+
20+
pub struct Struct;
21+
pub enum Enum {
22+
X,
23+
Y,
24+
}
25+
pub union Union {
26+
x: u32,
27+
}
28+
pub const CONST: u32 = 0;
29+
pub static STATIC: &str = "baguette";
30+
pub fn function() {}

0 commit comments

Comments
 (0)