4 constructor: ({ @window, @commands, @modes, @esc }) ->
14 for { name } in @commands
15 @storage.commands[name] = {}
18 @storage.modes[name] = {}
20 enterMode: (mode, args) ->
21 # Note: `args` is an array of arguments to be passed to the mode's `enter` method. We cannot use
22 # `args...`, since that destroys the `this` context for the mode's `enter` method.
24 @modes[mode].enter(this, @storage.modes[mode], args)
27 for name, mode of @modes
28 mode.onEnterNormalMode?(this, @storage.modes[name])
31 handleKeyDown: (event, @lastKeyStr) ->
33 @keys.push(@lastKeyStr)
34 if @mode == MODE_NORMAL or @lastKeyStr == @esc
35 { match, exact, command, index } = @searchForCommand(@keys, @commands)
37 @keys = @keys[index..]
38 command.func(this, @storage.commands[command.name]) if exact
39 return @lastKeyStr != @esc
41 ok = @modes[@mode].handleKeyDown(this, @storage.modes[@mode], event)
48 handleKeyPress: (event) ->
49 return @lastKeyStr != @esc and @suppress
51 handleKeyUp: (event) ->
54 return @lastKeyStr != @esc and suppress
56 # Intentionally taking `keys` and `commands` as parameters (instead of simply using `@keys` and
57 # `@commands`), so that the method can be reused by custom modes.
58 searchForCommand: (keys, commands) ->
59 for index in [0...keys.length] by 1
60 str = keys[index..].join(',')
61 for command in commands
62 for key in command.keys()
63 if key.startsWith(str) and command.enabled()
64 return {match: true, exact: (key == str), command, index}
67 # What is minimally required for a command
69 constructor: (@keyValues, @name) ->
70 keys: -> return @keyValues
71 enabled: -> return true