diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs index f34be120d29..a7771c31f85 100644 --- a/src/librustdoc/html/render/search_index.rs +++ b/src/librustdoc/html/render/search_index.rs @@ -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, itemid: F, lastpathid: &mut usize, - crate_paths: &mut Vec<(ItemType, Symbol)>, + crate_paths: &mut Vec<(ItemType, Vec)>, 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, primitives: &mut FxHashMap, lastpathid: &mut usize, - crate_paths: &mut Vec<(ItemType, Symbol)>, + crate_paths: &mut Vec<(ItemType, Vec)>, ) { 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)>, // 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>, } + struct Paths { + ty: ItemType, + name: Symbol, + path: Option, + } + + impl Serialize for Paths { + fn serialize(&self, serializer: S) -> Result + 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(&self, serializer: S) -> Result 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::>(); + 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::>(), )?; - crate_data.serialize_field( - "p", - &self.paths.iter().map(|(it, s)| (it, s.as_str())).collect::>(), - )?; + crate_data.serialize_field("p", &paths)?; if has_aliases { crate_data.serialize_field("a", &self.aliases)?; } diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index 00b6b3a4420..449168c460b 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -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}${name}\ ); } // `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}${name}\ 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}${name}\ 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}${name}\ // 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}${name}\ // 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}${name}\ 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(