diff options
| author | Stephen Blott | 2015-05-10 06:45:37 +0100 |
|---|---|---|
| committer | Stephen Blott | 2015-05-10 06:45:37 +0100 |
| commit | 198dd8fa89148f3389c289bf71c40b9ab1a5681f (patch) | |
| tree | 754824aa284c75b46c19db0a76d49fe0a23ff9e0 | |
| parent | 5fdbb8e579c068a54e9a397097d87063a3d8a146 (diff) | |
| download | vimium-198dd8fa89148f3389c289bf71c40b9ab1a5681f.tar.bz2 | |
Search completion; refactor job-running logic.
| -rw-r--r-- | background_scripts/completion.coffee | 130 | ||||
| -rw-r--r-- | lib/utils.coffee | 16 |
2 files changed, 76 insertions, 70 deletions
diff --git a/background_scripts/completion.coffee b/background_scripts/completion.coffee index 024ea54c..40123337 100644 --- a/background_scripts/completion.coffee +++ b/background_scripts/completion.coffee @@ -499,76 +499,66 @@ class MultiCompleter cancel: (port) -> completer.cancel? port for completer in @completers - filter: do -> - defaultCallbackOptions = - # Completers may provide a continuation function. This will be run after all completers have posted - # their suggestions, and is used to post additional (slow) asynchronous suggestions (e.g. search-engine - # completions fetched over HTTP). - continuation: null - # Completers may provide a filter function. This allows one completer to filter out suggestions from - # other completers. - filter: null - - (request, onComplete) -> - @debug = true - # Allow only one query to run at a time, and remember the most recent query. - return @mostRecentQuery = arguments if @filterInProgress - - { queryTerms } = request - RegexpCache.clear() - @mostRecentQuery = null - @filterInProgress = true - suggestions = [] - continuations = [] - filters = [] - activeCompleters = [0...@completers.length] - - # Call filter() on every completer and wait for them all to finish before filtering and posting the - # results, then calling any continuations. - for completer, index in @completers - do (index) => - completer.filter request, (newSuggestions = [], { continuation, filter } = defaultCallbackOptions) => - - # Store the results. - suggestions.push newSuggestions... - continuations.push continuation if continuation? - filters.push filter if filter? - - activeCompleters = activeCompleters.filter (i) -> i != index - if activeCompleters.length == 0 - # All the completers have now yielded their (initial) results, we're good to go. - - # Apply filters. - suggestions = suggestions.filter filter for filter in filters - - # Should we run continuations? - shouldRunContinuations = 0 < continuations.length and not @mostRecentQuery? - - # Post results, unless there are none AND we will be running a continuation. This avoids - # collapsing the vomnibar briefly before expanding it again, which looks ugly. - unless suggestions.length == 0 and shouldRunContinuations - onComplete - results: @prepareSuggestions queryTerms, suggestions - mayCacheResults: continuations.length == 0 - - # Run any continuations, unless there's a pending query. - if shouldRunContinuations - for continuation in continuations - console.log "launching continuation..." if @debug - continuation suggestions, (newSuggestions) => - console.log "posting continuation" if @debug - suggestions.push newSuggestions... - onComplete - results: @prepareSuggestions queryTerms, suggestions - # FIXME(smblott) This currently assumes that there is at most one continuation. We - # should really be counting pending/completed continuations. - mayCacheResults: true - - # Admit subsequent queries, and launch any pending query. - @filterInProgress = false - if @mostRecentQuery - console.log "running pending query:", @mostRecentQuery[0] if @debug - @filter @mostRecentQuery... + filter: (request, onComplete) -> + @debug = true + # Allow only one query to run at a time. + return @mostRecentQuery = arguments if @filterInProgress + + RegexpCache.clear() + { queryTerms } = request + + @mostRecentQuery = null + @filterInProgress = true + + suggestions = [] + continuations = [] + filters = [] + + # Run each of the completers (asynchronously). + jobs = new JobRunner @completers.map (completer) -> + (callback) -> + completer.filter request, (newSuggestions = [], { continuation, filter } = {}) -> + suggestions.push newSuggestions... + continuations.push continuation if continuation? + filters.push filter if filter? + callback() + + # Once all completers have finished, process and post the results, and run any continuations or pending + # queries. + jobs.onReady => + # Apply filters. + suggestions = suggestions.filter filter for filter in filters + + # Should we run continuations? + shouldRunContinuations = 0 < continuations.length and not @mostRecentQuery? + + # Post results, unless there are none AND we will be running a continuation. This avoids + # collapsing the vomnibar briefly before expanding it again, which looks ugly. + unless suggestions.length == 0 and shouldRunContinuations + onComplete + results: @prepareSuggestions queryTerms, suggestions + mayCacheResults: continuations.length == 0 + + # Run any continuations (asynchronously). + if shouldRunContinuations + continuationJobs = new JobRunner continuations.map (continuation) -> + (callback) -> + continuation suggestions, (newSuggestions) -> + suggestions.push newSuggestions... + callback() + + continuationJobs.onReady => + # We post these results even if a new query has started. The vomnibar will not display the + # completions, but will cache the results. + onComplete + results: @prepareSuggestions queryTerms, suggestions + mayCacheResults: true + + # Admit subsequent queries, and launch any pending query. + @filterInProgress = false + if @mostRecentQuery + console.log "running pending query:", @mostRecentQuery[0] if @debug + @filter @mostRecentQuery... prepareSuggestions: (queryTerms, suggestions) -> suggestion.computeRelevancy queryTerms for suggestion in suggestions diff --git a/lib/utils.coffee b/lib/utils.coffee index b0abfd8f..16adc305 100644 --- a/lib/utils.coffee +++ b/lib/utils.coffee @@ -278,7 +278,23 @@ class AsyncDataFetcher use: (callback) -> if @data? then callback @data else @queue.push callback +# This takes a list of jobs (functions) and runs them, asynchronously. Functions queued with @onReady() are +# run once all of the jobs have completed. +class JobRunner + constructor: (@jobs) -> + @fetcher = new AsyncDataFetcher (callback) => + for job in @jobs + do (job) => + Utils.nextTick => + job => + @jobs = @jobs.filter (j) -> j != job + callback true if @jobs.length == 0 + + onReady: (callback) -> + @fetcher.use callback + root = exports ? window root.Utils = Utils root.SimpleCache = SimpleCache root.AsyncDataFetcher = AsyncDataFetcher +root.JobRunner = JobRunner |
