Auto merge of #119912 - notriddle:notriddle/reexport-dedup, r=GuillaumeGomez
rustdoc-search: single result for items with multiple paths Part of #15723 Preview: https://notriddle.com/rustdoc-html-demo-9/reexport-dup/std/index.html?search=hashmap This change uses the same "exact" paths as trait implementors and type alias inlining to track items with multiple reachable paths. This way, if you search for `vec`, you get only the `std` exports of it, and not the one from `alloc`. It still includes all the items in the search index so that you can search for them by all available paths. For example, try `core::option` and `std::option`, and notice that the results page doesn't show duplicates, but still shows all the items in their respective crates.
This commit is contained in:
commit
e3181b091e
14 changed files with 330 additions and 31 deletions
|
@ -346,16 +346,28 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> {
|
||||||
{
|
{
|
||||||
let desc =
|
let desc =
|
||||||
short_markdown_summary(&item.doc_value(), &item.link_names(self.cache));
|
short_markdown_summary(&item.doc_value(), &item.link_names(self.cache));
|
||||||
|
// For searching purposes, a re-export is a duplicate if:
|
||||||
|
//
|
||||||
|
// - It's either an inline, or a true re-export
|
||||||
|
// - It's got the same name
|
||||||
|
// - Both of them have the same exact path
|
||||||
|
let defid = (match &*item.kind {
|
||||||
|
&clean::ItemKind::ImportItem(ref import) => import.source.did,
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.or_else(|| item.item_id.as_def_id());
|
||||||
// In case this is a field from a tuple struct, we don't add it into
|
// In case this is a field from a tuple struct, we don't add it into
|
||||||
// the search index because its name is something like "0", which is
|
// the search index because its name is something like "0", which is
|
||||||
// not useful for rustdoc search.
|
// not useful for rustdoc search.
|
||||||
self.cache.search_index.push(IndexItem {
|
self.cache.search_index.push(IndexItem {
|
||||||
ty,
|
ty,
|
||||||
|
defid,
|
||||||
name: s,
|
name: s,
|
||||||
path: join_with_double_colon(path),
|
path: join_with_double_colon(path),
|
||||||
desc,
|
desc,
|
||||||
parent,
|
parent,
|
||||||
parent_idx: None,
|
parent_idx: None,
|
||||||
|
exact_path: None,
|
||||||
impl_id: if let Some(ParentStackItem::Impl { item_id, .. }) =
|
impl_id: if let Some(ParentStackItem::Impl { item_id, .. }) =
|
||||||
self.cache.parent_stack.last()
|
self.cache.parent_stack.last()
|
||||||
{
|
{
|
||||||
|
|
|
@ -111,11 +111,13 @@ pub(crate) enum RenderMode {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct IndexItem {
|
pub(crate) struct IndexItem {
|
||||||
pub(crate) ty: ItemType,
|
pub(crate) ty: ItemType,
|
||||||
|
pub(crate) defid: Option<DefId>,
|
||||||
pub(crate) name: Symbol,
|
pub(crate) name: Symbol,
|
||||||
pub(crate) path: String,
|
pub(crate) path: String,
|
||||||
pub(crate) desc: String,
|
pub(crate) desc: String,
|
||||||
pub(crate) parent: Option<DefId>,
|
pub(crate) parent: Option<DefId>,
|
||||||
pub(crate) parent_idx: Option<isize>,
|
pub(crate) parent_idx: Option<isize>,
|
||||||
|
pub(crate) exact_path: Option<String>,
|
||||||
pub(crate) impl_id: Option<DefId>,
|
pub(crate) impl_id: Option<DefId>,
|
||||||
pub(crate) search_type: Option<IndexItemFunctionType>,
|
pub(crate) search_type: Option<IndexItemFunctionType>,
|
||||||
pub(crate) aliases: Box<[Symbol]>,
|
pub(crate) aliases: Box<[Symbol]>,
|
||||||
|
|
|
@ -59,10 +59,13 @@ pub(crate) fn build_index<'tcx>(
|
||||||
cache: &mut Cache,
|
cache: &mut Cache,
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
) -> SerializedSearchIndex {
|
) -> SerializedSearchIndex {
|
||||||
|
// Maps from ID to position in the `crate_paths` array.
|
||||||
let mut itemid_to_pathid = FxHashMap::default();
|
let mut itemid_to_pathid = FxHashMap::default();
|
||||||
let mut primitives = FxHashMap::default();
|
let mut primitives = FxHashMap::default();
|
||||||
let mut associated_types = FxHashMap::default();
|
let mut associated_types = FxHashMap::default();
|
||||||
let mut crate_paths = vec![];
|
|
||||||
|
// item type, display path, re-exported internal path
|
||||||
|
let mut crate_paths: Vec<(ItemType, Vec<Symbol>, Option<Vec<Symbol>>)> = vec![];
|
||||||
|
|
||||||
// Attach all orphan items to the type's definition if the type
|
// Attach all orphan items to the type's definition if the type
|
||||||
// has since been learned.
|
// has since been learned.
|
||||||
|
@ -72,11 +75,13 @@ pub(crate) fn build_index<'tcx>(
|
||||||
let desc = short_markdown_summary(&item.doc_value(), &item.link_names(cache));
|
let desc = short_markdown_summary(&item.doc_value(), &item.link_names(cache));
|
||||||
cache.search_index.push(IndexItem {
|
cache.search_index.push(IndexItem {
|
||||||
ty: item.type_(),
|
ty: item.type_(),
|
||||||
|
defid: item.item_id.as_def_id(),
|
||||||
name: item.name.unwrap(),
|
name: item.name.unwrap(),
|
||||||
path: join_with_double_colon(&fqp[..fqp.len() - 1]),
|
path: join_with_double_colon(&fqp[..fqp.len() - 1]),
|
||||||
desc,
|
desc,
|
||||||
parent: Some(parent),
|
parent: Some(parent),
|
||||||
parent_idx: None,
|
parent_idx: None,
|
||||||
|
exact_path: None,
|
||||||
impl_id,
|
impl_id,
|
||||||
search_type: get_function_type_for_search(
|
search_type: get_function_type_for_search(
|
||||||
item,
|
item,
|
||||||
|
@ -126,9 +131,10 @@ pub(crate) fn build_index<'tcx>(
|
||||||
map: &mut FxHashMap<F, isize>,
|
map: &mut FxHashMap<F, isize>,
|
||||||
itemid: F,
|
itemid: F,
|
||||||
lastpathid: &mut isize,
|
lastpathid: &mut isize,
|
||||||
crate_paths: &mut Vec<(ItemType, Vec<Symbol>)>,
|
crate_paths: &mut Vec<(ItemType, Vec<Symbol>, Option<Vec<Symbol>>)>,
|
||||||
item_type: ItemType,
|
item_type: ItemType,
|
||||||
path: &[Symbol],
|
path: &[Symbol],
|
||||||
|
exact_path: Option<&[Symbol]>,
|
||||||
) -> RenderTypeId {
|
) -> RenderTypeId {
|
||||||
match map.entry(itemid) {
|
match map.entry(itemid) {
|
||||||
Entry::Occupied(entry) => RenderTypeId::Index(*entry.get()),
|
Entry::Occupied(entry) => RenderTypeId::Index(*entry.get()),
|
||||||
|
@ -136,7 +142,11 @@ pub(crate) fn build_index<'tcx>(
|
||||||
let pathid = *lastpathid;
|
let pathid = *lastpathid;
|
||||||
entry.insert(pathid);
|
entry.insert(pathid);
|
||||||
*lastpathid += 1;
|
*lastpathid += 1;
|
||||||
crate_paths.push((item_type, path.to_vec()));
|
crate_paths.push((
|
||||||
|
item_type,
|
||||||
|
path.to_vec(),
|
||||||
|
exact_path.map(|path| path.to_vec()),
|
||||||
|
));
|
||||||
RenderTypeId::Index(pathid)
|
RenderTypeId::Index(pathid)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -149,14 +159,24 @@ pub(crate) fn build_index<'tcx>(
|
||||||
primitives: &mut FxHashMap<Symbol, isize>,
|
primitives: &mut FxHashMap<Symbol, isize>,
|
||||||
associated_types: &mut FxHashMap<Symbol, isize>,
|
associated_types: &mut FxHashMap<Symbol, isize>,
|
||||||
lastpathid: &mut isize,
|
lastpathid: &mut isize,
|
||||||
crate_paths: &mut Vec<(ItemType, Vec<Symbol>)>,
|
crate_paths: &mut Vec<(ItemType, Vec<Symbol>, Option<Vec<Symbol>>)>,
|
||||||
) -> Option<RenderTypeId> {
|
) -> Option<RenderTypeId> {
|
||||||
let Cache { ref paths, ref external_paths, .. } = *cache;
|
let Cache { ref paths, ref external_paths, ref exact_paths, .. } = *cache;
|
||||||
match id {
|
match id {
|
||||||
RenderTypeId::DefId(defid) => {
|
RenderTypeId::DefId(defid) => {
|
||||||
if let Some(&(ref fqp, item_type)) =
|
if let Some(&(ref fqp, item_type)) =
|
||||||
paths.get(&defid).or_else(|| external_paths.get(&defid))
|
paths.get(&defid).or_else(|| external_paths.get(&defid))
|
||||||
{
|
{
|
||||||
|
let exact_fqp = exact_paths
|
||||||
|
.get(&defid)
|
||||||
|
.or_else(|| external_paths.get(&defid).map(|&(ref fqp, _)| fqp))
|
||||||
|
// Re-exports only count if the name is exactly the same.
|
||||||
|
// This is a size optimization, since it means we only need
|
||||||
|
// to store the name once (and the path is re-used for everything
|
||||||
|
// exported from this same module). It's also likely to Do
|
||||||
|
// What I Mean, since if a re-export changes the name, it might
|
||||||
|
// also be a change in semantic meaning.
|
||||||
|
.filter(|fqp| fqp.last() == fqp.last());
|
||||||
Some(insert_into_map(
|
Some(insert_into_map(
|
||||||
itemid_to_pathid,
|
itemid_to_pathid,
|
||||||
ItemId::DefId(defid),
|
ItemId::DefId(defid),
|
||||||
|
@ -164,6 +184,7 @@ pub(crate) fn build_index<'tcx>(
|
||||||
crate_paths,
|
crate_paths,
|
||||||
item_type,
|
item_type,
|
||||||
fqp,
|
fqp,
|
||||||
|
exact_fqp.map(|x| &x[..]).filter(|exact_fqp| exact_fqp != fqp),
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
@ -178,6 +199,7 @@ pub(crate) fn build_index<'tcx>(
|
||||||
crate_paths,
|
crate_paths,
|
||||||
ItemType::Primitive,
|
ItemType::Primitive,
|
||||||
&[sym],
|
&[sym],
|
||||||
|
None,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
RenderTypeId::Index(_) => Some(id),
|
RenderTypeId::Index(_) => Some(id),
|
||||||
|
@ -188,6 +210,7 @@ pub(crate) fn build_index<'tcx>(
|
||||||
crate_paths,
|
crate_paths,
|
||||||
ItemType::AssocType,
|
ItemType::AssocType,
|
||||||
&[sym],
|
&[sym],
|
||||||
|
None,
|
||||||
)),
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -199,7 +222,7 @@ pub(crate) fn build_index<'tcx>(
|
||||||
primitives: &mut FxHashMap<Symbol, isize>,
|
primitives: &mut FxHashMap<Symbol, isize>,
|
||||||
associated_types: &mut FxHashMap<Symbol, isize>,
|
associated_types: &mut FxHashMap<Symbol, isize>,
|
||||||
lastpathid: &mut isize,
|
lastpathid: &mut isize,
|
||||||
crate_paths: &mut Vec<(ItemType, Vec<Symbol>)>,
|
crate_paths: &mut Vec<(ItemType, Vec<Symbol>, Option<Vec<Symbol>>)>,
|
||||||
) {
|
) {
|
||||||
if let Some(generics) = &mut ty.generics {
|
if let Some(generics) = &mut ty.generics {
|
||||||
for item in generics {
|
for item in generics {
|
||||||
|
@ -296,7 +319,7 @@ pub(crate) fn build_index<'tcx>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let Cache { ref paths, .. } = *cache;
|
let Cache { ref paths, ref exact_paths, ref external_paths, .. } = *cache;
|
||||||
|
|
||||||
// Then, on parent modules
|
// Then, on parent modules
|
||||||
let crate_items: Vec<&IndexItem> = search_index
|
let crate_items: Vec<&IndexItem> = search_index
|
||||||
|
@ -311,7 +334,13 @@ pub(crate) fn build_index<'tcx>(
|
||||||
lastpathid += 1;
|
lastpathid += 1;
|
||||||
|
|
||||||
if let Some(&(ref fqp, short)) = paths.get(&defid) {
|
if let Some(&(ref fqp, short)) = paths.get(&defid) {
|
||||||
crate_paths.push((short, fqp.clone()));
|
let exact_fqp = exact_paths
|
||||||
|
.get(&defid)
|
||||||
|
.or_else(|| external_paths.get(&defid).map(|&(ref fqp, _)| fqp))
|
||||||
|
.filter(|exact_fqp| {
|
||||||
|
exact_fqp.last() == Some(&item.name) && *exact_fqp != fqp
|
||||||
|
});
|
||||||
|
crate_paths.push((short, fqp.clone(), exact_fqp.cloned()));
|
||||||
Some(pathid)
|
Some(pathid)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
@ -319,6 +348,42 @@ pub(crate) fn build_index<'tcx>(
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if let Some(defid) = item.defid
|
||||||
|
&& item.parent_idx.is_none()
|
||||||
|
{
|
||||||
|
// If this is a re-export, retain the original path.
|
||||||
|
// Associated items don't use this.
|
||||||
|
// Their parent carries the exact fqp instead.
|
||||||
|
let exact_fqp = exact_paths
|
||||||
|
.get(&defid)
|
||||||
|
.or_else(|| external_paths.get(&defid).map(|&(ref fqp, _)| fqp));
|
||||||
|
item.exact_path = exact_fqp.and_then(|fqp| {
|
||||||
|
// Re-exports only count if the name is exactly the same.
|
||||||
|
// This is a size optimization, since it means we only need
|
||||||
|
// to store the name once (and the path is re-used for everything
|
||||||
|
// exported from this same module). It's also likely to Do
|
||||||
|
// What I Mean, since if a re-export changes the name, it might
|
||||||
|
// also be a change in semantic meaning.
|
||||||
|
if fqp.last() != Some(&item.name) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let path =
|
||||||
|
if item.ty == ItemType::Macro && tcx.has_attr(defid, sym::macro_export) {
|
||||||
|
// `#[macro_export]` always exports to the crate root.
|
||||||
|
tcx.crate_name(defid.krate).to_string()
|
||||||
|
} else {
|
||||||
|
if fqp.len() < 2 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
join_with_double_colon(&fqp[..fqp.len() - 1])
|
||||||
|
};
|
||||||
|
if path == item.path {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Some(path)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Omit the parent path if it is same to that of the prior item.
|
// Omit the parent path if it is same to that of the prior item.
|
||||||
if lastpath == &item.path {
|
if lastpath == &item.path {
|
||||||
item.path.clear();
|
item.path.clear();
|
||||||
|
@ -356,7 +421,7 @@ pub(crate) fn build_index<'tcx>(
|
||||||
|
|
||||||
struct CrateData<'a> {
|
struct CrateData<'a> {
|
||||||
items: Vec<&'a IndexItem>,
|
items: Vec<&'a IndexItem>,
|
||||||
paths: Vec<(ItemType, Vec<Symbol>)>,
|
paths: Vec<(ItemType, Vec<Symbol>, Option<Vec<Symbol>>)>,
|
||||||
// The String is alias name and the vec is the list of the elements with this alias.
|
// 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`.
|
// To be noted: the `usize` elements are indexes to `items`.
|
||||||
|
@ -374,6 +439,7 @@ pub(crate) fn build_index<'tcx>(
|
||||||
ty: ItemType,
|
ty: ItemType,
|
||||||
name: Symbol,
|
name: Symbol,
|
||||||
path: Option<usize>,
|
path: Option<usize>,
|
||||||
|
exact_path: Option<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Serialize for Paths {
|
impl Serialize for Paths {
|
||||||
|
@ -387,6 +453,10 @@ pub(crate) fn build_index<'tcx>(
|
||||||
if let Some(ref path) = self.path {
|
if let Some(ref path) = self.path {
|
||||||
seq.serialize_element(path)?;
|
seq.serialize_element(path)?;
|
||||||
}
|
}
|
||||||
|
if let Some(ref path) = self.exact_path {
|
||||||
|
assert!(self.path.is_some());
|
||||||
|
seq.serialize_element(path)?;
|
||||||
|
}
|
||||||
seq.end()
|
seq.end()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -409,14 +479,39 @@ pub(crate) fn build_index<'tcx>(
|
||||||
mod_paths.insert(&item.path, index);
|
mod_paths.insert(&item.path, index);
|
||||||
}
|
}
|
||||||
let mut paths = Vec::with_capacity(self.paths.len());
|
let mut paths = Vec::with_capacity(self.paths.len());
|
||||||
for (ty, path) in &self.paths {
|
for (ty, path, exact) in &self.paths {
|
||||||
if path.len() < 2 {
|
if path.len() < 2 {
|
||||||
paths.push(Paths { ty: *ty, name: path[0], path: None });
|
paths.push(Paths { ty: *ty, name: path[0], path: None, exact_path: None });
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let full_path = join_with_double_colon(&path[..path.len() - 1]);
|
let full_path = join_with_double_colon(&path[..path.len() - 1]);
|
||||||
|
let full_exact_path = exact
|
||||||
|
.as_ref()
|
||||||
|
.filter(|exact| exact.last() == path.last() && exact.len() >= 2)
|
||||||
|
.map(|exact| join_with_double_colon(&exact[..exact.len() - 1]));
|
||||||
|
let exact_path = extra_paths.len() + self.items.len();
|
||||||
|
let exact_path = full_exact_path.as_ref().map(|full_exact_path| match extra_paths
|
||||||
|
.entry(full_exact_path.clone())
|
||||||
|
{
|
||||||
|
Entry::Occupied(entry) => *entry.get(),
|
||||||
|
Entry::Vacant(entry) => {
|
||||||
|
if let Some(index) = mod_paths.get(&full_exact_path) {
|
||||||
|
return *index;
|
||||||
|
}
|
||||||
|
entry.insert(exact_path);
|
||||||
|
if !revert_extra_paths.contains_key(&exact_path) {
|
||||||
|
revert_extra_paths.insert(exact_path, full_exact_path.clone());
|
||||||
|
}
|
||||||
|
exact_path
|
||||||
|
}
|
||||||
|
});
|
||||||
if let Some(index) = mod_paths.get(&full_path) {
|
if let Some(index) = mod_paths.get(&full_path) {
|
||||||
paths.push(Paths { ty: *ty, name: *path.last().unwrap(), path: Some(*index) });
|
paths.push(Paths {
|
||||||
|
ty: *ty,
|
||||||
|
name: *path.last().unwrap(),
|
||||||
|
path: Some(*index),
|
||||||
|
exact_path,
|
||||||
|
});
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// It means it comes from an external crate so the item and its path will be
|
// It means it comes from an external crate so the item and its path will be
|
||||||
|
@ -424,28 +519,54 @@ pub(crate) fn build_index<'tcx>(
|
||||||
//
|
//
|
||||||
// `index` is put after the last `mod_paths`
|
// `index` is put after the last `mod_paths`
|
||||||
let index = extra_paths.len() + self.items.len();
|
let index = extra_paths.len() + self.items.len();
|
||||||
if !revert_extra_paths.contains_key(&index) {
|
match extra_paths.entry(full_path.clone()) {
|
||||||
revert_extra_paths.insert(index, full_path.clone());
|
|
||||||
}
|
|
||||||
match extra_paths.entry(full_path) {
|
|
||||||
Entry::Occupied(entry) => {
|
Entry::Occupied(entry) => {
|
||||||
paths.push(Paths {
|
paths.push(Paths {
|
||||||
ty: *ty,
|
ty: *ty,
|
||||||
name: *path.last().unwrap(),
|
name: *path.last().unwrap(),
|
||||||
path: Some(*entry.get()),
|
path: Some(*entry.get()),
|
||||||
|
exact_path,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
Entry::Vacant(entry) => {
|
Entry::Vacant(entry) => {
|
||||||
entry.insert(index);
|
entry.insert(index);
|
||||||
|
if !revert_extra_paths.contains_key(&index) {
|
||||||
|
revert_extra_paths.insert(index, full_path);
|
||||||
|
}
|
||||||
paths.push(Paths {
|
paths.push(Paths {
|
||||||
ty: *ty,
|
ty: *ty,
|
||||||
name: *path.last().unwrap(),
|
name: *path.last().unwrap(),
|
||||||
path: Some(index),
|
path: Some(index),
|
||||||
|
exact_path,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Direct exports use adjacent arrays for the current crate's items,
|
||||||
|
// but re-exported exact paths don't.
|
||||||
|
let mut re_exports = Vec::new();
|
||||||
|
for (item_index, item) in self.items.iter().enumerate() {
|
||||||
|
if let Some(exact_path) = item.exact_path.as_ref() {
|
||||||
|
if let Some(path_index) = mod_paths.get(&exact_path) {
|
||||||
|
re_exports.push((item_index, *path_index));
|
||||||
|
} else {
|
||||||
|
let path_index = extra_paths.len() + self.items.len();
|
||||||
|
let path_index = match extra_paths.entry(exact_path.clone()) {
|
||||||
|
Entry::Occupied(entry) => *entry.get(),
|
||||||
|
Entry::Vacant(entry) => {
|
||||||
|
entry.insert(path_index);
|
||||||
|
if !revert_extra_paths.contains_key(&path_index) {
|
||||||
|
revert_extra_paths.insert(path_index, exact_path.clone());
|
||||||
|
}
|
||||||
|
path_index
|
||||||
|
}
|
||||||
|
};
|
||||||
|
re_exports.push((item_index, path_index));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let mut names = Vec::with_capacity(self.items.len());
|
let mut names = Vec::with_capacity(self.items.len());
|
||||||
let mut types = String::with_capacity(self.items.len());
|
let mut types = String::with_capacity(self.items.len());
|
||||||
let mut full_paths = Vec::with_capacity(self.items.len());
|
let mut full_paths = Vec::with_capacity(self.items.len());
|
||||||
|
@ -501,6 +622,7 @@ pub(crate) fn build_index<'tcx>(
|
||||||
crate_data.serialize_field("f", &functions)?;
|
crate_data.serialize_field("f", &functions)?;
|
||||||
crate_data.serialize_field("D", &self.desc_index)?;
|
crate_data.serialize_field("D", &self.desc_index)?;
|
||||||
crate_data.serialize_field("p", &paths)?;
|
crate_data.serialize_field("p", &paths)?;
|
||||||
|
crate_data.serialize_field("r", &re_exports)?;
|
||||||
crate_data.serialize_field("b", &self.associated_item_disambiguators)?;
|
crate_data.serialize_field("b", &self.associated_item_disambiguators)?;
|
||||||
crate_data.serialize_field("c", &bitmap_to_string(&deprecated))?;
|
crate_data.serialize_field("c", &bitmap_to_string(&deprecated))?;
|
||||||
crate_data.serialize_field("e", &bitmap_to_string(&self.empty_desc))?;
|
crate_data.serialize_field("e", &bitmap_to_string(&self.empty_desc))?;
|
||||||
|
|
|
@ -239,20 +239,27 @@ let FunctionType;
|
||||||
* `doc` contains the description of the crate.
|
* `doc` contains the description of the crate.
|
||||||
*
|
*
|
||||||
* `p` is a list of path/type pairs. It is used for parents and function parameters.
|
* `p` is a list of path/type pairs. It is used for parents and function parameters.
|
||||||
|
* The first item is the type, the second is the name, the third is the visible path (if any) and
|
||||||
|
* the fourth is the canonical path used for deduplication (if any).
|
||||||
|
*
|
||||||
|
* `r` is the canonical path used for deduplication of re-exported items.
|
||||||
|
* It is not used for associated items like methods (that's the fourth element
|
||||||
|
* of `p`) but is used for modules items like free functions.
|
||||||
*
|
*
|
||||||
* `c` is an array of item indices that are deprecated.
|
* `c` is an array of item indices that are deprecated.
|
||||||
* @typedef {{
|
* @typedef {{
|
||||||
* doc: string,
|
* doc: string,
|
||||||
* a: Object,
|
* a: Object,
|
||||||
* n: Array<string>,
|
* n: Array<string>,
|
||||||
* t: String,
|
* t: string,
|
||||||
* d: Array<string>,
|
* d: Array<string>,
|
||||||
* q: Array<[Number, string]>,
|
* q: Array<[number, string]>,
|
||||||
* i: Array<Number>,
|
* i: Array<number>,
|
||||||
* f: string,
|
* f: string,
|
||||||
* p: Array<Object>,
|
* p: Array<[number, string] | [number, string, number] | [number, string, number, number]>,
|
||||||
* b: Array<[Number, String]>,
|
* b: Array<[number, String]>,
|
||||||
* c: Array<Number>
|
* c: Array<number>,
|
||||||
|
* r: Array<[number, number]>,
|
||||||
* }}
|
* }}
|
||||||
*/
|
*/
|
||||||
let RawSearchIndexCrate;
|
let RawSearchIndexCrate;
|
||||||
|
|
|
@ -79,6 +79,7 @@ const longItemTypes = [
|
||||||
|
|
||||||
// used for special search precedence
|
// used for special search precedence
|
||||||
const TY_GENERIC = itemTypes.indexOf("generic");
|
const TY_GENERIC = itemTypes.indexOf("generic");
|
||||||
|
const TY_IMPORT = itemTypes.indexOf("import");
|
||||||
const ROOT_PATH = typeof window !== "undefined" ? window.rootPath : "../";
|
const ROOT_PATH = typeof window !== "undefined" ? window.rootPath : "../";
|
||||||
|
|
||||||
// Hard limit on how deep to recurse into generics when doing type-driven search.
|
// Hard limit on how deep to recurse into generics when doing type-driven search.
|
||||||
|
@ -1324,14 +1325,23 @@ function initSearch(rawSearchIndex) {
|
||||||
obj.dist = result.dist;
|
obj.dist = result.dist;
|
||||||
const res = buildHrefAndPath(obj);
|
const res = buildHrefAndPath(obj);
|
||||||
obj.displayPath = pathSplitter(res[0]);
|
obj.displayPath = pathSplitter(res[0]);
|
||||||
obj.fullPath = obj.displayPath + obj.name;
|
|
||||||
// To be sure than it some items aren't considered as duplicate.
|
|
||||||
obj.fullPath += "|" + obj.ty;
|
|
||||||
|
|
||||||
|
// To be sure than it some items aren't considered as duplicate.
|
||||||
|
obj.fullPath = res[2] + "|" + obj.ty;
|
||||||
if (duplicates.has(obj.fullPath)) {
|
if (duplicates.has(obj.fullPath)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Exports are specifically not shown if the items they point at
|
||||||
|
// are already in the results.
|
||||||
|
if (obj.ty === TY_IMPORT && duplicates.has(res[2])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (duplicates.has(res[2] + "|" + TY_IMPORT)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
duplicates.add(obj.fullPath);
|
duplicates.add(obj.fullPath);
|
||||||
|
duplicates.add(res[2]);
|
||||||
|
|
||||||
obj.href = res[1];
|
obj.href = res[1];
|
||||||
out.push(obj);
|
out.push(obj);
|
||||||
|
@ -2085,6 +2095,7 @@ function initSearch(rawSearchIndex) {
|
||||||
path: item.path,
|
path: item.path,
|
||||||
descShard: item.descShard,
|
descShard: item.descShard,
|
||||||
descIndex: item.descIndex,
|
descIndex: item.descIndex,
|
||||||
|
exactPath: item.exactPath,
|
||||||
ty: item.ty,
|
ty: item.ty,
|
||||||
parent: item.parent,
|
parent: item.parent,
|
||||||
type: item.type,
|
type: item.type,
|
||||||
|
@ -2538,6 +2549,7 @@ function initSearch(rawSearchIndex) {
|
||||||
const type = itemTypes[item.ty];
|
const type = itemTypes[item.ty];
|
||||||
const name = item.name;
|
const name = item.name;
|
||||||
let path = item.path;
|
let path = item.path;
|
||||||
|
let exactPath = item.exactPath;
|
||||||
|
|
||||||
if (type === "mod") {
|
if (type === "mod") {
|
||||||
displayPath = path + "::";
|
displayPath = path + "::";
|
||||||
|
@ -2559,6 +2571,7 @@ function initSearch(rawSearchIndex) {
|
||||||
const parentType = itemTypes[myparent.ty];
|
const parentType = itemTypes[myparent.ty];
|
||||||
let pageType = parentType;
|
let pageType = parentType;
|
||||||
let pageName = myparent.name;
|
let pageName = myparent.name;
|
||||||
|
exactPath = `${myparent.exactPath}::${myparent.name}`;
|
||||||
|
|
||||||
if (parentType === "primitive") {
|
if (parentType === "primitive") {
|
||||||
displayPath = myparent.name + "::";
|
displayPath = myparent.name + "::";
|
||||||
|
@ -2587,7 +2600,7 @@ function initSearch(rawSearchIndex) {
|
||||||
href = ROOT_PATH + item.path.replace(/::/g, "/") +
|
href = ROOT_PATH + item.path.replace(/::/g, "/") +
|
||||||
"/" + type + "." + name + ".html";
|
"/" + type + "." + name + ".html";
|
||||||
}
|
}
|
||||||
return [displayPath, href];
|
return [displayPath, href, `${exactPath}::${name}`];
|
||||||
}
|
}
|
||||||
|
|
||||||
function pathSplitter(path) {
|
function pathSplitter(path) {
|
||||||
|
@ -2980,6 +2993,7 @@ ${item.displayPath}<span class="${type}">${name}</span>\
|
||||||
id: pathIndex,
|
id: pathIndex,
|
||||||
ty: TY_GENERIC,
|
ty: TY_GENERIC,
|
||||||
path: null,
|
path: null,
|
||||||
|
exactPath: null,
|
||||||
generics,
|
generics,
|
||||||
bindings,
|
bindings,
|
||||||
};
|
};
|
||||||
|
@ -2989,6 +3003,7 @@ ${item.displayPath}<span class="${type}">${name}</span>\
|
||||||
id: null,
|
id: null,
|
||||||
ty: null,
|
ty: null,
|
||||||
path: null,
|
path: null,
|
||||||
|
exactPath: null,
|
||||||
generics,
|
generics,
|
||||||
bindings,
|
bindings,
|
||||||
};
|
};
|
||||||
|
@ -2998,6 +3013,7 @@ ${item.displayPath}<span class="${type}">${name}</span>\
|
||||||
id: buildTypeMapIndex(item.name, isAssocType),
|
id: buildTypeMapIndex(item.name, isAssocType),
|
||||||
ty: item.ty,
|
ty: item.ty,
|
||||||
path: item.path,
|
path: item.path,
|
||||||
|
exactPath: item.exactPath,
|
||||||
generics,
|
generics,
|
||||||
bindings,
|
bindings,
|
||||||
};
|
};
|
||||||
|
@ -3453,6 +3469,8 @@ ${item.displayPath}<span class="${type}">${name}</span>\
|
||||||
path: "",
|
path: "",
|
||||||
descShard,
|
descShard,
|
||||||
descIndex,
|
descIndex,
|
||||||
|
exactPath: "",
|
||||||
|
desc: crateCorpus.doc,
|
||||||
parent: undefined,
|
parent: undefined,
|
||||||
type: null,
|
type: null,
|
||||||
id,
|
id,
|
||||||
|
@ -3478,6 +3496,9 @@ ${item.displayPath}<span class="${type}">${name}</span>\
|
||||||
// i.e. if indices 4 and 11 are present, but 5-10 and 12-13 are not present,
|
// i.e. if indices 4 and 11 are present, but 5-10 and 12-13 are not present,
|
||||||
// 5-10 will fall back to the path for 4 and 12-13 will fall back to the path for 11
|
// 5-10 will fall back to the path for 4 and 12-13 will fall back to the path for 11
|
||||||
const itemPaths = new Map(crateCorpus.q);
|
const itemPaths = new Map(crateCorpus.q);
|
||||||
|
// An array of [(Number) item index, (Number) path index]
|
||||||
|
// Used to de-duplicate inlined and re-exported stuff
|
||||||
|
const itemReexports = new Map(crateCorpus.r);
|
||||||
// an array of (Number) the parent path index + 1 to `paths`, or 0 if none
|
// an array of (Number) the parent path index + 1 to `paths`, or 0 if none
|
||||||
const itemParentIdxs = crateCorpus.i;
|
const itemParentIdxs = crateCorpus.i;
|
||||||
// a map Number, string for impl disambiguators
|
// a map Number, string for impl disambiguators
|
||||||
|
@ -3511,9 +3532,10 @@ ${item.displayPath}<span class="${type}">${name}</span>\
|
||||||
path = itemPaths.has(elem[2]) ? itemPaths.get(elem[2]) : lastPath;
|
path = itemPaths.has(elem[2]) ? itemPaths.get(elem[2]) : lastPath;
|
||||||
lastPath = path;
|
lastPath = path;
|
||||||
}
|
}
|
||||||
|
const exactPath = elem.length > 3 ? itemPaths.get(elem[3]) : path;
|
||||||
|
|
||||||
lowercasePaths.push({ty: ty, name: name.toLowerCase(), path: path});
|
lowercasePaths.push({ty, name: name.toLowerCase(), path, exactPath});
|
||||||
paths[i] = {ty: ty, name: name, path: path};
|
paths[i] = {ty, name, path, exactPath};
|
||||||
}
|
}
|
||||||
|
|
||||||
// convert `item*` into an object form, and construct word indices.
|
// convert `item*` into an object form, and construct word indices.
|
||||||
|
@ -3572,6 +3594,7 @@ ${item.displayPath}<span class="${type}">${name}</span>\
|
||||||
path,
|
path,
|
||||||
descShard,
|
descShard,
|
||||||
descIndex,
|
descIndex,
|
||||||
|
exactPath: itemReexports.has(i) ? itemPaths.get(itemReexports.get(i)) : path,
|
||||||
parent: itemParentIdxs[i] > 0 ? paths[itemParentIdxs[i] - 1] : undefined,
|
parent: itemParentIdxs[i] > 0 ? paths[itemParentIdxs[i] - 1] : undefined,
|
||||||
type,
|
type,
|
||||||
id,
|
id,
|
||||||
|
|
|
@ -3,17 +3,47 @@ const EXPECTED = [
|
||||||
'query': 'Vec::new',
|
'query': 'Vec::new',
|
||||||
'others': [
|
'others': [
|
||||||
{ 'path': 'std::vec::Vec', 'name': 'new' },
|
{ 'path': 'std::vec::Vec', 'name': 'new' },
|
||||||
{ 'path': 'alloc::vec::Vec', 'name': 'new' },
|
|
||||||
{ 'path': 'std::vec::Vec', 'name': 'new_in' },
|
{ 'path': 'std::vec::Vec', 'name': 'new_in' },
|
||||||
{ 'path': 'alloc::vec::Vec', 'name': 'new_in' },
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'query': 'prelude::vec',
|
||||||
|
'others': [
|
||||||
|
{ 'path': 'std::prelude::rust_2024', 'name': 'Vec' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'query': 'Vec new',
|
'query': 'Vec new',
|
||||||
'others': [
|
'others': [
|
||||||
{ 'path': 'std::vec::Vec', 'name': 'new' },
|
{ 'path': 'std::vec::Vec', 'name': 'new' },
|
||||||
{ 'path': 'alloc::vec::Vec', 'name': 'new' },
|
|
||||||
{ 'path': 'std::vec::Vec', 'name': 'new_in' },
|
{ 'path': 'std::vec::Vec', 'name': 'new_in' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'query': 'std::Vec::new',
|
||||||
|
'others': [
|
||||||
|
{ 'path': 'std::vec::Vec', 'name': 'new' },
|
||||||
|
{ 'path': 'std::vec::Vec', 'name': 'new_in' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'query': 'std Vec new',
|
||||||
|
'others': [
|
||||||
|
{ 'path': 'std::vec::Vec', 'name': 'new' },
|
||||||
|
{ 'path': 'std::vec::Vec', 'name': 'new_in' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'query': 'alloc::Vec::new',
|
||||||
|
'others': [
|
||||||
|
{ 'path': 'alloc::vec::Vec', 'name': 'new' },
|
||||||
|
{ 'path': 'alloc::vec::Vec', 'name': 'new_in' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'query': 'alloc Vec new',
|
||||||
|
'others': [
|
||||||
|
{ 'path': 'alloc::vec::Vec', 'name': 'new' },
|
||||||
{ 'path': 'alloc::vec::Vec', 'name': 'new_in' },
|
{ 'path': 'alloc::vec::Vec', 'name': 'new_in' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
7
tests/rustdoc-js/auxiliary/macro-in-module.rs
Normal file
7
tests/rustdoc-js/auxiliary/macro-in-module.rs
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
#[macro_use]
|
||||||
|
mod hidden_macro_module {
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! vec {
|
||||||
|
() => {};
|
||||||
|
}
|
||||||
|
}
|
11
tests/rustdoc-js/reexport-dedup-macro.js
Normal file
11
tests/rustdoc-js/reexport-dedup-macro.js
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
// exact-check
|
||||||
|
|
||||||
|
const EXPECTED = [
|
||||||
|
{
|
||||||
|
'query': 'vec',
|
||||||
|
'others': [
|
||||||
|
{ 'path': 'foo', 'name': 'vec', 'exactPath': 'macro_in_module' },
|
||||||
|
{ 'path': 'foo', 'name': 'myspecialvec', 'exactPath': 'foo' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
15
tests/rustdoc-js/reexport-dedup-macro.rs
Normal file
15
tests/rustdoc-js/reexport-dedup-macro.rs
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
//@ aux-crate: macro_in_module=macro-in-module.rs
|
||||||
|
#![crate_name="foo"]
|
||||||
|
extern crate macro_in_module;
|
||||||
|
|
||||||
|
// Test case based on the relationship between alloc and std.
|
||||||
|
#[doc(inline)]
|
||||||
|
pub use macro_in_module::vec;
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
mod hidden_macro_module {
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! myspecialvec {
|
||||||
|
() => {};
|
||||||
|
}
|
||||||
|
}
|
16
tests/rustdoc-js/reexport-dedup-method.js
Normal file
16
tests/rustdoc-js/reexport-dedup-method.js
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
// exact-check
|
||||||
|
|
||||||
|
const EXPECTED = [
|
||||||
|
{
|
||||||
|
'query': 'Subscriber dostuff',
|
||||||
|
'others': [
|
||||||
|
{ 'path': 'foo::fmt::Subscriber', 'name': 'dostuff' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'query': 'AnotherOne dostuff',
|
||||||
|
'others': [
|
||||||
|
{ 'path': 'foo::AnotherOne', 'name': 'dostuff' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
18
tests/rustdoc-js/reexport-dedup-method.rs
Normal file
18
tests/rustdoc-js/reexport-dedup-method.rs
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
// This test enforces that the (renamed) reexports are present in the search results.
|
||||||
|
#![crate_name="foo"]
|
||||||
|
|
||||||
|
pub mod fmt {
|
||||||
|
pub struct Subscriber;
|
||||||
|
impl Subscriber {
|
||||||
|
pub fn dostuff(&self) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mod foo {
|
||||||
|
pub struct AnotherOne;
|
||||||
|
impl AnotherOne {
|
||||||
|
pub fn dostuff(&self) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub use foo::AnotherOne;
|
||||||
|
pub use fmt::Subscriber;
|
22
tests/rustdoc-js/reexport-dedup.js
Normal file
22
tests/rustdoc-js/reexport-dedup.js
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
// exact-check
|
||||||
|
|
||||||
|
const EXPECTED = [
|
||||||
|
{
|
||||||
|
'query': 'Subscriber',
|
||||||
|
'others': [
|
||||||
|
{ 'path': 'foo', 'name': 'Subscriber' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'query': 'fmt Subscriber',
|
||||||
|
'others': [
|
||||||
|
{ 'path': 'foo::fmt', 'name': 'Subscriber' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'query': 'AnotherOne',
|
||||||
|
'others': [
|
||||||
|
{ 'path': 'foo', 'name': 'AnotherOne' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
12
tests/rustdoc-js/reexport-dedup.rs
Normal file
12
tests/rustdoc-js/reexport-dedup.rs
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
// This test enforces that the (renamed) reexports are present in the search results.
|
||||||
|
#![crate_name="foo"]
|
||||||
|
|
||||||
|
pub mod fmt {
|
||||||
|
pub struct Subscriber;
|
||||||
|
}
|
||||||
|
mod foo {
|
||||||
|
pub struct AnotherOne;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub use foo::AnotherOne;
|
||||||
|
pub use fmt::Subscriber;
|
|
@ -1,4 +1,6 @@
|
||||||
// This test enforces that the (renamed) reexports are present in the search results.
|
// This test enforces that the (renamed) reexports are present in the search results.
|
||||||
|
// This is a DWIM case, since renaming the export probably means the intent is also different.
|
||||||
|
// For the de-duplication case of exactly the same name, see reexport-dedup
|
||||||
|
|
||||||
pub mod fmt {
|
pub mod fmt {
|
||||||
pub struct Subscriber;
|
pub struct Subscriber;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue