diff --git a/spec/context-menu-manager-spec.coffee b/spec/context-menu-manager-spec.coffee index f99d7b004..e443a69d2 100644 --- a/spec/context-menu-manager-spec.coffee +++ b/spec/context-menu-manager-spec.coffee @@ -231,3 +231,75 @@ describe "ContextMenuManager", -> } ] ]) + + describe "::templateForEvent(target)", -> + [keymaps, item] = [] + + beforeEach -> + keymaps = atom.keymaps.add('source', { + '.child': { + 'ctrl-a': 'test:my-command', + 'shift-b': 'test:my-other-command' + } + }) + item = { + label: 'My Command', + command: 'test:my-command', + submenu: [ + { + label: 'My Other Command', + command: 'test:my-other-command', + } + ] + } + contextMenu.add('.parent': [item]) + + afterEach -> + keymaps.dispose() + + + it "adds Electron-style accelerators to items that have keybindings", -> + dispatchedEvent = {target: child} + expect(contextMenu.templateForEvent(dispatchedEvent)).toEqual( + [ + label: 'My Command', + command: 'test:my-command', + accelerator: 'Ctrl+A', + submenu: [ + { + label: 'My Other Command', + command: 'test:my-other-command', + accelerator: 'Shift+B', + } + ] + ]) + + it "adds accelerators when a parent node has key bindings for a given command", -> + dispatchedEvent = {target: grandchild} + expect(contextMenu.templateForEvent(dispatchedEvent)).toEqual( + [ + label: 'My Command', + command: 'test:my-command', + accelerator: 'Ctrl+A', + submenu: [ + { + label: 'My Other Command', + command: 'test:my-other-command', + accelerator: 'Shift+B', + } + ] + ]) + + it "does not add accelerators when a child node has key bindings for a given command", -> + dispatchedEvent = {target: parent} + expect(contextMenu.templateForEvent(dispatchedEvent)).toEqual( + [ + label: 'My Command', + command: 'test:my-command', + submenu: [ + { + label: 'My Other Command', + command: 'test:my-other-command', + } + ] + ]) diff --git a/src/context-menu-manager.coffee b/src/context-menu-manager.coffee index 8a25373b0..caa495354 100644 --- a/src/context-menu-manager.coffee +++ b/src/context-menu-manager.coffee @@ -147,9 +147,20 @@ class ContextMenuManager currentTarget = currentTarget.parentElement @pruneRedundantSeparators(template) + @addAccelerators(template, event.target) template + # Adds an `accelerator` property to items that have key bindings. Electron + # uses this property to surface the relevant keymaps in the context menu. + addAccelerators: (template, target) -> + for id, item of template + keymaps = @keymapManager.findKeyBindings({command: item.command, target}) + accelerator = MenuHelpers.acceleratorForKeystroke(keymaps?[0]?.keystrokes) + item.accelerator = accelerator if accelerator + if Array.isArray(item.submenu) + @addAccelerators(item.submenu, target) + pruneRedundantSeparators: (menu) -> keepNextItemIfSeparator = false index = 0 diff --git a/src/main-process/application-menu.coffee b/src/main-process/application-menu.coffee index f06e4933f..681677603 100644 --- a/src/main-process/application-menu.coffee +++ b/src/main-process/application-menu.coffee @@ -1,5 +1,6 @@ {app, Menu} = require 'electron' _ = require 'underscore-plus' +MenuHelpers = require '../menu-helpers' # Used to manage the global application menu. # @@ -154,19 +155,7 @@ class ApplicationMenu # are Arrays containing the keystroke. # # Returns a String containing the keystroke in a format that can be interpreted - # by atom shell to provide nice icons where available. + # by Electron to provide nice icons where available. acceleratorForCommand: (command, keystrokesByCommand) -> firstKeystroke = keystrokesByCommand[command]?[0] - return null unless firstKeystroke - - modifiers = firstKeystroke.split(/-(?=.)/) - key = modifiers.pop().toUpperCase().replace('+', 'Plus') - - modifiers = modifiers.map (modifier) -> - modifier.replace(/shift/ig, "Shift") - .replace(/cmd/ig, "Command") - .replace(/ctrl/ig, "Ctrl") - .replace(/alt/ig, "Alt") - - keys = modifiers.concat([key]) - keys.join("+") + MenuHelpers.acceleratorForKeystroke(firstKeystroke) diff --git a/src/menu-helpers.coffee b/src/menu-helpers.coffee index 8ab10c048..d648d81ed 100644 --- a/src/menu-helpers.coffee +++ b/src/menu-helpers.coffee @@ -46,9 +46,29 @@ normalizeLabel = (label) -> label.replace(/\&/g, '') cloneMenuItem = (item) -> - item = _.pick(item, 'type', 'label', 'enabled', 'visible', 'command', 'submenu', 'commandDetail', 'role') + item = _.pick(item, 'type', 'label', 'enabled', 'visible', 'command', 'submenu', 'commandDetail', 'role', 'accelerator') if item.submenu? item.submenu = item.submenu.map (submenuItem) -> cloneMenuItem(submenuItem) item -module.exports = {merge, unmerge, normalizeLabel, cloneMenuItem} +# Determine the Electron accelerator for a given Atom keystroke. +# +# keystroke - The keystroke. +# +# Returns a String containing the keystroke in a format that can be interpreted +# by Electron to provide nice icons where available. +acceleratorForKeystroke = (keystroke) -> + return null unless keystroke + modifiers = keystroke.split(/-(?=.)/) + key = modifiers.pop().toUpperCase().replace('+', 'Plus') + + modifiers = modifiers.map (modifier) -> + modifier.replace(/shift/ig, "Shift") + .replace(/cmd/ig, "Command") + .replace(/ctrl/ig, "Ctrl") + .replace(/alt/ig, "Alt") + + keys = modifiers.concat([key]) + keys.join("+") + +module.exports = {merge, unmerge, normalizeLabel, cloneMenuItem, acceleratorForKeystroke}