Rollup merge of #71724 - GuillaumeGomez:doc-alias-improvements, r=ollie27
Doc alias improvements After [this message](https://github.com/rust-lang/rust/issues/50146#issuecomment-496601755), I realized that the **doc alias**. So this PR does the followings: * Align the alias discovery on items added into the search-index. It brings a few nice advantages: * Instead of cloning the data between the two (in rustdoc source code), we now have the search-index one and aliases which reference to the first one. So we go from one big map containing a lot of duplicated data to just integers... * In the front-end (main.js), I improved the code around aliases to allow them to go through the same transformation as other items when we show the search results. * Improve the search tester in order to perform multiple requests into one file (I think it's better in this case than having a file for each case considering how many there are...) * I also had to add the new function inside the tester (`handleAliases`) Once this PR is merged, I intend to finally stabilize this feature. r? @ollie27 cc @rust-lang/rustdoc
This commit is contained in:
commit
154db50d86
9 changed files with 562 additions and 144 deletions
|
@ -643,6 +643,15 @@ impl Attributes {
|
|||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn get_doc_aliases(&self) -> FxHashSet<String> {
|
||||
self.other_attrs
|
||||
.lists(sym::doc)
|
||||
.filter(|a| a.check_name(sym::alias))
|
||||
.filter_map(|a| a.value_str().map(|s| s.to_string().replace("\"", "")))
|
||||
.filter(|v| !v.is_empty())
|
||||
.collect::<FxHashSet<_>>()
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Attributes {
|
||||
|
|
|
@ -114,7 +114,6 @@ pub fn render<T: Print, S: Print>(
|
|||
window.rootPath = \"{root_path}\";\
|
||||
window.currentCrate = \"{krate}\";\
|
||||
</script>\
|
||||
<script src=\"{root_path}aliases{suffix}.js\"></script>\
|
||||
<script src=\"{static_root_path}main{suffix}.js\"></script>\
|
||||
{static_extra_scripts}\
|
||||
{extra_scripts}\
|
||||
|
|
|
@ -293,7 +293,12 @@ impl Serialize for IndexItem {
|
|||
where
|
||||
S: Serializer,
|
||||
{
|
||||
assert_eq!(self.parent.is_some(), self.parent_idx.is_some());
|
||||
assert_eq!(
|
||||
self.parent.is_some(),
|
||||
self.parent_idx.is_some(),
|
||||
"`{}` is missing idx",
|
||||
self.name
|
||||
);
|
||||
|
||||
(self.ty, &self.name, &self.path, &self.desc, self.parent_idx, &self.search_type)
|
||||
.serialize(serializer)
|
||||
|
@ -819,42 +824,6 @@ themePicker.onblur = handleThemeButtonsBlur;
|
|||
Ok((ret, krates))
|
||||
}
|
||||
|
||||
fn show_item(item: &IndexItem, krate: &str) -> String {
|
||||
format!(
|
||||
"{{'crate':'{}','ty':{},'name':'{}','desc':'{}','p':'{}'{}}}",
|
||||
krate,
|
||||
item.ty as usize,
|
||||
item.name,
|
||||
item.desc.replace("'", "\\'"),
|
||||
item.path,
|
||||
if let Some(p) = item.parent_idx { format!(",'parent':{}", p) } else { String::new() }
|
||||
)
|
||||
}
|
||||
|
||||
let dst = cx.dst.join(&format!("aliases{}.js", cx.shared.resource_suffix));
|
||||
{
|
||||
let (mut all_aliases, _) = try_err!(collect(&dst, &krate.name, "ALIASES"), &dst);
|
||||
let mut output = String::with_capacity(100);
|
||||
for (alias, items) in &cx.cache.aliases {
|
||||
if items.is_empty() {
|
||||
continue;
|
||||
}
|
||||
output.push_str(&format!(
|
||||
"\"{}\":[{}],",
|
||||
alias,
|
||||
items.iter().map(|v| show_item(v, &krate.name)).collect::<Vec<_>>().join(",")
|
||||
));
|
||||
}
|
||||
all_aliases.push(format!("ALIASES[\"{}\"] = {{{}}};", krate.name, output));
|
||||
all_aliases.sort();
|
||||
let mut v = Buffer::html();
|
||||
writeln!(&mut v, "var ALIASES = {{}};");
|
||||
for aliases in &all_aliases {
|
||||
writeln!(&mut v, "{}", aliases);
|
||||
}
|
||||
cx.shared.fs.write(&dst, v.into_inner().into_bytes())?;
|
||||
}
|
||||
|
||||
use std::ffi::OsString;
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
|
@ -120,7 +120,7 @@ crate struct Cache {
|
|||
|
||||
/// Aliases added through `#[doc(alias = "...")]`. Since a few items can have the same alias,
|
||||
/// we need the alias element to have an array of items.
|
||||
pub(super) aliases: FxHashMap<String, Vec<IndexItem>>,
|
||||
pub(super) aliases: BTreeMap<String, Vec<usize>>,
|
||||
}
|
||||
|
||||
impl Cache {
|
||||
|
@ -311,7 +311,7 @@ impl DocFolder for Cache {
|
|||
};
|
||||
|
||||
match parent {
|
||||
(parent, Some(path)) if is_inherent_impl_item || (!self.stripped_mod) => {
|
||||
(parent, Some(path)) if is_inherent_impl_item || !self.stripped_mod => {
|
||||
debug_assert!(!item.is_stripped());
|
||||
|
||||
// A crate has a module at its root, containing all items,
|
||||
|
@ -327,6 +327,13 @@ impl DocFolder for Cache {
|
|||
parent_idx: None,
|
||||
search_type: get_index_search_type(&item),
|
||||
});
|
||||
|
||||
for alias in item.attrs.get_doc_aliases() {
|
||||
self.aliases
|
||||
.entry(alias.to_lowercase())
|
||||
.or_insert(Vec::new())
|
||||
.push(self.search_index.len() - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
(Some(parent), None) if is_inherent_impl_item => {
|
||||
|
@ -376,11 +383,8 @@ impl DocFolder for Cache {
|
|||
{
|
||||
self.paths.insert(item.def_id, (self.stack.clone(), item.type_()));
|
||||
}
|
||||
self.add_aliases(&item);
|
||||
}
|
||||
|
||||
clean::PrimitiveItem(..) => {
|
||||
self.add_aliases(&item);
|
||||
self.paths.insert(item.def_id, (self.stack.clone(), item.type_()));
|
||||
}
|
||||
|
||||
|
@ -488,40 +492,6 @@ impl DocFolder for Cache {
|
|||
}
|
||||
}
|
||||
|
||||
impl Cache {
|
||||
fn add_aliases(&mut self, item: &clean::Item) {
|
||||
if item.def_id.index == CRATE_DEF_INDEX {
|
||||
return;
|
||||
}
|
||||
if let Some(ref item_name) = item.name {
|
||||
let path = self
|
||||
.paths
|
||||
.get(&item.def_id)
|
||||
.map(|p| p.0[..p.0.len() - 1].join("::"))
|
||||
.unwrap_or("std".to_owned());
|
||||
for alias in item
|
||||
.attrs
|
||||
.lists(sym::doc)
|
||||
.filter(|a| a.check_name(sym::alias))
|
||||
.filter_map(|a| a.value_str().map(|s| s.to_string().replace("\"", "")))
|
||||
.filter(|v| !v.is_empty())
|
||||
.collect::<FxHashSet<_>>()
|
||||
.into_iter()
|
||||
{
|
||||
self.aliases.entry(alias).or_insert(Vec::with_capacity(1)).push(IndexItem {
|
||||
ty: item.type_(),
|
||||
name: item_name.to_string(),
|
||||
path: path.clone(),
|
||||
desc: shorten(plain_summary_line(item.doc_value())),
|
||||
parent: None,
|
||||
parent_idx: None,
|
||||
search_type: get_index_search_type(&item),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempts to find where an external crate is located, given that we're
|
||||
/// rendering in to the specified source destination.
|
||||
fn extern_location(
|
||||
|
@ -567,7 +537,8 @@ fn build_index(krate: &clean::Crate, cache: &mut Cache) -> String {
|
|||
let mut crate_items = Vec::with_capacity(cache.search_index.len());
|
||||
let mut crate_paths = vec![];
|
||||
|
||||
let Cache { ref mut search_index, ref orphan_impl_items, ref paths, .. } = *cache;
|
||||
let Cache { ref mut search_index, ref orphan_impl_items, ref paths, ref mut aliases, .. } =
|
||||
*cache;
|
||||
|
||||
// Attach all orphan items to the type's definition if the type
|
||||
// has since been learned.
|
||||
|
@ -582,6 +553,12 @@ fn build_index(krate: &clean::Crate, cache: &mut Cache) -> String {
|
|||
parent_idx: None,
|
||||
search_type: get_index_search_type(&item),
|
||||
});
|
||||
for alias in item.attrs.get_doc_aliases() {
|
||||
aliases
|
||||
.entry(alias.to_lowercase())
|
||||
.or_insert(Vec::new())
|
||||
.push(search_index.len() - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -630,6 +607,12 @@ fn build_index(krate: &clean::Crate, cache: &mut Cache) -> String {
|
|||
items: Vec<&'a IndexItem>,
|
||||
#[serde(rename = "p")]
|
||||
paths: Vec<(ItemType, String)>,
|
||||
// The String is alias name and the vec is the list of the elements with this alias.
|
||||
//
|
||||
// To be noted: the `usize` elements are indexes to `items`.
|
||||
#[serde(rename = "a")]
|
||||
#[serde(skip_serializing_if = "BTreeMap::is_empty")]
|
||||
aliases: &'a BTreeMap<String, Vec<usize>>,
|
||||
}
|
||||
|
||||
// Collect the index into a string
|
||||
|
@ -640,6 +623,7 @@ fn build_index(krate: &clean::Crate, cache: &mut Cache) -> String {
|
|||
doc: crate_doc,
|
||||
items: crate_items,
|
||||
paths: crate_paths,
|
||||
aliases,
|
||||
})
|
||||
.expect("failed serde conversion")
|
||||
// All these `replace` calls are because we have to go through JS string for JSON content.
|
||||
|
|
|
@ -531,6 +531,7 @@ function getSearchElement() {
|
|||
var OUTPUT_DATA = 1;
|
||||
var NO_TYPE_FILTER = -1;
|
||||
var currentResults, index, searchIndex;
|
||||
var ALIASES = {};
|
||||
var params = getQueryStringParams();
|
||||
|
||||
// Populate search bar with query string search term when provided,
|
||||
|
@ -963,6 +964,72 @@ function getSearchElement() {
|
|||
return itemTypes[ty.ty] + ty.path + ty.name;
|
||||
}
|
||||
|
||||
function createAliasFromItem(item) {
|
||||
return {
|
||||
crate: item.crate,
|
||||
name: item.name,
|
||||
path: item.path,
|
||||
desc: item.desc,
|
||||
ty: item.ty,
|
||||
parent: item.parent,
|
||||
type: item.type,
|
||||
is_alias: true,
|
||||
};
|
||||
}
|
||||
|
||||
function handleAliases(ret, query, filterCrates) {
|
||||
// We separate aliases and crate aliases because we want to have current crate
|
||||
// aliases to be before the others in the displayed results.
|
||||
var aliases = [];
|
||||
var crateAliases = [];
|
||||
var i;
|
||||
if (filterCrates !== undefined &&
|
||||
ALIASES[filterCrates] &&
|
||||
ALIASES[filterCrates][query.search]) {
|
||||
for (i = 0; i < ALIASES[crate][query.search].length; ++i) {
|
||||
aliases.push(
|
||||
createAliasFromItem(searchIndex[ALIASES[filterCrates][query.search]]));
|
||||
}
|
||||
} else {
|
||||
Object.keys(ALIASES).forEach(function(crate) {
|
||||
if (ALIASES[crate][query.search]) {
|
||||
var pushTo = crate === window.currentCrate ? crateAliases : aliases;
|
||||
for (i = 0; i < ALIASES[crate][query.search].length; ++i) {
|
||||
pushTo.push(
|
||||
createAliasFromItem(
|
||||
searchIndex[ALIASES[crate][query.search][i]]));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var sortFunc = function(aaa, bbb) {
|
||||
if (aaa.path < bbb.path) {
|
||||
return 1;
|
||||
} else if (aaa.path === bbb.path) {
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
};
|
||||
crateAliases.sort(sortFunc);
|
||||
aliases.sort(sortFunc);
|
||||
|
||||
var pushFunc = function(alias) {
|
||||
alias.alias = query.raw;
|
||||
var res = buildHrefAndPath(alias);
|
||||
alias.displayPath = pathSplitter(res[0]);
|
||||
alias.fullPath = alias.displayPath + alias.name;
|
||||
alias.href = res[1];
|
||||
|
||||
ret.others.unshift(alias);
|
||||
if (ret.others.length > MAX_RESULTS) {
|
||||
ret.others.pop();
|
||||
}
|
||||
};
|
||||
onEach(aliases, pushFunc);
|
||||
onEach(crateAliases, pushFunc);
|
||||
}
|
||||
|
||||
// quoted values mean literal search
|
||||
var nSearchWords = searchWords.length;
|
||||
var i;
|
||||
|
@ -1190,23 +1257,7 @@ function getSearchElement() {
|
|||
"returned": sortResults(results_returned, true),
|
||||
"others": sortResults(results),
|
||||
};
|
||||
if (ALIASES && ALIASES[window.currentCrate] &&
|
||||
ALIASES[window.currentCrate][query.raw]) {
|
||||
var aliases = ALIASES[window.currentCrate][query.raw];
|
||||
for (i = 0; i < aliases.length; ++i) {
|
||||
aliases[i].is_alias = true;
|
||||
aliases[i].alias = query.raw;
|
||||
aliases[i].path = aliases[i].p;
|
||||
var res = buildHrefAndPath(aliases[i]);
|
||||
aliases[i].displayPath = pathSplitter(res[0]);
|
||||
aliases[i].fullPath = aliases[i].displayPath + aliases[i].name;
|
||||
aliases[i].href = res[1];
|
||||
ret.others.unshift(aliases[i]);
|
||||
if (ret.others.length > MAX_RESULTS) {
|
||||
ret.others.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
handleAliases(ret, query, filterCrates);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -1599,14 +1650,13 @@ function getSearchElement() {
|
|||
"returned": mergeArrays(results.returned),
|
||||
"others": mergeArrays(results.others),
|
||||
};
|
||||
} else {
|
||||
}
|
||||
return {
|
||||
"in_args": results.in_args[0],
|
||||
"returned": results.returned[0],
|
||||
"others": results.others[0],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function getFilterCrates() {
|
||||
var elem = document.getElementById("crate-search");
|
||||
|
@ -1656,10 +1706,13 @@ function getSearchElement() {
|
|||
searchIndex = [];
|
||||
var searchWords = [];
|
||||
var i;
|
||||
var currentIndex = 0;
|
||||
|
||||
for (var crate in rawSearchIndex) {
|
||||
if (!rawSearchIndex.hasOwnProperty(crate)) { continue; }
|
||||
|
||||
var crateSize = 0;
|
||||
|
||||
searchWords.push(crate);
|
||||
searchIndex.push({
|
||||
crate: crate,
|
||||
|
@ -1669,6 +1722,7 @@ function getSearchElement() {
|
|||
desc: rawSearchIndex[crate].doc,
|
||||
type: null,
|
||||
});
|
||||
currentIndex += 1;
|
||||
|
||||
// an array of [(Number) item type,
|
||||
// (String) name,
|
||||
|
@ -1680,6 +1734,9 @@ function getSearchElement() {
|
|||
// an array of [(Number) item type,
|
||||
// (String) name]
|
||||
var paths = rawSearchIndex[crate].p;
|
||||
// a array of [(String) alias name
|
||||
// [Number] index to items]
|
||||
var aliases = rawSearchIndex[crate].a;
|
||||
|
||||
// convert `rawPaths` entries into object form
|
||||
var len = paths.length;
|
||||
|
@ -1698,9 +1755,18 @@ function getSearchElement() {
|
|||
var lastPath = "";
|
||||
for (i = 0; i < len; ++i) {
|
||||
var rawRow = items[i];
|
||||
var row = {crate: crate, ty: rawRow[0], name: rawRow[1],
|
||||
path: rawRow[2] || lastPath, desc: rawRow[3],
|
||||
parent: paths[rawRow[4]], type: rawRow[5]};
|
||||
if (!rawRow[2]) {
|
||||
rawRow[2] = lastPath;
|
||||
}
|
||||
var row = {
|
||||
crate: crate,
|
||||
ty: rawRow[0],
|
||||
name: rawRow[1],
|
||||
path: rawRow[2],
|
||||
desc: rawRow[3],
|
||||
parent: paths[rawRow[4]],
|
||||
type: rawRow[5],
|
||||
};
|
||||
searchIndex.push(row);
|
||||
if (typeof row.name === "string") {
|
||||
var word = row.name.toLowerCase();
|
||||
|
@ -1709,7 +1775,25 @@ function getSearchElement() {
|
|||
searchWords.push("");
|
||||
}
|
||||
lastPath = row.path;
|
||||
crateSize += 1;
|
||||
}
|
||||
|
||||
if (aliases) {
|
||||
ALIASES[crate] = {};
|
||||
var j, local_aliases;
|
||||
for (var alias_name in aliases) {
|
||||
if (!aliases.hasOwnProperty(alias_name)) { continue; }
|
||||
|
||||
if (!ALIASES[crate].hasOwnProperty(alias_name)) {
|
||||
ALIASES[crate][alias_name] = [];
|
||||
}
|
||||
local_aliases = aliases[alias_name];
|
||||
for (j = 0; j < local_aliases.length; ++j) {
|
||||
ALIASES[crate][alias_name].push(local_aliases[j] + currentIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
currentIndex += crateSize;
|
||||
}
|
||||
return searchWords;
|
||||
}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
// ignore-order
|
||||
|
||||
const QUERY = '+';
|
||||
|
||||
const EXPECTED = {
|
||||
'others': [
|
||||
{ 'path': 'std::ops', 'name': 'AddAssign' },
|
||||
{ 'path': 'std::ops', 'name': 'Add' },
|
||||
{ 'path': 'core::ops', 'name': 'AddAssign' },
|
||||
{ 'path': 'core::ops', 'name': 'Add' },
|
||||
],
|
||||
};
|
||||
|
|
263
src/test/rustdoc-js/doc-alias.js
Normal file
263
src/test/rustdoc-js/doc-alias.js
Normal file
|
@ -0,0 +1,263 @@
|
|||
// exact-check
|
||||
|
||||
const QUERY = [
|
||||
'StructItem',
|
||||
'StructFieldItem',
|
||||
'StructMethodItem',
|
||||
'ImplTraitItem',
|
||||
'ImplAssociatedConstItem',
|
||||
'ImplTraitFunction',
|
||||
'EnumItem',
|
||||
'VariantItem',
|
||||
'EnumMethodItem',
|
||||
'TypedefItem',
|
||||
'TraitItem',
|
||||
'TraitTypeItem',
|
||||
'AssociatedConstItem',
|
||||
'TraitFunctionItem',
|
||||
'FunctionItem',
|
||||
'ModuleItem',
|
||||
'ConstItem',
|
||||
'StaticItem',
|
||||
'UnionItem',
|
||||
'UnionFieldItem',
|
||||
'UnionMethodItem',
|
||||
'MacroItem',
|
||||
];
|
||||
|
||||
const EXPECTED = [
|
||||
{
|
||||
'others': [
|
||||
{
|
||||
'path': 'doc_alias',
|
||||
'name': 'Struct',
|
||||
'alias': 'StructItem',
|
||||
'href': '../doc_alias/struct.Struct.html',
|
||||
'is_alias': true
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
'others': [
|
||||
{
|
||||
'path': 'doc_alias::Struct',
|
||||
'name': 'field',
|
||||
'alias': 'StructFieldItem',
|
||||
'href': '../doc_alias/struct.Struct.html#structfield.field',
|
||||
'is_alias': true
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
'others': [
|
||||
{
|
||||
'path': 'doc_alias::Struct',
|
||||
'name': 'method',
|
||||
'alias': 'StructMethodItem',
|
||||
'href': '../doc_alias/struct.Struct.html#method.method',
|
||||
'is_alias': true
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
// ImplTraitItem
|
||||
'others': [],
|
||||
},
|
||||
{
|
||||
// ImplAssociatedConstItem
|
||||
'others': [],
|
||||
},
|
||||
{
|
||||
'others': [
|
||||
{
|
||||
'path': 'doc_alias::Struct',
|
||||
'name': 'function',
|
||||
'alias': 'ImplTraitFunction',
|
||||
'href': '../doc_alias/struct.Struct.html#method.function',
|
||||
'is_alias': true
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
'others': [
|
||||
{
|
||||
'path': 'doc_alias',
|
||||
'name': 'Enum',
|
||||
'alias': 'EnumItem',
|
||||
'href': '../doc_alias/enum.Enum.html',
|
||||
'is_alias': true
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
'others': [
|
||||
{
|
||||
'path': 'doc_alias::Enum',
|
||||
'name': 'Variant',
|
||||
'alias': 'VariantItem',
|
||||
'href': '../doc_alias/enum.Enum.html#variant.Variant',
|
||||
'is_alias': true
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
'others': [
|
||||
{
|
||||
'path': 'doc_alias::Enum',
|
||||
'name': 'method',
|
||||
'alias': 'EnumMethodItem',
|
||||
'href': '../doc_alias/enum.Enum.html#method.method',
|
||||
'is_alias': true
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
'others': [
|
||||
{
|
||||
'path': 'doc_alias',
|
||||
'name': 'Typedef',
|
||||
'alias': 'TypedefItem',
|
||||
'href': '../doc_alias/type.Typedef.html',
|
||||
'is_alias': true
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
'others': [
|
||||
{
|
||||
'path': 'doc_alias',
|
||||
'name': 'Trait',
|
||||
'alias': 'TraitItem',
|
||||
'href': '../doc_alias/trait.Trait.html',
|
||||
'is_alias': true
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
'others': [
|
||||
{
|
||||
'path': 'doc_alias::Trait',
|
||||
'name': 'Target',
|
||||
'alias': 'TraitTypeItem',
|
||||
'href': '../doc_alias/trait.Trait.html#associatedtype.Target',
|
||||
'is_alias': true
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
'others': [
|
||||
{
|
||||
'path': 'doc_alias::Trait',
|
||||
'name': 'AssociatedConst',
|
||||
'alias': 'AssociatedConstItem',
|
||||
'href': '../doc_alias/trait.Trait.html#associatedconstant.AssociatedConst',
|
||||
'is_alias': true
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
'others': [
|
||||
{
|
||||
'path': 'doc_alias::Trait',
|
||||
'name': 'function',
|
||||
'alias': 'TraitFunctionItem',
|
||||
'href': '../doc_alias/trait.Trait.html#tymethod.function',
|
||||
'is_alias': true
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
'others': [
|
||||
{
|
||||
'path': 'doc_alias',
|
||||
'name': 'function',
|
||||
'alias': 'FunctionItem',
|
||||
'href': '../doc_alias/fn.function.html',
|
||||
'is_alias': true
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
'others': [
|
||||
{
|
||||
'path': 'doc_alias',
|
||||
'name': 'Module',
|
||||
'alias': 'ModuleItem',
|
||||
'href': '../doc_alias/Module/index.html',
|
||||
'is_alias': true
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
'others': [
|
||||
{
|
||||
'path': 'doc_alias',
|
||||
'name': 'Const',
|
||||
'alias': 'ConstItem',
|
||||
'href': '../doc_alias/constant.Const.html',
|
||||
'is_alias': true
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
'others': [
|
||||
{
|
||||
'path': 'doc_alias',
|
||||
'name': 'Static',
|
||||
'alias': 'StaticItem',
|
||||
'href': '../doc_alias/static.Static.html',
|
||||
'is_alias': true
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
'others': [
|
||||
{
|
||||
'path': 'doc_alias',
|
||||
'name': 'Union',
|
||||
'alias': 'UnionItem',
|
||||
'href': '../doc_alias/union.Union.html',
|
||||
'is_alias': true
|
||||
},
|
||||
// Not an alias!
|
||||
{
|
||||
'path': 'doc_alias::Union',
|
||||
'name': 'union_item',
|
||||
'href': '../doc_alias/union.Union.html#structfield.union_item'
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
'others': [
|
||||
{
|
||||
'path': 'doc_alias::Union',
|
||||
'name': 'union_item',
|
||||
'alias': 'UnionFieldItem',
|
||||
'href': '../doc_alias/union.Union.html#structfield.union_item',
|
||||
'is_alias': true
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
'others': [
|
||||
{
|
||||
'path': 'doc_alias::Union',
|
||||
'name': 'method',
|
||||
'alias': 'UnionMethodItem',
|
||||
'href': '../doc_alias/union.Union.html#method.method',
|
||||
'is_alias': true
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
'others': [
|
||||
{
|
||||
'path': 'doc_alias',
|
||||
'name': 'Macro',
|
||||
'alias': 'MacroItem',
|
||||
'href': '../doc_alias/macro.Macro.html',
|
||||
'is_alias': true
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
79
src/test/rustdoc-js/doc-alias.rs
Normal file
79
src/test/rustdoc-js/doc-alias.rs
Normal file
|
@ -0,0 +1,79 @@
|
|||
#![feature(doc_alias)]
|
||||
|
||||
#[doc(alias = "StructItem")]
|
||||
pub struct Struct {
|
||||
#[doc(alias = "StructFieldItem")]
|
||||
pub field: u32,
|
||||
}
|
||||
|
||||
impl Struct {
|
||||
#[doc(alias = "StructMethodItem")]
|
||||
pub fn method(&self) {}
|
||||
}
|
||||
|
||||
impl Trait for Struct {
|
||||
// Shouldn't be listed in aliases!
|
||||
#[doc(alias = "ImplTraitItem")]
|
||||
type Target = u32;
|
||||
// Shouldn't be listed in aliases!
|
||||
#[doc(alias = "ImplAssociatedConstItem")]
|
||||
const AssociatedConst: i32 = 12;
|
||||
|
||||
#[doc(alias = "ImplTraitFunction")]
|
||||
fn function() -> Self::Target { 0 }
|
||||
}
|
||||
|
||||
#[doc(alias = "EnumItem")]
|
||||
pub enum Enum {
|
||||
#[doc(alias = "VariantItem")]
|
||||
Variant,
|
||||
}
|
||||
|
||||
impl Enum {
|
||||
#[doc(alias = "EnumMethodItem")]
|
||||
pub fn method(&self) {}
|
||||
}
|
||||
|
||||
#[doc(alias = "TypedefItem")]
|
||||
pub type Typedef = i32;
|
||||
|
||||
#[doc(alias = "TraitItem")]
|
||||
pub trait Trait {
|
||||
#[doc(alias = "TraitTypeItem")]
|
||||
type Target;
|
||||
#[doc(alias = "AssociatedConstItem")]
|
||||
const AssociatedConst: i32;
|
||||
|
||||
#[doc(alias = "TraitFunctionItem")]
|
||||
fn function() -> Self::Target;
|
||||
}
|
||||
|
||||
#[doc(alias = "FunctionItem")]
|
||||
pub fn function() {}
|
||||
|
||||
#[doc(alias = "ModuleItem")]
|
||||
pub mod Module {}
|
||||
|
||||
#[doc(alias = "ConstItem")]
|
||||
pub const Const: u32 = 0;
|
||||
|
||||
#[doc(alias = "StaticItem")]
|
||||
pub static Static: u32 = 0;
|
||||
|
||||
#[doc(alias = "UnionItem")]
|
||||
pub union Union {
|
||||
#[doc(alias = "UnionFieldItem")]
|
||||
pub union_item: u32,
|
||||
pub y: f32,
|
||||
}
|
||||
|
||||
impl Union {
|
||||
#[doc(alias = "UnionMethodItem")]
|
||||
pub fn method(&self) {}
|
||||
}
|
||||
|
||||
#[doc(alias = "MacroItem")]
|
||||
#[macro_export]
|
||||
macro_rules! Macro {
|
||||
() => {}
|
||||
}
|
|
@ -181,7 +181,7 @@ function loadThings(thingsToLoad, kindOfLoad, funcToCall, fileContent) {
|
|||
for (var i = 0; i < thingsToLoad.length; ++i) {
|
||||
var tmp = funcToCall(fileContent, thingsToLoad[i]);
|
||||
if (tmp === null) {
|
||||
console.error('unable to find ' + kindOfLoad + ' "' + thingsToLoad[i] + '"');
|
||||
console.log('unable to find ' + kindOfLoad + ' "' + thingsToLoad[i] + '"');
|
||||
process.exit(1);
|
||||
}
|
||||
content += tmp;
|
||||
|
@ -218,12 +218,13 @@ function lookForEntry(entry, data) {
|
|||
return null;
|
||||
}
|
||||
|
||||
function loadMainJsAndIndex(mainJs, aliases, searchIndex, crate) {
|
||||
function loadMainJsAndIndex(mainJs, searchIndex, storageJs, crate) {
|
||||
if (searchIndex[searchIndex.length - 1].length === 0) {
|
||||
searchIndex.pop();
|
||||
}
|
||||
searchIndex.pop();
|
||||
searchIndex = loadContent(searchIndex.join("\n") + '\nexports.searchIndex = searchIndex;');
|
||||
var fullSearchIndex = searchIndex.join("\n") + '\nexports.rawSearchIndex = searchIndex;';
|
||||
searchIndex = loadContent(fullSearchIndex);
|
||||
var finalJS = "";
|
||||
|
||||
var arraysToLoad = ["itemTypes"];
|
||||
|
@ -235,34 +236,28 @@ function loadMainJsAndIndex(mainJs, aliases, searchIndex, crate) {
|
|||
// execQuery last parameter is built in buildIndex.
|
||||
// buildIndex requires the hashmap from search-index.
|
||||
var functionsToLoad = ["buildHrefAndPath", "pathSplitter", "levenshtein", "validateResult",
|
||||
"getQuery", "buildIndex", "execQuery", "execSearch"];
|
||||
"handleAliases", "getQuery", "buildIndex", "execQuery", "execSearch"];
|
||||
|
||||
ALIASES = {};
|
||||
finalJS += 'window = { "currentCrate": "' + crate + '" };\n';
|
||||
finalJS += 'var rootPath = "../";\n';
|
||||
finalJS += aliases;
|
||||
finalJS += loadThings(["onEach"], 'function', extractFunction, storageJs);
|
||||
finalJS += loadThings(arraysToLoad, 'array', extractArrayVariable, mainJs);
|
||||
finalJS += loadThings(variablesToLoad, 'variable', extractVariable, mainJs);
|
||||
finalJS += loadThings(functionsToLoad, 'function', extractFunction, mainJs);
|
||||
|
||||
var loaded = loadContent(finalJS);
|
||||
var index = loaded.buildIndex(searchIndex.searchIndex);
|
||||
var index = loaded.buildIndex(searchIndex.rawSearchIndex);
|
||||
|
||||
return [loaded, index];
|
||||
}
|
||||
|
||||
function runChecks(testFile, loaded, index) {
|
||||
var errors = 0;
|
||||
var loadedFile = loadContent(
|
||||
readFile(testFile) + 'exports.QUERY = QUERY;exports.EXPECTED = EXPECTED;');
|
||||
|
||||
const expected = loadedFile.EXPECTED;
|
||||
const query = loadedFile.QUERY;
|
||||
function runSearch(query, expected, index, loaded, loadedFile, queryName) {
|
||||
const filter_crate = loadedFile.FILTER_CRATE;
|
||||
const ignore_order = loadedFile.ignore_order;
|
||||
const exact_check = loadedFile.exact_check;
|
||||
const should_fail = loadedFile.should_fail;
|
||||
|
||||
var results = loaded.execSearch(loaded.getQuery(query), index);
|
||||
var results = loaded.execSearch(loaded.getQuery(query), index, filter_crate);
|
||||
var error_text = [];
|
||||
|
||||
for (var key in expected) {
|
||||
|
@ -278,41 +273,77 @@ function runChecks(testFile, loaded, index) {
|
|||
for (var i = 0; i < entry.length; ++i) {
|
||||
var entry_pos = lookForEntry(entry[i], results[key]);
|
||||
if (entry_pos === null) {
|
||||
error_text.push("==> Result not found in '" + key + "': '" +
|
||||
error_text.push(queryName + "==> Result not found in '" + key + "': '" +
|
||||
JSON.stringify(entry[i]) + "'");
|
||||
} else if (exact_check === true && prev_pos + 1 !== entry_pos) {
|
||||
error_text.push("==> Exact check failed at position " + (prev_pos + 1) + ": " +
|
||||
"expected '" + JSON.stringify(entry[i]) + "' but found '" +
|
||||
error_text.push(queryName + "==> Exact check failed at position " + (prev_pos + 1) +
|
||||
": expected '" + JSON.stringify(entry[i]) + "' but found '" +
|
||||
JSON.stringify(results[key][i]) + "'");
|
||||
} else if (ignore_order === false && entry_pos < prev_pos) {
|
||||
error_text.push("==> '" + JSON.stringify(entry[i]) + "' was supposed to be " +
|
||||
" before '" + JSON.stringify(results[key][entry_pos]) + "'");
|
||||
error_text.push(queryName + "==> '" + JSON.stringify(entry[i]) + "' was supposed " +
|
||||
"to be before '" + JSON.stringify(results[key][entry_pos]) + "'");
|
||||
} else {
|
||||
prev_pos = entry_pos;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (error_text.length === 0 && should_fail === true) {
|
||||
errors += 1;
|
||||
console.error("FAILED");
|
||||
console.error("==> Test was supposed to fail but all items were found...");
|
||||
} else if (error_text.length !== 0 && should_fail === false) {
|
||||
errors += 1;
|
||||
console.error("FAILED");
|
||||
console.error(error_text.join("\n"));
|
||||
return error_text;
|
||||
}
|
||||
|
||||
function checkResult(error_text, loadedFile, displaySuccess) {
|
||||
if (error_text.length === 0 && loadedFile.should_fail === true) {
|
||||
console.log("FAILED");
|
||||
console.log("==> Test was supposed to fail but all items were found...");
|
||||
} else if (error_text.length !== 0 && loadedFile.should_fail === false) {
|
||||
console.log("FAILED");
|
||||
console.log(error_text.join("\n"));
|
||||
} else {
|
||||
if (displaySuccess) {
|
||||
console.log("OK");
|
||||
}
|
||||
return errors;
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
function runChecks(testFile, loaded, index) {
|
||||
var loadedFile = loadContent(
|
||||
readFile(testFile) + 'exports.QUERY = QUERY;exports.EXPECTED = EXPECTED;');
|
||||
|
||||
const expected = loadedFile.EXPECTED;
|
||||
const query = loadedFile.QUERY;
|
||||
|
||||
if (Array.isArray(query)) {
|
||||
if (!Array.isArray(expected)) {
|
||||
console.log("FAILED");
|
||||
console.log("==> If QUERY variable is an array, EXPECTED should be an array too");
|
||||
return 1;
|
||||
} else if (query.length !== expected.length) {
|
||||
console.log("FAILED");
|
||||
console.log("==> QUERY variable should have the same length as EXPECTED");
|
||||
return 1;
|
||||
}
|
||||
for (var i = 0; i < query.length; ++i) {
|
||||
var error_text = runSearch(query[i], expected[i], index, loaded, loadedFile,
|
||||
"[ query `" + query[i] + "`]");
|
||||
if (checkResult(error_text, loadedFile, false) !== 0) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
console.log("OK");
|
||||
return 0;
|
||||
}
|
||||
var error_text = runSearch(query, expected, index, loaded, loadedFile, "");
|
||||
return checkResult(error_text, loadedFile, true);
|
||||
}
|
||||
|
||||
function load_files(doc_folder, resource_suffix, crate) {
|
||||
var mainJs = readFile(path.join(doc_folder, "main" + resource_suffix + ".js"));
|
||||
var aliases = readFile(path.join(doc_folder, "aliases" + resource_suffix + ".js"));
|
||||
var storageJs = readFile(path.join(doc_folder, "storage" + resource_suffix + ".js"));
|
||||
var searchIndex = readFile(
|
||||
path.join(doc_folder, "search-index" + resource_suffix + ".js")).split("\n");
|
||||
|
||||
return loadMainJsAndIndex(mainJs, aliases, searchIndex, crate);
|
||||
return loadMainJsAndIndex(mainJs, searchIndex, storageJs, crate);
|
||||
}
|
||||
|
||||
function showHelp() {
|
||||
|
@ -349,7 +380,7 @@ function parseOptions(args) {
|
|||
|| args[i] === "--crate-name") {
|
||||
i += 1;
|
||||
if (i >= args.length) {
|
||||
console.error("Missing argument after `" + args[i - 1] + "` option.");
|
||||
console.log("Missing argument after `" + args[i - 1] + "` option.");
|
||||
return null;
|
||||
}
|
||||
opts[correspondances[args[i - 1]]] = args[i];
|
||||
|
@ -357,17 +388,17 @@ function parseOptions(args) {
|
|||
showHelp();
|
||||
process.exit(0);
|
||||
} else {
|
||||
console.error("Unknown option `" + args[i] + "`.");
|
||||
console.error("Use `--help` to see the list of options");
|
||||
console.log("Unknown option `" + args[i] + "`.");
|
||||
console.log("Use `--help` to see the list of options");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
if (opts["doc_folder"].length < 1) {
|
||||
console.error("Missing `--doc-folder` option.");
|
||||
console.log("Missing `--doc-folder` option.");
|
||||
} else if (opts["crate_name"].length < 1) {
|
||||
console.error("Missing `--crate-name` option.");
|
||||
console.log("Missing `--crate-name` option.");
|
||||
} else if (opts["test_folder"].length < 1 && opts["test_file"].length < 1) {
|
||||
console.error("At least one of `--test-folder` or `--test-file` option is required.");
|
||||
console.log("At least one of `--test-folder` or `--test-file` option is required.");
|
||||
} else {
|
||||
return opts;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue