1
Fork 0

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
This commit is contained in:
bors 2016-02-22 23:13:08 +00:00
commit 37c6f2881c
3 changed files with 117 additions and 116 deletions

View file

@ -52,7 +52,7 @@ use std::sync::Arc;
use externalfiles::ExternalHtml; use externalfiles::ExternalHtml;
use serialize::json::{self, ToJson}; use serialize::json::{ToJson, Json, as_json};
use syntax::{abi, ast}; use syntax::{abi, ast};
use syntax::feature_gate::UnstableFeatures; use syntax::feature_gate::UnstableFeatures;
use rustc::middle::cstore::LOCAL_CRATE; use rustc::middle::cstore::LOCAL_CRATE;
@ -290,22 +290,40 @@ struct IndexItem {
path: String, path: String,
desc: String, desc: String,
parent: Option<DefId>, parent: Option<DefId>,
parent_idx: Option<usize>,
search_type: Option<IndexItemFunctionType>, search_type: Option<IndexItemFunctionType>,
} }
impl ToJson for IndexItem {
fn to_json(&self) -> Json {
assert_eq!(self.parent.is_some(), self.parent_idx.is_some());
let mut data = Vec::with_capacity(6);
data.push((self.ty as usize).to_json());
data.push(self.name.to_json());
data.push(self.path.to_json());
data.push(self.desc.to_json());
data.push(self.parent_idx.to_json());
data.push(self.search_type.to_json());
Json::Array(data)
}
}
/// A type used for the search index. /// A type used for the search index.
struct Type { struct Type {
name: Option<String>, name: Option<String>,
} }
impl fmt::Display for Type { impl ToJson for Type {
/// Formats type as {name: $name}. fn to_json(&self) -> Json {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
// Wrapping struct fmt should never call us when self.name is None,
// but just to be safe we write `null` in that case.
match self.name { match self.name {
Some(ref n) => write!(f, "{{\"name\":\"{}\"}}", n), Some(ref name) => {
None => write!(f, "null") let mut data = BTreeMap::new();
data.insert("name".to_owned(), name.to_json());
Json::Object(data)
},
None => Json::Null
} }
} }
} }
@ -316,26 +334,17 @@ struct IndexItemFunctionType {
output: Option<Type> output: Option<Type>
} }
impl fmt::Display for IndexItemFunctionType { impl ToJson for IndexItemFunctionType {
/// Formats a full fn type as a JSON {inputs: [Type], outputs: Type/null}. fn to_json(&self) -> Json {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
// If we couldn't figure out a type, just write `null`. // If we couldn't figure out a type, just write `null`.
if self.inputs.iter().any(|ref i| i.name.is_none()) || if self.inputs.iter().chain(self.output.iter()).any(|ref i| i.name.is_none()) {
(self.output.is_some() && self.output.as_ref().unwrap().name.is_none()) { Json::Null
return write!(f, "null") } else {
let mut data = BTreeMap::new();
data.insert("inputs".to_owned(), self.inputs.to_json());
data.insert("output".to_owned(), self.output.to_json());
Json::Object(data)
} }
let inputs: Vec<String> = self.inputs.iter().map(|ref t| {
format!("{}", t)
}).collect();
try!(write!(f, "{{\"inputs\":[{}],\"output\":", inputs.join(",")));
match self.output {
Some(ref t) => try!(write!(f, "{}", t)),
None => try!(write!(f, "null"))
};
Ok(try!(write!(f, "}}")))
} }
} }
@ -534,101 +543,79 @@ pub fn run(mut krate: clean::Crate,
cx.krate(krate) cx.krate(krate)
} }
/// Build the search index from the collected metadata
fn build_index(krate: &clean::Crate, cache: &mut Cache) -> String { fn build_index(krate: &clean::Crate, cache: &mut Cache) -> String {
// Build the search index from the collected metadata
let mut nodeid_to_pathid = HashMap::new(); let mut nodeid_to_pathid = HashMap::new();
let mut pathid_to_nodeid = Vec::new(); let mut crate_items = Vec::with_capacity(cache.search_index.len());
{ let mut crate_paths = Vec::<Json>::new();
let Cache { ref mut search_index,
ref orphan_methods,
ref mut paths, .. } = *cache;
// Attach all orphan methods to the type's definition if the type let Cache { ref mut search_index,
// has since been learned. ref orphan_methods,
for &(did, ref item) in orphan_methods { ref mut paths, .. } = *cache;
match paths.get(&did) {
Some(&(ref fqp, _)) => {
// Needed to determine `self` type.
let parent_basename = Some(fqp[fqp.len() - 1].clone());
search_index.push(IndexItem {
ty: shortty(item),
name: item.name.clone().unwrap(),
path: fqp[..fqp.len() - 1].join("::"),
desc: Escape(&shorter(item.doc_value())).to_string(),
parent: Some(did),
search_type: get_index_search_type(&item, parent_basename),
});
},
None => {}
}
}
// Reduce `NodeId` in paths into smaller sequential numbers, // Attach all orphan methods to the type's definition if the type
// and prune the paths that do not appear in the index. // has since been learned.
for item in search_index.iter() { for &(did, ref item) in orphan_methods {
match item.parent { match paths.get(&did) {
Some(nodeid) => { Some(&(ref fqp, _)) => {
if !nodeid_to_pathid.contains_key(&nodeid) { // Needed to determine `self` type.
let pathid = pathid_to_nodeid.len(); let parent_basename = Some(fqp[fqp.len() - 1].clone());
nodeid_to_pathid.insert(nodeid, pathid); search_index.push(IndexItem {
pathid_to_nodeid.push(nodeid); ty: shortty(item),
} name: item.name.clone().unwrap(),
} path: fqp[..fqp.len() - 1].join("::"),
None => {} desc: Escape(&shorter(item.doc_value())).to_string(),
} parent: Some(did),
parent_idx: None,
search_type: get_index_search_type(&item, parent_basename),
});
},
None => {}
} }
assert_eq!(nodeid_to_pathid.len(), pathid_to_nodeid.len());
} }
// Reduce `NodeId` in paths into smaller sequential numbers,
// and prune the paths that do not appear in the index.
let mut lastpath = String::new();
let mut lastpathid = 0usize;
for item in search_index {
item.parent_idx = item.parent.map(|nodeid| {
if nodeid_to_pathid.contains_key(&nodeid) {
*nodeid_to_pathid.get(&nodeid).unwrap()
} else {
let pathid = lastpathid;
nodeid_to_pathid.insert(nodeid, pathid);
lastpathid += 1;
let &(ref fqp, short) = paths.get(&nodeid).unwrap();
crate_paths.push(((short as usize), fqp.last().unwrap().clone()).to_json());
pathid
}
});
// Omit the parent path if it is same to that of the prior item.
if lastpath == item.path {
item.path.clear();
} else {
lastpath = item.path.clone();
}
crate_items.push(item.to_json());
}
let crate_doc = krate.module.as_ref().map(|module| {
Escape(&shorter(module.doc_value())).to_string()
}).unwrap_or(String::new());
let mut crate_data = BTreeMap::new();
crate_data.insert("doc".to_owned(), Json::String(crate_doc));
crate_data.insert("items".to_owned(), Json::Array(crate_items));
crate_data.insert("paths".to_owned(), Json::Array(crate_paths));
// Collect the index into a string // Collect the index into a string
let mut w = io::Cursor::new(Vec::new()); format!("searchIndex[{}] = {};",
write!(&mut w, r#"searchIndex['{}'] = {{"items":["#, krate.name).unwrap(); as_json(&krate.name),
Json::Object(crate_data))
let mut lastpath = "".to_string();
for (i, item) in cache.search_index.iter().enumerate() {
// Omit the path if it is same to that of the prior item.
let path;
if lastpath == item.path {
path = "";
} else {
lastpath = item.path.to_string();
path = &item.path;
};
if i > 0 {
write!(&mut w, ",").unwrap();
}
write!(&mut w, r#"[{},"{}","{}",{}"#,
item.ty as usize, item.name, path,
item.desc.to_json().to_string()).unwrap();
match item.parent {
Some(nodeid) => {
let pathid = *nodeid_to_pathid.get(&nodeid).unwrap();
write!(&mut w, ",{}", pathid).unwrap();
}
None => write!(&mut w, ",null").unwrap()
}
match item.search_type {
Some(ref t) => write!(&mut w, ",{}", t).unwrap(),
None => write!(&mut w, ",null").unwrap()
}
write!(&mut w, "]").unwrap();
}
write!(&mut w, r#"],"paths":["#).unwrap();
for (i, &did) in pathid_to_nodeid.iter().enumerate() {
let &(ref fqp, short) = cache.paths.get(&did).unwrap();
if i > 0 {
write!(&mut w, ",").unwrap();
}
write!(&mut w, r#"[{},"{}"]"#,
short as usize, *fqp.last().unwrap()).unwrap();
}
write!(&mut w, "]}};").unwrap();
String::from_utf8(w.into_inner()).unwrap()
} }
fn write_shared(cx: &Context, fn write_shared(cx: &Context,
@ -693,7 +680,7 @@ fn write_shared(cx: &Context,
if !line.starts_with(key) { if !line.starts_with(key) {
continue continue
} }
if line.starts_with(&format!("{}['{}']", key, krate)) { if line.starts_with(&format!(r#"{}["{}"]"#, key, krate)) {
continue continue
} }
ret.push(line.to_string()); ret.push(line.to_string());
@ -1067,6 +1054,7 @@ impl DocFolder for Cache {
path: path.join("::").to_string(), path: path.join("::").to_string(),
desc: Escape(&shorter(item.doc_value())).to_string(), desc: Escape(&shorter(item.doc_value())).to_string(),
parent: parent, parent: parent,
parent_idx: None,
search_type: get_index_search_type(&item, parent_basename), search_type: get_index_search_type(&item, parent_basename),
}); });
} }
@ -1387,7 +1375,7 @@ impl Context {
let js_dst = this.dst.join("sidebar-items.js"); let js_dst = this.dst.join("sidebar-items.js");
let mut js_out = BufWriter::new(try_err!(File::create(&js_dst), &js_dst)); let mut js_out = BufWriter::new(try_err!(File::create(&js_dst), &js_dst));
try_err!(write!(&mut js_out, "initSidebarItems({});", try_err!(write!(&mut js_out, "initSidebarItems({});",
json::as_json(&items)), &js_dst); as_json(&items)), &js_dst);
} }
for item in m.items { for item in m.items {

View file

@ -580,6 +580,9 @@
displayPath = ""; displayPath = "";
href = rootPath + item.path.replace(/::/g, '/') + href = rootPath + item.path.replace(/::/g, '/') +
'/' + type + '.' + name + '.html'; '/' + type + '.' + name + '.html';
} else if (type === "externcrate") {
displayPath = "";
href = rootPath + name + '/index.html';
} else if (item.parent !== undefined) { } else if (item.parent !== undefined) {
var myparent = item.parent; var myparent = item.parent;
var anchor = '#' + type + '.' + name; var anchor = '#' + type + '.' + name;
@ -678,6 +681,16 @@
for (var crate in rawSearchIndex) { for (var crate in rawSearchIndex) {
if (!rawSearchIndex.hasOwnProperty(crate)) { continue; } if (!rawSearchIndex.hasOwnProperty(crate)) { continue; }
searchWords.push(crate);
searchIndex.push({
crate: crate,
ty: 1, // == ExternCrate
name: crate,
path: "",
desc: rawSearchIndex[crate].doc,
type: null,
});
// an array of [(Number) item type, // an array of [(Number) item type,
// (String) name, // (String) name,
// (String) full path or empty string for previous path, // (String) full path or empty string for previous path,

View file

@ -82,7 +82,7 @@ pre {
} }
.content a.primitive { color: #39a7bf; } .content a.primitive { color: #39a7bf; }
.content span.mod, .content a.mod, block a.current.mod { color: #4d76ae; } .content span.externcrate, span.mod, .content a.mod, block a.current.mod { color: #4d76ae; }
.content span.fn, .content a.fn, .block a.current.fn, .content span.fn, .content a.fn, .block a.current.fn,
.content span.method, .content a.method, .block a.current.method, .content span.method, .content a.method, .block a.current.method,
.content span.tymethod, .content a.tymethod, .block a.current.tymethod, .content span.tymethod, .content a.tymethod, .block a.current.tymethod,