Skip to content

Commit 596e10f

Browse files
committed
Auto merge of #55707 - GuillaumeGomez:file-sidebar, r=QuietMisdreavus
Add source file sidebar This is just a start currently but that gives a good overview of what it'll look like: <img width="1440" alt="screenshot 2018-11-06 at 01 39 15" src="https://user-images.githubusercontent.com/3050060/48035592-05336180-e165-11e8-82e1-5ead0c345eb9.png"> r? @QuietMisdreavus
2 parents 91d5d56 + 82a7b6f commit 596e10f

File tree

10 files changed

+425
-63
lines changed

10 files changed

+425
-63
lines changed

src/librustdoc/html/layout.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ pub struct Page<'a> {
3333

3434
pub fn render<T: fmt::Display, S: fmt::Display>(
3535
dst: &mut dyn io::Write, layout: &Layout, page: &Page, sidebar: &S, t: &T,
36-
css_file_extension: bool, themes: &[PathBuf])
36+
css_file_extension: bool, themes: &[PathBuf], extra_scripts: &[&str])
3737
-> io::Result<()>
3838
{
3939
write!(dst,
@@ -149,6 +149,7 @@ pub fn render<T: fmt::Display, S: fmt::Display>(
149149
</script>\
150150
<script src=\"{root_path}aliases.js\"></script>\
151151
<script src=\"{root_path}main{suffix}.js\"></script>\
152+
{extra_scripts}\
152153
<script defer src=\"{root_path}search-index.js\"></script>\
153154
</body>\
154155
</html>",
@@ -192,6 +193,11 @@ pub fn render<T: fmt::Display, S: fmt::Display>(
192193
page.resource_suffix))
193194
.collect::<String>(),
194195
suffix=page.resource_suffix,
196+
extra_scripts=extra_scripts.iter().map(|e| {
197+
format!("<script src=\"{root_path}{extra_script}.js\"></script>",
198+
root_path=page.root_path,
199+
extra_script=e)
200+
}).collect::<String>(),
195201
)
196202
}
197203

src/librustdoc/html/render.rs

+89-5
Original file line numberDiff line numberDiff line change
@@ -859,6 +859,11 @@ themePicker.onblur = handleThemeButtonsBlur;
859859
write_minify(cx.dst.join(&format!("settings{}.js", cx.shared.resource_suffix)),
860860
static_files::SETTINGS_JS,
861861
options.enable_minification)?;
862+
if cx.shared.include_sources {
863+
write_minify(cx.dst.join(&format!("source-script{}.js", cx.shared.resource_suffix)),
864+
static_files::sidebar::SOURCE_SCRIPT,
865+
options.enable_minification)?;
866+
}
862867

863868
{
864869
let mut data = format!("var resourcesSuffix = \"{}\";\n",
@@ -969,10 +974,88 @@ themePicker.onblur = handleThemeButtonsBlur;
969974
}
970975
}
971976

