[rustdoc] Fix path in type-based search

This commit is contained in:
Guillaume Gomez 2023-09-01 14:30:31 +02:00
parent 539957130d
commit 09160b3f45
2 changed files with 139 additions and 36 deletions

View File

@ -4,7 +4,7 @@ use std::collections::BTreeMap;
use rustc_data_structures::fx::FxHashMap;
use rustc_middle::ty::TyCtxt;
use rustc_span::symbol::Symbol;
use serde::ser::{Serialize, SerializeStruct, Serializer};
use serde::ser::{Serialize, SerializeSeq, SerializeStruct, Serializer};
use crate::clean;
use crate::clean::types::{Function, Generics, ItemId, Type, WherePredicate};
@ -78,9 +78,9 @@ pub(crate) fn build_index<'tcx>(
map: &mut FxHashMap<F, usize>,
itemid: F,
lastpathid: &mut usize,
crate_paths: &mut Vec<(ItemType, Symbol)>,
crate_paths: &mut Vec<(ItemType, Vec<Symbol>)>,
item_type: ItemType,
path: Symbol,
path: &[Symbol],
) {
match map.entry(itemid) {
Entry::Occupied(entry) => ty.id = Some(RenderTypeId::Index(*entry.get())),
@ -88,7 +88,7 @@ pub(crate) fn build_index<'tcx>(
let pathid = *lastpathid;
entry.insert(pathid);
*lastpathid += 1;
crate_paths.push((item_type, path));
crate_paths.push((item_type, path.to_vec()));
ty.id = Some(RenderTypeId::Index(pathid));
}
}
@ -100,7 +100,7 @@ pub(crate) fn build_index<'tcx>(
itemid_to_pathid: &mut FxHashMap<ItemId, usize>,
primitives: &mut FxHashMap<Symbol, usize>,
lastpathid: &mut usize,
crate_paths: &mut Vec<(ItemType, Symbol)>,
crate_paths: &mut Vec<(ItemType, Vec<Symbol>)>,
) {
if let Some(generics) = &mut ty.generics {
for item in generics {
@ -131,7 +131,7 @@ pub(crate) fn build_index<'tcx>(
lastpathid,
crate_paths,
item_type,
*fqp.last().unwrap(),
fqp,
);
} else {
ty.id = None;
@ -146,7 +146,7 @@ pub(crate) fn build_index<'tcx>(
lastpathid,
crate_paths,
ItemType::Primitive,
sym,
&[sym],
);
}
RenderTypeId::Index(_) => {}
@ -191,7 +191,7 @@ pub(crate) fn build_index<'tcx>(
lastpathid += 1;
if let Some(&(ref fqp, short)) = paths.get(&defid) {
crate_paths.push((short, *fqp.last().unwrap()));
crate_paths.push((short, fqp.clone()));
Some(pathid)
} else {
None
@ -213,18 +213,58 @@ pub(crate) fn build_index<'tcx>(
struct CrateData<'a> {
doc: String,
items: Vec<&'a IndexItem>,
paths: Vec<(ItemType, Symbol)>,
paths: Vec<(ItemType, Vec<Symbol>)>,
// 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`.
aliases: &'a BTreeMap<String, Vec<usize>>,
}
struct Paths {
ty: ItemType,
name: Symbol,
path: Option<usize>,
}
impl Serialize for Paths {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut seq = serializer.serialize_seq(None)?;
seq.serialize_element(&self.ty)?;
seq.serialize_element(self.name.as_str())?;
if let Some(ref path) = self.path {
seq.serialize_element(path)?;
}
seq.end()
}
}
impl<'a> Serialize for CrateData<'a> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut mod_paths = FxHashMap::default();
for (index, item) in self.items.iter().enumerate() {
if item.path.is_empty() {
continue;
}
mod_paths.insert(&item.path, index);
}
let paths = self
.paths
.iter()
.map(|(ty, path)| {
if path.len() < 2 {
return Paths { ty: *ty, name: path[0], path: None };
}
let index = mod_paths.get(&join_with_double_colon(&path[..path.len() - 1]));
Paths { ty: *ty, name: *path.last().unwrap(), path: index.copied() }
})
.collect::<Vec<_>>();
let has_aliases = !self.aliases.is_empty();
let mut crate_data =
serializer.serialize_struct("CrateData", if has_aliases { 9 } else { 8 })?;
@ -321,10 +361,7 @@ pub(crate) fn build_index<'tcx>(
.filter_map(|(index, item)| item.deprecation.map(|_| index))
.collect::<Vec<_>>(),
)?;
crate_data.serialize_field(
"p",
&self.paths.iter().map(|(it, s)| (it, s.as_str())).collect::<Vec<_>>(),
)?;
crate_data.serialize_field("p", &paths)?;
if has_aliases {
crate_data.serialize_field("a", &self.aliases)?;
}

View File

