From 4fc96415b0e01d7b0a68f74578ab9c69d5a0dd1a Mon Sep 17 00:00:00 2001 From: Stephen Blott Date: Fri, 29 May 2015 10:18:50 +0100 Subject: Rework parsing of custom key mappings. Most of this is just a tidy up of code that's been around for a long time. The only difference, however, is that now a key mapping can include extra data ("extras") after the name of the command. For example, map s vomnibar.activate keyword=g Here, and extra property "extras" is added to the command registry: extras: ["keyword=g"] This is a first step towards direct vomnibar activation for custom search. --- background_scripts/commands.coffee | 64 ++++++++++++++------------------------ 1 file changed, 24 insertions(+), 40 deletions(-) diff --git a/background_scripts/commands.coffee b/background_scripts/commands.coffee index abfbd9e2..1714ae56 100644 --- a/background_scripts/commands.coffee +++ b/background_scripts/commands.coffee @@ -24,21 +24,13 @@ Commands = noRepeat: options.noRepeat repeatLimit: options.repeatLimit - mapKeyToCommand: (key, command) -> + mapKeyToCommand: ({ key, command, extras }) -> unless @availableCommands[command] - console.log(command, "doesn't exist!") + console.log command, "doesn't exist!" return - commandDetails = @availableCommands[command] - - @keyToCommandRegistry[key] = - command: command - isBackgroundCommand: commandDetails.isBackgroundCommand - passCountToFunction: commandDetails.passCountToFunction - noRepeat: commandDetails.noRepeat - repeatLimit: commandDetails.repeatLimit - - unmapKey: (key) -> delete @keyToCommandRegistry[key] + extras ?= [] + @keyToCommandRegistry[key] = extend { command, extras }, @availableCommands[command] # Lower-case the appropriate portions of named keys. # @@ -54,37 +46,29 @@ Commands = "<" + (if optionalPrefix then optionalPrefix else "") + keyName.toLowerCase() + ">") parseCustomKeyMappings: (customKeyMappings) -> - lines = customKeyMappings.split("\n") - - for line in lines - continue if (line[0] == "\"" || line[0] == "#") - splitLine = line.replace(/\s+$/, "").split(/\s+/) - - lineCommand = splitLine[0] - - if (lineCommand == "map") - continue if (splitLine.length != 3) - key = @normalizeKey(splitLine[1]) - vimiumCommand = splitLine[2] - - continue unless @availableCommands[vimiumCommand] - - console.log("Mapping", key, "to", vimiumCommand) - @mapKeyToCommand(key, vimiumCommand) - else if (lineCommand == "unmap") - continue if (splitLine.length != 2) - - key = @normalizeKey(splitLine[1]) - console.log("Unmapping", key) - @unmapKey(key) - else if (lineCommand == "unmapAll") - @keyToCommandRegistry = {} + for line in customKeyMappings.split "\n" + unless line[0] == "\"" or line[0] == "#" + tokens = line.replace(/\s+$/, "").split /\s+/ + switch tokens[0] + when "map" + [ _, key, command, extras... ] = tokens + if command? and @availableCommands[command] + key = @normalizeKey key + console.log "Mapping", key, "to", command + @mapKeyToCommand { key, command, extras } + + when "unmap" + if tokens.length == 2 + key = @normalizeKey tokens[1] + console.log "Unmapping", key + delete @keyToCommandRegistry[key] + + when "unmapAll" + @keyToCommandRegistry = {} clearKeyMappingsAndSetDefaults: -> @keyToCommandRegistry = {} - - for key of defaultKeyMappings - @mapKeyToCommand(key, defaultKeyMappings[key]) + @mapKeyToCommand { key, command } for key, command of defaultKeyMappings # An ordered listing of all available commands, grouped by type. This is the order they will # be shown in the help page. -- cgit v1.2.3 From 17e233a2bdbb725a14b9aa363ed659a5bd9feeb2 Mon Sep 17 00:00:00 2001 From: Stephen Blott Date: Fri, 29 May 2015 10:30:25 +0100 Subject: Pass command's registry entry to content script. --- background_scripts/main.coffee | 14 +++++++------- content_scripts/vimium_frontend.coffee | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/background_scripts/main.coffee b/background_scripts/main.coffee index a13d9d98..e7a1f82c 100644 --- a/background_scripts/main.coffee +++ b/background_scripts/main.coffee @@ -558,13 +558,13 @@ checkKeyQueue = (keysToCheck, tabId, frameId) -> if runCommand if not registryEntry.isBackgroundCommand - chrome.tabs.sendMessage(tabId, - name: "executePageCommand", - command: registryEntry.command, - frameId: frameId, - count: count, - passCountToFunction: registryEntry.passCountToFunction, - completionKeys: generateCompletionKeys("")) + chrome.tabs.sendMessage tabId, + name: "executePageCommand" + command: registryEntry.command + frameId: frameId + count: count + completionKeys: generateCompletionKeys "" + registryEntry: registryEntry refreshedCompletionKeys = true else if registryEntry.passCountToFunction diff --git a/content_scripts/vimium_frontend.coffee b/content_scripts/vimium_frontend.coffee index 41fb772b..6b27924d 100644 --- a/content_scripts/vimium_frontend.coffee +++ b/content_scripts/vimium_frontend.coffee @@ -314,7 +314,7 @@ executePageCommand = (request) -> # All other commands are handled in their frame (but only if Vimium is enabled). return unless frameId == request.frameId and isEnabledForUrl - if (request.passCountToFunction) + if request.registryEntry.passCountToFunction Utils.invokeCommandString(request.command, [request.count]) else Utils.invokeCommandString(request.command) for i in [0...request.count] -- cgit v1.2.3 From bd3cf0386a88d5878ee352c4d0c80005eaf2a93a Mon Sep 17 00:00:00 2001 From: Stephen Blott Date: Fri, 29 May 2015 10:33:30 +0100 Subject: Pass the command's registry entry to vomnibar commands. --- content_scripts/vimium_frontend.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content_scripts/vimium_frontend.coffee b/content_scripts/vimium_frontend.coffee index 6b27924d..8c6dd374 100644 --- a/content_scripts/vimium_frontend.coffee +++ b/content_scripts/vimium_frontend.coffee @@ -307,7 +307,7 @@ executePageCommand = (request) -> if DomUtils.isTopFrame() # We pass the frameId from request. That's the frame which originated the request, so that's the frame # which should receive the focus when the vomnibar closes. - Utils.invokeCommandString request.command, [ request.frameId ] + Utils.invokeCommandString request.command, [ request.frameId, request.registryEntry ] refreshCompletionKeys request return -- cgit v1.2.3 From 087f7e8d180c9048ee32bdb243eabe2c15021fc7 Mon Sep 17 00:00:00 2001 From: Stephen Blott Date: Fri, 29 May 2015 10:58:34 +0100 Subject: Implement pre-population of custom-search keywords. Now, a mapping of the form: map s vomnibar.activate keyword=g makes the vomnibar open (on "s") with the Google custom-search engine activated immediately (assuming it's configured). The corresponding custom search-engine configuration would be: g: http://www.google.com/search?q=%s Google --- content_scripts/vomnibar.coffee | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/content_scripts/vomnibar.coffee b/content_scripts/vomnibar.coffee index 2529c077..76698634 100644 --- a/content_scripts/vomnibar.coffee +++ b/content_scripts/vomnibar.coffee @@ -4,14 +4,22 @@ Vomnibar = vomnibarUI: null + getOptions: (registryEntry = { extras: [] }) -> + extras = {} + for extra in registryEntry.extras + [ key, value ] = extra.split "=" + extras.query = "#{value} " if key? and key == "keyword" and value? and 0 < value.length + extras + # sourceFrameId here (and below) is the ID of the frame from which this request originates, which may be different # from the current frame. - activate: (sourceFrameId) -> @open sourceFrameId, {completer:"omni"} - activateInNewTab: (sourceFrameId) -> @open sourceFrameId, { - completer: "omni" - selectFirst: false - newTab: true - } + + activate: (sourceFrameId, registryEntry) -> + @open sourceFrameId, extend @getOptions(registryEntry), completer:"omni" + + activateInNewTab: (sourceFrameId, registryEntry) -> + @open sourceFrameId, extend @getOptions(registryEntry), completer: "omni", newTab: true + activateTabSelection: (sourceFrameId) -> @open sourceFrameId, { completer: "tabs" selectFirst: true -- cgit v1.2.3 From 6a97f4cf5e1f1c21336d2fce9da20c3bc5633d05 Mon Sep 17 00:00:00 2001 From: Stephen Blott Date: Fri, 29 May 2015 14:20:19 +0100 Subject: Prepopulate @customSearchMode in vomnibar. This avoids a flicker whereby the keyword is first inserted into the input, then removed. --- content_scripts/vomnibar.coffee | 2 +- pages/vomnibar.coffee | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/content_scripts/vomnibar.coffee b/content_scripts/vomnibar.coffee index 76698634..d46b2daf 100644 --- a/content_scripts/vomnibar.coffee +++ b/content_scripts/vomnibar.coffee @@ -8,7 +8,7 @@ Vomnibar = extras = {} for extra in registryEntry.extras [ key, value ] = extra.split "=" - extras.query = "#{value} " if key? and key == "keyword" and value? and 0 < value.length + extras.keyword = value if key? and key == "keyword" and value? and 0 < value.length extras # sourceFrameId here (and below) is the ID of the frame from which this request originates, which may be different diff --git a/pages/vomnibar.coffee b/pages/vomnibar.coffee index 71f22900..d5659fdc 100644 --- a/pages/vomnibar.coffee +++ b/pages/vomnibar.coffee @@ -17,6 +17,7 @@ Vomnibar = query: "" newTab: false selectFirst: false + keyword: null extend options, userOptions extend options, refreshInterval: if options.completer == "omni" then 150 else 0 @@ -28,6 +29,7 @@ Vomnibar = @vomnibarUI.setRefreshInterval options.refreshInterval @vomnibarUI.setForceNewTab options.newTab @vomnibarUI.setQuery options.query + @vomnibarUI.setKeyword options.keyword @vomnibarUI.update true hide: -> @vomnibarUI?.hide() @@ -40,6 +42,7 @@ class VomnibarUI @initDom() setQuery: (query) -> @input.value = query + setKeyword: (keyword) -> @customSearchMode = keyword setInitialSelectionValue: (@initialSelectionValue) -> setRefreshInterval: (@refreshInterval) -> setForceNewTab: (@forceNewTab) -> -- cgit v1.2.3 From ff44941dc8d7f504d07d51e58955250609df6684 Mon Sep 17 00:00:00 2001 From: Stephen Blott Date: Fri, 29 May 2015 16:29:13 +0100 Subject: Move search-engine parsing to Utils. This will allow us to use the same search-engine parsing code in the background page and in content scripts. --- background_scripts/completion.coffee | 24 ++--------------------- lib/utils.coffee | 37 ++++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 22 deletions(-) diff --git a/background_scripts/completion.coffee b/background_scripts/completion.coffee index bae73b8d..c83066a6 100644 --- a/background_scripts/completion.coffee +++ b/background_scripts/completion.coffee @@ -413,7 +413,6 @@ class TabCompleter class SearchEngineCompleter @debug: false - searchEngines: null previousSuggestions: null cancel: -> @@ -422,7 +421,7 @@ class SearchEngineCompleter # This looks up the custom search engine and, if one is found, notes it and removes its keyword from the # query terms. preprocessRequest: (request) -> - @searchEngines.use (engines) => + SearchEngines.use (engines) => { queryTerms, query } = request extend request, searchEngines: engines, keywords: key for own key of engines keyword = queryTerms[0] @@ -436,26 +435,7 @@ class SearchEngineCompleter refresh: (port) -> @previousSuggestions = {} - # Parse the search-engine configuration. - @searchEngines = new AsyncDataFetcher (callback) -> - engines = {} - for line in Settings.get("searchEngines").split "\n" - line = line.trim() - continue if /^[#"]/.test line - tokens = line.split /\s+/ - continue unless 2 <= tokens.length - keyword = tokens[0].split(":")[0] - url = tokens[1] - description = tokens[2..].join(" ") || "search (#{keyword})" - continue unless Utils.hasFullUrlPrefix url - engines[keyword] = - keyword: keyword - searchUrl: url - description: description - searchUrlPrefix: url.split("%s")[0] - - callback engines - + SearchEngines.refreshAndUse Settings.get("searchEngines"), (engines) -> # Let the front-end vomnibar know the search-engine keywords. It needs to know them so that, when the # query goes from "w" to "w ", the vomnibar can synchronously launch the next filter() request (which # avoids an ugly delay/flicker). diff --git a/lib/utils.coffee b/lib/utils.coffee index 65e26b7a..cb7b4d5c 100644 --- a/lib/utils.coffee +++ b/lib/utils.coffee @@ -228,6 +228,42 @@ Utils = # Like Nodejs's nextTick. nextTick: (func) -> @setTimeout 0, func +# Utility for parsing and using the custom search-engine configuration. We re-use the previous parse if the +# search-engine configuration is unchanged. +SearchEngines = + previousSearchEngines: null + searchEngines: null + + refresh: (searchEngines) -> + unless @previousSearchEngines? and searchEngines == @previousSearchEngines + @previousSearchEngines = searchEngines + @searchEngines = new AsyncDataFetcher (callback) -> + engines = {} + for line in searchEngines.split "\n" + line = line.trim() + continue if /^[#"]/.test line + tokens = line.split /\s+/ + continue unless 2 <= tokens.length + keyword = tokens[0].split(":")[0] + url = tokens[1] + description = tokens[2..].join(" ") || "search (#{keyword})" + continue unless Utils.hasFullUrlPrefix url + engines[keyword] = + keyword: keyword + searchUrl: url + description: description + searchUrlPrefix: url.split("%s")[0] + + callback engines + + # Use the parsed search-engine configuration, possibly asynchronously. + use: (callback) -> + @searchEngines.use callback + + # Both set (refresh) the search-engine configuration and use it at the same time. + refreshAndUse: (searchEngines, callback) -> + @refresh searchEngines + @use callback # This creates a new function out of an existing function, where the new function takes fewer arguments. This # allows us to pass around functions instead of functions + a partial list of arguments. @@ -325,6 +361,7 @@ class JobRunner root = exports ? window root.Utils = Utils +root.SearchEngines = SearchEngines root.SimpleCache = SimpleCache root.AsyncDataFetcher = AsyncDataFetcher root.JobRunner = JobRunner -- cgit v1.2.3 From 97bfe5f039ae9044fc634448ad3a97b1cdc05792 Mon Sep 17 00:00:00 2001 From: Stephen Blott Date: Fri, 29 May 2015 17:06:55 +0100 Subject: Verify keyword for custom search-engine activation. For ... map s Vomnibar.activate keyword=g ... we verify that "g" is indeed a custom search-engine keyword before setting it. If it is not, we output a console.log message and launch a vanilla vomnibar. (An alternative would be to bail.) --- content_scripts/vimium_frontend.coffee | 1 + content_scripts/vomnibar.coffee | 30 ++++++++++++++++++++++-------- lib/utils.coffee | 10 +++------- tests/dom_tests/vomnibar_test.coffee | 1 + 4 files changed, 27 insertions(+), 15 deletions(-) diff --git a/content_scripts/vimium_frontend.coffee b/content_scripts/vimium_frontend.coffee index 8c6dd374..005412e5 100644 --- a/content_scripts/vimium_frontend.coffee +++ b/content_scripts/vimium_frontend.coffee @@ -62,6 +62,7 @@ settings = helpDialog_showAdvancedCommands: null smoothScroll: null grabBackFocus: null + searchEngines: null init: -> @port = chrome.runtime.connect name: "settings" diff --git a/content_scripts/vomnibar.coffee b/content_scripts/vomnibar.coffee index d46b2daf..06b546a6 100644 --- a/content_scripts/vomnibar.coffee +++ b/content_scripts/vomnibar.coffee @@ -4,21 +4,35 @@ Vomnibar = vomnibarUI: null - getOptions: (registryEntry = { extras: [] }) -> - extras = {} - for extra in registryEntry.extras - [ key, value ] = extra.split "=" - extras.keyword = value if key? and key == "keyword" and value? and 0 < value.length - extras + # Parse any additional options from the command's registry entry. Currently, this only includes a flag of + # the form "keyword=X", for direct activation of a custom search engine. + parseRegistryEntry: (registryEntry = { extras: [] }, callback = null) -> + options = {} + searchEngines = settings.get("searchEngines") ? "" + SearchEngines.refreshAndUse searchEngines, (engines) -> + for extra in registryEntry.extras + [ key, value ] = extra.split "=" + switch key + when "keyword" + if value? and engines[value]? + options.keyword = value + else + console.log "Vimium configuration error: no such custom search engine: #{extra}." + else + console.log "Vimium configuration error: unused flag: #{extra}." + + callback? options # sourceFrameId here (and below) is the ID of the frame from which this request originates, which may be different # from the current frame. activate: (sourceFrameId, registryEntry) -> - @open sourceFrameId, extend @getOptions(registryEntry), completer:"omni" + @parseRegistryEntry registryEntry, (options) => + @open sourceFrameId, extend options, completer:"omni" activateInNewTab: (sourceFrameId, registryEntry) -> - @open sourceFrameId, extend @getOptions(registryEntry), completer: "omni", newTab: true + @parseRegistryEntry registryEntry, (options) => + @open sourceFrameId, extend options, completer:"omni", newTab: true activateTabSelection: (sourceFrameId) -> @open sourceFrameId, { completer: "tabs" diff --git a/lib/utils.coffee b/lib/utils.coffee index cb7b4d5c..77e2b68d 100644 --- a/lib/utils.coffee +++ b/lib/utils.coffee @@ -245,14 +245,10 @@ SearchEngines = tokens = line.split /\s+/ continue unless 2 <= tokens.length keyword = tokens[0].split(":")[0] - url = tokens[1] + searchUrl = tokens[1] description = tokens[2..].join(" ") || "search (#{keyword})" - continue unless Utils.hasFullUrlPrefix url - engines[keyword] = - keyword: keyword - searchUrl: url - description: description - searchUrlPrefix: url.split("%s")[0] + continue unless Utils.hasFullUrlPrefix searchUrl + engines[keyword] = { keyword, searchUrl, description } callback engines diff --git a/tests/dom_tests/vomnibar_test.coffee b/tests/dom_tests/vomnibar_test.coffee index 380175f3..3eda6234 100644 --- a/tests/dom_tests/vomnibar_test.coffee +++ b/tests/dom_tests/vomnibar_test.coffee @@ -1,4 +1,5 @@ vomnibarFrame = null +SearchEngines.refresh "" context "Keep selection within bounds", -- cgit v1.2.3 From 5930a6e3510f2bd052771601581aa410728d68e3 Mon Sep 17 00:00:00 2001 From: Stephen Blott Date: Sat, 30 May 2015 06:16:24 +0100 Subject: Use the term "options" instead of "flags" for command options. "Flags" implies binary toggles. The term "options" seems more consistent with what's actually going on here. --- background_scripts/commands.coffee | 10 +++++----- content_scripts/vomnibar.coffee | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/background_scripts/commands.coffee b/background_scripts/commands.coffee index 1714ae56..fa5354df 100644 --- a/background_scripts/commands.coffee +++ b/background_scripts/commands.coffee @@ -24,13 +24,13 @@ Commands = noRepeat: options.noRepeat repeatLimit: options.repeatLimit - mapKeyToCommand: ({ key, command, extras }) -> + mapKeyToCommand: ({ key, command, options }) -> unless @availableCommands[command] console.log command, "doesn't exist!" return - extras ?= [] - @keyToCommandRegistry[key] = extend { command, extras }, @availableCommands[command] + options ?= [] + @keyToCommandRegistry[key] = extend { command, options }, @availableCommands[command] # Lower-case the appropriate portions of named keys. # @@ -51,11 +51,11 @@ Commands = tokens = line.replace(/\s+$/, "").split /\s+/ switch tokens[0] when "map" - [ _, key, command, extras... ] = tokens + [ _, key, command, options... ] = tokens if command? and @availableCommands[command] key = @normalizeKey key console.log "Mapping", key, "to", command - @mapKeyToCommand { key, command, extras } + @mapKeyToCommand { key, command, options } when "unmap" if tokens.length == 2 diff --git a/content_scripts/vomnibar.coffee b/content_scripts/vomnibar.coffee index 06b546a6..4bd8e8fd 100644 --- a/content_scripts/vomnibar.coffee +++ b/content_scripts/vomnibar.coffee @@ -6,20 +6,20 @@ Vomnibar = # Parse any additional options from the command's registry entry. Currently, this only includes a flag of # the form "keyword=X", for direct activation of a custom search engine. - parseRegistryEntry: (registryEntry = { extras: [] }, callback = null) -> + parseRegistryEntry: (registryEntry = { options: [] }, callback = null) -> options = {} searchEngines = settings.get("searchEngines") ? "" SearchEngines.refreshAndUse searchEngines, (engines) -> - for extra in registryEntry.extras - [ key, value ] = extra.split "=" + for option in registryEntry.options + [ key, value ] = option.split "=" switch key when "keyword" if value? and engines[value]? options.keyword = value else - console.log "Vimium configuration error: no such custom search engine: #{extra}." + console.log "Vimium configuration error: no such custom search engine: #{option}." else - console.log "Vimium configuration error: unused flag: #{extra}." + console.log "Vimium configuration error: unused flag: #{option}." callback? options -- cgit v1.2.3