diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index 4e1bbbbf59d89..8a7c1917f8235 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -56,6 +56,7 @@ const itemTypes = [ "derive", "traitalias", // 25 "generic", + "crate", ]; // used for special search precedence @@ -63,6 +64,8 @@ const TY_PRIMITIVE = itemTypes.indexOf("primitive"); const TY_GENERIC = itemTypes.indexOf("generic"); const TY_IMPORT = itemTypes.indexOf("import"); const TY_TRAIT = itemTypes.indexOf("trait"); +// minor hack to implement the `crate:` syntax +const TY_CRATE = itemTypes.indexOf("crate"); const ROOT_PATH = typeof window !== "undefined" ? window.rootPath : "../"; // Hard limit on how deep to recurse into generics when doing type-driven search. @@ -291,6 +294,20 @@ function getFilteredNextElem(query, parserState, elems, isInGenerics) { parserState.pos += 1; parserState.totalElems -= 1; query.literalSearch = false; + if (parserState.typeFilter === "crate") { + while (parserState.userQuery[parserState.pos] === " ") { + parserState.pos += 1; + } + const start = parserState.pos; + const foundCrate = consumeIdent(parserState); + if (!foundCrate) { + throw ["Expected ident after ", "crate:", ", found ", parserState.userQuery[start]]; + } + const name = parserState.userQuery.substring(start, parserState.pos); + elems.push(makePrimitiveElement(name, { typeFilter: "crate" })); + parserState.typeFilter = null; + return getFilteredNextElem(query, parserState, elems, isInGenerics); + } getNextElem(query, parserState, elems, isInGenerics); } } @@ -1870,6 +1887,7 @@ class DocSearch { correction: null, proposeCorrectionFrom: null, proposeCorrectionTo: null, + filterCrates: null, // bloom filter build from type ids typeFingerprint: new Uint32Array(4), }; @@ -1996,6 +2014,20 @@ class DocSearch { query.error = err; return query; } + + function handleCrateFilters(elem) { + if (elem.typeFilter === TY_CRATE) { + query.filterCrates = elem.name; + return false; + } + return true; + + } + const nonCrateElems = query.elems.filter(handleCrateFilters); + if (nonCrateElems.length !== query.elems.length) { + query.elems = nonCrateElems; + } + if (!query.literalSearch) { // If there is more than one element in the query, we switch to literalSearch in any // case. @@ -4416,7 +4448,6 @@ function updateSearchHistory(url) { async function search(forced) { const query = DocSearch.parseQuery(searchState.input.value.trim()); let filterCrates = getFilterCrates(); - if (!forced && query.userQuery === currentResults) { if (query.userQuery.length > 0) { putBackSearch(); @@ -4428,6 +4459,9 @@ async function search(forced) { const params = searchState.getQueryStringParams(); + if (query.filterCrates !== null) { + filterCrates = query.filterCrates; + } // In case we have no information about the saved crate and there is a URL query parameter, // we override it with the URL query parameter. if (filterCrates === null && params["filter-crate"] !== undefined) { @@ -4613,8 +4647,19 @@ function registerSearchEvents() { function updateCrate(ev) { if (ev.target.value === "all crates") { // If we don't remove it from the URL, it'll be picked up again by the search. - const query = searchState.input.value.trim(); + const query = searchState.input.value.trim() + .replace(/crate:[a-zA-Z_0-9]+/, ""); updateSearchHistory(buildUrl(query, null)); + searchState.input.value = query; + } else { + const crate = ev.target.value; + // add/update the `crate:` syntax in the search bar + let newquery = searchState.input.value + .replace(/crate:[a-zA-Z_0-9]+/, "crate:" + crate); + if (!newquery.includes("crate:")) { + newquery = "crate:" + crate + " " + searchState.input.value; + } + searchState.input.value = newquery; } // In case you "cut" the entry from the search input, then change the crate filter // before paste back the previous search, you get the old search results without