aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStephen Blott2015-05-10 06:45:37 +0100
committerStephen Blott2015-05-10 06:45:37 +0100
commit198dd8fa89148f3389c289bf71c40b9ab1a5681f (patch)
tree754824aa284c75b46c19db0a76d49fe0a23ff9e0
parent5fdbb8e579c068a54e9a397097d87063a3d8a146 (diff)
downloadvimium-198dd8fa89148f3389c289bf71c40b9ab1a5681f.tar.bz2
Search completion; refactor job-running logic.
-rw-r--r--background_scripts/completion.coffee130
-rw-r--r--lib/utils.coffee16
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