Skip to content

Commit 09160b3

Browse files
[rustdoc] Fix path in type-based search
1 parent 5399571 commit 09160b3

File tree

2 files changed

+139
-36
lines changed

2 files changed

+139
-36
lines changed

src/librustdoc/html/render/search_index.rs

+50-13
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use std::collections::BTreeMap;
44
use rustc_data_structures::fx::FxHashMap;
55
use rustc_middle::ty::TyCtxt;
66
use rustc_span::symbol::Symbol;
7-
use serde::ser::{Serialize, SerializeStruct, Serializer};
7+
use serde::ser::{Serialize, SerializeSeq, SerializeStruct, Serializer};
88

99
use crate::clean;
1010
use crate::clean::types::{Function, Generics, ItemId, Type, WherePredicate};
@@ -78,17 +78,17 @@ pub(crate) fn build_index<'tcx>(
7878
map: &mut FxHashMap<F, usize>,
7979
itemid: F,
8080
lastpathid: &mut usize,
81-
crate_paths: &mut Vec<(ItemType, Symbol)>,
81+
crate_paths: &mut Vec<(ItemType, Vec<Symbol>)>,
8282
item_type: ItemType,
83-
path: Symbol,
83+
path: &[Symbol],
8484
) {
8585
match map.entry(itemid) {
8686
Entry::Occupied(entry) => ty.id = Some(RenderTypeId::Index(*entry.get())),
8787
Entry::Vacant(entry) => {
8888
let pathid = *lastpathid;
8989
entry.insert(pathid);
9090
*lastpathid += 1;
91-
crate_paths.push((item_type, path));
91+
crate_paths.push((item_type, path.to_vec()));
9292
ty.id = Some(RenderTypeId::Index(pathid));
9393
}
9494
}
@@ -100,7 +100,7 @@ pub(crate) fn build_index<'tcx>(
100100
itemid_to_pathid: &mut FxHashMap<ItemId, usize>,
101101
primitives: &mut FxHashMap<Symbol, usize>,
102102
lastpathid: &mut usize,
103-
crate_paths: &mut Vec<(ItemType, Symbol)>,
103+
crate_paths: &mut Vec<(ItemType, Vec<Symbol>)>,
104104
) {
105105
if let Some(generics) = &mut ty.generics {
106106
for item in generics {
@@ -131,7 +131,7 @@ pub(crate) fn build_index<'tcx>(
131131
lastpathid,
132132
crate_paths,
133133
item_type,
134-
*fqp.last().unwrap(),
134+
fqp,
135135
);
136136
} else {
137137
ty.id = None;
@@ -146,7 +146,7 @@ pub(crate) fn build_index<'tcx>(
146146
lastpathid,
147147
crate_paths,
148148
ItemType::Primitive,
149-
sym,
149+
&[sym],
150150
);
151151
}
152152
RenderTypeId::Index(_) => {}
@@ -191,7 +191,7 @@ pub(crate) fn build_index<'tcx>(
191191
lastpathid += 1;
192192

193193
if let Some(&(ref fqp, short)) = paths.get(&defid) {
194-
crate_paths.push((short, *fqp.last().unwrap()));
194+
crate_paths.push((short, fqp.clone()));
195195
Some(pathid)
196196
} else {
197197
None
@@ -213,18 +213,58 @@ pub(crate) fn build_index<'tcx>(
213213
struct CrateData<'a> {
214214
doc: String,
215215
items: Vec<&'a IndexItem>,
216-
paths: Vec<(ItemType, Symbol)>,
216+
paths: Vec<(ItemType, Vec<Symbol>)>,
217217
// The String is alias name and the vec is the list of the elements with this alias.
218218
//
219219
// To be noted: the `usize` elements are indexes to `items`.
220220
aliases: &'a BTreeMap<String, Vec<usize>>,
221221
}
222222

223+
struct Paths {
224+
ty: ItemType,
225+
name: Symbol,
226+
path: Option<usize>,
227+
}
228+
229+
impl Serialize for Paths {
230+
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
231+
where
232+
S: Serializer,
233+
{
234+
let mut seq = serializer.serialize_seq(None)?;
235+
seq.serialize_element(&self.ty)?;
236+
seq.serialize_element(self.name.as_str())?;
237+
if let Some(ref path) = self.path {
238+
seq.serialize_element(path)?;
239+
}
240+
seq.end()
241+
}
242+
}
243+
223244
impl<'a> Serialize for CrateData<'a> {
224245
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
225246
where
226247
S: Serializer,
227248
{
249+
let mut mod_paths = FxHashMap::default();
250+
for (index, item) in self.items.iter().enumerate() {
251+
if item.path.is_empty() {
252+
continue;
253+
}
254+
mod_paths.insert(&item.path, index);
255+
}
256+
let paths = self
257+
.paths
258+
.iter()
259+
.map(|(ty, path)| {
260+
if path.len() < 2 {
261+
return Paths { ty: *ty, name: path[0], path: None };
262+
}
263+
let index = mod_paths.get(&join_with_double_colon(&path[..path.len() - 1]));
264+
Paths { ty: *ty, name: *path.last().unwrap(), path: index.copied() }
265+
})
266+
.collect::<Vec<_>>();
267+
228268
let has_aliases = !self.aliases.is_empty();
229269
let mut crate_data =
230270
serializer.serialize_struct("CrateData", if has_aliases { 9 } else { 8 })?;
@@ -321,10 +361,7 @@ pub(crate) fn build_index<'tcx>(
321361
.filter_map(|(index, item)| item.deprecation.map(|_| index))
322362
.collect::<Vec<_>>(),
323363
)?;
324-
crate_data.serialize_field(
325-
"p",
326-
&self.paths.iter().map(|(it, s)| (it, s.as_str())).collect::<Vec<_>>(),
327-
)?;
364+
crate_data.serialize_field("p", &paths)?;
328365
if has_aliases {
329366
crate_data.serialize_field("a", &self.aliases)?;
330367
}

src/librustdoc/html/static/js/search.js

+89-23
Original file line numberDiff line numberDiff line change
@@ -1462,6 +1462,32 @@ function initSearch(rawSearchIndex) {
14621462
if (!typePassesFilter(queryElem.typeFilter, fnType.ty)) {
14631463
continue;
14641464
}
1465+
const queryElemPathLength = queryElem.pathWithoutLast.length;
1466+
// If the query element is a path (it contains `::`), we need to check if this
1467+
// path is compatible with the target type.
1468+
if (queryElemPathLength > 0) {
1469+
const fnTypePath = fnType.path !== undefined && fnType.path !== null ?
1470+
fnType.path.split("::") : [];
1471+
// If the path provided in the query element is longer than this type,
1472+
// no need to check it since it won't match in any case.
1473+
if (queryElemPathLength > fnTypePath.length) {
1474+
continue;
1475+
}
1476+
let i = 0;
1477+
for (const path of fnTypePath) {
1478+
if (path === queryElem.pathWithoutLast[i]) {
1479+
i += 1;
1480+
if (i >= queryElemPathLength) {
1481+
break;
1482+
}
1483+
}
1484+
}
1485+
if (i < queryElemPathLength) {
1486+
// If we didn't find all parts of the path of the query element inside
1487+
// the fn type, then it's not the right one.
1488+
continue;
1489+
}
1490+
}
14651491
if (queryElem.generics.length === 0 || checkGenerics(fnType, queryElem)) {
14661492
currentFnTypeList.splice(i, 1);
14671493
const result = doHandleQueryElemList(currentFnTypeList, queryElemList);
@@ -1862,14 +1888,14 @@ function initSearch(rawSearchIndex) {
18621888
* @param {QueryElement} elem
18631889
*/
18641890
function convertNameToId(elem) {
1865-
if (typeNameIdMap.has(elem.name)) {
1866-
elem.id = typeNameIdMap.get(elem.name);
1891+
if (typeNameIdMap.has(elem.pathLast)) {
1892+
elem.id = typeNameIdMap.get(elem.pathLast);
18671893
} else if (!parsedQuery.literalSearch) {
18681894
let match = -1;
18691895
let matchDist = maxEditDistance + 1;
18701896
let matchName = "";
18711897
for (const [name, id] of typeNameIdMap) {
1872-
const dist = editDistance(name, elem.name, maxEditDistance);
1898+
const dist = editDistance(name, elem.pathLast, maxEditDistance);
18731899
if (dist <= matchDist && dist <= maxEditDistance) {
18741900
if (dist === matchDist && matchName > name) {
18751901
continue;
@@ -2385,10 +2411,19 @@ ${item.displayPath}<span class="${type}">${name}</span>\
23852411
);
23862412
}
23872413
// `0` is used as a sentinel because it's fewer bytes than `null`
2388-
const item = pathIndex === 0 ? null : lowercasePaths[pathIndex - 1];
2414+
if (pathIndex === 0) {
2415+
return {
2416+
id: -1,
2417+
ty: null,
2418+
path: null,
2419+
generics: generics,
2420+
};
2421+
}
2422+
const item = lowercasePaths[pathIndex - 1];
23892423
return {
2390-
id: item === null ? -1 : buildTypeMapIndex(item.name),
2391-
ty: item === null ? null : item.ty,
2424+
id: buildTypeMapIndex(item.name),
2425+
ty: item.ty,
2426+
path: item.path,
23922427
generics: generics,
23932428
};
23942429
});
@@ -2417,15 +2452,25 @@ ${item.displayPath}<span class="${type}">${name}</span>\
24172452
if (functionSearchType === 0) {
24182453
return null;
24192454
}
2420-
let inputs, output, item;
2455+
let inputs, output;
24212456
if (typeof functionSearchType[INPUTS_DATA] === "number") {
24222457
const pathIndex = functionSearchType[INPUTS_DATA];
2423-
item = pathIndex === 0 ? null : lowercasePaths[pathIndex - 1];
2424-
inputs = [{
2425-
id: item === null ? -1 : buildTypeMapIndex(item.name),
2426-
ty: item === null ? null : item.ty,
2427-
generics: [],
2428-
}];
2458+
if (pathIndex === 0) {
2459+
inputs = [{
2460+
id: -1,
2461+
ty: null,
2462+
path: null,
2463+
generics: [],
2464+
}];
2465+
} else {
2466+
const item = lowercasePaths[pathIndex - 1];
2467+
inputs = [{
2468+
id: buildTypeMapIndex(item.name),
2469+
ty: item.ty,
2470+
path: item.path,
2471+
generics: [],
2472+
}];
2473+
}
24292474
} else {
24302475
inputs = buildItemSearchTypeAll(
24312476
functionSearchType[INPUTS_DATA],
@@ -2435,12 +2480,22 @@ ${item.displayPath}<span class="${type}">${name}</span>\
24352480
if (functionSearchType.length > 1) {
24362481
if (typeof functionSearchType[OUTPUT_DATA] === "number") {
24372482
const pathIndex = functionSearchType[OUTPUT_DATA];
2438-
item = pathIndex === 0 ? null : lowercasePaths[pathIndex - 1];
2439-
output = [{
2440-
id: item === null ? -1 : buildTypeMapIndex(item.name),
2441-
ty: item === null ? null : item.ty,
2442-
generics: [],
2443-
}];
2483+
if (pathIndex === 0) {
2484+
output = [{
2485+
id: -1,
2486+
ty: null,
2487+
path: null,
2488+
generics: [],
2489+
}];
2490+
} else {
2491+
const item = lowercasePaths[pathIndex - 1];
2492+
output = [{
2493+
id: buildTypeMapIndex(item.name),
2494+
ty: item.ty,
2495+
path: item.path,
2496+
generics: [],
2497+
}];
2498+
}
24442499
} else {
24452500
output = buildItemSearchTypeAll(
24462501
functionSearchType[OUTPUT_DATA],
@@ -2573,9 +2628,19 @@ ${item.displayPath}<span class="${type}">${name}</span>\
25732628
// convert `rawPaths` entries into object form
25742629
// generate normalizedPaths for function search mode
25752630
let len = paths.length;
2631+
let lastPath = itemPaths.get(0);
25762632
for (let i = 0; i < len; ++i) {
2577-
lowercasePaths.push({ty: paths[i][0], name: paths[i][1].toLowerCase()});
2578-
paths[i] = {ty: paths[i][0], name: paths[i][1]};
2633+
const elem = paths[i];
2634+
const ty = elem[0];
2635+
const name = elem[1];
2636+
let path = null;
2637+
if (elem.length > 2) {
2638+
path = itemPaths.has(elem[2]) ? itemPaths.get(elem[2]) : lastPath;
2639+
lastPath = path;
2640+
}
2641+
2642+
lowercasePaths.push({ty: ty, name: name.toLowerCase(), path: path});
2643+
paths[i] = {ty: ty, name: name, path: path};
25792644
}
25802645

25812646
// convert `item*` into an object form, and construct word indices.
@@ -2585,8 +2650,8 @@ ${item.displayPath}<span class="${type}">${name}</span>\
25852650
// operation that is cached for the life of the page state so that
25862651
// all other search operations have access to this cached data for
25872652
// faster analysis operations
2653+
lastPath = "";
25882654
len = itemTypes.length;
2589-
let lastPath = "";
25902655
for (let i = 0; i < len; ++i) {
25912656
let word = "";
25922657
// This object should have exactly the same set of fields as the "crateRow"
@@ -2595,11 +2660,12 @@ ${item.displayPath}<span class="${type}">${name}</span>\
25952660
word = itemNames[i].toLowerCase();
25962661
}
25972662
searchWords.push(word);
2663+
const path = itemPaths.has(i) ? itemPaths.get(i) : lastPath;
25982664
const row = {
25992665
crate: crate,
26002666
ty: itemTypes.charCodeAt(i) - charA,
26012667
name: itemNames[i],
2602-
path: itemPaths.has(i) ? itemPaths.get(i) : lastPath,
2668+
path: path,
26032669
desc: itemDescs[i],
26042670
parent: itemParentIdxs[i] > 0 ? paths[itemParentIdxs[i] - 1] : undefined,
26052671
type: buildFunctionSearchType(

0 commit comments

Comments
 (0)