]> git.lizzy.rs Git - rust.git/blobdiff - src/librustdoc/html/render.rs
Auto merge of #31715 - mitaa:rdoc-index-crate, r=alexcrichton
[rust.git] / src / librustdoc / html / render.rs
index 12bb10e348c484125611ac5bb2ad0b91acb12c0e..692d230446cda7b356b028021237f610c28bb141 100644 (file)
@@ -52,7 +52,7 @@
 
 use externalfiles::ExternalHtml;
 
-use serialize::json::{self, ToJson};
+use serialize::json::{ToJson, Json, as_json};
 use syntax::{abi, ast};
 use syntax::feature_gate::UnstableFeatures;
 use rustc::middle::cstore::LOCAL_CRATE;
@@ -290,22 +290,40 @@ struct IndexItem {
     path: String,
     desc: String,
     parent: Option<DefId>,
+    parent_idx: Option<usize>,
     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.
 struct Type {
     name: Option<String>,
 }
 
-impl fmt::Display for Type {
-    /// Formats type as {name: $name}.
-    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.
+impl ToJson for Type {
+    fn to_json(&self) -> Json {
         match self.name {
-            Some(ref n) => write!(f, "{{\"name\":\"{}\"}}", n),
-            None => write!(f, "null")
+            Some(ref name) => {
+                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>
 }
 
-impl fmt::Display for IndexItemFunctionType {
-    /// Formats a full fn type as a JSON {inputs: [Type], outputs: Type/null}.
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+impl ToJson for IndexItemFunctionType {
+    fn to_json(&self) -> Json {
         // If we couldn't figure out a type, just write `null`.
-        if self.inputs.iter().any(|ref i| i.name.is_none()) ||
-           (self.output.is_some() && self.output.as_ref().unwrap().name.is_none()) {
-            return write!(f, "null")
+        if self.inputs.iter().chain(self.output.iter()).any(|ref i| i.name.is_none()) {
+            Json::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)
 }
 
+/// Build the search index from the collected metadata
 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 pathid_to_nodeid = Vec::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
-        // has since been learned.
-        for &(did, ref item) in orphan_methods {
-            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,
-        // and prune the paths that do not appear in the index.
-        for item in search_index.iter() {
-            match item.parent {
-                Some(nodeid) => {
-                    if !nodeid_to_pathid.contains_key(&nodeid) {
-                        let pathid = pathid_to_nodeid.len();
-                        nodeid_to_pathid.insert(nodeid, pathid);
-                        pathid_to_nodeid.push(nodeid);
-                    }
-                }
-                None => {}
-            }
+    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
+    // has since been learned.
+    for &(did, ref item) in orphan_methods {
+        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),
+                    parent_idx: None,
+                    search_type: get_index_search_type(&item, parent_basename),
+                });
+            },
+            None => {}
         }
-        assert_eq!(nodeid_to_pathid.len(), pathid_to_nodeid.len());
     }
 
-    // Collect the index into a string
-    let mut w = io::Cursor::new(Vec::new());
-    write!(&mut w, r#"searchIndex['{}'] = {{"items":["#, krate.name).unwrap();
+    // 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;
 
-    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;
-        };
+    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;
 
-        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();
+                let &(ref fqp, short) = paths.get(&nodeid).unwrap();
+                crate_paths.push(((short as usize), fqp.last().unwrap().clone()).to_json());
+                pathid
             }
-            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();
+        // 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();
         }
-        write!(&mut w, r#"[{},"{}"]"#,
-               short as usize, *fqp.last().unwrap()).unwrap();
+        crate_items.push(item.to_json());
     }
 
-    write!(&mut w, "]}};").unwrap();
+    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));
 