977+
use std::ffi::OsString;
978+
979+
#[derive(Debug)]
980+
struct Hierarchy {
981+
elem: OsString,
982+
children: FxHashMap<OsString, Hierarchy>,
983+
elems: FxHashSet<OsString>,
984+
}
985+
986+
impl Hierarchy {
987+
fn new(elem: OsString) -> Hierarchy {
988+
Hierarchy {
989+
elem,
990+
children: FxHashMap::default(),
991+
elems: FxHashSet::default(),
992+
}
993+
}
994+
995+
fn to_json_string(&self) -> String {
996+
let mut subs: Vec<&Hierarchy> = self.children.values().collect();
997+
subs.sort_unstable_by(|a, b| a.elem.cmp(&b.elem));
998+
let mut files = self.elems.iter()
999+
.map(|s| format!("\"{}\"",
1000+
s.to_str()
1001+
.expect("invalid osstring conversion")))
1002+
.collect::<Vec<_>>();
1003+
files.sort_unstable_by(|a, b| a.cmp(b));
1004+
// FIXME(imperio): we could avoid to generate "dirs" and "files" if they're empty.
1005+
format!("{{\"name\":\"{name}\",\"dirs\":[{subs}],\"files\":[{files}]}}",
1006+
name=self.elem.to_str().expect("invalid osstring conversion"),
1007+
subs=subs.iter().map(|s| s.to_json_string()).collect::<Vec<_>>().join(","),
1008+
files=files.join(","))
1009+
}
1010+
}
1011+
1012+
if cx.shared.include_sources {
1013+
use std::path::Component;
1014+
1015+
let mut hierarchy = Hierarchy::new(OsString::new());
1016+
for source in cx.shared.local_sources.iter()
1017+
.filter_map(|p| p.0.strip_prefix(&cx.shared.src_root)
1018+
.ok()) {
1019+
let mut h = &mut hierarchy;
1020+
let mut elems = source.components()
1021+
.filter_map(|s| {
1022+
match s {
1023+
Component::Normal(s) => Some(s.to_owned()),
1024+
_ => None,
1025+
}
1026+
})
1027+
.peekable();
1028+
loop {
1029+
let cur_elem = elems.next().expect("empty file path");
1030+
if elems.peek().is_none() {
1031+
h.elems.insert(cur_elem);
1032+
break;
1033+
} else {
1034+
let e = cur_elem.clone();
1035+
h.children.entry(cur_elem.clone()).or_insert_with(|| Hierarchy::new(e));
1036+
h = h.children.get_mut(&cur_elem).expect("not found child");
1037+
}
1038+
}
1039+
}
1040+
1041+
let dst = cx.dst.join("source-files.js");
1042+
let (mut all_sources, _krates) = try_err!(collect(&dst, &krate.name, "sourcesIndex"), &dst);
1043+
all_sources.push(format!("sourcesIndex['{}'] = {};",
1044+
&krate.name,
1045+
hierarchy.to_json_string()));
1046+
all_sources.sort();
1047+
let mut w = try_err!(File::create(&dst), &dst);
1048+
try_err!(writeln!(&mut w,
1049+
"var N = null;var sourcesIndex = {{}};\n{}",
1050+
all_sources.join("\n")),
1051+
&dst);
1052+
}
1053+
9721054
// Update the search index
9731055
let dst = cx.dst.join("search-index.js");
9741056
let (mut all_indexes, mut krates) = try_err!(collect(&dst, &krate.name, "searchIndex"), &dst);
9751057
all_indexes.push(search_index);
1058+
9761059
// Sort the indexes by crate so the file will be generated identically even
9771060
// with rustdoc running in parallel.
9781061
all_indexes.sort();
@@ -1020,7 +1103,7 @@ themePicker.onblur = handleThemeButtonsBlur;
10201103
try_err!(layout::render(&mut w, &cx.shared.layout,
10211104
&page, &(""), &content,
10221105
cx.shared.css_file_extension.is_some(),
1023-
&cx.shared.themes), &dst);
1106+
&cx.shared.themes, &[]), &dst);
10241107
try_err!(w.flush(), &dst);
10251108
}
10261109
}
@@ -1292,7 +1375,8 @@ impl<'a> SourceCollector<'a> {
12921375
layout::render(&mut w, &self.scx.layout,
12931376
&page, &(""), &Source(contents),
12941377
self.scx.css_file_extension.is_some(),
1295-
&self.scx.themes)?;
1378+
&self.scx.themes, &["source-files",
1379+
&format!("source-script{}", page.resource_suffix)])?;
12961380
w.flush()?;
12971381
self.scx.local_sources.insert(p.clone(), href);
12981382
Ok(())
@@ -1890,7 +1974,7 @@ impl Context {
18901974
try_err!(layout::render(&mut w, &self.shared.layout,
18911975
&page, &sidebar, &all,
18921976
self.shared.css_file_extension.is_some(),
1893-
&self.shared.themes),
1977+
&self.shared.themes, &[]),
18941978
&final_file);
18951979

18961980
// Generating settings page.
@@ -1910,7 +1994,7 @@ impl Context {
19101994
try_err!(layout::render(&mut w, &layout,
19111995
&page, &sidebar, &settings,
19121996
self.shared.css_file_extension.is_some(),
1913-
&themes),
1997+
&themes, &[]),
19141998
&settings_file);
19151999

19162000
Ok(())
@@ -1968,7 +2052,7 @@ impl Context {
19682052
&Sidebar{ cx: self, item: it },
19692053
&Item{ cx: self, item: it },
19702054
self.shared.css_file_extension.is_some(),
1971-
&self.shared.themes)?;
2055+
&self.shared.themes, &[])?;
19722056
} else {
19732057
let mut url = self.root_path();
19742058
if let Some(&(ref names, ty)) = cache().paths.get(&it.def_id) {

src/librustdoc/html/static/main.js

+13-53
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,19 @@
1313
/*jslint browser: true, es5: true */
1414
/*globals $: true, rootPath: true */
1515

16+
if (!String.prototype.startsWith) {
17+
String.prototype.startsWith = function(searchString, position) {
18+
position = position || 0;
19+
return this.indexOf(searchString, position) === position;
20+
};
21+
}
22+
if (!String.prototype.endsWith) {
23+
String.prototype.endsWith = function(suffix, length) {
24+
var l = length || this.length;
25+
return this.indexOf(suffix, l - suffix.length) !== -1;
26+
};
27+
}
28+
1629
(function() {
1730
"use strict";
1831

@@ -57,19 +70,6 @@
5770

5871
var titleBeforeSearch = document.title;
5972

60-
if (!String.prototype.startsWith) {
61-
String.prototype.startsWith = function(searchString, position) {
62-
position = position || 0;
63-
return this.indexOf(searchString, position) === position;
64-
};
65-
}
66-
if (!String.prototype.endsWith) {
67-
String.prototype.endsWith = function(suffix, length) {
68-
var l = length || this.length;
69-
return this.indexOf(suffix, l - suffix.length) !== -1;
70-
};
71-
}
72-
7373
function getPageId() {
7474
var id = document.location.href.split('#')[1];
7575
if (id) {
@@ -78,46 +78,6 @@
7878
return null;
7979
}
8080

81-
function hasClass(elem, className) {
82-
if (elem && className && elem.className) {
83-
var elemClass = elem.className;
84-
var start = elemClass.indexOf(className);
85-
if (start === -1) {
86-
return false;
87-
} else if (elemClass.length === className.length) {
88-
return true;
89-
} else {
90-
if (start > 0 && elemClass[start - 1] !== ' ') {
91-
return false;
92-
}
93-
var end = start + className.length;
94-
return !(end < elemClass.length && elemClass[end] !== ' ');
95-
}
96-
}
97-
return false;
98-
}
99-
100-
function addClass(elem, className) {
101-
if (elem && className && !hasClass(elem, className)) {
102-
if (elem.className && elem.className.length > 0) {
103-
elem.className += ' ' + className;
104-
} else {
105-
elem.className = className;
106-
}
107-
}
108-
}
109-
110-
function removeClass(elem, className) {
111-
if (elem && className && elem.className) {
112-
elem.className = (" " + elem.className + " ").replace(" " + className + " ", " ")
113-
.trim();
114-
}
115-
}
116-
117-
function isHidden(elem) {
118-
return (elem.offsetParent === null)
119-
}
120-
12181
function showSidebar() {
12282
var elems = document.getElementsByClassName("sidebar-elems")[0];
12383
if (elems) {

src/librustdoc/html/static/rustdoc.css

+70-4
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,8 @@ h3.impl, h3.method, h3.type {
113113

114114
h1, h2, h3, h4,
115115
.sidebar, a.source, .search-input, .content table :not(code)>a,
116-
.collapse-toggle, div.item-list .out-of-band {
116+
.collapse-toggle, div.item-list .out-of-band,
117+
#source-sidebar, #sidebar-toggle {
117118
font-family: "Fira Sans", sans-serif;
118119
}
119120

@@ -668,9 +669,9 @@ a {
668669
padding-right: 10px;
669670
}
670671
.content .search-results td:first-child a:after {
671-
clear: both;
672-
content: "";
673-
display: block;
672+
clear: both;
673+
content: "";
674+
display: block;
674675
}
675676
.content .search-results td:first-child a span {
676677
float: left;
@@ -1459,3 +1460,68 @@ kbd {
14591460
.non-exhaustive {
14601461
margin-bottom: 1em;
14611462
}
1463+
1464+
#sidebar-toggle {
1465+
position: fixed;
1466+
top: 30px;
1467+
left: 300px;
1468+
z-index: 10;
1469+
padding: 3px;
1470+
border-top-right-radius: 3px;
1471+
border-bottom-right-radius: 3px;
1472+
cursor: pointer;
1473+
font-weight: bold;
1474+
transition: left .5s;
1475+
font-size: 1.2em;
1476+
border: 1px solid;
1477+
border-left: 0;
1478+
}
1479+
#source-sidebar {
1480+
position: fixed;
1481+
top: 0;
1482+
bottom: 0;
1483+
left: 0;
1484+
width: 300px;
1485+
z-index: 1;
1486+
overflow: auto;
1487+
transition: left .5s;
1488+
border-right: 1px solid;
1489+
}
1490+
#source-sidebar > .title {
1491+
font-size: 1.5em;
1492+
text-align: center;
1493+
border-bottom: 1px solid;
1494+
margin-bottom: 6px;
1495+
}
1496+
1497+
div.children {
1498+
padding-left: 27px;
1499+
display: none;
1500+
}
1501+
div.name {
1502+
cursor: pointer;
1503+
position: relative;
1504+
margin-left: 16px;
1505+
}
1506+
div.files > a {
1507+
display: block;
1508+
padding: 0 3px;
1509+
}
1510+
div.files > a:hover, div.name:hover {
1511+
background-color: #a14b4b;
1512+
}
1513+
div.name.expand + .children {
1514+
display: block;
1515+
}
1516+
div.name::before {
1517+
content: "\25B6";
1518+
padding-left: 4px;
1519+
font-size: 0.7em;
1520+
position: absolute;
1521+
left: -16px;
1522+
top: 4px;
1523+
}
1524+
div.name.expand::before {
1525+
transform: rotate(90deg);
1526+
left: -14px;
1527+
}

0 commit comments

Comments
 (0)