From a18ad484f2fdc0019c15d94405c915f8bfe6d76f Mon Sep 17 00:00:00 2001 From: mike-work Date: Wed, 7 May 2014 23:57:56 +0100 Subject: Adding in search engines feature to fix #1009 --- background_scripts/completion.coffee | 22 ++++++++++++++++++++++ background_scripts/main.coffee | 2 ++ background_scripts/settings.coffee | 21 +++++++++++++++++++++ pages/options.coffee | 4 ++-- pages/options.html | 27 ++++++++++++++++++++++++++- tests/unit_tests/completion_test.coffee | 14 ++++++++++++++ tests/unit_tests/settings_test.coffee | 9 +++++++++ 7 files changed, 96 insertions(+), 3 deletions(-) diff --git a/background_scripts/completion.coffee b/background_scripts/completion.coffee index beb7003a..2868308a 100644 --- a/background_scripts/completion.coffee +++ b/background_scripts/completion.coffee @@ -248,6 +248,27 @@ class TabCompleter computeRelevancy: (suggestion) -> RankingUtils.wordRelevancy(suggestion.queryTerms, suggestion.url, suggestion.title) +# A completer which will return your search engines +class SearchEngineCompleter + searchEngines: {} + + filter: (queryTerms, onComplete) -> + searchEngineMatch = this.getSearchEngineMatches(queryTerms[0]) + suggestions = [] + if searchEngineMatch + searchEngineMatch = searchEngineMatch.replace(/%s/g, queryTerms[1..].join(" ")) + suggestion = new Suggestion(queryTerms, "search", searchEngineMatch, queryTerms[0] + ": " + queryTerms[1..].join(" "), @computeRelevancy) + suggestions.push(suggestion) + onComplete(suggestions) + + computeRelevancy: -> 1 + + refresh: -> + this.searchEngines = root.Settings.getSearchEngines() + + getSearchEngineMatches: (queryTerm) -> + this.searchEngines[queryTerm] + # A completer which calls filter() on many completers, aggregates the results, ranks them, and returns the top # 10. Queries from the vomnibar frontend script come through a multi completer. class MultiCompleter @@ -436,6 +457,7 @@ root.MultiCompleter = MultiCompleter root.HistoryCompleter = HistoryCompleter root.DomainCompleter = DomainCompleter root.TabCompleter = TabCompleter +root.SearchEngineCompleter = SearchEngineCompleter root.HistoryCache = HistoryCache root.RankingUtils = RankingUtils root.RegexpCache = RegexpCache diff --git a/background_scripts/main.coffee b/background_scripts/main.coffee index b2b4669c..00b96643 100644 --- a/background_scripts/main.coffee +++ b/background_scripts/main.coffee @@ -24,9 +24,11 @@ completionSources = history: new HistoryCompleter() domains: new DomainCompleter() tabs: new TabCompleter() + seachEngines: new SearchEngineCompleter() completers = omni: new MultiCompleter([ + completionSources.seachEngines, completionSources.bookmarks, completionSources.history, completionSources.domains]) diff --git a/background_scripts/settings.coffee b/background_scripts/settings.coffee index c26da5a4..175f3262 100644 --- a/background_scripts/settings.coffee +++ b/background_scripts/settings.coffee @@ -32,10 +32,28 @@ root.Settings = Settings = root.Commands.parseCustomKeyMappings value root.refreshCompletionKeysAfterMappingSave() + searchEngines: (value) -> + root.Settings.parseSearchEngines value + # postUpdateHooks convenience wrapper performPostUpdateHook: (key, value) -> @postUpdateHooks[key] value if @postUpdateHooks[key] + # Here we have our functions that parse the search engines + # this is a map that we use to store our search engines for use. + searchEnginesMap: {} + + # this parses the search engines settings and clears the old searchEngines and sets the new one + parseSearchEngines: (searchEnginesText) -> + @searchEnginesMap = {} + # find the split pairs by first splitting by line then splitting on the first `: ` + split_pairs = ( pair.split( /: (.+)/, 2) for pair in searchEnginesText.split( /\n/ ) when pair[0] != "#" ) + @searchEnginesMap[a[0]] = a[1] for a in split_pairs + @searchEnginesMap + getSearchEngines: -> + this.parseSearchEngines(@get("searchEngines") || "") if Object.keys(@searchEnginesMap).length == 0 + @searchEnginesMap + # options.coffee and options.html only handle booleans and strings; therefore all defaults must be booleans # or strings defaults: @@ -78,9 +96,12 @@ root.Settings = Settings = nextPatterns: "next,more,>,\u2192,\xbb,\u226b,>>" # default/fall back search engine searchUrl: "http://www.google.com/search?q=" + # put in an example search engine + searchEngines: "w: http://www.wikipedia.org/w/index.php?title=Special:Search&search=%s" settingsVersion: Utils.getCurrentVersion() + # We use settingsVersion to coordinate any necessary schema changes. if Utils.compareVersions("1.42", Settings.get("settingsVersion")) != -1 Settings.set("scrollStepSize", parseFloat Settings.get("scrollStepSize")) diff --git a/pages/options.coffee b/pages/options.coffee index 34696f68..d73d8f15 100644 --- a/pages/options.coffee +++ b/pages/options.coffee @@ -4,9 +4,9 @@ bgSettings = chrome.extension.getBackgroundPage().Settings editableFields = [ "scrollStepSize", "excludedUrls", "linkHintCharacters", "linkHintNumbers", "userDefinedLinkHintCss", "keyMappings", "filterLinkHints", "previousPatterns", - "nextPatterns", "hideHud", "regexFindMode", "searchUrl"] + "nextPatterns", "hideHud", "regexFindMode", "searchUrl", "searchEngines"] -canBeEmptyFields = ["excludedUrls", "keyMappings", "userDefinedLinkHintCss"] +canBeEmptyFields = ["excludedUrls", "keyMappings", "userDefinedLinkHintCss", "searchEngines"] document.addEventListener "DOMContentLoaded", -> populateOptions() diff --git a/pages/options.html b/pages/options.html index 8c7c007a..5450a774 100644 --- a/pages/options.html +++ b/pages/options.html @@ -122,6 +122,10 @@ width: 100%; min-height: 135px; } + textarea#searchEngines { + width: 100%; + min-height: 135px; + } input#previousPatterns, input#nextPatterns { width: 100%; } @@ -330,12 +334,33 @@ unmapAll
- Set which search engine is used when searching from the Vomnibar (examples: "http://duckduckgo.com/?q=", "http://www.google.com/search?q="). + Set which search engine is used when searching from the Vomnibar (examples: "http://duckduckgo.com/?q=").
+ + Search Engines + +
+
+ Allow customised search engines the format is `token: http://address.com/q?=%s` +
+ token must not contain a colon. +
+ comments are allowed and start with '#' +
+ all `%s` will be replaced with the query string +
+ for a list of default and extra search engines please see here +
+ to subsequently search for something just type token follewed by your search terms +
+
+ + + diff --git a/tests/unit_tests/completion_test.coffee b/tests/unit_tests/completion_test.coffee index fb267f63..43ecb49d 100644 --- a/tests/unit_tests/completion_test.coffee +++ b/tests/unit_tests/completion_test.coffee @@ -209,6 +209,20 @@ context "tab completer", assert.arrayEqual ["tab2.com"], results.map (tab) -> tab.url assert.arrayEqual [2], results.map (tab) -> tab.tabId +context "search engines", + setup -> + searchEngines = "foo: bar?q=%s\n# comment\nbaz: qux?q=%s" + Settings.set 'searchEngines', searchEngines + @completer = new SearchEngineCompleter() + # note, I couldn't just call @completer.refresh() here as I couldn't set root.Settings without errors + # workaround is below, would be good for someone that understands the testing system better than me to improve + @completer.searchEngines = Settings.getSearchEngines() + + should "return search engine suggestion", -> + results = filterCompleter(@completer, ["foo", "hello"]) + assert.arrayEqual ["bar?q=hello"], results.map (result) -> result.url + assert.arrayEqual ["foo: hello"], results.map (result) -> result.title + context "suggestions", should "escape html in page titles", -> suggestion = new Suggestion(["queryterm"], "tab", "url", "title ", returns(1)) diff --git a/tests/unit_tests/settings_test.coffee b/tests/unit_tests/settings_test.coffee index 25bb3628..1283497c 100644 --- a/tests/unit_tests/settings_test.coffee +++ b/tests/unit_tests/settings_test.coffee @@ -70,5 +70,14 @@ context "settings", chrome.storage.sync.set { scrollStepSize: JSON.stringify(message) } assert.equal message, Sync.message + should "set search engines, retrieve them correctly and check that it has been parsed correctly", -> + searchEngines = "foo: bar?q=%s\n# comment\nbaz: qux?q=%s" + parsedSearchEngines = {"foo": "bar?q=%s", "baz": "qux?q=%s"} + Settings.set 'searchEngines', searchEngines + assert.equal(searchEngines, Settings.get('searchEngines')) + result = Settings.getSearchEngines() + assert.isTrue(parsedSearchEngines["foo"] == result["foo"] && + parsedSearchEngines["baz"] == result["baz"] && Object.keys(result).length == 2) + should "sync a key which is not a known setting (without crashing)", -> chrome.storage.sync.set { notASetting: JSON.stringify("notAUsefullValue") } -- cgit v1.2.3