From 2a80a72d6449ad80287dae60bb52efd2d36cc775 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 10 Jan 2012 23:54:14 -0800 Subject: [PATCH] Bind keydown on root view to catch events that aren't handled by ace Also fixed a bug where the GlobalKeymap wasn't returning true when it matched a binding, which caused key events to be processed twice when they bubbled out of the editor and hit the root view. --- spec/atom/editor-spec.coffee | 6 ------ spec/atom/global-keymap-spec.coffee | 10 ++++++---- spec/atom/root-view-spec.coffee | 27 ++++++++++++++++++--------- src/atom/editor.coffee | 2 +- src/atom/global-keymap.coffee | 2 +- src/atom/root-view.coffee | 4 ++-- 6 files changed, 28 insertions(+), 23 deletions(-) diff --git a/spec/atom/editor-spec.coffee b/spec/atom/editor-spec.coffee index 3294c7d3b..8c7bed58a 100644 --- a/spec/atom/editor-spec.coffee +++ b/spec/atom/editor-spec.coffee @@ -161,12 +161,6 @@ describe "Editor", -> describe "when onTextInput is called on the aceEditor (triggered by an input event)", -> it "does not call handleKeyEvent on the key event handler, because there is no event", -> editor.keyEventHandler = handler - editor.aceEditor.onTextInput("x", false) - expect(handler.handleKeyEvent).not.toHaveBeenCalled() - - - - diff --git a/spec/atom/global-keymap-spec.coffee b/spec/atom/global-keymap-spec.coffee index 911563bd3..ecc083cb5 100644 --- a/spec/atom/global-keymap-spec.coffee +++ b/spec/atom/global-keymap-spec.coffee @@ -30,8 +30,9 @@ describe "GlobalKeymap", -> fragment.on 'insertChar', insertCharHandler 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", -> - keymap.handleKeyEvent(keypressEvent('x', target: fragment[0])) + it "triggers the command event associated with that binding on the target node and returns true", -> + result = keymap.handleKeyEvent(keypressEvent('x', target: fragment[0])) + expect(result).toBeTruthy() expect(deleteCharHandler).toHaveBeenCalled() expect(insertCharHandler).not.toHaveBeenCalled() @@ -47,9 +48,10 @@ describe "GlobalKeymap", -> expect(keymap.handleKeyEvent(keypressEvent('0', target: fragment[0]))).toBe(false) 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", -> + it "triggers the command event associated with that binding on the target node and returns true", -> target = fragment.find('.child-node')[0] - keymap.handleKeyEvent(keypressEvent('x', target: target)) + result = keymap.handleKeyEvent(keypressEvent('x', target: target)) + expect(result).toBeTruthy() expect(deleteCharHandler).toHaveBeenCalled() expect(insertCharHandler).not.toHaveBeenCalled() diff --git a/spec/atom/root-view-spec.coffee b/spec/atom/root-view-spec.coffee index 72d65ccf6..38c26b278 100644 --- a/spec/atom/root-view-spec.coffee +++ b/spec/atom/root-view-spec.coffee @@ -81,15 +81,24 @@ describe "RootView", -> rootView.fileFinder.select() expect(rootView.editor.buffer.url).toBe(project.url + firstLi.text()) - describe "when a key is typed in the editor that has a binding in the keymap", -> - it "triggers the key binding's command as an event and does not insert a character", -> - rootView.globalKeymap.bindKeys('.editor', 'x': 'fooCommand') + describe "global keymap wiring", -> + commandHandler = null + beforeEach -> + commandHandler = jasmine.createSpy('commandHandler') + rootView.on('foo-command', commandHandler) + rootView.globalKeymap.bindKeys('*', 'x': 'foo-command') - fooCommandHandler = jasmine.createSpy('fooCommandHandler') - rootView.editor.on('fooCommand', fooCommandHandler) + describe "when a key is typed in the editor that has a binding in the keymap", -> + it "triggers the key binding's command as an event and stops its propagation", -> + event = keydownEvent 'x', target: rootView.find('textarea')[0] + spyOn event, 'stopPropagation' + rootView.editor.aceEditor.onCommandKey event, 0, event.which + expect(commandHandler).toHaveBeenCalled() + expect(event.stopPropagation).toHaveBeenCalled() - event = keydownEvent 'x', target: rootView.find('textarea')[0] - rootView.editor.aceEditor.onCommandKey event, 0, event.which - - expect(fooCommandHandler).toHaveBeenCalled() + describe "when a keydown event is triggered on the RootView (not originating from Ace)", -> + it "triggers matching keybindings for that event", -> + event = keydownEvent 'x', target: rootView[0] + rootView.trigger(event) + expect(commandHandler).toHaveBeenCalled() diff --git a/src/atom/editor.coffee b/src/atom/editor.coffee index 32bf01f11..1cc8f8c4d 100644 --- a/src/atom/editor.coffee +++ b/src/atom/editor.coffee @@ -38,7 +38,7 @@ class Editor extends Template @aceEditor.setKeyboardHandler handleKeyboard: (data, hashId, keyString, keyCode, event) => if event and @keyEventHandler?.handleKeyEvent(event) - {command: {exec: ->}} + { command: { exec: -> }} else null diff --git a/src/atom/global-keymap.coffee b/src/atom/global-keymap.coffee index e472c3237..093f3d8e2 100644 --- a/src/atom/global-keymap.coffee +++ b/src/atom/global-keymap.coffee @@ -20,6 +20,6 @@ class GlobalKeymap for bindingSet in candidateBindingSets if command = bindingSet.commandForEvent(event) $(event.target).trigger(command) - return + return true currentNode = currentNode.parent() false diff --git a/src/atom/root-view.coffee b/src/atom/root-view.coffee index 89abdd7d5..10627feb0 100644 --- a/src/atom/root-view.coffee +++ b/src/atom/root-view.coffee @@ -22,7 +22,7 @@ class RootView extends Template initialize: ({url}) -> @globalKeymap = new GlobalKeymap - + @on 'keydown', (e) => @globalKeymap.handleKeyEvent(e) @editor.keyEventHandler = @globalKeymap @globalKeymap.bindKeys '*' @@ -30,7 +30,7 @@ class RootView extends Template 'meta-w': 'close' 'meta-t': 'toggle-file-finder' - @on 'toggle-file-finder', (e) => @toggleFileFinder() + @on 'toggle-file-finder', => @toggleFileFinder() if url @project = new Project(fs.directory(url))