@ -1462,6 +1462,32 @@ function initSearch(rawSearchIndex) {
if (!typePassesFilter(queryElem.typeFilter, fnType.ty)) {
continue;
}
const queryElemPathLength = queryElem.pathWithoutLast.length;
// If the query element is a path (it contains `::`), we need to check if this
// path is compatible with the target type.
if (queryElemPathLength > 0) {
const fnTypePath = fnType.path !== undefined && fnType.path !== null ?
fnType.path.split("::") : [];
// If the path provided in the query element is longer than this type,
// no need to check it since it won't match in any case.
if (queryElemPathLength > fnTypePath.length) {
continue;
}
let i = 0;
for (const path of fnTypePath) {
if (path === queryElem.pathWithoutLast[i]) {
i += 1;
if (i >= queryElemPathLength) {
break;
}
}
}
if (i < queryElemPathLength) {
// If we didn't find all parts of the path of the query element inside
// the fn type, then it's not the right one.
continue;
}
}
if (queryElem.generics.length === 0 || checkGenerics(fnType, queryElem)) {
currentFnTypeList.splice(i, 1);
const result = doHandleQueryElemList(currentFnTypeList, queryElemList);
@ -1862,14 +1888,14 @@ function initSearch(rawSearchIndex) {
* @param {QueryElement} elem
*/
function convertNameToId(elem) {
if (typeNameIdMap.has(elem.name)) {
elem.id = typeNameIdMap.get(elem.name);
if (typeNameIdMap.has(elem.pathLast)) {
elem.id = typeNameIdMap.get(elem.pathLast);
} else if (!parsedQuery.literalSearch) {
let match = -1;
let matchDist = maxEditDistance + 1;
let matchName = "";
for (const [name, id] of typeNameIdMap) {
const dist = editDistance(name, elem.name, maxEditDistance);
const dist = editDistance(name, elem.pathLast, maxEditDistance);
if (dist <= matchDist && dist <= maxEditDistance) {
if (dist === matchDist && matchName > name) {
continue;
@ -2385,10 +2411,19 @@ ${item.displayPath}<span class="${type}">${name}</span>\
);
}
// `0` is used as a sentinel because it's fewer bytes than `null`
const item = pathIndex === 0 ? null : lowercasePaths[pathIndex - 1];
if (pathIndex === 0) {
return {
id: -1,
ty: null,
path: null,
generics: generics,
};
}
const item = lowercasePaths[pathIndex - 1];
return {
id: item === null ? -1 : buildTypeMapIndex(item.name),
ty: item === null ? null : item.ty,
id: buildTypeMapIndex(item.name),
ty: item.ty,
path: item.path,
generics: generics,
};
});
@ -2417,15 +2452,25 @@ ${item.displayPath}<span class="${type}">${name}</span>\
if (functionSearchType === 0) {
return null;
}
let inputs, output, item;
let inputs, output;
if (typeof functionSearchType[INPUTS_DATA] === "number") {
const pathIndex = functionSearchType[INPUTS_DATA];
item = pathIndex === 0 ? null : lowercasePaths[pathIndex - 1];
inputs = [{
id: item === null ? -1 : buildTypeMapIndex(item.name),
ty: item === null ? null : item.ty,
generics: [],
}];
if (pathIndex === 0) {
inputs = [{
id: -1,
ty: null,
path: null,
generics: [],
}];
} else {
const item = lowercasePaths[pathIndex - 1];
inputs = [{
id: buildTypeMapIndex(item.name),
ty: item.ty,
path: item.path,
generics: [],
}];
}
} else {
inputs = buildItemSearchTypeAll(
functionSearchType[INPUTS_DATA],
@ -2435,12 +2480,22 @@ ${item.displayPath}<span class="${type}">${name}</span>\
if (functionSearchType.length > 1) {
if (typeof functionSearchType[OUTPUT_DATA] === "number") {
const pathIndex = functionSearchType[OUTPUT_DATA];
item = pathIndex === 0 ? null : lowercasePaths[pathIndex - 1];
output = [{
id: item === null ? -1 : buildTypeMapIndex(item.name),
ty: item === null ? null : item.ty,
generics: [],
}];
if (pathIndex === 0) {
output = [{
id: -1,
ty: null,
path: null,
generics: [],
}];
} else {
const item = lowercasePaths[pathIndex - 1];
output = [{
id: buildTypeMapIndex(item.name),
ty: item.ty,
path: item.path,
generics: [],
}];
}
} else {
output = buildItemSearchTypeAll(
functionSearchType[OUTPUT_DATA],
@ -2573,9 +2628,19 @@ ${item.displayPath}<span class="${type}">${name}</span>\
// convert `rawPaths` entries into object form
// generate normalizedPaths for function search mode
let len = paths.length;
let lastPath = itemPaths.get(0);
for (let i = 0; i < len; ++i) {
lowercasePaths.push({ty: paths[i][0], name: paths[i][1].toLowerCase()});
paths[i] = {ty: paths[i][0], name: paths[i][1]};
const elem = paths[i];
const ty = elem[0];
const name = elem[1];
let path = null;
if (elem.length > 2) {
path = itemPaths.has(elem[2]) ? itemPaths.get(elem[2]) : lastPath;
lastPath = path;
}
lowercasePaths.push({ty: ty, name: name.toLowerCase(), path: path});
paths[i] = {ty: ty, name: name, path: path};
}
// convert `item*` into an object form, and construct word indices.
@ -2585,8 +2650,8 @@ ${item.displayPath}<span class="${type}">${name}</span>\
// operation that is cached for the life of the page state so that
// all other search operations have access to this cached data for
// faster analysis operations
lastPath = "";
len = itemTypes.length;
let lastPath = "";
for (let i = 0; i < len; ++i) {
let word = "";
// This object should have exactly the same set of fields as the "crateRow"
@ -2595,11 +2660,12 @@ ${item.displayPath}<span class="${type}">${name}</span>\
word = itemNames[i].toLowerCase();
}
searchWords.push(word);
const path = itemPaths.has(i) ? itemPaths.get(i) : lastPath;
const row = {
crate: crate,
ty: itemTypes.charCodeAt(i) - charA,
name: itemNames[i],
path: itemPaths.has(i) ? itemPaths.get(i) : lastPath,
path: path,
desc: itemDescs[i],
parent: itemParentIdxs[i] > 0 ? paths[itemParentIdxs[i] - 1] : undefined,
type: buildFunctionSearchType(