@@ -1208,13 +1208,62 @@ function initSearch(rawSearchIndex) {
12081208 if ( ! fnTypes || fnTypes . length === 0 ) {
12091209 return false ;
12101210 }
1211+ /**
1212+ * @type Map<integer, QueryElement[]>
1213+ */
1214+ const queryElemSet = new Map ( ) ;
1215+ const addQueryElemToQueryElemSet = function addQueryElemToQueryElemSet ( queryElem ) {
1216+ let currentQueryElemList ;
1217+ if ( queryElemSet . has ( queryElem . id ) ) {
1218+ currentQueryElemList = queryElemSet . get ( queryElem . id ) ;
1219+ } else {
1220+ currentQueryElemList = [ ] ;
1221+ queryElemSet . set ( queryElem . id , currentQueryElemList ) ;
1222+ }
1223+ currentQueryElemList . push ( queryElem ) ;
1224+ } ;
1225+ for ( const queryElem of queryElems ) {
1226+ addQueryElemToQueryElemSet ( queryElem ) ;
1227+ }
12111228 /**
12121229 * @type Map<integer, FunctionType[]>
12131230 */
12141231 const fnTypeSet = new Map ( ) ;
12151232 const addFnTypeToFnTypeSet = function addFnTypeToFnTypeSet ( fnType ) {
1216- if ( fnType . id === - 1 ) {
1217- // Pure generic, needs to check into it.
1233+ // Pure generic, or an item that's not matched by any query elems.
1234+ // Try [unboxing] it.
1235+ //
1236+ // [unboxing]:
1237+ // http://ndmitchell.com/downloads/slides-hoogle_fast_type_searching-09_aug_2008.pdf
1238+ const queryContainsArrayOrSliceElem = queryElemSet . has ( typeNameIdOfArrayOrSlice ) ;
1239+ if ( fnType . id === - 1 || ! (
1240+ queryElemSet . has ( fnType . id ) ||
1241+ ( fnType . id === typeNameIdOfSlice && queryContainsArrayOrSliceElem ) ||
1242+ ( fnType . id === typeNameIdOfArray && queryContainsArrayOrSliceElem )
1243+ ) ) {
1244+ for ( const innerFnType of fnType . generics ) {
1245+ addFnTypeToFnTypeSet ( innerFnType ) ;
1246+ }
1247+ return ;
1248+ }
1249+ let currentQueryElemList = queryElemSet . get ( fnType . id ) || [ ] ;
1250+ let matchIdx = currentQueryElemList . findIndex ( queryElem => {
1251+ return typePassesFilter ( queryElem . typeFilter , fnType . ty ) &&
1252+ checkGenerics ( fnType , queryElem ) ;
1253+ } ) ;
1254+ if ( matchIdx === - 1 &&
1255+ ( fnType . id === typeNameIdOfSlice || fnType . id === typeNameIdOfArray ) &&
1256+ queryContainsArrayOrSliceElem
1257+ ) {
1258+ currentQueryElemList = queryElemSet . get ( typeNameIdOfArrayOrSlice ) || [ ] ;
1259+ matchIdx = currentQueryElemList . findIndex ( queryElem => {
1260+ return typePassesFilter ( queryElem . typeFilter , fnType . ty ) &&
1261+ checkGenerics ( fnType , queryElem ) ;
1262+ } ) ;
1263+ }
1264+ // None of the query elems match the function type.
1265+ // Try [unboxing] it.
1266+ if ( matchIdx === - 1 ) {
12181267 for ( const innerFnType of fnType . generics ) {
12191268 addFnTypeToFnTypeSet ( innerFnType ) ;
12201269 }
@@ -1232,85 +1281,66 @@ function initSearch(rawSearchIndex) {
12321281 for ( const fnType of fnTypes ) {
12331282 addFnTypeToFnTypeSet ( fnType ) ;
12341283 }
1235- // We need to find the type that matches the most to remove it in order
1236- // to move forward.
1237- const handleQueryElem = queryElem => {
1238- if ( ! fnTypeSet . has ( queryElem . id ) ) {
1239- return false ;
1284+ const doHandleQueryElemList = ( currentFnTypeList , queryElemList ) => {
1285+ if ( queryElemList . length === 0 ) {
1286+ return true ;
12401287 }
1241- const currentFnTypeList = fnTypeSet . get ( queryElem . id ) ;
1242- const matchIdx = currentFnTypeList . findIndex ( fnType => {
1288+ // Multiple items in one list might match multiple items in another.
1289+ // Since an item with fewer generics can match an item with more, we
1290+ // need to check all combinations for a potential match.
1291+ const queryElem = queryElemList . pop ( ) ;
1292+ const l = currentFnTypeList . length ;
1293+ for ( let i = 0 ; i < l ; i += 1 ) {
1294+ const fnType = currentFnTypeList [ i ] ;
12431295 if ( ! typePassesFilter ( queryElem . typeFilter , fnType . ty ) ) {
1244- return false ;
1296+ continue ;
1297+ }
1298+ if ( queryElem . generics . length === 0 || checkGenerics ( fnType , queryElem ) ) {
1299+ currentFnTypeList . splice ( i , 1 ) ;
1300+ const result = doHandleQueryElemList ( currentFnTypeList , queryElemList ) ;
1301+ if ( result ) {
1302+ return true ;
1303+ }
1304+ currentFnTypeList . splice ( i , 0 , fnType ) ;
12451305 }
1246- return queryElem . generics . length === 0 || checkGenerics ( fnType , queryElem ) ;
1247- } ) ;
1248- if ( matchIdx === - 1 ) {
1249- return false ;
1250- }
1251- currentFnTypeList . splice ( matchIdx , 1 ) ;
1252- if ( currentFnTypeList . length === 0 ) {
1253- fnTypeSet . delete ( queryElem . id ) ;
12541306 }
1255- return true ;
1307+ return false ;
12561308 } ;
1257- // To do the right thing with type filters, we first process generics
1258- // that have them, removing matching ones from the "bag," then do the
1259- // ones with no type filter, which can match any entry regardless of its
1260- // own type.
1261- const needsUnboxed = [ ] ;
1262- for ( const queryElem of queryElems ) {
1263- if ( queryElem . typeFilter === TY_PRIMITIVE &&
1264- queryElem . id === typeNameIdOfArrayOrSlice ) {
1265- const queryElemArray = {
1266- id : typeNameIdOfArray ,
1267- typeFilter : TY_PRIMITIVE ,
1268- generics : queryElem . generics ,
1269- } ;
1270- const queryElemSlice = {
1271- id : typeNameIdOfSlice ,
1272- typeFilter : TY_PRIMITIVE ,
1273- generics : queryElem . generics ,
1274- } ;
1275- if ( ! handleQueryElem ( queryElemArray ) && ! handleQueryElem ( queryElemSlice ) ) {
1276- needsUnboxed . push ( queryElem ) ;
1309+ const handleQueryElemList = ( id , queryElemList ) => {
1310+ if ( ! fnTypeSet . has ( id ) ) {
1311+ if ( id === typeNameIdOfArrayOrSlice ) {
1312+ return handleQueryElemList ( typeNameIdOfSlice , queryElemList ) ||
1313+ handleQueryElemList ( typeNameIdOfArray , queryElemList ) ;
12771314 }
1278- } else if ( queryElem . typeFilter !== - 1 && ! handleQueryElem ( queryElem ) ) {
1279- needsUnboxed . push ( queryElem ) ;
1315+ return false ;
12801316 }
1281- }
1282- for ( const queryElem of queryElems ) {
1283- if ( queryElem . typeFilter === - 1 && ! handleQueryElem ( queryElem ) ) {
1284- needsUnboxed . push ( queryElem ) ;
1317+ const currentFnTypeList = fnTypeSet . get ( id ) ;
1318+ if ( currentFnTypeList . length < queryElemList . length ) {
1319+ // It's not possible for all the query elems to find a match.
1320+ return false ;
12851321 }
1286- }
1287- // If the current item does not match, try [unboxing] the generic.
1288- // [unboxing]:
1289- // https://ndmitchell.com/downloads/slides-hoogle_fast_type_searching-09_aug_2008.pdf
1290- unboxing: while ( needsUnboxed . length !== 0 ) {
1291- for ( const [ i , queryElem ] of needsUnboxed . entries ( ) ) {
1292- if ( handleQueryElem ( queryElem ) ) {
1293- needsUnboxed . splice ( i , 1 ) ;
1294- continue unboxing;
1322+ const result = doHandleQueryElemList ( currentFnTypeList , queryElemList ) ;
1323+ if ( result ) {
1324+ // Found a solution.
1325+ // Any items that weren't used for it can be unboxed, and might form
1326+ // part of the solution for another item.
1327+ for ( const innerFnType of currentFnTypeList ) {
1328+ addFnTypeToFnTypeSet ( innerFnType ) ;
12951329 }
1330+ fnTypeSet . delete ( id ) ;
12961331 }
1297- for ( const [ id , fnTypeList ] of fnTypeSet ) {
1298- for ( const [ i , fnType ] of fnTypeList . entries ( ) ) {
1299- if ( fnType . generics . length !== 0 ) {
1300- fnTypeList . splice ( i , 1 ) ;
1301- for ( const innerFnType of fnType . generics ) {
1302- addFnTypeToFnTypeSet ( innerFnType ) ;
1303- }
1304- if ( fnTypeList . length === 0 ) {
1305- fnTypeSet . delete ( id ) ;
1306- }
1307- continue unboxing;
1308- }
1332+ return result ;
1333+ } ;
1334+ let queryElemSetSize = - 1 ;
1335+ while ( queryElemSetSize !== queryElemSet . size ) {
1336+ queryElemSetSize = queryElemSet . size ;
1337+ for ( const [ id , queryElemList ] of queryElemSet ) {
1338+ if ( handleQueryElemList ( id , queryElemList ) ) {
1339+ queryElemSet . delete ( id ) ;
13091340 }
13101341 }
1311- return false ;
13121342 }
1313- return true ;
1343+ return queryElemSetSize === 0 ;
13141344 }
13151345
13161346 /**
0 commit comments