Reverse keymap resolver


Let’s say my package contains this keymap:

'.platform-darwin atom-workspace':
  'cmd-q': 'my-package:some-action'

'.platform-win32 atom-workspace, .platform-linux atom-workspace':
  'ctrl-q': 'my-package:some-action'

I’d like to display the exact relevant keybinding to the user, e.g. on OS X I’d display <kbd class='key-binding'>⌘Q</kbd>. Ideally there would be a “reverse keymap resolver” that takes a DOM node and an action key and returns all key bindings that map to the action, if any.

As a workaround, I could read the platform selector off of document.body, but selectors might get more complicated than that.

What’s the most idiomatic way to do this?


Have you checked the documentation for the KeymapManager?


FWIW, I’ve done something like that in another project using electron and Atom’s commands and keymap managers:

keybindings = getCommandKeyBinding(command)

if keybindings.length
  for binding in keybindings
    kbd = document.createElement('kbd')
    kbd.textContent = formatKeybinding(binding)


And I used the following code to format the keybinding:

  'alt': '⌥'
  'cmd': '⌘'
  'ctrl': '⌃'
  'shift': '⇧'
  'pageup': '⇞'
  'pagedown': '⇟'
  'backspace': '⌫'
  'enter': '↩'
  'home': '↖'
  'end': '↘'
  'capslock': '⇪'
  'tab': '⇥'
  'left': '⬅'
  'right': '➡'
  'up': '⬆'
  'down': '⬇'
  'space': 'Space'

formatKeybinding = (str) ->
  split = (str) ->
    res = []
    s = ''

    for char,i in str
      if char is '-' or char is '+'
        if s is ''
          s += char
          res.push s
          s = ''
        s += char

    res.push s

  sequence = str.split(' ')
  for combination,i in sequence
    keys = split(combination)
    for key,j in keys
      key = key.toLowerCase()
      if KEY_MAP[key]?
        keys[j] = KEY_MAP[key]
        keys[j] = key.toUpperCase()

    sequence[i] = keys.join('')

  sequence.join(' ')

And finally all the methods I use to resolve the commands and keybinding from a given context:

getCommandsForContext = (context) ->
    allCommands = atom.commands.getSnapshot()
    filtered = {}
    filter = (d) -> d.selector is context

    for command,descriptors of allCommands
      if descriptors.some(filter)
        filtered[command] = descriptors.filter(filter)[0]


  getCommandKeyBinding = (command, keybindings, descriptor) ->
    keybindings ?= atom.keymaps.getKeyBindings()
    descriptor ?= atom.commands.getSnapshot()[command]

    filter = (binding) ->
      (binding.selector is descriptor.selector or
      binding.selector is 'atom-workspace') and
      binding.command is command

    keybindings.filter(filter).map((b) -> b.keystrokes)

  getKeyBindingsForContexts =  (contexts...) ->
    o = {}
    for context in contexts
      oo = getKeyBindings(getCommandsForContext(context))
      o[k] = v for k,v of oo

  getKeyBindings = (commands) ->
    keybindings = atom.keymaps.getKeyBindings()
    result = {}

    for command,descriptor of commands
      bindings = getCommandKeyBinding(command, keybindings, descriptor)
      result[command] = bindings if bindings.length


It may not solve every cases but I didn’t had much problem with this code.


KeyMapManager is indeed the way to go:

  command: 'my-package:some-action',
  target: document.querySelector('.my-package-root')

returns 'cmd-q' on OSX.

Thanks everyone!