@@ -1178,98 +1178,137 @@ function initSearch(rawSearchIndex) {
11781178 }
11791179
11801180 /**
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`),
11831183 *
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.
11861186 *
1187- * @return {boolean } - Returns true if a match, false otherwise.
1187+ * @return {boolean } - Returns true if a match, false otherwise.
11881188 */
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 ) {
11961202 // This search engine implements order-agnostic unification. There
11971203 // should be no missing duplicates (generics have "bag semantics"),
11981204 // 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 ) {
12001209 return false ;
12011210 }
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 ) {
12051217 // 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 ) ;
12081220 }
12091221 return ;
12101222 }
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 ) ;
12141226 } else {
1215- currentEntryElems = [ ] ;
1216- elems . set ( entry . id , currentEntryElems ) ;
1227+ currentFnTypeList = [ ] ;
1228+ fnTypeSet . set ( fnType . id , currentFnTypeList ) ;
12171229 }
1218- currentEntryElems . push ( entry ) ;
1230+ currentFnTypeList . push ( fnType ) ;
12191231 } ;
1220- for ( const entry of row . generics ) {
1221- addEntryToElems ( entry ) ;
1232+ for ( const fnType of fnTypes ) {
1233+ addFnTypeToFnTypeSet ( fnType ) ;
12221234 }
12231235 // We need to find the type that matches the most to remove it in order
12241236 // to move forward.
1225- const handleGeneric = generic => {
1226- if ( ! elems . has ( generic . id ) ) {
1237+ const handleQueryElem = queryElem => {
1238+ if ( ! fnTypeSet . has ( queryElem . id ) ) {
12271239 return false ;
12281240 }
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 ) ) {
12321244 return false ;
12331245 }
1234- return typePassesFilter ( generic . typeFilter , tmp_elem . ty ) ;
1246+ return queryElem . generics . length === 0 || checkGenerics ( fnType , queryElem ) ;
12351247 } ) ;
12361248 if ( matchIdx === - 1 ) {
12371249 return false ;
12381250 }
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 ) ;
12421254 }
12431255 return true ;
12441256 } ;
12451257 // To do the right thing with type filters, we first process generics
12461258 // that have them, removing matching ones from the "bag," then do the
12471259 // ones with no type filter, which can match any entry regardless of its
12481260 // 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 = {
12531266 id : typeNameIdOfArray ,
12541267 typeFilter : TY_PRIMITIVE ,
1255- generics : generic . generics ,
1268+ generics : queryElem . generics ,
12561269 } ;
1257- const genericSlice = {
1270+ const queryElemSlice = {
12581271 id : typeNameIdOfSlice ,
12591272 typeFilter : TY_PRIMITIVE ,
1260- generics : generic . generics ,
1273+ generics : queryElem . generics ,
12611274 } ;
1262- if ( ! handleGeneric ( genericArray ) && ! handleGeneric ( genericSlice ) ) {
1263- return false ;
1275+ if ( ! handleQueryElem ( queryElemArray ) && ! handleQueryElem ( queryElemSlice ) ) {
1276+ needsUnboxed . push ( queryElem ) ;
12641277 }
1265- } else if ( generic . typeFilter !== - 1 && ! handleGeneric ( generic ) ) {
1266- return false ;
1278+ } else if ( queryElem . typeFilter !== - 1 && ! handleQueryElem ( queryElem ) ) {
1279+ needsUnboxed . push ( queryElem ) ;
12671280 }
12681281 }
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+ }
12721310 }
1311+ return false ;
12731312 }
12741313 return true ;
12751314 }
@@ -1278,13 +1317,13 @@ function initSearch(rawSearchIndex) {
12781317 * This function checks if the object (`row`) matches the given type (`elem`) and its
12791318 * generics (if any).
12801319 *
1281- * @param {Row } row
1320+ * @param {Array<FunctionType> } list
12821321 * @param {QueryElement } elem - The element from the parsed query.
12831322 *
12841323 * @return {boolean } - Returns true if found, false otherwise.
12851324 */
1286- function checkIfInGenerics ( row , elem ) {
1287- for ( const entry of row . generics ) {
1325+ function checkIfInList ( list , elem ) {
1326+ for ( const entry of list ) {
12881327 if ( checkType ( entry , elem ) ) {
12891328 return true ;
12901329 }
@@ -1304,7 +1343,7 @@ function initSearch(rawSearchIndex) {
13041343 function checkType ( row , elem ) {
13051344 if ( row . id === - 1 ) {
13061345 // 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 ;
13081347 }
13091348
13101349 const matchesExact = row . id === elem . id ;
@@ -1322,59 +1361,7 @@ function initSearch(rawSearchIndex) {
13221361 // If the current item does not match, try [unboxing] the generic.
13231362 // [unboxing]:
13241363 // 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 ) ;
13781365 }
13791366
13801367 function checkPath ( contains , ty , maxEditDistance ) {
@@ -1575,14 +1562,14 @@ function initSearch(rawSearchIndex) {
15751562 const fullId = row . id ;
15761563 const searchWord = searchWords [ pos ] ;
15771564
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 ) {
15801567 // path_dist is 0 because no parent path information is currently stored
15811568 // in the search index
15821569 addIntoResults ( results_in_args , fullId , pos , - 1 , 0 , 0 , maxEditDistance ) ;
15831570 }
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 ) {
15861573 addIntoResults ( results_returned , fullId , pos , - 1 , 0 , 0 , maxEditDistance ) ;
15871574 }
15881575
@@ -1638,32 +1625,15 @@ function initSearch(rawSearchIndex) {
16381625 * @param {Object } results
16391626 */
16401627 function handleArgs ( row , pos , results ) {
1641- if ( ! row || ( filterCrates !== null && row . crate !== filterCrates ) ) {
1628+ if ( ! row || ( filterCrates !== null && row . crate !== filterCrates ) || ! row . type ) {
16421629 return ;
16431630 }
16441631
16451632 // 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 ) ) {
16641634 return ;
16651635 }
1666- if ( ! checkArgs ( parsedQuery . returned , checkReturned ) ) {
1636+ if ( ! unifyFunctionTypes ( row . type . output , parsedQuery . returned ) ) {
16671637 return ;
16681638 }
16691639
@@ -1750,12 +1720,9 @@ function initSearch(rawSearchIndex) {
17501720 elem = parsedQuery . returned [ 0 ] ;
17511721 for ( i = 0 , nSearchWords = searchWords . length ; i < nSearchWords ; ++ i ) {
17521722 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 ) {
17591726 addIntoResults (
17601727 results_others ,
17611728 row . id ,
0 commit comments