1
Fork 0

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

View file

@ -1462,6 +1462,32 @@ function initSearch(rawSearchIndex) {
if (!typePassesFilter(queryElem.typeFilter, fnType.ty)) { if (!typePassesFilter(queryElem.typeFilter, fnType.ty)) {
continue; 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)) { if (queryElem.generics.length === 0 || checkGenerics(fnType, queryElem)) {
currentFnTypeList.splice(i, 1); currentFnTypeList.splice(i, 1);
const result = doHandleQueryElemList(currentFnTypeList, queryElemList); const result = doHandleQueryElemList(currentFnTypeList, queryElemList);
@ -1862,14 +1888,14 @@ function initSearch(rawSearchIndex) {
* @param {QueryElement} elem * @param {QueryElement} elem
*/ */
function convertNameToId(elem) { function convertNameToId(elem) {
if (typeNameIdMap.has(elem.name)) { if (typeNameIdMap.has(elem.pathLast)) {
elem.id = typeNameIdMap.get(elem.name); elem.id = typeNameIdMap.get(elem.pathLast);
} else if (!parsedQuery.literalSearch) { } else if (!parsedQuery.literalSearch) {
let match = -1; let match = -1;
let matchDist = maxEditDistance + 1; let matchDist = maxEditDistance + 1;
let matchName = ""; let matchName = "";
for (const [name, id] of typeNameIdMap) { 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 && dist <= maxEditDistance) {
if (dist === matchDist && matchName > name) { if (dist === matchDist && matchName > name) {
continue; 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` // `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 { return {
id: item === null ? -1 : buildTypeMapIndex(item.name), id: buildTypeMapIndex(item.name),
ty: item === null ? null : item.ty, ty: item.ty,
path: item.path,
generics: generics, generics: generics,
}; };
}); });
@ -2417,15 +2452,25 @@ ${item.displayPath}<span class="${type}">${name}</span>\
if (functionSearchType === 0) { if (functionSearchType === 0) {
return null; return null;
} }
let inputs, output, item; let inputs, output;
if (typeof functionSearchType[INPUTS_DATA] === "number") { if (typeof functionSearchType[INPUTS_DATA] === "number") {
const pathIndex = functionSearchType[INPUTS_DATA]; const pathIndex = functionSearchType[INPUTS_DATA];
item = pathIndex === 0 ? null : lowercasePaths[pathIndex - 1]; if (pathIndex === 0) {
inputs = [{ inputs = [{
id: item === null ? -1 : buildTypeMapIndex(item.name), id: -1,
ty: item === null ? null : item.ty, ty: null,
generics: [], path: null,
}]; generics: [],
}];
} else {
const item = lowercasePaths[pathIndex - 1];
inputs = [{
id: buildTypeMapIndex(item.name),
ty: item.ty,
path: item.path,
generics: [],
}];
}
} else { } else {
inputs = buildItemSearchTypeAll( inputs = buildItemSearchTypeAll(
functionSearchType[INPUTS_DATA], functionSearchType[INPUTS_DATA],
@ -2435,12 +2480,22 @@ ${item.displayPath}<span class="${type}">${name}</span>\
if (functionSearchType.length > 1) { if (functionSearchType.length > 1) {
if (typeof functionSearchType[OUTPUT_DATA] === "number") { if (typeof functionSearchType[OUTPUT_DATA] === "number") {
const pathIndex = functionSearchType[OUTPUT_DATA]; const pathIndex = functionSearchType[OUTPUT_DATA];
item = pathIndex === 0 ? null : lowercasePaths[pathIndex - 1]; if (pathIndex === 0) {
output = [{ output = [{
id: item === null ? -1 : buildTypeMapIndex(item.name), id: -1,
ty: item === null ? null : item.ty, ty: null,
generics: [], path: null,
}]; generics: [],
}];
} else {
const item = lowercasePaths[pathIndex - 1];
output = [{
id: buildTypeMapIndex(item.name),
ty: item.ty,
path: item.path,
generics: [],
}];
}
} else { } else {
output = buildItemSearchTypeAll( output = buildItemSearchTypeAll(
functionSearchType[OUTPUT_DATA], functionSearchType[OUTPUT_DATA],
@ -2573,9 +2628,19 @@ ${item.displayPath}<span class="${type}">${name}</span>\
// convert `rawPaths` entries into object form // convert `rawPaths` entries into object form
// generate normalizedPaths for function search mode // generate normalizedPaths for function search mode
let len = paths.length; let len = paths.length;
let lastPath = itemPaths.get(0);
for (let i = 0; i < len; ++i) { for (let i = 0; i < len; ++i) {
lowercasePaths.push({ty: paths[i][0], name: paths[i][1].toLowerCase()}); const elem = paths[i];
paths[i] = {ty: paths[i][0], name: paths[i][1]}; 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. // 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 // operation that is cached for the life of the page state so that
// all other search operations have access to this cached data for // all other search operations have access to this cached data for
// faster analysis operations // faster analysis operations
lastPath = "";
len = itemTypes.length; len = itemTypes.length;
let lastPath = "";
for (let i = 0; i < len; ++i) { for (let i = 0; i < len; ++i) {
let word = ""; let word = "";
// This object should have exactly the same set of fields as the "crateRow" // 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(); word = itemNames[i].toLowerCase();
} }
searchWords.push(word); searchWords.push(word);
const path = itemPaths.has(i) ? itemPaths.get(i) : lastPath;
const row = { const row = {
crate: crate, crate: crate,
ty: itemTypes.charCodeAt(i) - charA, ty: itemTypes.charCodeAt(i) - charA,
name: itemNames[i], name: itemNames[i],
path: itemPaths.has(i) ? itemPaths.get(i) : lastPath, path: path,
desc: itemDescs[i], desc: itemDescs[i],
parent: itemParentIdxs[i] > 0 ? paths[itemParentIdxs[i] - 1] : undefined, parent: itemParentIdxs[i] > 0 ? paths[itemParentIdxs[i] - 1] : undefined,
type: buildFunctionSearchType( type: buildFunctionSearchType(