diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 1457c454c5766..ca55d1f04ad2f 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -1,4 +1,4 @@ -// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // @@ -24,6 +24,8 @@ use syntax::ast; use syntax::ast_util; use clean; +use html::item_type; +use html::item_type::ItemType; use html::render; use html::render::{cache_key, current_location_key}; @@ -172,17 +174,17 @@ fn external_path(w: &mut io::Writer, p: &clean::Path, print_all: bool, }, |_cache| { Some((Vec::from_slice(fqn), match kind { - clean::TypeStruct => "struct", - clean::TypeEnum => "enum", - clean::TypeFunction => "fn", - clean::TypeTrait => "trait", + clean::TypeStruct => item_type::Struct, + clean::TypeEnum => item_type::Enum, + clean::TypeFunction => item_type::Function, + clean::TypeTrait => item_type::Trait, })) }) } fn path(w: &mut io::Writer, path: &clean::Path, print_all: bool, root: |&render::Cache, &[~str]| -> Option<~str>, - info: |&render::Cache| -> Option<(Vec<~str> , &'static str)>) + info: |&render::Cache| -> Option<(Vec<~str> , ItemType)>) -> fmt::Result { // The generics will get written to both the title and link @@ -252,12 +254,12 @@ fn path(w: &mut io::Writer, path: &clean::Path, print_all: bool, url.push_str("/"); } match shortty { - "mod" => { + item_type::Module => { url.push_str(*fqp.last().unwrap()); url.push_str("/index.html"); } _ => { - url.push_str(shortty); + url.push_str(shortty.to_static_str()); url.push_str("."); url.push_str(*fqp.last().unwrap()); url.push_str(".html"); diff --git a/src/librustdoc/html/item_type.rs b/src/librustdoc/html/item_type.rs new file mode 100644 index 0000000000000..f59e8cb248738 --- /dev/null +++ b/src/librustdoc/html/item_type.rs @@ -0,0 +1,97 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Item types. + +use std::fmt; +use clean; + +/// Item type. Corresponds to `clean::ItemEnum` variants. +/// +/// The search index uses item types encoded as smaller numbers which equal to +/// discriminants. JavaScript then is used to decode them into the original value. +/// Consequently, every change to this type should be synchronized to +/// the `itemTypes` mapping table in `static/main.js`. +#[deriving(Eq, Clone)] +pub enum ItemType { + Module = 0, + Struct = 1, + Enum = 2, + Function = 3, + Typedef = 4, + Static = 5, + Trait = 6, + Impl = 7, + ViewItem = 8, + TyMethod = 9, + Method = 10, + StructField = 11, + Variant = 12, + ForeignFunction = 13, + ForeignStatic = 14, + Macro = 15, +} + +impl ItemType { + pub fn to_static_str(&self) -> &'static str { + match *self { + Module => "mod", + Struct => "struct", + Enum => "enum", + Function => "fn", + Typedef => "typedef", + Static => "static", + Trait => "trait", + Impl => "impl", + ViewItem => "viewitem", + TyMethod => "tymethod", + Method => "method", + StructField => "structfield", + Variant => "variant", + ForeignFunction => "ffi", + ForeignStatic => "ffs", + Macro => "macro", + } + } +} + +impl fmt::Show for ItemType { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.to_static_str().fmt(f) + } +} + +impl fmt::Unsigned for ItemType { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + (*self as uint).fmt(f) + } +} + +pub fn shortty(item: &clean::Item) -> ItemType { + match item.inner { + clean::ModuleItem(..) => Module, + clean::StructItem(..) => Struct, + clean::EnumItem(..) => Enum, + clean::FunctionItem(..) => Function, + clean::TypedefItem(..) => Typedef, + clean::StaticItem(..) => Static, + clean::TraitItem(..) => Trait, + clean::ImplItem(..) => Impl, + clean::ViewItemItem(..) => ViewItem, + clean::TyMethodItem(..) => TyMethod, + clean::MethodItem(..) => Method, + clean::StructFieldItem(..) => StructField, + clean::VariantItem(..) => Variant, + clean::ForeignFunctionItem(..) => ForeignFunction, + clean::ForeignStaticItem(..) => ForeignStatic, + clean::MacroItem(..) => Macro, + } +} + diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index ccee4f0ffc9f3..669a489a4fb6e 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -52,6 +52,8 @@ use rustc::util::nodemap::NodeSet; use clean; use doctree; use fold::DocFolder; +use html::item_type; +use html::item_type::{ItemType, shortty}; use html::format::{VisSpace, Method, FnStyleSpace}; use html::layout; use html::markdown; @@ -138,7 +140,7 @@ pub struct Cache { /// URLs when a type is being linked to. External paths are not located in /// this map because the `External` type itself has all the information /// necessary. - pub paths: HashMap , &'static str)>, + pub paths: HashMap , ItemType)>, /// This map contains information about all known traits of this crate. /// Implementations of a crate should inherit the documentation of the @@ -193,7 +195,7 @@ struct Sidebar<'a> { cx: &'a Context, item: &'a clean::Item, } /// Struct representing one entry in the JS search index. These are all emitted /// by hand to a large JS file at the end of cache-creation. struct IndexItem { - ty: &'static str, + ty: ItemType, name: ~str, path: ~str, desc: ~str, @@ -262,6 +264,9 @@ pub fn run(mut krate: clean::Crate, dst: Path) -> io::IoResult<()> { }); cache.stack.push(krate.name.clone()); krate = cache.fold_crate(krate); + + let mut nodeid_to_pathid = HashMap::new(); + let mut pathid_to_nodeid = Vec::new(); { let Cache { search_index: ref mut index, orphan_methods: ref meths, paths: ref mut paths, ..} = cache; @@ -283,48 +288,67 @@ pub fn run(mut krate: clean::Crate, dst: Path) -> io::IoResult<()> { } }; - // Prune the paths that do not appear in the index. - let mut unseen: HashSet = paths.keys().map(|&id| id).collect(); + // Reduce `NodeId` in paths into smaller sequential numbers, + // and prune the paths that do not appear in the index. for item in index.iter() { match item.parent { - Some(ref pid) => { unseen.remove(pid); } + 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 => {} } } - for pid in unseen.iter() { - paths.remove(pid); - } + assert_eq!(nodeid_to_pathid.len(), pathid_to_nodeid.len()); } // Publish the search index let index = { let mut w = MemWriter::new(); - try!(write!(&mut w, "searchIndex['{}'] = [", krate.name)); + try!(write!(&mut w, r#"searchIndex['{}'] = \{"items":["#, krate.name)); + + let mut lastpath = ~""; 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.clone(); + path = item.path.as_slice(); + }; + if i > 0 { try!(write!(&mut w, ",")); } - try!(write!(&mut w, "\\{ty:\"{}\",name:\"{}\",path:\"{}\",desc:{}", - item.ty, item.name, item.path, + try!(write!(&mut w, r#"[{:u},"{}","{}",{}"#, + item.ty, item.name, path, item.desc.to_json().to_str())); match item.parent { - Some(id) => { - try!(write!(&mut w, ",parent:'{}'", id)); + Some(nodeid) => { + let pathid = *nodeid_to_pathid.find(&nodeid).unwrap(); + try!(write!(&mut w, ",{}", pathid)); } None => {} } - try!(write!(&mut w, "\\}")); + try!(write!(&mut w, "]")); } - try!(write!(&mut w, "];")); - try!(write!(&mut w, "allPaths['{}'] = \\{", krate.name)); - for (i, (&id, &(ref fqp, short))) in cache.paths.iter().enumerate() { + + try!(write!(&mut w, r#"],"paths":["#)); + + for (i, &nodeid) in pathid_to_nodeid.iter().enumerate() { + let &(ref fqp, short) = cache.paths.find(&nodeid).unwrap(); if i > 0 { try!(write!(&mut w, ",")); } - try!(write!(&mut w, "'{}':\\{type:'{}',name:'{}'\\}", - id, short, *fqp.last().unwrap())); + try!(write!(&mut w, r#"[{:u},"{}"]"#, + short, *fqp.last().unwrap())); } - try!(write!(&mut w, "\\};")); + + try!(write!(&mut w, r"]\};")); str::from_utf8(w.unwrap().as_slice()).unwrap().to_owned() }; @@ -360,7 +384,7 @@ pub fn run(mut krate: clean::Crate, dst: Path) -> io::IoResult<()> { } } let mut w = try!(File::create(&dst)); - try!(writeln!(&mut w, r"var searchIndex = \{\}; var allPaths = \{\};")); + try!(writeln!(&mut w, r"var searchIndex = \{\};")); for index in all_indexes.iter() { try!(writeln!(&mut w, "{}", *index)); } @@ -613,12 +637,13 @@ impl DocFolder for Cache { } else { let last = self.parent_stack.last().unwrap(); let path = match self.paths.find(last) { - Some(&(_, "trait")) => + Some(&(_, item_type::Trait)) => Some(self.stack.slice_to(self.stack.len() - 1)), // The current stack not necessarily has correlation for // where the type was defined. On the other hand, // `paths` always has the right information if present. - Some(&(ref fqp, "struct")) | Some(&(ref fqp, "enum")) => + Some(&(ref fqp, item_type::Struct)) | + Some(&(ref fqp, item_type::Enum)) => Some(fqp.slice_to(fqp.len() - 1)), Some(..) => Some(self.stack.as_slice()), None => None @@ -678,7 +703,7 @@ impl DocFolder for Cache { clean::VariantItem(..) => { let mut stack = self.stack.clone(); stack.pop(); - self.paths.insert(item.id, (stack, "enum")); + self.paths.insert(item.id, (stack, item_type::Enum)); } _ => {} } @@ -836,7 +861,7 @@ impl Context { } title.push_str(" - Rust"); let page = layout::Page { - ty: shortty(it), + ty: shortty(it).to_static_str(), root_path: cx.root_path.as_slice(), title: title.as_slice(), }; @@ -890,27 +915,6 @@ impl Context { } } -fn shortty(item: &clean::Item) -> &'static str { - match item.inner { - clean::ModuleItem(..) => "mod", - clean::StructItem(..) => "struct", - clean::EnumItem(..) => "enum", - clean::FunctionItem(..) => "fn", - clean::TypedefItem(..) => "typedef", - clean::StaticItem(..) => "static", - clean::TraitItem(..) => "trait", - clean::ImplItem(..) => "impl", - clean::ViewItemItem(..) => "viewitem", - clean::TyMethodItem(..) => "tymethod", - clean::MethodItem(..) => "method", - clean::StructFieldItem(..) => "structfield", - clean::VariantItem(..) => "variant", - clean::ForeignFunctionItem(..) => "ffi", - clean::ForeignStaticItem(..) => "ffs", - clean::MacroItem(..) => "macro", - } -} - impl<'a> Item<'a> { fn ismodule(&self) -> bool { match self.item.inner { @@ -1000,7 +1004,7 @@ impl<'a> fmt::Show for Item<'a> { fn item_path(item: &clean::Item) -> ~str { match item.inner { clean::ModuleItem(..) => *item.name.get_ref() + "/index.html", - _ => shortty(item) + "." + *item.name.get_ref() + ".html" + _ => shortty(item).to_static_str() + "." + *item.name.get_ref() + ".html" } } @@ -1086,13 +1090,13 @@ fn item_module(w: &mut Writer, cx: &Context, indices.sort_by(|&i1, &i2| cmp(&items[i1], &items[i2], i1, i2)); debug!("{:?}", indices); - let mut curty = ""; + let mut curty = None; for &idx in indices.iter() { let myitem = &items[idx]; - let myty = shortty(myitem); + let myty = Some(shortty(myitem)); if myty != curty { - if curty != "" { + if curty.is_some() { try!(write!(w, "")); } curty = myty; @@ -1695,8 +1699,9 @@ impl<'a> fmt::Show for Sidebar<'a> { }; try!(write!(w, "

