diff options
Diffstat (limited to 'background_scripts/completion.coffee')
| -rw-r--r-- | background_scripts/completion.coffee | 166 | 
1 files changed, 73 insertions, 93 deletions
| diff --git a/background_scripts/completion.coffee b/background_scripts/completion.coffee index 02c5478a..bae73b8d 100644 --- a/background_scripts/completion.coffee +++ b/background_scripts/completion.coffee @@ -43,23 +43,33 @@ class Suggestion      # or @relevancyFunction.      @relevancy ?= @relevancyFunction this -  generateHtml: -> +  generateHtml: (request) ->      return @html if @html      relevancyHtml = if @showRelevancy then "<span class='relevancy'>#{@computeRelevancy()}</span>" else ""      insertTextClass = if @insertText then "vomnibarInsertText" else "vomnibarNoInsertText"      insertTextIndicator = "↪" # A right hooked arrow. +    @title = @insertText if @insertText and request.isCustomSearch      # NOTE(philc): We're using these vimium-specific class names so we don't collide with the page's CSS.      @html = -      """ -      <div class="vimiumReset vomnibarTopHalf"> -         <span class="vimiumReset vomnibarSource #{insertTextClass}">#{insertTextIndicator}</span><span class="vimiumReset vomnibarSource">#{@type}</span> -         <span class="vimiumReset vomnibarTitle">#{@highlightQueryTerms Utils.escapeHtml @title}</span> -       </div> -       <div class="vimiumReset vomnibarBottomHalf"> -        <span class="vimiumReset vomnibarSource vomnibarNoInsertText">#{insertTextIndicator}</span><span class="vimiumReset vomnibarUrl">#{@highlightUrlTerms Utils.escapeHtml @shortenUrl()}</span> -        #{relevancyHtml} -      </div> -      """ +      if request.isCustomSearch +        """ +        <div class="vimiumReset vomnibarTopHalf"> +           <span class="vimiumReset vomnibarSource #{insertTextClass}">#{insertTextIndicator}</span><span class="vimiumReset vomnibarSource">#{@type}</span> +           <span class="vimiumReset vomnibarTitle">#{@highlightQueryTerms Utils.escapeHtml @title}</span> +           #{relevancyHtml} +         </div> +        """ +      else +        """ +        <div class="vimiumReset vomnibarTopHalf"> +           <span class="vimiumReset vomnibarSource #{insertTextClass}">#{insertTextIndicator}</span><span class="vimiumReset vomnibarSource">#{@type}</span> +           <span class="vimiumReset vomnibarTitle">#{@highlightQueryTerms Utils.escapeHtml @title}</span> +         </div> +         <div class="vimiumReset vomnibarBottomHalf"> +          <span class="vimiumReset vomnibarSource vomnibarNoInsertText">#{insertTextIndicator}</span><span class="vimiumReset vomnibarUrl">#{@highlightUrlTerms Utils.escapeHtml @shortenUrl()}</span> +          #{relevancyHtml} +        </div> +        """    # Use neat trick to snatch a domain (http://stackoverflow.com/a/8498668).    getUrlRoot: (url) -> @@ -293,7 +303,7 @@ class DomainCompleter          queryTerms: queryTerms          type: "domain"          url: domains[0]?[0] ? "" # This is the URL or an empty string, but not null. -        relevancy: 1 +        relevancy: 2.0        ].filter (s) -> 0 < s.url.length    # Returns a list of domains of the form: [ [domain, relevancy], ... ] @@ -414,7 +424,7 @@ class SearchEngineCompleter    preprocessRequest: (request) ->      @searchEngines.use (engines) =>        { queryTerms, query } = request -      request.searchEngines = engines +      extend request, searchEngines: engines, keywords: key for own key of engines        keyword = queryTerms[0]        # Note. For a keyword "w", we match "w search terms" and "w ", but not "w" on its own.        if keyword and engines[keyword] and (1 < queryTerms.length or /\S\s/.test query) @@ -422,6 +432,7 @@ class SearchEngineCompleter            queryTerms: queryTerms[1..]            keyword: keyword            engine: engines[keyword] +          isCustomSearch: true    refresh: (port) ->      @previousSuggestions = {} @@ -454,92 +465,77 @@ class SearchEngineCompleter    filter: (request, onComplete) ->      { queryTerms, query, engine } = request +    return onComplete [] unless engine -    { custom, searchUrl, description } = -      if engine -        { keyword, searchUrl, description } = engine -        extend request, { searchUrl, customSearchMode: true } -        custom: true -        searchUrl: searchUrl -        description: description -      else -        custom: false -        searchUrl: Settings.get "searchUrl" -        description: "search" - -    return onComplete [] unless custom or 0 < queryTerms.length +    { keyword, searchUrl, description } = engine +    extend request, searchUrl, customSearchMode: true -    factor = Math.max 0.0, Math.min 1.0, Settings.get "omniSearchWeight" -    haveCompletionEngine = (0.0 < factor or custom) and CompletionSearch.haveCompletionEngine searchUrl +    factor = 0.5 +    haveCompletionEngine = CompletionSearch.haveCompletionEngine searchUrl      # This filter is applied to all of the suggestions from all of the completers, after they have been      # aggregated by the MultiCompleter.      filter = (suggestions) -> -      if custom and haveCompletionEngine -        # We only accept suggestions: -        #   - from this completer, or -        #   - from other completers, but then only if their URL matches this search engine and matches this -        #     query (that is only if their URL could have been generated by this search engine). -        suggestions.filter (suggestion) -> -          suggestion.type == description or -            # This is a suggestion for the same search engine. -            (suggestion.url.startsWith(engine.searchUrlPrefix) and -              # And the URL suffix (which must contain the query part) matches the current query. -              RankingUtils.matches queryTerms, suggestion.url[engine.searchUrlPrefix.length..]) - -      else if not custom -        # Filter out any suggestion which is just what the user would get if they hit <Enter> anyway.  For -        # example, don't offer "https://www.google.com/search?q=vimium" if the query is "vimium". -        defaultUrl = Utils.createSearchUrl queryTerms, searchUrl -        defaultQuery = queryTerms.join " " -        suggestions.filter (suggestion) -> Utils.extractQuery(searchUrl, suggestion.url) != defaultQuery -      else -        suggestions +      suggestions.filter (suggestion) -> +        # We only keep suggestions which either *were* generated by this search engine, or *could have +        # been* generated by this search engine (and match the current query). +        suggestion.isSearchSuggestion or suggestion.isCustomSearch or +          ( +            terms = Utils.extractQuery searchUrl, suggestion.url +            terms and RankingUtils.matches queryTerms, terms +          )      # If a previous suggestion still matches the query, then we keep it (even if the completion engine may not      # return it for the current query).  This allows the user to pick suggestions by typing fragments of their      # text, without regard to whether the completion engine can complete the actual text of the query.      previousSuggestions = -      for url, suggestion of @previousSuggestions -        continue unless RankingUtils.matches queryTerms, suggestion.title -        # Reset various fields, they may not be correct wrt. the current query. -        extend suggestion, relevancy: null, html: null, queryTerms: queryTerms -        suggestion.relevancy = null -        suggestion +      if queryTerms.length == 0 +        [] +      else +        for url, suggestion of @previousSuggestions +          continue unless RankingUtils.matches queryTerms, suggestion.title +          # Reset various fields, they may not be correct wrt. the current query. +          extend suggestion, relevancy: null, html: null, queryTerms: queryTerms +          suggestion.relevancy = null +          suggestion      primarySuggestion = new Suggestion        queryTerms: queryTerms        type: description        url: Utils.createSearchUrl queryTerms, searchUrl        title: queryTerms.join " " -      relevancy: 1 -      autoSelect: custom -      highlightTerms: not haveCompletionEngine +      relevancy: 2.0 +      autoSelect: true +      highlightTerms: false        isSearchSuggestion: true +      isPrimarySuggestion: true -    mkSuggestion = (suggestion) => -      url = Utils.createSearchUrl suggestion, searchUrl -      @previousSuggestions[url] = new Suggestion -        queryTerms: queryTerms -        type: description -        url: url -        title: suggestion -        insertText: suggestion -        highlightTerms: false -        highlightTermsExcludeUrl: true -        isCustomSearch: custom -        relevancyFunction: @computeRelevancy -        relevancyData: factor +    return onComplete [ primarySuggestion ], { filter } if queryTerms.length == 0 + +    mkSuggestion = do => +      count = 0 +      (suggestion) => +        url = Utils.createSearchUrl suggestion, searchUrl +        @previousSuggestions[url] = new Suggestion +          queryTerms: queryTerms +          type: description +          url: url +          title: suggestion +          insertText: suggestion +          highlightTerms: false +          highlightTermsExcludeUrl: true +          isCustomSearch: true +          relevancy: if ++count == 1 then 1.0 else null +          relevancyFunction: @computeRelevancy      cachedSuggestions =        if haveCompletionEngine then CompletionSearch.complete searchUrl, queryTerms else null      suggestions = previousSuggestions -    suggestions.push primarySuggestion if custom -    suggestions.push cachedSuggestions.map(mkSuggestion)... if custom and cachedSuggestions? +    suggestions.push primarySuggestion      if queryTerms.length == 0 or cachedSuggestions? or not haveCompletionEngine -      # There is no prospect of adding further completions. +      # There is no prospect of adding further completions, so we're done.        suggestions.push cachedSuggestions.map(mkSuggestion)... if cachedSuggestions?        onComplete suggestions, { filter, continuation: null }      else @@ -547,20 +543,7 @@ class SearchEngineCompleter        # continuation.        onComplete suggestions,          filter: filter -        continuation: (suggestions, onComplete) => - -          # We can skip querying the completion engine if any new suggestions we propose will not score highly -          # enough to make the list anyway.  We construct a suggestion which perfectly matches the query, and -          # ask the relevancy function what score it would get.  If that score is less than the score of the -          # lowest-ranked suggestion from another completer (and there are already 10 suggestions), then -          # there's no need to query the completion engine. -          perfectRelevancyScore = @computeRelevancy new Suggestion -            queryTerms: queryTerms, title: queryTerms.join(" "), relevancyData: factor - -          if 10 <= suggestions.length and perfectRelevancyScore < suggestions[suggestions.length-1].relevancy -            console.log "skip (cannot make the grade):", suggestions.length, query if SearchEngineCompleter.debug -            return onComplete [] - +        continuation: (onComplete) =>            CompletionSearch.complete searchUrl, queryTerms, (suggestions = []) =>              console.log "fetched suggestions:", suggestions.length, query if SearchEngineCompleter.debug              onComplete suggestions.map mkSuggestion @@ -571,7 +554,7 @@ class SearchEngineCompleter      #   scores here, and those provided by other completers.      # - Relevancy depends only on the title (which is the search terms), and not on the URL.      Suggestion.boostRelevancyScore 0.5, -      relevancyData * RankingUtils.wordRelevancy queryTerms, title, title +      0.7 * RankingUtils.wordRelevancy queryTerms, title, title    postProcessSuggestions: (request, suggestions) ->      return unless request.searchEngines @@ -586,9 +569,6 @@ class SearchEngineCompleter              # suggestion, then custom search-engine mode should be activated.              suggestion.customSearchMode = engine.keyword              suggestion.title ||= suggestion.insertText -            # NOTE(smblott) The following is disabled: experimentation with UI. -            # suggestion.highlightTermsExcludeUrl = true -            # suggestion.type = engine.description ? "custom search history"              break  # A completer which calls filter() on many completers, aggregates the results, ranks them, and returns the top @@ -643,7 +623,7 @@ class MultiCompleter        if shouldRunContinuations          jobs = new JobRunner continuations.map (continuation) ->            (callback) -> -            continuation suggestions, (newSuggestions) -> +            continuation (newSuggestions) ->                suggestions.push newSuggestions...                callback() @@ -676,7 +656,7 @@ class MultiCompleter      completer.postProcessSuggestions? request, suggestions for completer in @completers      # Generate HTML for the remaining suggestions and return them. -    suggestion.generateHtml() for suggestion in suggestions +    suggestion.generateHtml request for suggestion in suggestions      suggestions  # Utilities which help us compute a relevancy score for a given item. | 
