diff --git a/spec/atom/global-keymap-spec.coffee b/spec/atom/global-keymap-spec.coffee index 371119b8a..91858fab8 100644 --- a/spec/atom/global-keymap-spec.coffee +++ b/spec/atom/global-keymap-spec.coffee @@ -28,13 +28,18 @@ describe "GlobalKeymap", -> fragment.on 'deleteChar', deleteCharHandler fragment.on 'insertChar', insertCharHandler + it "adds a 'keystroke' string to the event object", -> + event = keydownEvent('x', altKey: true, metaKey: true) + keymap.handleKeyEvent(event) + expect(event.keystroke).toBe 'alt-meta-x' + describe "when no binding matches the event", -> it "returns true, so the event continues to propagate", -> - expect(keymap.handleKeyEvent(keypressEvent('0', target: fragment[0]))).toBeTruthy() + expect(keymap.handleKeyEvent(keydownEvent('0', target: fragment[0]))).toBeTruthy() describe "when the event's target node matches a selector with a matching binding", -> it "triggers the command event associated with that binding on the target node and returns false", -> - result = keymap.handleKeyEvent(keypressEvent('x', target: fragment[0])) + result = keymap.handleKeyEvent(keydownEvent('x', target: fragment[0])) expect(result).toBe(false) expect(deleteCharHandler).toHaveBeenCalled() expect(insertCharHandler).not.toHaveBeenCalled() @@ -42,18 +47,18 @@ describe "GlobalKeymap", -> deleteCharHandler.reset() fragment.removeClass('command-mode').addClass('insert-mode') - event = keypressEvent('x', target: fragment[0]) + event = keydownEvent('x', target: fragment[0]) keymap.handleKeyEvent(event) expect(deleteCharHandler).not.toHaveBeenCalled() expect(insertCharHandler).toHaveBeenCalled() commandEvent = insertCharHandler.argsForCall[0][0] expect(commandEvent.keyEvent).toBe event - expect(event.char).toBe 'x' + expect(event.keystroke).toBe 'x' describe "when the event's target node *descends* from a selector with a matching binding", -> it "triggers the command event associated with that binding on the target node and returns false", -> target = fragment.find('.child-node')[0] - result = keymap.handleKeyEvent(keypressEvent('x', target: target)) + result = keymap.handleKeyEvent(keydownEvent('x', target: target)) expect(result).toBe(false) expect(deleteCharHandler).toHaveBeenCalled() expect(insertCharHandler).not.toHaveBeenCalled() @@ -61,7 +66,7 @@ describe "GlobalKeymap", -> deleteCharHandler.reset() fragment.removeClass('command-mode').addClass('insert-mode') - keymap.handleKeyEvent(keypressEvent('x', target: target)) + keymap.handleKeyEvent(keydownEvent('x', target: target)) expect(deleteCharHandler).not.toHaveBeenCalled() expect(insertCharHandler).toHaveBeenCalled() @@ -72,7 +77,7 @@ describe "GlobalKeymap", -> fragment.on 'foo', fooHandler target = fragment.find('.grandchild-node')[0] - keymap.handleKeyEvent(keypressEvent('x', target: target)) + keymap.handleKeyEvent(keydownEvent('x', target: target)) expect(fooHandler).toHaveBeenCalled() expect(deleteCharHandler).not.toHaveBeenCalled() expect(insertCharHandler).not.toHaveBeenCalled() @@ -92,7 +97,7 @@ describe "GlobalKeymap", -> fragment.on 'baz', bazHandler target = fragment.find('.grandchild-node')[0] - keymap.handleKeyEvent(keypressEvent('x', target: target)) + keymap.handleKeyEvent(keydownEvent('x', target: target)) expect(fooHandler).not.toHaveBeenCalled() expect(barHandler).not.toHaveBeenCalled() @@ -111,12 +116,12 @@ describe "GlobalKeymap", -> fragment.on 'baz', bazHandler target = fragment.find('.grandchild-node')[0] - keymap.handleKeyEvent(keypressEvent('x', target: target)) + keymap.handleKeyEvent(keydownEvent('x', target: target)) expect(barHandler).toHaveBeenCalled() expect(fooHandler).not.toHaveBeenCalled() - keymap.handleKeyEvent(keypressEvent('y', target: target)) + keymap.handleKeyEvent(keydownEvent('y', target: target)) expect(bazHandler).toHaveBeenCalled() describe ".bindKeys(selector, fnOrMap)", -> @@ -126,7 +131,7 @@ describe "GlobalKeymap", -> keymap.bindKeys '.child-node', handler target = fragment.find('.grandchild-node')[0] - event = keypressEvent('y', target: target) + event = keydownEvent('y', target: target) keymap.handleKeyEvent event expect(handler).toHaveBeenCalledWith(event) @@ -181,4 +186,4 @@ describe "GlobalKeymap", -> keymap.handleKeyEvent(keydownEvent('z', target: target)) expect(fooHandler).toHaveBeenCalled() - + diff --git a/spec/spec-helper.coffee b/spec/spec-helper.coffee index e2dcdbefa..cde8a86d8 100644 --- a/spec/spec-helper.coffee +++ b/spec/spec-helper.coffee @@ -17,9 +17,6 @@ eventPropertiesFromPattern = (pattern) -> delete parsedPattern.key # key doesn't exist on browser-generated key events parsedPattern -window.keypressEvent = (pattern, properties={}) -> - $.Event "keypress", _.extend(eventPropertiesFromPattern(pattern), properties) - window.keydownEvent = (pattern, properties={}) -> $.Event "keydown", _.extend(eventPropertiesFromPattern(pattern), properties) diff --git a/src/atom/global-keymap.coffee b/src/atom/global-keymap.coffee index b5f00891f..b117cbe62 100644 --- a/src/atom/global-keymap.coffee +++ b/src/atom/global-keymap.coffee @@ -18,6 +18,7 @@ class GlobalKeymap @bindKeys(selector, bindings) handleKeyEvent: (event) -> + event.keystroke = @keystrokeStringForEvent(event) currentNode = $(event.target) while currentNode.length candidateBindingSets = @bindingSets.filter (set) -> currentNode.is(set.selector) @@ -33,14 +34,68 @@ class GlobalKeymap true reset: -> - @BindingSets = [] + @bindingSets = [] triggerCommandEvent: (keyEvent, commandName) -> commandEvent = $.Event(commandName) - keyEvent.char = @charForKeyEvent(keyEvent) commandEvent.keyEvent = keyEvent $(keyEvent.target).trigger(commandEvent) - charForKeyEvent: (keyEvent) -> - String.fromCharCode(keyEvent.which).toLowerCase() + keystrokeStringForEvent: (event) -> + if event.type is 'keypress' + char = String.fromCharCode event.which + else + char = @keyCodeToChar[event.which] + + modifiers = '' + if event.altKey and char isnt 'alt' + modifiers += 'alt-' + if event.ctrlKey and char isnt 'ctrl' + modifiers += 'ctrl-' + if event.metaKey and char isnt 'meta' + modifiers += 'meta-' + + if event.shiftKey + if event.type is 'keypress' + "#{modifiers}#{char}" + else if shiftChar = @shiftKeyCodeToChar[event.which] + "#{modifiers}#{shiftChar}" + else if char is 'shift' + "#{modifiers}shift" + else if (char) + "#{modifiers}shift-#{char}" + else + null + else if char + "#{modifiers}#{char}" + else + null + + keyCodeToChar: + 8: 'backspace', 9: 'tab', 13: 'enter', 16: 'shift', 17: 'ctrl', + 18: 'alt', 19: 'pause', 20: 'capslock', 27: 'esc', 32: 'space', + 33: 'pageup', 34: 'pagedown', 35: 'end', 36: 'home', 37: 'left', + 38: 'up', 39: 'right', 40: 'down', 45: 'insert', 46: 'del', + 48: '0', 49: '1', 50: '2', 51: '3', 52: '4', 53: '5', 54: '6', + 55: '7', 56: '8', 57: '9', 65: 'a', 66: 'b', 67: 'c', 68: 'd', + 69: 'e', 70: 'f', 71: 'g', 72: 'h', 73: 'i', 74: 'j', 75: 'k', + 76: 'l', 77: 'm', 78: 'n', 79: 'o', 80: 'p', 81: 'q', 82: 'r', + 83: 's', 84: 't', 85: 'u', 86: 'v', 87: 'w', 88: 'x', 89: 'y', + 90: 'z', 91: 'meta', 93: 'meta', 96: '0', 97: '1', 98: '2', + 99: '3', 100: '4', 101: '5', 102: '6', 103: '7', 104: '8', + 105: '9', 106: '*', 107: '+', 109: '-', 110: '.', 111: '/', + 112: 'f1', 113: 'f2', 114: 'f3', 115: 'f4', 116: 'f5', 117: 'f6', + 118: 'f7', 119: 'f8', 120: 'f9', 121: 'f10', 122: 'f11', + 123: 'f12', 144: 'numlock', 145: 'scroll', 186: ';', 187: '=', + 188: ',', 189: '-', 190: '.', 191: '/', 192: '`', 219: '[', + 220: '\\', 221: ']', 222: '\'' + + shiftKeyCodeToChar: + 48: ')', 49: '!', 50: '@', 51: '#', 52: '$', 53: '%', 54: '^', + 55: '&', 56: '*', 57: '(', 65: 'A', 66: 'B', 67: 'C', 68: 'D', + 69: 'E', 70: 'F', 71: 'G', 72: 'H', 73: 'I', 74: 'J', 75: 'K', + 76: 'L', 77: 'M', 78: 'N', 79: 'O', 80: 'P', 81: 'Q', 82: 'R', + 83: 'S', 84: 'T', 85: 'U', 86: 'V', 87: 'W', 88: 'X', 89: 'Y', + 90: 'Z', 186: ':', 187: '+', 188: '<', 189: '_', 190: '>', + 191: '?', 192: '~', 219: '{', 220: '|', 221: '}', 222: '"'