@@ -1178,98 +1178,137 @@ function initSearch(rawSearchIndex) {
1178
1178
}
1179
1179
1180
1180
/**
1181
- * This function checks if the object (`row`) generics match the given type (`elem`)
1182
- * generics.
1181
+ * This function checks generics in search query `queryElem` can all be found in the
1182
+ * search index (`fnType`),
1183
1183
*
1184
- * @param {Row } row - The object to check.
1185
- * @param {QueryElement } elem - The element from the parsed query.
1184
+ * @param {FunctionType } fnType - The object to check.
1185
+ * @param {QueryElement } queryElem - The element from the parsed query.
1186
1186
*
1187
- * @return {boolean } - Returns true if a match, false otherwise.
1187
+ * @return {boolean } - Returns true if a match, false otherwise.
1188
1188
*/
1189
- function checkGenerics ( row , elem ) {
1190
- if ( row . generics . length === 0 || elem . generics . length === 0 ) {
1191
- return false ;
1192
- }
1193
- // This function is called if the names match, but we need to make
1194
- // sure that all generics match as well.
1195
- //
1189
+ function checkGenerics ( fnType , queryElem ) {
1190
+ return unifyFunctionTypes ( fnType . generics , queryElem . generics ) ;
1191
+ }
1192
+ /**
1193
+ * This function checks if a list of search query `queryElems` can all be found in the
1194
+ * search index (`fnTypes`).
1195
+ *
1196
+ * @param {Array<FunctionType> } fnTypes - The objects to check.
1197
+ * @param {Array<QueryElement> } queryElems - The elements from the parsed query.
1198
+ *
1199
+ * @return {boolean } - Returns true if a match, false otherwise.
1200
+ */
1201
+ function unifyFunctionTypes ( fnTypes , queryElems ) {
1196
1202
// This search engine implements order-agnostic unification. There
1197
1203
// should be no missing duplicates (generics have "bag semantics"),
1198
1204
// and the row is allowed to have extras.
1199
- if ( elem . generics . length <= 0 || row . generics . length < elem . generics . length ) {
1205
+ if ( queryElems . length === 0 ) {
1206
+ return true ;
1207
+ }
1208
+ if ( ! fnTypes || fnTypes . length === 0 ) {
1200
1209
return false ;
1201
1210
}
1202
- const elems = new Map ( ) ;
1203
- const addEntryToElems = function addEntryToElems ( entry ) {
1204
- if ( entry . id === - 1 ) {
1211
+ /**
1212
+ * @type Map<integer, FunctionType[]>
1213
+ */
1214
+ const fnTypeSet = new Map ( ) ;
1215
+ const addFnTypeToFnTypeSet = function addFnTypeToFnTypeSet ( fnType ) {
1216
+ if ( fnType . id === - 1 ) {
1205
1217
// Pure generic, needs to check into it.
1206
- for ( const inner_entry of entry . generics ) {
1207
- addEntryToElems ( inner_entry ) ;
1218
+ for ( const innerFnType of fnType . generics ) {
1219
+ addFnTypeToFnTypeSet ( innerFnType ) ;
1208
1220
}
1209
1221
return ;
1210
1222
}
1211
- let currentEntryElems ;
1212
- if ( elems . has ( entry . id ) ) {
1213
- currentEntryElems = elems . get ( entry . id ) ;
1223
+ let currentFnTypeList ;
1224
+ if ( fnTypeSet . has ( fnType . id ) ) {
1225
+ currentFnTypeList = fnTypeSet . get ( fnType . id ) ;
1214
1226
} else {
1215
- currentEntryElems = [ ] ;
1216
- elems . set ( entry . id , currentEntryElems ) ;
1227
+ currentFnTypeList = [ ] ;
1228
+ fnTypeSet . set ( fnType . id , currentFnTypeList ) ;
1217
1229
}
1218
- currentEntryElems . push ( entry ) ;
1230
+ currentFnTypeList . push ( fnType ) ;
1219
1231
} ;
1220
- for ( const entry of row . generics ) {
1221
- addEntryToElems ( entry ) ;
1232
+ for ( const fnType of fnTypes ) {
1233
+ addFnTypeToFnTypeSet ( fnType ) ;
1222
1234
}
1223
1235
// We need to find the type that matches the most to remove it in order
1224
1236
// to move forward.
1225
- const handleGeneric = generic => {
1226
- if ( ! elems . has ( generic . id ) ) {
1237
+ const handleQueryElem = queryElem => {
1238
+ if ( ! fnTypeSet . has ( queryElem . id ) ) {
1227
1239
return false ;
1228
1240
}
1229
- const matchElems = elems . get ( generic . id ) ;
1230
- const matchIdx = matchElems . findIndex ( tmp_elem => {
1231
- if ( generic . generics . length > 0 && ! checkGenerics ( tmp_elem , generic ) ) {
1241
+ const currentFnTypeList = fnTypeSet . get ( queryElem . id ) ;
1242
+ const matchIdx = currentFnTypeList . findIndex ( fnType => {
1243
+ if ( ! typePassesFilter ( queryElem . typeFilter , fnType . ty ) ) {
1232
1244
return false ;
1233
1245
}
1234
- return typePassesFilter ( generic . typeFilter , tmp_elem . ty ) ;
1246
+ return queryElem . generics . length === 0 || checkGenerics ( fnType , queryElem ) ;
1235
1247
} ) ;
1236
1248
if ( matchIdx === - 1 ) {
1237
1249
return false ;
1238
1250
}
1239
- matchElems . splice ( matchIdx , 1 ) ;
1240
- if ( matchElems . length === 0 ) {
1241
- elems . delete ( generic . id ) ;
1251
+ currentFnTypeList . splice ( matchIdx , 1 ) ;
1252
+ if ( currentFnTypeList . length === 0 ) {
1253
+ fnTypeSet . delete ( queryElem . id ) ;
1242
1254
}
1243
1255
return true ;
1244
1256
} ;
1245
1257
// To do the right thing with type filters, we first process generics
1246
1258
// that have them, removing matching ones from the "bag," then do the
1247
1259
// ones with no type filter, which can match any entry regardless of its
1248
1260
// own type.
1249
- for ( const generic of elem . generics ) {
1250
- if ( generic . typeFilter === TY_PRIMITIVE &&
1251
- generic . id === typeNameIdOfArrayOrSlice ) {
1252
- const genericArray = {
1261
+ const needsUnboxed = [ ] ;
1262
+ for ( const queryElem of queryElems ) {
1263
+ if ( queryElem . typeFilter === TY_PRIMITIVE &&
1264
+ queryElem . id === typeNameIdOfArrayOrSlice ) {
1265
+ const queryElemArray = {
1253
1266
id : typeNameIdOfArray ,
1254
1267
typeFilter : TY_PRIMITIVE ,
1255
- generics : generic . generics ,
1268
+ generics : queryElem . generics ,
1256
1269
} ;
1257
- const genericSlice = {
1270
+ const queryElemSlice = {
1258
1271
id : typeNameIdOfSlice ,
1259
1272
typeFilter : TY_PRIMITIVE ,
1260
- generics : generic . generics ,
1273
+ generics : queryElem . generics ,
1261
1274
} ;
1262
- if ( ! handleGeneric ( genericArray ) && ! handleGeneric ( genericSlice ) ) {
1263
- return false ;
1275
+ if ( ! handleQueryElem ( queryElemArray ) && ! handleQueryElem ( queryElemSlice ) ) {
1276
+ needsUnboxed . push ( queryElem ) ;
1264
1277
}
1265
- } else if ( generic . typeFilter !== - 1 && ! handleGeneric ( generic ) ) {
1266
- return false ;
1278
+ } else if ( queryElem . typeFilter !== - 1 && ! handleQueryElem ( queryElem ) ) {
1279
+ needsUnboxed . push ( queryElem ) ;
1267
1280
}
1268
1281
}
1269
- for ( const generic of elem . generics ) {
1270
- if ( generic . typeFilter === - 1 && ! handleGeneric ( generic ) ) {
1271
- return false ;
1282
+ for ( const queryElem of queryElems ) {
1283
+ if ( queryElem . typeFilter === - 1 && ! handleQueryElem ( queryElem ) ) {
1284
+ needsUnboxed . push ( queryElem ) ;
1285
+ }
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;
1295
+ }
1296
+ }
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
+ }
1309
+ }
1272
1310
}
1311
+ return false ;
1273
1312
}
1274
1313
return true ;
1275
1314
}
@@ -1278,13 +1317,13 @@ function initSearch(rawSearchIndex) {
1278
1317
* This function checks if the object (`row`) matches the given type (`elem`) and its
1279
1318
* generics (if any).
1280
1319
*
1281
- * @param {Row } row
1320
+ * @param {Array<FunctionType> } list
1282
1321
* @param {QueryElement } elem - The element from the parsed query.
1283
1322
*
1284
1323
* @return {boolean } - Returns true if found, false otherwise.
1285
1324
*/
1286
- function checkIfInGenerics ( row , elem ) {
1287
- for ( const entry of row . generics ) {
1325
+ function checkIfInList ( list , elem ) {
1326
+ for ( const entry of list ) {
1288
1327
if ( checkType ( entry , elem ) ) {
1289
1328
return true ;
1290
1329
}
@@ -1304,7 +1343,7 @@ function initSearch(rawSearchIndex) {
1304
1343
function checkType ( row , elem ) {
1305
1344
if ( row . id === - 1 ) {
1306
1345
// This is a pure "generic" search, no need to run other checks.
1307
- return row . generics . length > 0 ? checkIfInGenerics ( row , elem ) : false ;
1346
+ return row . generics . length > 0 ? checkIfInList ( row . generics , elem ) : false ;
1308
1347
}
1309
1348
1310
1349
const matchesExact = row . id === elem . id ;
@@ -1322,59 +1361,7 @@ function initSearch(rawSearchIndex) {
1322
1361
// If the current item does not match, try [unboxing] the generic.
1323
1362
// [unboxing]:
1324
1363
// https://ndmitchell.com/downloads/slides-hoogle_fast_type_searching-09_aug_2008.pdf
1325
- return checkIfInGenerics ( row , elem ) ;
1326
- }
1327
-
1328
- /**
1329
- * This function checks if the object (`row`) has an argument with the given type (`elem`).
1330
- *
1331
- * @param {Row } row
1332
- * @param {QueryElement } elem - The element from the parsed query.
1333
- * @param {Array<integer> } skipPositions - Do not return one of these positions.
1334
- *
1335
- * @return {integer } - Returns the position of the match, or -1 if none.
1336
- */
1337
- function findArg ( row , elem , skipPositions ) {
1338
- if ( row && row . type && row . type . inputs && row . type . inputs . length > 0 ) {
1339
- let i = 0 ;
1340
- for ( const input of row . type . inputs ) {
1341
- if ( skipPositions . indexOf ( i ) !== - 1 ) {
1342
- i += 1 ;
1343
- continue ;
1344
- }
1345
- if ( checkType ( input , elem ) ) {
1346
- return i ;
1347
- }
1348
- i += 1 ;
1349
- }
1350
- }
1351
- return - 1 ;
1352
- }
1353
-
1354
- /**
1355
- * This function checks if the object (`row`) returns the given type (`elem`).
1356
- *
1357
- * @param {Row } row
1358
- * @param {QueryElement } elem - The element from the parsed query.
1359
- * @param {Array<integer> } skipPositions - Do not return one of these positions.
1360
- *
1361
- * @return {integer } - Returns the position of the matching item, or -1 if none.
1362
- */
1363
- function checkReturned ( row , elem , skipPositions ) {
1364
- if ( row && row . type && row . type . output . length > 0 ) {
1365
- let i = 0 ;
1366
- for ( const ret_ty of row . type . output ) {
1367
- if ( skipPositions . indexOf ( i ) !== - 1 ) {
1368
- i += 1 ;
1369
- continue ;
1370
- }
1371
- if ( checkType ( ret_ty , elem ) ) {
1372
- return i ;
1373
- }
1374
- i += 1 ;
1375
- }
1376
- }
1377
- return - 1 ;
1364
+ return checkIfInList ( row . generics , elem ) ;
1378
1365
}
1379
1366
1380
1367
function checkPath ( contains , ty , maxEditDistance ) {
@@ -1575,14 +1562,14 @@ function initSearch(rawSearchIndex) {
1575
1562
const fullId = row . id ;
1576
1563
const searchWord = searchWords [ pos ] ;
1577
1564
1578
- const in_args = findArg ( row , elem , [ ] ) ;
1579
- if ( in_args !== - 1 ) {
1565
+ const in_args = row . type && row . type . inputs && checkIfInList ( row . type . inputs , elem ) ;
1566
+ if ( in_args ) {
1580
1567
// path_dist is 0 because no parent path information is currently stored
1581
1568
// in the search index
1582
1569
addIntoResults ( results_in_args , fullId , pos , - 1 , 0 , 0 , maxEditDistance ) ;
1583
1570
}
1584
- const returned = checkReturned ( row , elem , [ ] ) ;
1585
- if ( returned !== - 1 ) {
1571
+ const returned = row . type && row . type . output && checkIfInList ( row . type . output , elem ) ;
1572
+ if ( returned ) {
1586
1573
addIntoResults ( results_returned , fullId , pos , - 1 , 0 , 0 , maxEditDistance ) ;
1587
1574
}
1588
1575
@@ -1638,32 +1625,15 @@ function initSearch(rawSearchIndex) {
1638
1625
* @param {Object } results
1639
1626
*/
1640
1627
function handleArgs ( row , pos , results ) {
1641
- if ( ! row || ( filterCrates !== null && row . crate !== filterCrates ) ) {
1628
+ if ( ! row || ( filterCrates !== null && row . crate !== filterCrates ) || ! row . type ) {
1642
1629
return ;
1643
1630
}
1644
1631
1645
1632
// If the result is too "bad", we return false and it ends this search.
1646
- function checkArgs ( elems , callback ) {
1647
- const skipPositions = [ ] ;
1648
- for ( const elem of elems ) {
1649
- // There is more than one parameter to the query so all checks should be "exact"
1650
- const position = callback (
1651
- row ,
1652
- elem ,
1653
- skipPositions
1654
- ) ;
1655
- if ( position !== - 1 ) {
1656
- skipPositions . push ( position ) ;
1657
- } else {
1658
- return false ;
1659
- }
1660
- }
1661
- return true ;
1662
- }
1663
- if ( ! checkArgs ( parsedQuery . elems , findArg ) ) {
1633
+ if ( ! unifyFunctionTypes ( row . type . inputs , parsedQuery . elems ) ) {
1664
1634
return ;
1665
1635
}
1666
- if ( ! checkArgs ( parsedQuery . returned , checkReturned ) ) {
1636
+ if ( ! unifyFunctionTypes ( row . type . output , parsedQuery . returned ) ) {
1667
1637
return ;
1668
1638
}
1669
1639
@@ -1750,12 +1720,9 @@ function initSearch(rawSearchIndex) {
1750
1720
elem = parsedQuery . returned [ 0 ] ;
1751
1721
for ( i = 0 , nSearchWords = searchWords . length ; i < nSearchWords ; ++ i ) {
1752
1722
row = searchIndex [ i ] ;
1753
- in_returned = checkReturned (
1754
- row ,
1755
- elem ,
1756
- [ ]
1757
- ) ;
1758
- if ( in_returned !== - 1 ) {
1723
+ in_returned = row . type &&
1724
+ unifyFunctionTypes ( row . type . output , parsedQuery . returned ) ;
1725
+ if ( in_returned ) {
1759
1726
addIntoResults (
1760
1727
results_others ,
1761
1728
row . id ,
0 commit comments