Skip to content

Commit 37c6f28

Browse files
committed
Auto merge of #31715 - mitaa:rdoc-index-crate, r=alexcrichton
This allows to search for crates in documentation and simplifies the json serialization of the search-index. fixes #14077
2 parents c8fc481 + 81f673d commit 37c6f28

File tree

3 files changed

+112
-111
lines changed

3 files changed

+112
-111
lines changed

src/librustdoc/html/render.rs

Lines changed: 98 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ use std::sync::Arc;
5252

5353
use externalfiles::ExternalHtml;
5454

55-
use serialize::json::{self, ToJson};
55+
use serialize::json::{ToJson, Json, as_json};
5656
use syntax::{abi, ast};
5757
use syntax::feature_gate::UnstableFeatures;
5858
use rustc::middle::cstore::LOCAL_CRATE;
@@ -290,22 +290,40 @@ struct IndexItem {
290290
path: String,
291291
desc: String,
292292
parent: Option<DefId>,
293+
parent_idx: Option<usize>,
293294
search_type: Option<IndexItemFunctionType>,
294295
}
295296

297+
impl ToJson for IndexItem {
298+
fn to_json(&self) -> Json {
299+
assert_eq!(self.parent.is_some(), self.parent_idx.is_some());
300+
301+
let mut data = Vec::with_capacity(6);
302+
data.push((self.ty as usize).to_json());
303+
data.push(self.name.to_json());
304+
data.push(self.path.to_json());
305+
data.push(self.desc.to_json());
306+
data.push(self.parent_idx.to_json());
307+
data.push(self.search_type.to_json());
308+
309+
Json::Array(data)
310+
}
311+
}
312+
296313
/// A type used for the search index.
297314
struct Type {
298315
name: Option<String>,
299316
}
300317

