From 6596e30392a1ca053223825eda5cde060394a4aa Mon Sep 17 00:00:00 2001 From: Stephen Blott Date: Sun, 9 Oct 2016 15:20:15 +0100 Subject: Add translate command for key mappings. Under *Custom key mappings* (on the options page), this implements: translate x y Whenever the users types `x` in normal mode or in visual mode, the `x` is replaced by `y`. For example: map รง l (which apparently would be helpful on Brazilian keyboards). Issues: - Do we want yet another hack like this? This would be documented only on the wiki. - If we allowed `translate ` (and extended `isEscape()` to use the translation), then we'd get the `exitMode` command for free (#2253). - Alternatively, instead of adding a new "command" called `translate`, we could overload the existing `map` command. Since these are single-key mappings, there's no ambiguity. (Although, I guess there's a risk some user has junk in their key mappings and would be taken by surprise). Inspired by isssue posted by @vhoyer (#2305). Fixes #2305. --- background_scripts/commands.coffee | 8 ++++++++ content_scripts/mode_key_handler.coffee | 9 +++++++++ 2 files changed, 17 insertions(+) diff --git a/background_scripts/commands.coffee b/background_scripts/commands.coffee index 7a4526c2..17c05f90 100644 --- a/background_scripts/commands.coffee +++ b/background_scripts/commands.coffee @@ -9,6 +9,7 @@ Commands = @clearKeyMappingsAndSetDefaults() @parseCustomKeyMappings customKeyMappings @generateKeyStateMapping() + chrome.storage.local.set keyTranslationRegistry: @keyTranslationRegistry availableCommands: {} keyToCommandRegistry: {} @@ -84,6 +85,12 @@ Commands = when "unmapAll" @keyToCommandRegistry = {} + when "translate" + if tokens.length == 3 + fromChar = @parseKeySequence tokens[1] + toChar = @parseKeySequence tokens[2] + @keyTranslationRegistry[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 @@ -109,6 +116,7 @@ Commands = clearKeyMappingsAndSetDefaults: -> @keyToCommandRegistry = {} + @keyTranslationRegistry = {} for own key, command of defaultKeyMappings keySequence = @parseKeySequence key key = keySequence.join "" diff --git a/content_scripts/mode_key_handler.coffee b/content_scripts/mode_key_handler.coffee index 8c0ae4b8..7904d801 100644 --- a/content_scripts/mode_key_handler.coffee +++ b/content_scripts/mode_key_handler.coffee @@ -33,8 +33,16 @@ class KeyHandlerMode extends Mode # We cannot track keyup events if we lose the focus. blur: (event) => @alwaysContinueBubbling => @keydownEvents = {} if event.target == window + @keyTranslationRegistry = {} + chrome.storage.local.get "keyTranslationRegistry", (obj) => + @keyTranslationRegistry = obj.keyTranslationRegistry + chrome.storage.onChanged.addListener (changes, area) => + if area == "local" and changes.keyTranslationRegistry?.newValue? + @keyTranslationRegistry = changes.keyTranslationRegistry.newValue + onKeydown: (event) -> keyChar = KeyboardUtils.getKeyCharString event + keyChar = @keyTranslationRegistry[keyChar] ? keyChar isEscape = KeyboardUtils.isEscape event if isEscape and (@countPrefix != 0 or @keyState.length != 1) @keydownEvents[event.keyCode] = true @@ -61,6 +69,7 @@ class KeyHandlerMode extends Mode onKeypress: (event) -> keyChar = KeyboardUtils.getKeyCharString event + keyChar = @keyTranslationRegistry[keyChar] ? keyChar if @isMappedKey keyChar @handleKeyChar keyChar else if @isCountKey keyChar -- cgit v1.2.3 From 9c1012ad3a731b015b8a70b58828fbcd0acb7db0 Mon Sep 17 00:00:00 2001 From: Stephen Blott Date: Sun, 9 Oct 2016 16:31:23 +0100 Subject: Extend key translation to include Escape. Here, these map to escape: translate x translate --- content_scripts/mode_key_handler.coffee | 6 +----- lib/keyboard_utils.coffee | 35 ++++++++++++++++++++++----------- lib/utils.coffee | 7 +++++++ 3 files changed, 31 insertions(+), 17 deletions(-) diff --git a/content_scripts/mode_key_handler.coffee b/content_scripts/mode_key_handler.coffee index 7904d801..9f8206fb 100644 --- a/content_scripts/mode_key_handler.coffee +++ b/content_scripts/mode_key_handler.coffee @@ -34,11 +34,7 @@ class KeyHandlerMode extends Mode blur: (event) => @alwaysContinueBubbling => @keydownEvents = {} if event.target == window @keyTranslationRegistry = {} - chrome.storage.local.get "keyTranslationRegistry", (obj) => - @keyTranslationRegistry = obj.keyTranslationRegistry - chrome.storage.onChanged.addListener (changes, area) => - if area == "local" and changes.keyTranslationRegistry?.newValue? - @keyTranslationRegistry = changes.keyTranslationRegistry.newValue + Utils.monitorChromeStorage "keyTranslationRegistry", (value) => @keyTranslationRegistry = value onKeydown: (event) -> keyChar = KeyboardUtils.getKeyCharString event diff --git a/lib/keyboard_utils.coffee b/lib/keyboard_utils.coffee index f0e791d4..7f48d69e 100644 --- a/lib/keyboard_utils.coffee +++ b/lib/keyboard_utils.coffee @@ -79,10 +79,18 @@ KeyboardUtils = isPrimaryModifierKey: (event) -> if (@platform == "Mac") then event.metaKey else event.ctrlKey - isEscape: (event) -> - # c-[ is mapped to ESC in Vim by default. - (event.keyCode == @keyCodes.ESC) || - (event.ctrlKey && @getKeyChar(event) == '[' and not event.metaKey and not event.altKey) + isEscape: do -> + keyTranslationRegistry = {} + # NOTE: "?" here for the tests. + Utils?.monitorChromeStorage "keyTranslationRegistry", (value) => keyTranslationRegistry = value + + (event) -> + event.keyCode == @keyCodes.ESC || do => + keyChar = @getKeyChar event + keyChar.length == 1 and do => + keyChar = @getModifiedKeyChar keyChar, event + keyChar = keyTranslationRegistry[keyChar] ? keyChar + keyChar == "" # TODO. This is probably a poor way of detecting printable characters. However, it shouldn't incorrectly # identify any of chrome's own keyboard shortcuts as printable. @@ -108,16 +116,19 @@ KeyboardUtils = # Handle special keys and normal input keys with modifiers being pressed. keyChar = @getKeyChar event if 1 < keyChar.length or (keyChar.length == 1 and (event.metaKey or event.ctrlKey or event.altKey)) - modifiers = [] + @getModifiedKeyChar keyChar, event + + getModifiedKeyChar: (keyChar, event) -> + modifiers = [] - keyChar = keyChar.toUpperCase() if event.shiftKey - # These must be in alphabetical order (to match the sorted modifier order in Commands.normalizeKey). - modifiers.push "a" if event.altKey - modifiers.push "c" if event.ctrlKey - modifiers.push "m" if event.metaKey + keyChar = keyChar.toUpperCase() if event.shiftKey + # These must be in alphabetical order (to match the sorted modifier order in Commands.normalizeKey). + modifiers.push "a" if event.altKey + modifiers.push "c" if event.ctrlKey + modifiers.push "m" if event.metaKey - keyChar = [modifiers..., keyChar].join "-" - if 1 < keyChar.length then "<#{keyChar}>" else keyChar + keyChar = [modifiers..., keyChar].join "-" + if 1 < keyChar.length then "<#{keyChar}>" else keyChar KeyboardUtils.init() diff --git a/lib/utils.coffee b/lib/utils.coffee index c06d8ac5..5a028186 100644 --- a/lib/utils.coffee +++ b/lib/utils.coffee @@ -209,6 +209,13 @@ Utils = makeIdempotent: (func) -> (args...) -> ([previousFunc, func] = [func, null])[0]? args... + monitorChromeStorage: (key, setter) -> + # NOTE: "?" here for the tests. + chrome?.storage.local.get key, (obj) => + setter obj[key] if obj[key]? + chrome.storage.onChanged.addListener (changes, area) => + setter changes[key].newValue if changes[key]?.newValue? + # Utility for parsing and using the custom search-engine configuration. We re-use the previous parse if the # search-engine configuration is unchanged. SearchEngines = -- cgit v1.2.3 From 1df19294c544b897bf5fe52401050dd43b69129d Mon Sep 17 00:00:00 2001 From: Stephen Blott Date: Sun, 9 Oct 2016 17:48:59 +0100 Subject: Tweak (simplify) #2306. --- lib/keyboard_utils.coffee | 32 ++++++++++++++------------------ 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/lib/keyboard_utils.coffee b/lib/keyboard_utils.coffee index 7f48d69e..c8591c3c 100644 --- a/lib/keyboard_utils.coffee +++ b/lib/keyboard_utils.coffee @@ -86,11 +86,10 @@ KeyboardUtils = (event) -> event.keyCode == @keyCodes.ESC || do => - keyChar = @getKeyChar event - keyChar.length == 1 and do => - keyChar = @getModifiedKeyChar keyChar, event - keyChar = keyTranslationRegistry[keyChar] ? keyChar - keyChar == "" + keyChar = @getKeyCharString event, true + keyChar = keyTranslationRegistry[keyChar] ? keyChar + # is mapped to Escape in Vim by default. + keyChar == "" # TODO. This is probably a poor way of detecting printable characters. However, it shouldn't incorrectly # identify any of chrome's own keyboard shortcuts as printable. @@ -105,7 +104,7 @@ KeyboardUtils = # Return the Vimium key representation for this keyboard event. Return a falsy value (the empty string or # undefined) when no Vimium representation is appropriate. - getKeyCharString: (event) -> + getKeyCharString: (event, allKeydownEvents = false) -> switch event.type when "keypress" # Ignore modifier keys by themselves. @@ -115,20 +114,17 @@ KeyboardUtils = when "keydown" # Handle special keys and normal input keys with modifiers being pressed. keyChar = @getKeyChar event - if 1 < keyChar.length or (keyChar.length == 1 and (event.metaKey or event.ctrlKey or event.altKey)) - @getModifiedKeyChar keyChar, event - - getModifiedKeyChar: (keyChar, event) -> - modifiers = [] + if 1 < keyChar.length or (keyChar.length == 1 and (event.metaKey or event.ctrlKey or event.altKey)) or allKeydownEvents + modifiers = [] - keyChar = keyChar.toUpperCase() if event.shiftKey - # These must be in alphabetical order (to match the sorted modifier order in Commands.normalizeKey). - modifiers.push "a" if event.altKey - modifiers.push "c" if event.ctrlKey - modifiers.push "m" if event.metaKey + keyChar = keyChar.toUpperCase() if event.shiftKey + # These must be in alphabetical order (to match the sorted modifier order in Commands.normalizeKey). + modifiers.push "a" if event.altKey + modifiers.push "c" if event.ctrlKey + modifiers.push "m" if event.metaKey - keyChar = [modifiers..., keyChar].join "-" - if 1 < keyChar.length then "<#{keyChar}>" else keyChar + keyChar = [modifiers..., keyChar].join "-" + if 1 < keyChar.length then "<#{keyChar}>" else keyChar KeyboardUtils.init() -- cgit v1.2.3 From c5e26d75cb8525ba34f6f54f1a1c041df66075e3 Mon Sep 17 00:00:00 2001 From: Stephen Blott Date: Mon, 10 Oct 2016 13:27:32 +0100 Subject: Rename 'translate' to 'mapkey'. --- background_scripts/commands.coffee | 8 ++++---- content_scripts/mode_key_handler.coffee | 8 ++++---- lib/keyboard_utils.coffee | 6 +++--- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/background_scripts/commands.coffee b/background_scripts/commands.coffee index 17c05f90..d12b704d 100644 --- a/background_scripts/commands.coffee +++ b/background_scripts/commands.coffee @@ -9,7 +9,7 @@ Commands = @clearKeyMappingsAndSetDefaults() @parseCustomKeyMappings customKeyMappings @generateKeyStateMapping() - chrome.storage.local.set keyTranslationRegistry: @keyTranslationRegistry + chrome.storage.local.set mapKeyRegistry: @mapKeyRegistry availableCommands: {} keyToCommandRegistry: {} @@ -85,11 +85,11 @@ Commands = when "unmapAll" @keyToCommandRegistry = {} - when "translate" + when "mapkey" if tokens.length == 3 fromChar = @parseKeySequence tokens[1] toChar = @parseKeySequence tokens[2] - @keyTranslationRegistry[fromChar[0]] = toChar[0] if fromChar.length == toChar.length == 1 + @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 @@ -116,7 +116,7 @@ Commands = clearKeyMappingsAndSetDefaults: -> @keyToCommandRegistry = {} - @keyTranslationRegistry = {} + @mapKeyRegistry = {} for own key, command of defaultKeyMappings keySequence = @parseKeySequence key key = keySequence.join "" diff --git a/content_scripts/mode_key_handler.coffee b/content_scripts/mode_key_handler.coffee index 9f8206fb..480a79af 100644 --- a/content_scripts/mode_key_handler.coffee +++ b/content_scripts/mode_key_handler.coffee @@ -33,12 +33,12 @@ class KeyHandlerMode extends Mode # We cannot track keyup events if we lose the focus. blur: (event) => @alwaysContinueBubbling => @keydownEvents = {} if event.target == window - @keyTranslationRegistry = {} - Utils.monitorChromeStorage "keyTranslationRegistry", (value) => @keyTranslationRegistry = value + @mapKeyRegistry = {} + Utils.monitorChromeStorage "mapKeyRegistry", (value) => @mapKeyRegistry = value onKeydown: (event) -> keyChar = KeyboardUtils.getKeyCharString event - keyChar = @keyTranslationRegistry[keyChar] ? keyChar + keyChar = @mapKeyRegistry[keyChar] ? keyChar isEscape = KeyboardUtils.isEscape event if isEscape and (@countPrefix != 0 or @keyState.length != 1) @keydownEvents[event.keyCode] = true @@ -65,7 +65,7 @@ class KeyHandlerMode extends Mode onKeypress: (event) -> keyChar = KeyboardUtils.getKeyCharString event - keyChar = @keyTranslationRegistry[keyChar] ? keyChar + keyChar = @mapKeyRegistry[keyChar] ? keyChar if @isMappedKey keyChar @handleKeyChar keyChar else if @isCountKey keyChar diff --git a/lib/keyboard_utils.coffee b/lib/keyboard_utils.coffee index c8591c3c..967baba8 100644 --- a/lib/keyboard_utils.coffee +++ b/lib/keyboard_utils.coffee @@ -80,14 +80,14 @@ KeyboardUtils = isPrimaryModifierKey: (event) -> if (@platform == "Mac") then event.metaKey else event.ctrlKey isEscape: do -> - keyTranslationRegistry = {} + mapKeyRegistry = {} # NOTE: "?" here for the tests. - Utils?.monitorChromeStorage "keyTranslationRegistry", (value) => keyTranslationRegistry = value + Utils?.monitorChromeStorage "mapKeyRegistry", (value) => mapKeyRegistry = value (event) -> event.keyCode == @keyCodes.ESC || do => keyChar = @getKeyCharString event, true - keyChar = keyTranslationRegistry[keyChar] ? keyChar + keyChar = mapKeyRegistry[keyChar] ? keyChar # is mapped to Escape in Vim by default. keyChar == "" -- cgit v1.2.3 From 0346b2d971ffab26a8d37946aca1c550b316e317 Mon Sep 17 00:00:00 2001 From: Stephen Blott Date: Mon, 10 Oct 2016 13:54:12 +0100 Subject: Add TODO. --- lib/keyboard_utils.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/keyboard_utils.coffee b/lib/keyboard_utils.coffee index 967baba8..c49fb3f4 100644 --- a/lib/keyboard_utils.coffee +++ b/lib/keyboard_utils.coffee @@ -84,6 +84,7 @@ KeyboardUtils = # NOTE: "?" here for the tests. Utils?.monitorChromeStorage "mapKeyRegistry", (value) => mapKeyRegistry = value + # TODO(smblott) Change this to use event.key. (event) -> event.keyCode == @keyCodes.ESC || do => keyChar = @getKeyCharString event, true -- cgit v1.2.3