aboutsummaryrefslogtreecommitdiff
path: root/atom/packages/vim-mode/lib/operators
diff options
context:
space:
mode:
authorBen Beltran <ben@nsovocal.com>2015-07-10 11:12:25 -0500
committerBen Beltran <ben@nsovocal.com>2015-07-10 11:12:25 -0500
commit24c7594d62d8d7fbbcdb64b11ce4adc5d8e6991a (patch)
treeded312222bb108923da1820ba40b04d710d20e5b /atom/packages/vim-mode/lib/operators
parenteb786e82d170e2abc351a432ade616d6ecdeeb6b (diff)
Adds atom
Diffstat (limited to 'atom/packages/vim-mode/lib/operators')
-rw-r--r--atom/packages/vim-mode/lib/operators/general-operators.coffee255
-rw-r--r--atom/packages/vim-mode/lib/operators/increase-operators.coffee57
-rw-r--r--atom/packages/vim-mode/lib/operators/indent-operators.coffee28
-rw-r--r--atom/packages/vim-mode/lib/operators/index.coffee14
-rw-r--r--atom/packages/vim-mode/lib/operators/input.coffee231
-rw-r--r--atom/packages/vim-mode/lib/operators/put-operator.coffee73
-rw-r--r--atom/packages/vim-mode/lib/operators/replace-operator.coffee50
7 files changed, 708 insertions, 0 deletions
diff --git a/atom/packages/vim-mode/lib/operators/general-operators.coffee b/atom/packages/vim-mode/lib/operators/general-operators.coffee
new file mode 100644
index 0000000..dd99256
--- /dev/null
+++ b/atom/packages/vim-mode/lib/operators/general-operators.coffee
@@ -0,0 +1,255 @@
+_ = require 'underscore-plus'
+{Point, Range} = require 'atom'
+{ViewModel} = require '../view-models/view-model'
+Utils = require '../utils'
+settings = require '../settings'
+
+class OperatorError
+ constructor: (@message) ->
+ @name = 'Operator Error'
+
+class Operator
+ vimState: null
+ motion: null
+ complete: null
+ selectOptions: null
+
+ # selectOptions - The options object to pass through to the motion when
+ # selecting.
+ constructor: (@editor, @vimState, {@selectOptions}={}) ->
+ @complete = false
+
+ # Public: Determines when the command can be executed.
+ #
+ # Returns true if ready to execute and false otherwise.
+ isComplete: -> @complete
+
+ # Public: Determines if this command should be recorded in the command
+ # history for repeats.
+ #
+ # Returns true if this command should be recorded.
+ isRecordable: -> true
+
+ # Public: Marks this as ready to execute and saves the motion.
+ #
+ # motion - The motion used to select what to operate on.
+ #
+ # Returns nothing.
+ compose: (motion) ->
+ if not motion.select
+ throw new OperatorError('Must compose with a motion')
+
+ @motion = motion
+ @complete = true
+
+ canComposeWith: (operation) -> operation.select?
+
+ # Public: Preps text and sets the text register
+ #
+ # Returns nothing
+ setTextRegister: (register, text) ->
+ if @motion?.isLinewise?()
+ type = 'linewise'
+ if text[-1..] isnt '\n'
+ text += '\n'
+ else
+ type = Utils.copyType(text)
+ @vimState.setRegister(register, {text, type}) unless text is ''
+
+# Public: Generic class for an operator that requires extra input
+class OperatorWithInput extends Operator
+ constructor: (@editor, @vimState) ->
+ @editor = @editor
+ @complete = false
+
+ canComposeWith: (operation) -> operation.characters? or operation.select?
+
+ compose: (operation) ->
+ if operation.select?
+ @motion = operation
+ if operation.characters?
+ @input = operation
+ @complete = true
+
+#
+# It deletes everything selected by the following motion.
+#
+class Delete extends Operator
+ register: null
+ allowEOL: null
+
+ # allowEOL - Determines whether the cursor should be allowed to rest on the
+ # end of line character or not.
+ constructor: (@editor, @vimState, {@allowEOL, @selectOptions}={}) ->
+ @complete = false
+ @selectOptions ?= {}
+ @selectOptions.requireEOL ?= true
+ @register = settings.defaultRegister()
+
+ # Public: Deletes the text selected by the given motion.
+ #
+ # count - The number of times to execute.
+ #
+ # Returns nothing.
+ execute: (count) ->
+ if _.contains(@motion.select(count, @selectOptions), true)
+ @setTextRegister(@register, @editor.getSelectedText())
+ for selection in @editor.getSelections()
+ selection.deleteSelectedText()
+ for cursor in @editor.getCursors()
+ if @motion.isLinewise?()
+ cursor.skipLeadingWhitespace()
+ else
+ cursor.moveLeft() if cursor.isAtEndOfLine() and not cursor.isAtBeginningOfLine()
+
+ @vimState.activateCommandMode()
+
+#
+# It toggles the case of everything selected by the following motion
+#
+class ToggleCase extends Operator
+ constructor: (@editor, @vimState, {@complete, @selectOptions}={}) ->
+
+ execute: (count=1) ->
+ if @motion?
+ if _.contains(@motion.select(count, @selectOptions), true)
+ @editor.replaceSelectedText {}, (text) ->
+ text.split('').map((char) ->
+ lower = char.toLowerCase()
+ if char is lower
+ char.toUpperCase()
+ else
+ lower
+ ).join('')
+ else
+ @editor.transact =>
+ for cursor in @editor.getCursors()
+ point = cursor.getBufferPosition()
+ lineLength = @editor.lineTextForBufferRow(point.row).length
+ cursorCount = Math.min(count, lineLength - point.column)
+
+ _.times cursorCount, =>
+ point = cursor.getBufferPosition()
+ range = Range.fromPointWithDelta(point, 0, 1)
+ char = @editor.getTextInBufferRange(range)
+
+ if char is char.toLowerCase()
+ @editor.setTextInBufferRange(range, char.toUpperCase())
+ else
+ @editor.setTextInBufferRange(range, char.toLowerCase())
+
+ cursor.moveRight() unless point.column >= lineLength - 1
+
+ @vimState.activateCommandMode()
+
+#
+# In visual mode or after `g` with a motion, it makes the selection uppercase
+#
+class UpperCase extends Operator
+ constructor: (@editor, @vimState, {@selectOptions}={}) ->
+ @complete = false
+
+ execute: (count=1) ->
+ if _.contains(@motion.select(count, @selectOptions), true)
+ @editor.replaceSelectedText {}, (text) ->
+ text.toUpperCase()
+
+ @vimState.activateCommandMode()
+
+#
+# In visual mode or after `g` with a motion, it makes the selection lowercase
+#
+class LowerCase extends Operator
+ constructor: (@editor, @vimState, {@selectOptions}={}) ->
+ @complete = false
+
+ execute: (count=1) ->
+ if _.contains(@motion.select(count, @selectOptions), true)
+ @editor.replaceSelectedText {}, (text) ->
+ text.toLowerCase()
+
+ @vimState.activateCommandMode()
+
+#
+# It copies everything selected by the following motion.
+#
+class Yank extends Operator
+ register: null
+
+ constructor: (@editor, @vimState, {@allowEOL, @selectOptions}={}) ->
+ @register = settings.defaultRegister()
+
+ # Public: Copies the text selected by the given motion.
+ #
+ # count - The number of times to execute.
+ #
+ # Returns nothing.
+ execute: (count) ->
+ originalPositions = @editor.getCursorBufferPositions()
+ if _.contains(@motion.select(count), true)
+ text = @editor.getSelectedText()
+ startPositions = _.pluck(@editor.getSelectedBufferRanges(), "start")
+ newPositions = for originalPosition, i in originalPositions
+ if startPositions[i] and (@vimState.mode is 'visual' or not @motion.isLinewise?())
+ Point.min(startPositions[i], originalPositions[i])
+ else
+ originalPosition
+ else
+ text = ''
+ newPositions = originalPositions
+
+ @setTextRegister(@register, text)
+
+ @editor.setSelectedBufferRanges(newPositions.map (p) -> new Range(p, p))
+ @vimState.activateCommandMode()
+
+#
+# It combines the current line with the following line.
+#
+class Join extends Operator
+ constructor: (@editor, @vimState, {@selectOptions}={}) -> @complete = true
+
+ # Public: Combines the current with the following lines
+ #
+ # count - The number of times to execute.
+ #
+ # Returns nothing.
+ execute: (count=1) ->
+ @editor.transact =>
+ _.times count, =>
+ @editor.joinLines()
+ @vimState.activateCommandMode()
+
+#
+# Repeat the last operation
+#
+class Repeat extends Operator
+ constructor: (@editor, @vimState, {@selectOptions}={}) -> @complete = true
+
+ isRecordable: -> false
+
+ execute: (count=1) ->
+ @editor.transact =>
+ _.times count, =>
+ cmd = @vimState.history[0]
+ cmd?.execute()
+#
+# It creates a mark at the current cursor position
+#
+class Mark extends OperatorWithInput
+ constructor: (@editor, @vimState, {@selectOptions}={}) ->
+ super(@editor, @vimState)
+ @viewModel = new ViewModel(this, class: 'mark', singleChar: true, hidden: true)
+
+ # Public: Creates the mark in the specified mark register (from user input)
+ # at the current position
+ #
+ # Returns nothing.
+ execute: ->
+ @vimState.setMark(@input.characters, @editor.getCursorBufferPosition())
+ @vimState.activateCommandMode()
+
+module.exports = {
+ Operator, OperatorWithInput, OperatorError, Delete, ToggleCase,
+ UpperCase, LowerCase, Yank, Join, Repeat, Mark
+}
diff --git a/atom/packages/vim-mode/lib/operators/increase-operators.coffee b/atom/packages/vim-mode/lib/operators/increase-operators.coffee
new file mode 100644
index 0000000..e62f781
--- /dev/null
+++ b/atom/packages/vim-mode/lib/operators/increase-operators.coffee
@@ -0,0 +1,57 @@
+{Operator} = require './general-operators'
+{Range} = require 'atom'
+settings = require '../settings'
+
+#
+# It increases or decreases the next number on the line
+#
+class Increase extends Operator
+ step: 1
+
+ constructor: ->
+ super
+ @complete = true
+ @numberRegex = new RegExp(settings.numberRegex())
+
+ execute: (count=1) ->
+ @editor.transact =>
+ increased = false
+ for cursor in @editor.getCursors()
+ if @increaseNumber(count, cursor) then increased = true
+ atom.beep() unless increased
+
+ increaseNumber: (count, cursor) ->
+ # find position of current number, adapted from from SearchCurrentWord
+ cursorPosition = cursor.getBufferPosition()
+ numEnd = cursor.getEndOfCurrentWordBufferPosition(wordRegex: @numberRegex, allowNext: false)
+
+ if numEnd.column is cursorPosition.column
+ # either we don't have a current number, or it ends on cursor, i.e. precedes it, so look for the next one
+ numEnd = cursor.getEndOfCurrentWordBufferPosition(wordRegex: @numberRegex, allowNext: true)
+ return if numEnd.row isnt cursorPosition.row # don't look beyond the current line
+ return if numEnd.column is cursorPosition.column # no number after cursor
+
+ cursor.setBufferPosition numEnd
+ numStart = cursor.getBeginningOfCurrentWordBufferPosition(wordRegex: @numberRegex, allowPrevious: false)
+
+ range = new Range(numStart, numEnd)
+
+ # parse number, increase/decrease
+ number = parseInt(@editor.getTextInBufferRange(range), 10)
+ if isNaN(number)
+ cursor.setBufferPosition(cursorPosition)
+ return
+
+ number += @step*count
+
+ # replace current number with new
+ newValue = String(number)
+ @editor.setTextInBufferRange(range, newValue, normalizeLineEndings: false)
+
+ cursor.setBufferPosition(row: numStart.row, column: numStart.column-1+newValue.length)
+ return true
+
+class Decrease extends Increase
+ step: -1
+
+module.exports = {Increase, Decrease}
diff --git a/atom/packages/vim-mode/lib/operators/indent-operators.coffee b/atom/packages/vim-mode/lib/operators/indent-operators.coffee
new file mode 100644
index 0000000..0ad37e0
--- /dev/null
+++ b/atom/packages/vim-mode/lib/operators/indent-operators.coffee
@@ -0,0 +1,28 @@
+{Operator} = require './general-operators'
+
+class AdjustIndentation extends Operator
+ execute: (count=1) ->
+ mode = @vimState.mode
+ @motion.select(count)
+ {start} = @editor.getSelectedBufferRange()
+
+ @indent()
+
+ if mode isnt 'visual'
+ @editor.setCursorBufferPosition([start.row, 0])
+ @editor.moveToFirstCharacterOfLine()
+ @vimState.activateCommandMode()
+
+class Indent extends AdjustIndentation
+ indent: ->
+ @editor.indentSelectedRows()
+
+class Outdent extends AdjustIndentation
+ indent: ->
+ @editor.outdentSelectedRows()
+
+class Autoindent extends AdjustIndentation
+ indent: ->
+ @editor.autoIndentSelectedRows()
+
+module.exports = {Indent, Outdent, Autoindent}
diff --git a/atom/packages/vim-mode/lib/operators/index.coffee b/atom/packages/vim-mode/lib/operators/index.coffee
new file mode 100644
index 0000000..575def3
--- /dev/null
+++ b/atom/packages/vim-mode/lib/operators/index.coffee
@@ -0,0 +1,14 @@
+_ = require 'underscore-plus'
+IndentOperators = require './indent-operators'
+IncreaseOperators = require './increase-operators'
+Put = require './put-operator'
+InputOperators = require './input'
+Replace = require './replace-operator'
+Operators = require './general-operators'
+
+Operators.Put = Put
+Operators.Replace = Replace
+_.extend(Operators, IndentOperators)
+_.extend(Operators, IncreaseOperators)
+_.extend(Operators, InputOperators)
+module.exports = Operators
diff --git a/atom/packages/vim-mode/lib/operators/input.coffee b/atom/packages/vim-mode/lib/operators/input.coffee
new file mode 100644
index 0000000..3821045
--- /dev/null
+++ b/atom/packages/vim-mode/lib/operators/input.coffee
@@ -0,0 +1,231 @@
+{Operator, Delete} = require './general-operators'
+_ = require 'underscore-plus'
+settings = require '../settings'
+
+# The operation for text entered in input mode. Broadly speaking, input
+# operators manage an undo transaction and set a @typingCompleted variable when
+# it's done. When the input operation is completed, the typingCompleted variable
+# tells the operation to repeat itself instead of enter insert mode.
+class Insert extends Operator
+ standalone: true
+
+ isComplete: -> @standalone or super
+
+ confirmChanges: (changes) ->
+ bundler = new TransactionBundler(changes, @editor)
+ @typedText = bundler.buildInsertText()
+
+ execute: ->
+ if @typingCompleted
+ return unless @typedText? and @typedText.length > 0
+ @editor.insertText(@typedText, normalizeLineEndings: true)
+ for cursor in @editor.getCursors()
+ cursor.moveLeft() unless cursor.isAtBeginningOfLine()
+ else
+ @vimState.activateInsertMode()
+ @typingCompleted = true
+ return
+
+ inputOperator: -> true
+
+class InsertAfter extends Insert
+ execute: ->
+ @editor.moveRight() unless @editor.getLastCursor().isAtEndOfLine()
+ super
+
+class InsertAfterEndOfLine extends Insert
+ execute: ->
+ @editor.moveToEndOfLine()
+ super
+
+class InsertAtBeginningOfLine extends Insert
+ execute: ->
+ @editor.moveToBeginningOfLine()
+ @editor.moveToFirstCharacterOfLine()
+ super
+
+class InsertAboveWithNewline extends Insert
+ execute: (count=1) ->
+ @vimState.setInsertionCheckpoint() unless @typingCompleted
+ @editor.insertNewlineAbove()
+ @editor.getLastCursor().skipLeadingWhitespace()
+
+ if @typingCompleted
+ # We'll have captured the inserted newline, but we want to do that
+ # over again by hand, or differing indentations will be wrong.
+ @typedText = @typedText.trimLeft()
+ return super
+
+ @vimState.activateInsertMode()
+ @typingCompleted = true
+
+class InsertBelowWithNewline extends Insert
+ execute: (count=1) ->
+ @vimState.setInsertionCheckpoint() unless @typingCompleted
+ @editor.insertNewlineBelow()
+ @editor.getLastCursor().skipLeadingWhitespace()
+
+ if @typingCompleted
+ # We'll have captured the inserted newline, but we want to do that
+ # over again by hand, or differing indentations will be wrong.
+ @typedText = @typedText.trimLeft()
+ return super
+
+ @vimState.activateInsertMode()
+ @typingCompleted = true
+
+#
+# Delete the following motion and enter insert mode to replace it.
+#
+class Change extends Insert
+ standalone: false
+ register: null
+
+ constructor: (@editor, @vimState, {@selectOptions}={}) ->
+ @register = settings.defaultRegister()
+
+ # Public: Changes the text selected by the given motion.
+ #
+ # count - The number of times to execute.
+ #
+ # Returns nothing.
+ execute: (count) ->
+ # If we've typed, we're being repeated. If we're being repeated,
+ # undo transactions are already handled.
+ @vimState.setInsertionCheckpoint() unless @typingCompleted
+
+ if _.contains(@motion.select(count, excludeWhitespace: true), true)
+ @setTextRegister(@register, @editor.getSelectedText())
+ if @motion.isLinewise?()
+ @editor.insertNewline()
+ @editor.moveLeft()
+ else
+ for selection in @editor.getSelections()
+ selection.deleteSelectedText()
+
+ return super if @typingCompleted
+
+ @vimState.activateInsertMode()
+ @typingCompleted = true
+
+class Substitute extends Insert
+ register: null
+
+ constructor: (@editor, @vimState, {@selectOptions}={}) ->
+ @register = settings.defaultRegister()
+
+ execute: (count=1) ->
+ @vimState.setInsertionCheckpoint() unless @typingCompleted
+ _.times count, =>
+ @editor.selectRight()
+ @setTextRegister(@register, @editor.getSelectedText())
+ @editor.delete()
+
+ if @typingCompleted
+ @typedText = @typedText.trimLeft()
+ return super
+
+ @vimState.activateInsertMode()
+ @typingCompleted = true
+
+class SubstituteLine extends Insert
+ register: null
+
+ constructor: (@editor, @vimState, {@selectOptions}={}) ->
+ @register = settings.defaultRegister()
+
+ execute: (count=1) ->
+ @vimState.setInsertionCheckpoint() unless @typingCompleted
+ @editor.moveToBeginningOfLine()
+ _.times count, =>
+ @editor.selectToEndOfLine()
+ @editor.selectRight()
+ @setTextRegister(@register, @editor.getSelectedText())
+ @editor.delete()
+ @editor.insertNewlineAbove()
+ @editor.getLastCursor().skipLeadingWhitespace()
+
+ if @typingCompleted
+ @typedText = @typedText.trimLeft()
+ return super
+
+ @vimState.activateInsertMode()
+ @typingCompleted = true
+
+# Takes a transaction and turns it into a string of what was typed.
+# This class is an implementation detail of Insert
+class TransactionBundler
+ constructor: (@changes, @editor) ->
+ @start = null
+ @end = null
+
+ buildInsertText: ->
+ @addChange(change) for change in @changes
+ if @start?
+ @editor.getTextInBufferRange [@start, @end]
+ else
+ ""
+
+ addChange: (change) ->
+ return unless change.newRange?
+ if @isRemovingFromPrevious(change)
+ @subtractRange change.oldRange
+ if @isAddingWithinPrevious(change)
+ @addRange change.newRange
+
+ isAddingWithinPrevious: (change) ->
+ return false unless @isAdding(change)
+
+ return true if @start is null
+
+ @start.isLessThanOrEqual(change.newRange.start) and
+ @end.isGreaterThanOrEqual(change.newRange.start)
+
+ isRemovingFromPrevious: (change) ->
+ return false unless @isRemoving(change) and @start?
+
+ @start.isLessThanOrEqual(change.oldRange.start) and
+ @end.isGreaterThanOrEqual(change.oldRange.end)
+
+ isAdding: (change) ->
+ change.newText.length > 0
+
+ isRemoving: (change) ->
+ change.oldText.length > 0
+
+ addRange: (range) ->
+ if @start is null
+ {@start, @end} = range
+ return
+
+ rows = range.end.row - range.start.row
+
+ if (range.start.row is @end.row)
+ cols = range.end.column - range.start.column
+ else
+ cols = 0
+
+ @end = @end.translate [rows, cols]
+
+ subtractRange: (range) ->
+ rows = range.end.row - range.start.row
+
+ if (range.end.row is @end.row)
+ cols = range.end.column - range.start.column
+ else
+ cols = 0
+
+ @end = @end.translate [-rows, -cols]
+
+
+module.exports = {
+ Insert,
+ InsertAfter,
+ InsertAfterEndOfLine,
+ InsertAtBeginningOfLine,
+ InsertAboveWithNewline,
+ InsertBelowWithNewline,
+ Change,
+ Substitute,
+ SubstituteLine
+}
diff --git a/atom/packages/vim-mode/lib/operators/put-operator.coffee b/atom/packages/vim-mode/lib/operators/put-operator.coffee
new file mode 100644
index 0000000..63151f1
--- /dev/null
+++ b/atom/packages/vim-mode/lib/operators/put-operator.coffee
@@ -0,0 +1,73 @@
+_ = require 'underscore-plus'
+{Operator} = require './general-operators'
+settings = require '../settings'
+
+module.exports =
+#
+# It pastes everything contained within the specifed register
+#
+class Put extends Operator
+ register: null
+
+ constructor: (@editor, @vimState, {@location, @selectOptions}={}) ->
+ @location ?= 'after'
+ @complete = true
+ @register = settings.defaultRegister()
+
+ # Public: Pastes the text in the given register.
+ #
+ # count - The number of times to execute.
+ #
+ # Returns nothing.
+ execute: (count=1) ->
+ {text, type} = @vimState.getRegister(@register) or {}
+ return unless text
+
+ textToInsert = _.times(count, -> text).join('')
+
+ selection = @editor.getSelectedBufferRange()
+ if selection.isEmpty()
+ # Clean up some corner cases on the last line of the file
+ if type is 'linewise'
+ textToInsert = textToInsert.replace(/\n$/, '')
+ if @location is 'after' and @onLastRow()
+ textToInsert = "\n#{textToInsert}"
+ else
+ textToInsert = "#{textToInsert}\n"
+
+ if @location is 'after'
+ if type is 'linewise'
+ if @onLastRow()
+ @editor.moveToEndOfLine()
+
+ originalPosition = @editor.getCursorScreenPosition()
+ originalPosition.row += 1
+ else
+ @editor.moveDown()
+ else
+ unless @onLastColumn()
+ @editor.moveRight()
+
+ if type is 'linewise' and not originalPosition?
+ @editor.moveToBeginningOfLine()
+ originalPosition = @editor.getCursorScreenPosition()
+
+ @editor.insertText(textToInsert)
+
+ if originalPosition?
+ @editor.setCursorScreenPosition(originalPosition)
+ @editor.moveToFirstCharacterOfLine()
+
+ @vimState.activateCommandMode()
+ if type isnt 'linewise'
+ @editor.moveLeft()
+
+ # Private: Helper to determine if the editor is currently on the last row.
+ #
+ # Returns true on the last row and false otherwise.
+ onLastRow: ->
+ {row, column} = @editor.getCursorBufferPosition()
+ row is @editor.getBuffer().getLastRow()
+
+ onLastColumn: ->
+ @editor.getLastCursor().isAtEndOfLine()
diff --git a/atom/packages/vim-mode/lib/operators/replace-operator.coffee b/atom/packages/vim-mode/lib/operators/replace-operator.coffee
new file mode 100644
index 0000000..52157d0
--- /dev/null
+++ b/atom/packages/vim-mode/lib/operators/replace-operator.coffee
@@ -0,0 +1,50 @@
+_ = require 'underscore-plus'
+{OperatorWithInput} = require './general-operators'
+{ViewModel} = require '../view-models/view-model'
+{Range} = require 'atom'
+
+module.exports =
+class Replace extends OperatorWithInput
+ constructor: (@editor, @vimState, {@selectOptions}={}) ->
+ super(@editor, @vimState)
+ @viewModel = new ViewModel(this, class: 'replace', hidden: true, singleChar: true, defaultText: '\n')
+
+ execute: (count=1) ->
+ if @input.characters is ""
+ # replace canceled
+
+ if @vimState.mode is "visual"
+ @vimState.resetVisualMode()
+ else
+ @vimState.activateCommandMode()
+
+ return
+
+ @editor.transact =>
+ if @motion?
+ if _.contains(@motion.select(), true)
+ @editor.replaceSelectedText null, (text) =>
+ text.replace(/./g, @input.characters)
+ for selection in @editor.getSelections()
+ point = selection.getBufferRange().start
+ selection.setBufferRange(Range.fromPointWithDelta(point, 0, 0))
+ else
+ for cursor in @editor.getCursors()
+ pos = cursor.getBufferPosition()
+ currentRowLength = @editor.lineTextForBufferRow(pos.row).length
+ continue unless currentRowLength - pos.column >= count
+
+ _.times count, =>
+ point = cursor.getBufferPosition()
+ @editor.setTextInBufferRange(Range.fromPointWithDelta(point, 0, 1), @input.characters)
+ cursor.moveRight()
+ cursor.setBufferPosition(pos)
+
+ # Special case: when replaced with a newline move to the start of the
+ # next row.
+ if @input.characters is "\n"
+ _.times count, =>
+ @editor.moveDown()
+ @editor.moveToFirstCharacterOfLine()
+
+ @vimState.activateCommandMode()