diff options
| author | Phil Crosby | 2012-06-03 16:02:28 -0700 | 
|---|---|---|
| committer | Phil Crosby | 2012-06-03 16:56:39 -0700 | 
| commit | 98e9e8ca04918de3fae369e1f3b0285ba17d4188 (patch) | |
| tree | 76c9f0669990f368c3c660f995a853a74dc229a5 | |
| parent | 39885cd326737534e2afc976f2a8ce086c76fc66 (diff) | |
| download | vimium-98e9e8ca04918de3fae369e1f3b0285ba17d4188.tar.bz2 | |
Add a domain completer
| -rw-r--r-- | background_page.html | 8 | ||||
| -rw-r--r-- | background_scripts/completion.coffee | 50 | ||||
| -rw-r--r-- | tests/completion_test.coffee | 40 | 
3 files changed, 89 insertions, 9 deletions
| diff --git a/background_page.html b/background_page.html index 709a6633..8e1d3a6b 100644 --- a/background_page.html +++ b/background_page.html @@ -54,11 +54,15 @@    var completionSources = {      bookmarks: new BookmarkCompleter(), -    history: new HistoryCompleter() +    history: new HistoryCompleter(), +    domains: new DomainCompleter()    };    var completers = { -    omni: new MultiCompleter([completionSources.bookmarks, completionSources.history]) +    omni: new MultiCompleter([ +      completionSources.bookmarks, +      completionSources.history, +      completionSources.domains])    };    chrome.extension.onConnect.addListener(function(port, name) { diff --git a/background_scripts/completion.coffee b/background_scripts/completion.coffee index c9b4de78..ca0c6c45 100644 --- a/background_scripts/completion.coffee +++ b/background_scripts/completion.coffee @@ -110,11 +110,57 @@ class HistoryCompleter    refresh: -> +# The domain completer is designed to match a single-word query which looks like it is a domain. This supports +# the user experience where they quickly type a partial domain, hit tab -> enter, and expect to arrive there. +class DomainCompleter +  domains: null # A map of domain -> history + +  filter: (queryTerms, onComplete) -> +    return onComplete([]) if queryTerms.length > 1 +    if @domains +      @performSearch(queryTerms, onComplete) +    else +      @populateDomains => @performSearch(queryTerms, onComplete) + +  performSearch: (queryTerms, onComplete) -> +    query = queryTerms[0] +    domainCandidates = (domain for domain of @domains when domain.indexOf(query) >= 0) +    domains = @sortDomainsByRelevancy(queryTerms, domainCandidates) +    return onComplete([]) if domains.length == 0 +    topDomain = domains[0][0] +    onComplete([new Suggestion(queryTerms, "domain", topDomain, null, @computeRelevancy)]) + +  # Returns a list of domains of the form: [ [domain, relevancy], ... ] +  sortDomainsByRelevancy: (queryTerms, domainCandidates) -> +    results = [] +    for domain in domainCandidates +      recencyScore = RankingUtils.recencyScore(@domains[domain].lastVisitTime || 0) +      wordRelevancy = RankingUtils.wordRelevancy(queryTerms, domain, null) +      score = wordRelevancy + Math.max(recencyScore, wordRelevancy) / 2 +      results.push([domain, score]) +    results.sort (a, b) -> b[1] - a[1] +    results + +  populateDomains: (onComplete) -> +    HistoryCache.use (history) => +      @domains = {} +      history.forEach (entry) => +        # We want each key in our domains hash to point to the most recent History entry for that domain. +        # Thankfully, the domains in HistoryCache are sorted from oldest to most recent. +        domain = @parseDomain(entry.url) +        @domains[domain] = entry if domain +      onComplete() + +  parseDomain: (url) -> url.split("/")[2] || "" + +  # Suggestions from the Domain completer have the maximum relevancy. They should be shown first in the list. +  computeRelevancy: -> 1 +  class MultiCompleter    constructor: (@completers) ->      @maxResults = 10 # TODO(philc): Should this be configurable? -  refresh: -> completer.refresh() for completer in @completers +  refresh: -> completer.refresh() for completer in @completers when completer.refresh    filter: (queryTerms, onComplete) ->      suggestions = [] @@ -208,3 +254,5 @@ root.Suggestion = Suggestion  root.BookmarkCompleter = BookmarkCompleter  root.MultiCompleter = MultiCompleter  root.HistoryCompleter = HistoryCompleter +root.DomainCompleter = DomainCompleter +root.HistoryCache = HistoryCache diff --git a/tests/completion_test.coffee b/tests/completion_test.coffee index 813d3af3..dadf5860 100644 --- a/tests/completion_test.coffee +++ b/tests/completion_test.coffee @@ -35,15 +35,37 @@ context "history completer",      @completer = new HistoryCompleter()    should "return matching history entries when searching", -> -    @completer.filter(["story1"], (@results) =>) -    assert.arrayEqual [@history1.url], @results.map (entry) -> entry.url +    assert.arrayEqual [@history1.url], filterCompleter(@completer, ["story1"]).map (entry) -> entry.url    should "rank recent results higher than nonrecent results", ->      stub(Date, "now", returns(hours(24))) -    @completer.filter(["hist"], (@results) =>) -    @results.forEach (result) -> result.computeRelevancy() -    @results.sort (a, b) -> b.relevancy - a.relevancy -    assert.arrayEqual [@history2.url, @history1.url], @results.map (result) -> result.url +    results = filterCompleter(@completer, ["hist"]) +    results.forEach (result) -> result.computeRelevancy() +    results.sort (a, b) -> b.relevancy - a.relevancy +    assert.arrayEqual [@history2.url, @history1.url], results.map (result) -> result.url + +context "domain completer", +  setup -> +    @history1 = { title: "history1", url: "http://history1.com", lastVisitTime: hours(1) } +    @history2 = { title: "history2", url: "http://history2.com", lastVisitTime: hours(1) } + +    stub(HistoryCache, "use", (onComplete) => onComplete([@history1, @history2])) +    stub(Date, "now", returns(hours(24))) + +    @completer = new DomainCompleter() + +  should "return only a single matching domain", -> +    results = filterCompleter(@completer, ["story"]) +    assert.arrayEqual ["history1.com"], results.map (result) -> result.url + +  should "pick domains which are more recent", -> +    # This domains are the same except for their last visited time. +    assert.equal "history1.com", filterCompleter(@completer, ["story"])[0].url +    @history2.lastVisitTime = hours(3) +    assert.equal "history2.com", filterCompleter(@completer, ["story"])[0].url + +  should "returns no results when there's more than one query term, because clearly it's not a domain", -> +    assert.arrayEqual [], filterCompleter(@completer, ["his", "tory"])  context "suggestions",    should "escape html in page titles", -> @@ -58,6 +80,12 @@ context "suggestions",      suggestion = new Suggestion(["queryterm"], "tab", "http://ninjawords.com", "ninjawords", returns(1))      assert.equal -1, suggestion.generateHtml().indexOf("http://ninjawords.com") +# A convenience wrapper around completer.filter() so it can be called synchronously in tests. +filterCompleter = (completer, queryTerms) -> +  results = [] +  completer.filter(queryTerms, (completionResults) -> results = completionResults) +  results +  hours = (n) -> 1000 * 60 * 60 * n  Tests.run()
\ No newline at end of file | 
