about summary refs log tree commit diff
path: root/src/librustdoc/html/static/js/search.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/librustdoc/html/static/js/search.js')
-rw-r--r--src/librustdoc/html/static/js/search.js166
1 files changed, 98 insertions, 68 deletions
diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js
index ba5d3843360..56edc015049 100644
--- a/src/librustdoc/html/static/js/search.js
+++ b/src/librustdoc/html/static/js/search.js
@@ -1209,12 +1209,61 @@ function initSearch(rawSearchIndex) {
                 return false;
             }
             /**
+             * @type Map<integer, QueryElement[]>
+             */
+            const queryElemSet = new Map();
+            const addQueryElemToQueryElemSet = function addQueryElemToQueryElemSet(queryElem) {
+                let currentQueryElemList;
+                if (queryElemSet.has(queryElem.id)) {
+                    currentQueryElemList = queryElemSet.get(queryElem.id);
+                } else {
+                    currentQueryElemList = [];
+                    queryElemSet.set(queryElem.id, currentQueryElemList);
+                }
+                currentQueryElemList.push(queryElem);
+            };
+            for (const queryElem of queryElems) {
+                addQueryElemToQueryElemSet(queryElem);
+            }
+            /**
              * @type Map<integer, FunctionType[]>
              */
             const fnTypeSet = new Map();
             const addFnTypeToFnTypeSet = function addFnTypeToFnTypeSet(fnType) {
-                if (fnType.id === -1) {
-                    // Pure generic, needs to check into it.
+                // Pure generic, or an item that's not matched by any query elems.
+                // Try [unboxing] it.
+                //
+                // [unboxing]:
+                // http://ndmitchell.com/downloads/slides-hoogle_fast_type_searching-09_aug_2008.pdf
+                const queryContainsArrayOrSliceElem = queryElemSet.has(typeNameIdOfArrayOrSlice);
+                if (fnType.id === -1 || !(
+                    queryElemSet.has(fnType.id) ||
+                    (fnType.id === typeNameIdOfSlice && queryContainsArrayOrSliceElem) ||
+                    (fnType.id === typeNameIdOfArray && queryContainsArrayOrSliceElem)
+                )) {
+                    for (const innerFnType of fnType.generics) {
+                        addFnTypeToFnTypeSet(innerFnType);
+                    }
+                    return;
+                }
+                let currentQueryElemList = queryElemSet.get(fnType.id) || [];
+                let matchIdx = currentQueryElemList.findIndex(queryElem => {
+                    return typePassesFilter(queryElem.typeFilter, fnType.ty) &&
+                        checkGenerics(fnType, queryElem);
+                });
+                if (matchIdx === -1 &&
+                    (fnType.id === typeNameIdOfSlice || fnType.id === typeNameIdOfArray) &&
+                    queryContainsArrayOrSliceElem
+                ) {
+                    currentQueryElemList = queryElemSet.get(typeNameIdOfArrayOrSlice) || [];
+                    matchIdx = currentQueryElemList.findIndex(queryElem => {
+                        return typePassesFilter(queryElem.typeFilter, fnType.ty) &&
+                            checkGenerics(fnType, queryElem);
+                    });
+                }
+                // None of the query elems match the function type.
+                // Try [unboxing] it.
+                if (matchIdx === -1) {
                     for (const innerFnType of fnType.generics) {
                         addFnTypeToFnTypeSet(innerFnType);
                     }
@@ -1232,85 +1281,66 @@ function initSearch(rawSearchIndex) {
             for (const fnType of fnTypes) {
                 addFnTypeToFnTypeSet(fnType);
             }
-            // We need to find the type that matches the most to remove it in order
-            // to move forward.
-            const handleQueryElem = queryElem => {
-                if (!fnTypeSet.has(queryElem.id)) {
-                    return false;
+            const doHandleQueryElemList = (currentFnTypeList, queryElemList) => {
+                if (queryElemList.length === 0) {
+                    return true;
                 }
-                const currentFnTypeList = fnTypeSet.get(queryElem.id);
-                const matchIdx = currentFnTypeList.findIndex(fnType => {
+                // Multiple items in one list might match multiple items in another.
+                // Since an item with fewer generics can match an item with more, we
+                // need to check all combinations for a potential match.
+                const queryElem = queryElemList.pop();
+                const l = currentFnTypeList.length;
+                for (let i = 0; i < l; i += 1) {
+                    const fnType = currentFnTypeList[i];
                     if (!typePassesFilter(queryElem.typeFilter, fnType.ty)) {
-                        return false;
+                        continue;
+                    }
+                    if (queryElem.generics.length === 0 || checkGenerics(fnType, queryElem)) {
+                        currentFnTypeList.splice(i, 1);
+                        const result = doHandleQueryElemList(currentFnTypeList, queryElemList);
+                        if (result) {
+                            return true;
+                        }
+                        currentFnTypeList.splice(i, 0, fnType);
                     }
-                    return queryElem.generics.length === 0 || checkGenerics(fnType, queryElem);
-                });
-                if (matchIdx === -1) {
-                    return false;
-                }
-                currentFnTypeList.splice(matchIdx, 1);
-                if (currentFnTypeList.length === 0) {
-                    fnTypeSet.delete(queryElem.id);
                 }
-                return true;
+                return false;
             };
-            // To do the right thing with type filters, we first process generics
-            // that have them, removing matching ones from the "bag," then do the
-            // ones with no type filter, which can match any entry regardless of its
-            // own type.
-            const needsUnboxed = [];
-            for (const queryElem of queryElems) {
-                if (queryElem.typeFilter === TY_PRIMITIVE &&
-                    queryElem.id === typeNameIdOfArrayOrSlice) {
-                    const queryElemArray = {
-                        id: typeNameIdOfArray,
-                        typeFilter: TY_PRIMITIVE,
-                        generics: queryElem.generics,
-                    };
-                    const queryElemSlice = {
-                        id: typeNameIdOfSlice,
-                        typeFilter: TY_PRIMITIVE,
-                        generics: queryElem.generics,
-                    };
-                    if (!handleQueryElem(queryElemArray) && !handleQueryElem(queryElemSlice)) {
-                        needsUnboxed.push(queryElem);
+            const handleQueryElemList = (id, queryElemList) => {
+                if (!fnTypeSet.has(id)) {
+                    if (id === typeNameIdOfArrayOrSlice) {
+                        return handleQueryElemList(typeNameIdOfSlice, queryElemList) ||
+                            handleQueryElemList(typeNameIdOfArray, queryElemList);
                     }
-                } else if (queryElem.typeFilter !== -1 && !handleQueryElem(queryElem)) {
-                    needsUnboxed.push(queryElem);
+                    return false;
                 }
-            }
-            for (const queryElem of queryElems) {
-                if (queryElem.typeFilter === -1 && !handleQueryElem(queryElem)) {
-                    needsUnboxed.push(queryElem);
+                const currentFnTypeList = fnTypeSet.get(id);
+                if (currentFnTypeList.length < queryElemList.length) {
+                    // It's not possible for all the query elems to find a match.
+                    return false;
                 }
-            }
-            // If the current item does not match, try [unboxing] the generic.
-            // [unboxing]:
-            //   https://ndmitchell.com/downloads/slides-hoogle_fast_type_searching-09_aug_2008.pdf
-            unboxing: while (needsUnboxed.length !== 0) {
-                for (const [i, queryElem] of needsUnboxed.entries()) {
-                    if (handleQueryElem(queryElem)) {
-                        needsUnboxed.splice(i, 1);
-                        continue unboxing;
+                const result = doHandleQueryElemList(currentFnTypeList, queryElemList);
+                if (result) {
+                    // Found a solution.
+                    // Any items that weren't used for it can be unboxed, and might form
+                    // part of the solution for another item.
+                    for (const innerFnType of currentFnTypeList) {
+                        addFnTypeToFnTypeSet(innerFnType);
                     }
+                    fnTypeSet.delete(id);
                 }
-                for (const [id, fnTypeList] of fnTypeSet) {
-                    for (const [i, fnType] of fnTypeList.entries()) {
-                        if (fnType.generics.length !== 0) {
-                            fnTypeList.splice(i, 1);
-                            for (const innerFnType of fnType.generics) {
-                                addFnTypeToFnTypeSet(innerFnType);
-                            }
-                            if (fnTypeList.length === 0) {
-                                fnTypeSet.delete(id);
-                            }
-                            continue unboxing;
-                        }
+                return result;
+            };
+            let queryElemSetSize = -1;
+            while (queryElemSetSize !== queryElemSet.size) {
+                queryElemSetSize = queryElemSet.size;
+                for (const [id, queryElemList] of queryElemSet) {
+                    if (handleQueryElemList(id, queryElemList)) {
+                        queryElemSet.delete(id);
                     }
                 }
-                return false;
             }
-            return true;
+            return queryElemSetSize === 0;
         }
 
         /**