({ queryTerms })
| 493 | // If the query is empty, then return a list of open tabs, sorted by recency. |
| 494 | export class TabCompleter { |
| 495 | async filter({ queryTerms }) { |
| 496 | await bgUtils.tabRecency.init(); |
| 497 | // We search all tabs, not just those in the current window. |
| 498 | const tabs = await chrome.tabs.query({}); |
| 499 | const results = tabs.filter((tab) => ranking.matches(queryTerms, tab.url, tab.title)); |
| 500 | const suggestions = results |
| 501 | .map((tab) => { |
| 502 | const suggestion = new Suggestion({ |
| 503 | queryTerms, |
| 504 | description: "tab", |
| 505 | url: tab.url, |
| 506 | title: tab.title, |
| 507 | tabId: tab.id, |
| 508 | deDuplicate: false, |
| 509 | }); |
| 510 | suggestion.relevancy = this.computeRelevancy(suggestion); |
| 511 | return suggestion; |
| 512 | }) |
| 513 | .sort((a, b) => b.relevancy - a.relevancy); |
| 514 | // Boost relevancy with a multiplier so a relevant tab doesn't get crowded out by results from |
| 515 | // competing completers. To prevent tabs from crowding out everything else in turn, penalize |
| 516 | // them for being further down the results list by scaling on a hyperbola starting at 1 and |
| 517 | // approaching 0 asymptotically for higher indexes. The multiplier and the curve fall-off were |
| 518 | // subjectively chosen on the grounds that they seem to work pretty well. |
| 519 | suggestions.forEach(function (suggestion, i) { |
| 520 | suggestion.relevancy *= 8; |
| 521 | suggestion.relevancy /= (i / 4) + 1; |
| 522 | }); |
| 523 | return suggestions; |
| 524 | } |
| 525 | |
| 526 | computeRelevancy(suggestion) { |
| 527 | if (suggestion.queryTerms.length > 0) { |
no test coverage detected