301-
impl fmt::Display for Type {
302-
/// Formats type as {name: $name}.
303-
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
304-
// Wrapping struct fmt should never call us when self.name is None,
305-
// but just to be safe we write `null` in that case.
318+
impl ToJson for Type {
319+
fn to_json(&self) -> Json {
306320
match self.name {
307-
Some(ref n) => write!(f, "{{\"name\":\"{}\"}}", n),
308-
None => write!(f, "null")
321+
Some(ref name) => {
322+
let mut data = BTreeMap::new();
323+
data.insert("name".to_owned(), name.to_json());
324+
Json::Object(data)
325+
},
326+
None => Json::Null
309327
}
310328
}
311329
}
@@ -316,26 +334,17 @@ struct IndexItemFunctionType {
316334
output: Option<Type>
317335
}
318336

319-
impl fmt::Display for IndexItemFunctionType {
320-
/// Formats a full fn type as a JSON {inputs: [Type], outputs: Type/null}.
321-
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
337+
impl ToJson for IndexItemFunctionType {
338+
fn to_json(&self) -> Json {
322339
// If we couldn't figure out a type, just write `null`.
323-
if self.inputs.iter().any(|ref i| i.name.is_none()) ||
324-
(self.output.is_some() && self.output.as_ref().unwrap().name.is_none()) {
325-
return write!(f, "null")
340+
if self.inputs.iter().chain(self.output.iter()).any(|ref i| i.name.is_none()) {
341+
Json::Null
342+
} else {
343+
let mut data = BTreeMap::new();
344+
data.insert("inputs".to_owned(), self.inputs.to_json());
345+
data.insert("output".to_owned(), self.output.to_json());
346+
Json::Object(data)
326347
}
327-
328-
let inputs: Vec<String> = self.inputs.iter().map(|ref t| {
329-
format!("{}", t)
330-
}).collect();
331-
try!(write!(f, "{{\"inputs\":[{}],\"output\":", inputs.join(",")));
332-
333-
match self.output {
334-
Some(ref t) => try!(write!(f, "{}", t)),
335-
None => try!(write!(f, "null"))
336-
};
337-
338-
Ok(try!(write!(f, "}}")))
339348
}
340349
}
341350

@@ -534,101 +543,79 @@ pub fn run(mut krate: clean::Crate,
534543
cx.krate(krate)
535544
}
536545

546+
/// Build the search index from the collected metadata
537547
fn build_index(krate: &clean::Crate, cache: &mut Cache) -> String {
538-
// Build the search index from the collected metadata
539548
let mut nodeid_to_pathid = HashMap::new();
540-
let mut pathid_to_nodeid = Vec::new();
541-
{
542-
let Cache { ref mut search_index,
543-
ref orphan_methods,
544-
ref mut paths, .. } = *cache;
545-
546-
// Attach all orphan methods to the type's definition if the type
547-
// has since been learned.
548-
for &(did, ref item) in orphan_methods {
549-
match paths.get(&did) {
550-
Some(&(ref fqp, _)) => {
551-
// Needed to determine `self` type.
552-
let parent_basename = Some(fqp[fqp.len() - 1].clone());
553-
search_index.push(IndexItem {
554-
ty: shortty(item),
555-
name: item.name.clone().unwrap(),
556-
path: fqp[..fqp.len() - 1].join("::"),
557-
desc: Escape(&shorter(item.doc_value())).to_string(),
558-
parent: Some(did),
559-
search_type: get_index_search_type(&item, parent_basename),
560-
});
561-
},
562-
None => {}
563-
}
564-
}
565-
566-
// Reduce `NodeId` in paths into smaller sequential numbers,
567-
// and prune the paths that do not appear in the index.
568-
for item in search_index.iter() {
569-
match item.parent {
570-
Some(nodeid) => {
571-
if !nodeid_to_pathid.contains_key(&nodeid) {
572-
let pathid = pathid_to_nodeid.len();
573-
nodeid_to_pathid.insert(nodeid, pathid);
574-
pathid_to_nodeid.push(nodeid);
575-
}
576-
}
577-
None => {}
578-
}
549+
let mut crate_items = Vec::with_capacity(cache.search_index.len());
550+
let mut crate_paths = Vec::<Json>::new();
551+
552+
let Cache { ref mut search_index,
553+
ref orphan_methods,
554+
ref mut paths, .. } = *cache;
555+
556+
// Attach all orphan methods to the type's definition if the type
557+
// has since been learned.
558+
for &(did, ref item) in orphan_methods {
559+
match paths.get(&did) {
560+
Some(&(ref fqp, _)) => {
561+
// Needed to determine `self` type.
562+
let parent_basename = Some(fqp[fqp.len() - 1].clone());
563+
search_index.push(IndexItem {
564+
ty: shortty(item),
565+
name: item.name.clone().unwrap(),
566+
path: fqp[..fqp.len() - 1].join("::"),
567+
desc: Escape(&shorter(item.doc_value())).to_string(),
568+
parent: Some(did),
569+
parent_idx: None,
570+
search_type: get_index_search_type(&item, parent_basename),
571+
});
572+
},
573+
None => {}
579574
}
580-
assert_eq!(nodeid_to_pathid.len(), pathid_to_nodeid.len());
581575
}
582576

583-
// Collect the index into a string
584-
let mut w = io::Cursor::new(Vec::new());
585-
write!(&mut w, r#"searchIndex['{}'] = {{"items":["#, krate.name).unwrap();
577+
// Reduce `NodeId` in paths into smaller sequential numbers,
578+
// and prune the paths that do not appear in the index.
579+
let mut lastpath = String::new();
580+
let mut lastpathid = 0usize;
586581

587-
let mut lastpath = "".to_string();
588-
for (i, item) in cache.search_index.iter().enumerate() {
589-
// Omit the path if it is same to that of the prior item.
590-
let path;
591-
if lastpath == item.path {
592-
path = "";
593-
} else {
594-
lastpath = item.path.to_string();
595-
path = &item.path;
596-
};
582+
for item in search_index {
583+
item.parent_idx = item.parent.map(|nodeid| {
584+
if nodeid_to_pathid.contains_key(&nodeid) {
585+
*nodeid_to_pathid.get(&nodeid).unwrap()
586+
} else {
587+
let pathid = lastpathid;
588+
nodeid_to_pathid.insert(nodeid, pathid);
589+
lastpathid += 1;
597590

598-
if i > 0 {
599-
write!(&mut w, ",").unwrap();
600-
}
601-
write!(&mut w, r#"[{},"{}","{}",{}"#,
602-
item.ty as usize, item.name, path,
603-
item.desc.to_json().to_string()).unwrap();
604-
match item.parent {
605-
Some(nodeid) => {
606-
let pathid = *nodeid_to_pathid.get(&nodeid).unwrap();
607-
write!(&mut w, ",{}", pathid).unwrap();
591+
let &(ref fqp, short) = paths.get(&nodeid).unwrap();
592+
crate_paths.push(((short as usize), fqp.last().unwrap().clone()).to_json());
593+
pathid
608594
}
609-
None => write!(&mut w, ",null").unwrap()
610-
}
611-
match item.search_type {
612-
Some(ref t) => write!(&mut w, ",{}", t).unwrap(),
613-
None => write!(&mut w, ",null").unwrap()
614-
}
615-
write!(&mut w, "]").unwrap();
616-
}
617-
618-
write!(&mut w, r#"],"paths":["#).unwrap();
595+
});
619596

620-
for (i, &did) in pathid_to_nodeid.iter().enumerate() {
621-
let &(ref fqp, short) = cache.paths.get(&did).unwrap();
622-
if i > 0 {
623-
write!(&mut w, ",").unwrap();
597+
// Omit the parent path if it is same to that of the prior item.
598+
if lastpath == item.path {
599+
item.path.clear();
600+
} else {
601+
lastpath = item.path.clone();
624602
}
625-
write!(&mut w, r#"[{},"{}"]"#,
626-
short as usize, *fqp.last().unwrap()).unwrap();
603+
crate_items.push(item.to_json());
627604
}
628605

629-
write!(&mut w, "]}};").unwrap();
606+
let crate_doc = krate.module.as_ref().map(|module| {
607+
Escape(&shorter(module.doc_value())).to_string()
608+
}).unwrap_or(String::new());
630609

631-
String::from_utf8(w.into_inner()).unwrap()
610+
let mut crate_data = BTreeMap::new();
611+
crate_data.insert("doc".to_owned(), Json::String(crate_doc));
612+
crate_data.insert("items".to_owned(), Json::Array(crate_items));
613+
crate_data.insert("paths".to_owned(), Json::Array(crate_paths));
614+
615+
// Collect the index into a string
616+
format!("searchIndex[{}] = {};",
617+
as_json(&krate.name),
618+
Json::Object(crate_data))
632619
}
633620

634621
fn write_shared(cx: &Context,
@@ -693,7 +680,7 @@ fn write_shared(cx: &Context,
693680
if !line.starts_with(key) {
694681
continue
695682
}
696-
if line.starts_with(&format!("{}['{}']", key, krate)) {
683+
if line.starts_with(&format!(r#"{}["{}"]"#, key, krate)) {
697684
continue
698685
}
699686
ret.push(line.to_string());
@@ -1067,6 +1054,7 @@ impl DocFolder for Cache {
10671054
path: path.join("::").to_string(),
10681055
desc: Escape(&shorter(item.doc_value())).to_string(),
10691056
parent: parent,
1057+
parent_idx: None,
10701058
search_type: get_index_search_type(&item, parent_basename),
10711059
});
10721060
}
@@ -1387,7 +1375,7 @@ impl Context {
13871375
let js_dst = this.dst.join("sidebar-items.js");
13881376
let mut js_out = BufWriter::new(try_err!(File::create(&js_dst), &js_dst));
13891377
try_err!(write!(&mut js_out, "initSidebarItems({});",
1390-
json::as_json(&items)), &js_dst);
1378+
as_json(&items)), &js_dst);
13911379
}
13921380

13931381
for item in m.items {

src/librustdoc/html/static/main.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -580,6 +580,9 @@
580580
displayPath = "";
581581
href = rootPath + item.path.replace(/::/g, '/') +
582582
'/' + type + '.' + name + '.html';
583+
} else if (type === "externcrate") {
584+
displayPath = "";
585+
href = rootPath + name + '/index.html';
583586
} else if (item.parent !== undefined) {
584587
var myparent = item.parent;
585588
var anchor = '#' + type + '.' + name;
@@ -678,6 +681,16 @@
678681
for (var crate in rawSearchIndex) {
679682
if (!rawSearchIndex.hasOwnProperty(crate)) { continue; }
680683

684+
searchWords.push(crate);
685+
searchIndex.push({
686+
crate: crate,
687+
ty: 1, // == ExternCrate
688+
name: crate,
689+
path: "",
690+
desc: rawSearchIndex[crate].doc,
691+
type: null,
692+
});
693+
681694
// an array of [(Number) item type,
682695
// (String) name,
683696
// (String) full path or empty string for previous path,

src/librustdoc/html/static/styles/main.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ pre {
8282
}
8383

8484
.content a.primitive { color: #39a7bf; }
85-
.content span.mod, .content a.mod, block a.current.mod { color: #4d76ae; }
85+
.content span.externcrate, span.mod, .content a.mod, block a.current.mod { color: #4d76ae; }
8686
.content span.fn, .content a.fn, .block a.current.fn,
8787
.content span.method, .content a.method, .block a.current.method,
8888
.content span.tymethod, .content a.tymethod, .block a.current.tymethod,

0 commit comments

Comments
 (0)