-    String::from_utf8(w.into_inner()).unwrap()
+    // Collect the index into a string
+    format!("searchIndex[{}] = {};",
+            as_json(&krate.name),
+            Json::Object(crate_data))
 }
 
 fn write_shared(cx: &Context,
@@ -693,7 +680,7 @@ fn collect(path: &Path, krate: &str,
                 if !line.starts_with(key) {
                     continue
                 }
-                if line.starts_with(&format!("{}['{}']", key, krate)) {
+                if line.starts_with(&format!(r#"{}["{}"]"#, key, krate)) {
                     continue
                 }
                 ret.push(line.to_string());
@@ -1067,6 +1054,7 @@ fn fold_item(&mut self, item: clean::Item) -> Option<clean::Item> {
                             path: path.join("::").to_string(),
                             desc: Escape(&shorter(item.doc_value())).to_string(),
                             parent: parent,
+                            parent_idx: None,
                             search_type: get_index_search_type(&item, parent_basename),
                         });
                     }
@@ -1387,7 +1375,7 @@ fn render(w: File, cx: &Context, it: &clean::Item,
                         let js_dst = this.dst.join("sidebar-items.js");
                         let mut js_out = BufWriter::new(try_err!(File::create(&js_dst), &js_dst));
                         try_err!(write!(&mut js_out, "initSidebarItems({});",
-                                    json::as_json(&items)), &js_dst);
+                                    as_json(&items)), &js_dst);
                     }
 
                     for item in m.items {
@@ -1501,11 +1489,17 @@ fn href(&self, cx: &Context) -> Option<String> {
                           true, |component| {
                 path.push(component.to_string());
             });
-            Some(format!("{root}src/{krate}/{path}.html#{href}",
-                         root = self.cx.root_path,
-                         krate = self.cx.layout.krate,
-                         path = path.join("/"),
-                         href = href))
+            // If the span points into an external macro the
+            // source-file will be bogus, i.e `<foo macros>`
+            if Path::new(&self.item.source.filename).is_file() {
+                Some(format!("{root}src/{krate}/{path}.html#{href}",
+                             root = self.cx.root_path,
+                             krate = self.cx.layout.krate,
+                             path = path.join("/"),
+                             href = href))
+            } else {
+                None
+            }
 
         // If this item is not part of the local crate, then things get a little
         // trickier. We don't actually know the span of the external item, but
@@ -1653,8 +1647,8 @@ fn shorter<'a>(s: Option<&'a str>) -> String {
 
 #[inline]
 fn plain_summary_line(s: Option<&str>) -> String {
-    let line = shorter(s).replace("\n", " ");
-    markdown::plain_summary_line(&line[..])
+    let md = markdown::plain_summary_line(s.unwrap_or(""));
+    shorter(Some(&md)).replace("\n", " ")
 }
 
 fn document(w: &mut fmt::Formatter, cx: &Context, item: &clean::Item) -> fmt::Result {
@@ -1781,6 +1775,7 @@ fn cmp(i1: &clean::Item, i2: &clean::Item, idx1: usize, idx2: usize) -> Ordering
                 } else {
                     String::new()
                 };
+                let doc_value = myitem.doc_value().unwrap_or("");
                 try!(write!(w, "
                     <tr class='{stab} module-item'>
                         <td><a class='{class}' href='{href}'
@@ -1792,7 +1787,7 @@ fn cmp(i1: &clean::Item, i2: &clean::Item, idx1: usize, idx2: usize) -> Ordering
                 ",
                 name = *myitem.name.as_ref().unwrap(),
                 stab_docs = stab_docs,
-                docs = Markdown(&shorter(myitem.doc_value())),
+                docs = shorter(Some(&Markdown(doc_value).to_string())),
                 class = shortty(myitem),
                 stab = myitem.stability_class(),
                 href = item_path(myitem),
@@ -2550,25 +2545,25 @@ fn doctraititem(w: &mut fmt::Formatter, cx: &Context, item: &clean::Item,
                 }
             }
             clean::TypedefItem(ref tydef, _) => {
-                let id = derive_id(format!("assoc_type.{}", name));
+                let id = derive_id(format!("associatedtype.{}", name));
                 try!(write!(w, "<h4 id='{}' class='{}'><code>", id, shortty(item)));
                 try!(write!(w, "type {} = {}", name, tydef.type_));
                 try!(write!(w, "</code></h4>\n"));
             }
             clean::AssociatedConstItem(ref ty, ref default) => {
-                let id = derive_id(format!("assoc_const.{}", name));
+                let id = derive_id(format!("associatedconstant.{}", name));
                 try!(write!(w, "<h4 id='{}' class='{}'><code>", id, shortty(item)));
                 try!(assoc_const(w, item, ty, default.as_ref()));
                 try!(write!(w, "</code></h4>\n"));
             }
             clean::ConstantItem(ref c) => {
-                let id = derive_id(format!("assoc_const.{}", name));
+                let id = derive_id(format!("associatedconstant.{}", name));
                 try!(write!(w, "<h4 id='{}' class='{}'><code>", id, shortty(item)));
                 try!(assoc_const(w, item, &c.type_, Some(&c.expr)));
                 try!(write!(w, "</code></h4>\n"));
             }
             clean::AssociatedTypeItem(ref bounds, ref default) => {
-                let id = derive_id(format!("assoc_type.{}", name));
+                let id = derive_id(format!("associatedtype.{}", name));
                 try!(write!(w, "<h4 id='{}' class='{}'><code>", id, shortty(item)));
                 try!(assoc_type(w, item, bounds, default));
                 try!(write!(w, "</code></h4>\n"));