{}

", short, longty)); for item in items.iter() { + let curty = shortty(cur).to_static_str(); let class = if cur.name.get_ref() == item && - short == shortty(cur) { "current" } else { "" }; + short == curty { "current" } else { "" }; try!(write!(w, " fmt::Show for Sidebar<'a> { ty = short, tysel = short, class = class, - curty = shortty(cur), + curty = curty, name = item.as_slice())); } try!(write!(w, "
")); @@ -1726,7 +1731,7 @@ impl<'a> fmt::Show for Sidebar<'a> { fn build_sidebar(m: &clean::Module) -> HashMap<~str, Vec<~str> > { let mut map = HashMap::new(); for item in m.items.iter() { - let short = shortty(item); + let short = shortty(item).to_static_str(); let myname = match item.name { None => continue, Some(ref s) => s.to_owned(), diff --git a/src/librustdoc/html/static/main.js b/src/librustdoc/html/static/main.js index 1904ab27d17d9..4b79ae89eef6b 100644 --- a/src/librustdoc/html/static/main.js +++ b/src/librustdoc/html/static/main.js @@ -9,7 +9,7 @@ // except according to those terms. /*jslint browser: true, es5: true */ -/*globals $: true, rootPath: true, allPaths: true */ +/*globals $: true, rootPath: true */ (function() { "use strict"; @@ -135,7 +135,7 @@ function execQuery(query, max, searchWords) { var valLower = query.query.toLowerCase(), val = valLower, - typeFilter = query.type, + typeFilter = itemTypeFromName(query.type), results = [], split = valLower.split("::"); @@ -156,7 +156,7 @@ for (var i = 0; i < nSearchWords; i += 1) { if (searchWords[i] === val) { // filter type: ... queries - if (!typeFilter || typeFilter === searchIndex[i].ty) { + if (typeFilter < 0 || typeFilter === searchIndex[i].ty) { results.push({id: i, index: -1}); } } @@ -174,7 +174,7 @@ searchWords[j].replace(/_/g, "").indexOf(val) > -1) { // filter type: ... queries - if (!typeFilter || typeFilter === searchIndex[j].ty) { + if (typeFilter < 0 || typeFilter === searchIndex[j].ty) { results.push({id: j, index: searchWords[j].replace(/_/g, "").indexOf(val)}); } } @@ -258,7 +258,7 @@ var result = results[i], name = result.item.name.toLowerCase(), path = result.item.path.toLowerCase(), - parent = allPaths[result.item.crate][result.item.parent]; + parent = result.item.parent; var valid = validateResult(name, path, split, parent); if (!valid) { @@ -405,7 +405,7 @@ shown.push(item); name = item.name; - type = item.ty; + type = itemTypes[item.ty]; output += ''; @@ -422,12 +422,12 @@ '/index.html" class="' + type + '">' + name + ''; } else if (item.parent !== undefined) { - var myparent = allPaths[item.crate][item.parent]; + var myparent = item.parent; var anchor = '#' + type + '.' + name; output += item.path + '::' + myparent.name + '::