     else
       @continueBubbling
 
-  handleKeyChar: (event, keyChar) ->
-    bgLog "Handling key #{keyChar}, mode=#{@name}."
-    @advanceKeyState keyChar
-    command = (@keyState.filter (entry) -> entry.command)[0]
-    if command?
-      count = if 0 < @countPrefix then @countPrefix else 1
-      @reset()
-      bgLog "Calling mode=#{@name}, command=#{command.command}, count=#{count}."
-      @commandHandler {command, count}
-    false # Suppress event.
-
+  # This tests whether there is a mapping of keyChar in the current key state.
   keyCharIsMapped: (keyChar) ->
     (mapping for mapping in @keyState when keyChar of mapping)[0]?
 
-  # The next key state is the current mappings matching keyChar plus @keyMapping.
-  advanceKeyState: (keyChar) ->
-    newMappings = (mapping[keyChar] for mapping in @keyState when keyChar of mapping)
-    @keyState = [newMappings..., @keyMapping]
-
-  # Reset the state (as if no keys had been handled), but optionally retaining the count provided.
-  reset: (@countPrefix = 0) ->
-    bgLog "Clearing key queue, set count=#{@countPrefix}."
-    @keyState = [@keyMapping]
+  handleKeyChar: (event, keyChar) ->
+    bgLog "Handling key #{keyChar}, mode=#{@name}."
+    # Advance the key state.  The new key state is the current mappings of keyChar, plus @keyMapping.
+    @keyState = (mapping[keyChar] for mapping in @keyState when keyChar of mapping)
+    @keyState.push @keyMapping
+    command = (mapping for mapping in @keyState when mapping.command)[0]
+    if command?
+      count = if 0 < @countPrefix then @countPrefix else 1
+      bgLog "Calling mode=#{@name}, command=#{command.command}, count=#{count}."
+      @reset()
+      @commandHandler {command, count}
+    false # Suppress event.
 
   isCountKey: (keyChar) ->
     keyChar?.length == 1 and (if 0 < @countPrefix then '0' else '1') <= keyChar <= '9'
 
   # Keystrokes are *never* considered passKeys if the user has begun entering a command.  So, for example, if
-  # 't' is a passKey, then 'gt' and '99t' are neverthless handled as regular keys.
+  # 't' is a passKey, then the "t"-s of 'gt' and '99t' are neverthless handled as regular keys.
   unlessKeyCharIsPassKey: (keyChar, nonPassKeyCallback) ->
     if @countPrefix == 0 and @keyState.length == 1 and keyChar in (@passKeys ? "")
       @stopBubblingAndTrue


  onKeydown: (event) ->
    keyChar = KeyboardUtils.getKeyCharString event
    if KeyboardUtils.isEscape event
      if @countPrefix == 0 and @keyState.length == 1
        @continueBubbling
      else
        @keydownEvents[event.keyCode] = true
        @reset()
        false # Suppress event.
    else if keyChar and @keyCharIsMapped keyChar
      @unlessKeyCharIsPassKey keyChar, =>
        @keydownEvents[event.keyCode] = true
        @handleKeyChar event, keyChar
    else if keyChar
      @continueBubbling
    else if (keyChar = KeyboardUtils.getKeyChar event) and (@keyCharIsMapped(keyChar) or @isCountKey keyChar)
      # It looks like we will be handling a subsequent keypress event, so suppress propagation of this event
      # to prevent triggering page event listeners (e.g. Google instant Search).
      @unlessKeyCharIsPassKey keyChar, =>
        @keydownEvents[event.keyCode] = true
        DomUtils.suppressPropagation event

# Example key mapping (@keyMapping):
#   i:
#     command: "enterInsertMode", ... # This is a registryEntry object (as too are the other commands).
#   g:
#     g:
#       command: "scrollToTop", ...
#     t:
#       command: "nextTab", ...
#
# This key-mapping structure is generated by Commands.generateKeyStateMapping() and may be arbitrarily deep.
# Observe that @keyMapping["g"] is itself also a valid key mapping.  At any point, the key state (@keyState)
# consists of a (non-empty) list of such mappings.

class KeyHandlerMode extends Mode
  keydownEvents: {}
  setKeyMapping: (@keyMapping) -> @reset()
  setPassKeys: (@passKeys) -> @reset()

  # Reset the key state, optionally retaining the count provided.
  reset: (@countPrefix = 0) ->
    bgLog "Clearing key state, set count=#{@countPrefix}."
    @keyState = [@keyMapping]

  constructor: (options) ->
    @commandHandler = options.commandHandler ? (->)
