diff --git a/spec/keymap-spec.coffee b/spec/keymap-spec.coffee
index 798795f5b..0e659be11 100644
--- a/spec/keymap-spec.coffee
+++ b/spec/keymap-spec.coffee
@@ -8,9 +8,11 @@ describe "Keymap", ->
fragment = null
keymap = null
resourcePath = atom.getLoadSettings().resourcePath
+ configDirPath = null
beforeEach ->
- keymap = new Keymap({configDirPath: atom.getConfigDirPath(), resourcePath})
+ configDirPath = temp.mkdirSync('atom')
+ keymap = new Keymap({configDirPath, resourcePath})
fragment = $ """
@@ -19,6 +21,9 @@ describe "Keymap", ->
"""
+ afterEach ->
+ keymap.destroy()
+
describe ".handleKeyEvent(event)", ->
deleteCharHandler = null
insertCharHandler = null
@@ -348,3 +353,19 @@ describe "Keymap", ->
el = $$ -> @div class: 'brown'
bindings = keymap.keyBindingsForCommandMatchingElement('cultivate', el)
expect(bindings).toHaveLength 0
+
+ describe "when the user keymap file is changed", ->
+ it "is reloaded", ->
+ keymapFilePath = path.join(configDirPath, "keymap.cson")
+ fs.writeFileSync(keymapFilePath, '"body": "ctrl-l": "core:move-left"')
+ keymap.loadUserKeymap()
+
+ spyOn(keymap, 'loadUserKeymap').andCallThrough()
+ fs.writeFileSync(keymapFilePath, "'body': 'ctrl-l': 'core:move-right'")
+
+ waitsFor ->
+ keymap.loadUserKeymap.callCount > 0
+
+ runs ->
+ keyBinding = keymap.keyBindingsForKeystroke('ctrl-l')[0]
+ expect(keyBinding.command).toBe 'core:move-right'
diff --git a/src/atom.coffee b/src/atom.coffee
index 90c7049c1..8a7b005d2 100644
--- a/src/atom.coffee
+++ b/src/atom.coffee
@@ -286,6 +286,7 @@ class Atom extends Model
@workspaceView = null
@project.destroy()
@windowEventHandler?.unsubscribe()
+ @keymap.destroy()
@windowState = null
# Private:
diff --git a/src/keymap.coffee b/src/keymap.coffee
index f9568c41d..555a0df33 100644
--- a/src/keymap.coffee
+++ b/src/keymap.coffee
@@ -4,6 +4,7 @@ fs = require 'fs-plus'
path = require 'path'
CSON = require 'season'
KeyBinding = require './key-binding'
+File = require './file'
{Emitter} = require 'emissary'
Modifiers = ['alt', 'control', 'ctrl', 'shift', 'cmd']
@@ -28,6 +29,9 @@ class Keymap
constructor: ({@resourcePath, @configDirPath})->
@keyBindings = []
+ destroy: ->
+ @unwatchUserKeymap()
+
# Public: Returns an array of all {KeyBinding}s.
getKeyBindings: ->
_.clone(@keyBindings)
@@ -118,9 +122,20 @@ class Keymap
@loadDirectory(path.join(@resourcePath, 'keymaps'))
@emit('bundled-keymaps-loaded')
+ userKeymapPath: ->
+ CSON.resolve(path.join(@configDirPath, 'keymap'))
+
+ unwatchUserKeymap: ->
+ return unless keymapPath = @userKeymapPath()
+ @remove(keymapPath)
+ @userKeymapFile?.off()
+
loadUserKeymap: ->
- userKeymapPath = CSON.resolve(path.join(@configDirPath, 'keymap'))
- @load(userKeymapPath) if userKeymapPath
+ return unless keymapPath = @userKeymapPath()
+ @unwatchUserKeymap()
+ @load(keymapPath)
+ @userKeymapFile = new File(keymapPath)
+ @userKeymapFile.on 'contents-changed', => @loadUserKeymap()
loadDirectory: (directoryPath) ->
@load(filePath) for filePath in fs.listSync(directoryPath, ['.cson', '.json'])