Single-digit numeric prefixes can repeat commands in vim-mode

Vim mode has an operator stack. Every time an operator is pushed to the
stack, we ask if it is complete. If it's complete, we compose it with
the operator below it, then pop that operator if its complete. When no
operators remain on the stack, we call execute the final composed
operator. So far we only have DeleteChar (x) and NumericPrefix
operators.
This commit is contained in:
Nathan Sobo
2012-01-11 22:02:47 -08:00
parent 671d862441
commit 2c2423f985
7 changed files with 101 additions and 17 deletions

View File

@@ -61,7 +61,7 @@ class Editor extends Template
getCursor: ->
@getAceSession().getSelection().getCursor()
delete: ->
deleteChar: ->
@aceEditor.remove 'right'

View File

@@ -19,10 +19,20 @@ class GlobalKeymap
candidateBindingSets.sort (a, b) -> b.specificity - a.specificity
for bindingSet in candidateBindingSets
if command = bindingSet.commandForEvent(event)
$(event.target).trigger(command)
@triggerCommandEvent(event, command)
return false
currentNode = currentNode.parent()
true
reset: ->
@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()

View File

@@ -0,0 +1,25 @@
_ = require 'underscore'
module.exports =
NumericPrefix: class
count: null
operatorToRepeat: null
complete: null
constructor: (@count) ->
@complete = false
compose: (@operatorToRepeat) ->
@complete = true
isComplete: -> @complete
execute: (editor) ->
_.times @count, => @operatorToRepeat.execute(editor)
DeleteChar: class
execute: (editor) ->
editor.deleteChar()
isComplete: -> true

View File

@@ -1,20 +1,23 @@
_ = require 'underscore'
$ = require 'jquery'
{ NumericPrefix, DeleteChar } = require 'vim-mode-operators'
module.exports =
class VimMode
editor: null
opStack: null
constructor: (@editor) ->
atom.bindKeys '.command-mode'
'i': 'insert-mode:activate'
'x': 'command-mode:delete'
atom.bindKeys '.insert-mode'
'<esc>': 'command-mode:activate'
@opStack = []
atom.bindKeys '.command-mode', @commandModeBindings()
atom.bindKeys '.insert-mode', '<esc>': 'command-mode:activate'
@editor.addClass('command-mode')
@editor.on 'insert-mode:activate', => @activateInsertMode()
@editor.on 'command-mode:activate', => @activateCommandMode()
@editor.on 'command-mode:delete', => @delete()
@editor.on 'command-mode:delete-char', => @deleteChar()
@editor.on 'command-mode:numeric-prefix', (e) => @numericPrefix(e)
activateInsertMode: ->
@editor.removeClass('command-mode')
@@ -24,5 +27,33 @@ class VimMode
@editor.removeClass('insert-mode')
@editor.addClass('command-mode')
delete: ->
@editor.delete()
deleteChar: ->
@pushOperator(new DeleteChar)
numericPrefix: (e) ->
@pushOperator(new NumericPrefix(e.keyEvent.char))
commandModeBindings: ->
bindings =
'i': 'insert-mode:activate'
'x': 'command-mode:delete-char'
for i in [0..9]
bindings[i] = 'command-mode:numeric-prefix'
bindings
pushOperator: (op) ->
@opStack.push(op)
@processOpStack()
processOpStack: ->
return unless @topOperator().isComplete()
poppedOperator = @opStack.pop()
if @opStack.length
@topOperator().compose(poppedOperator)
@processOpStack()
else
poppedOperator.execute(@editor)
topOperator: ->
_.last @opStack