aboutsummaryrefslogtreecommitdiffstats
path: root/background_scripts
diff options
context:
space:
mode:
authorStephen Blott2016-12-11 15:22:02 +0000
committerGitHub2016-12-11 15:22:02 +0000
commit7fe88a70031c09f530ad2c22be88c30c7f1e787e (patch)
tree2f6b3bdefc8456d3427684f1a0079f81b86f3fbb /background_scripts
parentbd06c7ccfbfcf36e6d15fba72b7af90254b05da2 (diff)
parentbd607cf57512580cf5c9c7d81be5b79b93d1ae9f (diff)
downloadvimium-7fe88a70031c09f530ad2c22be88c30c7f1e787e.tar.bz2
Merge pull request #2368 from smblott-github/rework-key-mapping-parsing
Rework key-mapping parsing and help-dialog HTML
Diffstat (limited to 'background_scripts')
-rw-r--r--background_scripts/commands.coffee137
-rw-r--r--background_scripts/main.coffee51
2 files changed, 64 insertions, 124 deletions
diff --git a/background_scripts/commands.coffee b/background_scripts/commands.coffee
index 6aa8ca0d..aece4bec 100644
--- a/background_scripts/commands.coffee
+++ b/background_scripts/commands.coffee
@@ -1,36 +1,55 @@
Commands =
+ availableCommands: {}
+ keyToCommandRegistry: null
+ mapKeyRegistry: null
+
init: ->
- for own command, descriptor of commandDescriptions
- @addCommand(command, descriptor[0], descriptor[1])
- @loadKeyMappings Settings.get "keyMappings"
+ for own command, [description, options] of commandDescriptions
+ @availableCommands[command] = extend (options ? {}), description: description
+
Settings.postUpdateHooks["keyMappings"] = @loadKeyMappings.bind this
+ @loadKeyMappings Settings.get "keyMappings"
+ @prepareHelpPageData()
loadKeyMappings: (customKeyMappings) ->
- @clearKeyMappingsAndSetDefaults()
- @parseCustomKeyMappings customKeyMappings
- @generateKeyStateMapping()
- chrome.storage.local.set mapKeyRegistry: @mapKeyRegistry
-
- availableCommands: {}
- keyToCommandRegistry: {}
-
- # Registers a command, making it available to be optionally bound to a key.
- # options:
- # - background: whether this command needs to be run against the background page.
- addCommand: (command, description, options = {}) ->
- if command of @availableCommands
- BgUtils.log "#{command} is already defined! Check commands.coffee for duplicates."
- return
+ @keyToCommandRegistry = {}
+ @mapKeyRegistry = {}
- @availableCommands[command] = extend options, description: description
+ configLines = ("map #{key} #{command}" for own key, command of defaultKeyMappings)
+ configLines.push BgUtils.parseLines(customKeyMappings)...
+ seen = {}
+ unmapAll = false
+ for line in configLines.reverse()
+ tokens = line.split /\s+/
+ switch tokens[0]
+ when "map"
+ if 3 <= tokens.length and not unmapAll
+ [_, key, command, optionsList...] = tokens
+ if not seen[key] and registryEntry = @availableCommands[command]
+ seen[key] = true
+ keySequence = @parseKeySequence key
+ options = @parseCommandOptions command, optionsList
+ @keyToCommandRegistry[key] = extend {keySequence, command, options}, @availableCommands[command]
+ when "unmap"
+ if tokens.length == 2
+ seen[tokens[1]] = true
+ when "unmapAll"
+ unmapAll = true
+ when "mapkey"
+ if tokens.length == 3
+ fromChar = @parseKeySequence tokens[1]
+ toChar = @parseKeySequence tokens[2]
+ @mapKeyRegistry[fromChar[0]] ?= toChar[0] if fromChar.length == toChar.length == 1
- mapKeyToCommand: ({ key, keySequence, command, options }) ->
- unless @availableCommands[command]
- BgUtils.log "#{command} doesn't exist!"
- return
+ chrome.storage.local.set mapKeyRegistry: @mapKeyRegistry
+ @installKeyStateMapping()
- options ?= {}
- @keyToCommandRegistry[key] = extend { keySequence, command, options }, @availableCommands[command]
+ # Push the key mapping for passNextKey into Settings so that it's available in the front end for insert
+ # mode. We exclude single-key mappings (that is, printable keys) because when users press printable keys
+ # in insert mode they expect the character to be input, not to be droppped into some special Vimium
+ # mode.
+ Settings.set "passNextKeyKeys",
+ (key for own key of @keyToCommandRegistry when @keyToCommandRegistry[key].command == "passNextKey" and 1 < key.length)
# Lower-case the appropriate portions of named keys.
#
@@ -60,43 +79,6 @@ Commands =
else
[key[0], @parseKeySequence(key[1..])...]
- parseCustomKeyMappings: (customKeyMappings) ->
- for line in BgUtils.parseLines customKeyMappings
- tokens = line.split /\s+/
- switch tokens[0]
- when "map"
- [ _, key, command, optionList... ] = tokens
- keySequence = @parseKeySequence key
- if command? and @availableCommands[command]
- key = keySequence.join ""
- BgUtils.log "mapping [\"#{keySequence.join '", "'}\"] to #{command}"
- @mapKeyToCommand { key, command, keySequence, options: @parseCommandOptions command, optionList }
- else
- BgUtils.log "skipping [\"#{keySequence.join '", "'}\"] for #{command} -- something is not right"
-
- when "unmap"
- if tokens.length == 2
- keySequence = @parseKeySequence tokens[1]
- key = keySequence.join ""
- BgUtils.log "Unmapping #{key}"
- delete @keyToCommandRegistry[key]
-
- when "unmapAll"
- @keyToCommandRegistry = {}
-
- when "mapkey"
- if tokens.length == 3
- fromChar = @parseKeySequence tokens[1]
- toChar = @parseKeySequence tokens[2]
- @mapKeyRegistry[fromChar[0]] = toChar[0] if fromChar.length == toChar.length == 1
-
- # Push the key mapping for passNextKey into Settings so that it's available in the front end for insert
- # mode. We exclude single-key mappings (that is, printable keys) because when users press printable keys
- # in insert mode they expect the character to be input, not to be droppped into some special Vimium
- # mode.
- Settings.set "passNextKeyKeys",
- (key for own key of @keyToCommandRegistry when @keyToCommandRegistry[key].command == "passNextKey" and 1 < key.length)
-
# Command options follow command mappings, and are of one of two forms:
# key=value - a value
# key - a flag
@@ -113,16 +95,9 @@ Commands =
options
- clearKeyMappingsAndSetDefaults: ->
- @keyToCommandRegistry = {}
- @mapKeyRegistry = {}
- for own key, command of defaultKeyMappings
- keySequence = @parseKeySequence key
- key = keySequence.join ""
- @mapKeyToCommand { key, command, keySequence }
-
- # This generates a nested key-to-command mapping structure. There is an example in mode_key_handler.coffee.
- generateKeyStateMapping: ->
+ # This generates and installs a nested key-to-command mapping structure. There is an example in
+ # mode_key_handler.coffee.
+ installKeyStateMapping: ->
keyStateMapping = {}
for own keys, registryEntry of @keyToCommandRegistry
currentMapping = keyStateMapping
@@ -136,9 +111,25 @@ Commands =
else
currentMapping[key] = extend {}, registryEntry
# We don't need these properties in the content scripts.
- delete registryEntry[prop] for prop in ["keySequence", "description"]
+ delete currentMapping[key][prop] for prop in ["keySequence", "description"]
chrome.storage.local.set normalModeKeyStateMapping: keyStateMapping
+ # Build the "helpPageData" data structure which the help page needs and place it in Chrome storage.
+ prepareHelpPageData: ->
+ commandToKey = {}
+ for own key, registryEntry of @keyToCommandRegistry
+ (commandToKey[registryEntry.command] ?= []).push key
+ commandGroups = {}
+ for own group, commands of @commandGroups
+ commandGroups[group] = []
+ for command in commands
+ commandGroups[group].push
+ command: command
+ description: @availableCommands[command].description
+ keys: commandToKey[command] ? []
+ advanced: command in @advancedCommands
+ chrome.storage.local.set helpPageData: commandGroups
+
# An ordered listing of all available commands, grouped by type. This is the order they will
# be shown in the help page.
commandGroups:
diff --git a/background_scripts/main.coffee b/background_scripts/main.coffee
index e57e061d..8f99cf3e 100644
--- a/background_scripts/main.coffee
+++ b/background_scripts/main.coffee
@@ -80,56 +80,6 @@ onURLChange = (details) ->
chrome.webNavigation.onHistoryStateUpdated.addListener onURLChange # history.pushState.
chrome.webNavigation.onReferenceFragmentUpdated.addListener onURLChange # Hash changed.
-# Retrieves the help dialog HTML template from a file, and populates it with the latest keybindings.
-getHelpDialogHtml = ({showUnboundCommands, showCommandNames, customTitle}) ->
- commandsToKey = {}
- for own key of Commands.keyToCommandRegistry
- command = Commands.keyToCommandRegistry[key].command
- commandsToKey[command] = (commandsToKey[command] || []).concat(key)
-
- replacementStrings =
- version: Utils.getCurrentVersion()
- title: customTitle || "Help"
- tip: if showCommandNames then "Tip: click command names to yank them to the clipboard." else "&nbsp;"
-
- for own group of Commands.commandGroups
- replacementStrings[group] =
- helpDialogHtmlForCommandGroup(group, commandsToKey, Commands.availableCommands,
- showUnboundCommands, showCommandNames)
-
- replacementStrings
-
-#
-# Generates HTML for a given set of commands. commandGroups are defined in commands.js
-#
-helpDialogHtmlForCommandGroup = (group, commandsToKey, availableCommands,
- showUnboundCommands, showCommandNames) ->
- html = []
- for command in Commands.commandGroups[group]
- keys = commandsToKey[command] || []
- bindings = ("<span class='vimiumHelpDialogKey'>#{Utils.escapeHtml key}</span>" for key in keys).join ", "
- if (showUnboundCommands || commandsToKey[command])
- isAdvanced = Commands.advancedCommands.indexOf(command) >= 0
- description = availableCommands[command].description
- if keys.join(", ").length < 12
- helpDialogHtmlForCommand html, isAdvanced, bindings, description, showCommandNames, command
- else
- # If the length of the bindings is too long, then we display the bindings on a separate row from the
- # description. This prevents the column alignment from becoming out of whack.
- helpDialogHtmlForCommand html, isAdvanced, bindings, "", false, ""
- helpDialogHtmlForCommand html, isAdvanced, "", description, showCommandNames, command
- html.join("\n")
-
-helpDialogHtmlForCommand = (html, isAdvanced, bindings, description, showCommandNames, command) ->
- html.push "<tr class='vimiumReset #{"advanced" if isAdvanced}'>"
- if description
- html.push "<td class='vimiumReset'>#{bindings}</td>"
- html.push "<td class='vimiumReset'></td><td class='vimiumReset vimiumHelpDescription'>", description
- html.push("(<span class='vimiumReset commandName'>#{command}</span>)") if showCommandNames
- else
- html.push "<td class='vimiumReset' colspan='3' style='text-align: left;'>", bindings
- html.push("</td></tr>")
-
# Cache "content_scripts/vimium.css" in chrome.storage.local for UI components.
do ->
req = new XMLHttpRequest()
@@ -429,7 +379,6 @@ portHandlers =
sendRequestHandlers =
runBackgroundCommand: (request) -> BackgroundCommands[request.registryEntry.command] request
- getHelpDialogHtml: getHelpDialogHtml
# getCurrentTabUrl is used by the content scripts to get their full URL, because window.location cannot help
# with Chrome-specific URLs like "view-source:http:..".
getCurrentTabUrl: ({tab}) -> tab.url