Skip to content
This repository was archived by the owner on Apr 6, 2018. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 3 additions & 17 deletions lib/motions/general-motions.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -88,15 +88,6 @@ class Motion
moveSelection: (selection, count, options) ->
selection.modifySelection => @moveCursor(selection.cursor, count, options)

ensureCursorIsWithinLine: (cursor) ->
return if @vimState.mode is 'visual' or not cursor.selection.isEmpty()
{goalColumn} = cursor
{row, column} = cursor.getBufferPosition()
lastColumn = cursor.getCurrentLineBufferRange().end.column
if column >= lastColumn - 1
cursor.setBufferPosition([row, Math.max(lastColumn - 1, 0)])
cursor.goalColumn ?= goalColumn

isComplete: -> true

isRecordable: -> false
Expand Down Expand Up @@ -142,9 +133,8 @@ class MoveLeft extends Motion
operatesInclusively: false

moveCursor: (cursor, count=1) ->
_.times count, =>
_.times count, ->
cursor.moveLeft() if not cursor.isAtBeginningOfLine() or settings.wrapLeftRightMotion()
@ensureCursorIsWithinLine(cursor)

class MoveRight extends Motion
operatesInclusively: false
Expand All @@ -159,16 +149,14 @@ class MoveRight extends Motion

cursor.moveRight() unless cursor.isAtEndOfLine()
cursor.moveRight() if wrapToNextLine and cursor.isAtEndOfLine()
@ensureCursorIsWithinLine(cursor)

class MoveUp extends Motion
operatesLinewise: true

moveCursor: (cursor, count=1) ->
_.times count, =>
_.times count, ->
unless cursor.getScreenRow() is 0
cursor.moveUp()
@ensureCursorIsWithinLine(cursor)

class MoveDown extends Motion
operatesLinewise: true
Expand All @@ -177,7 +165,6 @@ class MoveDown extends Motion
_.times count, =>
unless cursor.getScreenRow() is @editor.getLastScreenRow()
cursor.moveDown()
@ensureCursorIsWithinLine(cursor)

class MoveToPreviousWord extends Motion
operatesInclusively: false
Expand Down Expand Up @@ -328,10 +315,9 @@ class MoveToLastCharacterOfLine extends Motion
operatesInclusively: false

moveCursor: (cursor, count=1) ->
_.times count, =>
_.times count, ->
cursor.moveToEndOfLine()
cursor.goalColumn = Infinity
@ensureCursorIsWithinLine(cursor)

class MoveToLastNonblankCharacterOfLineAndDown extends Motion
operatesInclusively: true
Expand Down
56 changes: 37 additions & 19 deletions lib/vim-state.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ class VimState
@activateVisualMode('characterwise') if @mode is 'normal'
, 100)

@subscriptions.add @editor.onDidChangeCursorPosition ({cursor}) => @ensureCursorIsWithinLine(cursor)
@subscriptions.add @editor.onDidAddCursor @ensureCursorIsWithinLine

@editorElement.classList.add("vim-mode")
@setupNormalMode()
if settings.startInInsertMode()
Expand Down Expand Up @@ -212,28 +215,33 @@ class VimState
# it.
pushOperations: (operations) ->
return unless operations?
operations = [operations] unless _.isArray(operations)

for operation in operations
# Motions in visual mode perform their selections.
if @mode is 'visual' and (operation instanceof Motions.Motion or operation instanceof TextObjects.TextObject)
operation.execute = operation.select

# if we have started an operation that responds to canComposeWith check if it can compose
# with the operation we're going to push onto the stack
if (topOp = @topOperation())? and topOp.canComposeWith? and not topOp.canComposeWith(operation)
@resetNormalMode()
@emitter.emit('failed-to-compose')
break
try
@processing = true
operations = [operations] unless _.isArray(operations)

for operation in operations
# Motions in visual mode perform their selections.
if @mode is 'visual' and (operation instanceof Motions.Motion or operation instanceof TextObjects.TextObject)
operation.execute = operation.select

# if we have started an operation that responds to canComposeWith check if it can compose
# with the operation we're going to push onto the stack
if (topOp = @topOperation())? and topOp.canComposeWith? and not topOp.canComposeWith(operation)
@resetNormalMode()
@emitter.emit('failed-to-compose')
break

@opStack.push(operation)
@opStack.push(operation)

# If we've received an operator in visual mode, mark the current
# selection as the motion to operate on.
if @mode is 'visual' and operation instanceof Operators.Operator
@opStack.push(new Motions.CurrentSelection(@editor, this))
# If we've received an operator in visual mode, mark the current
# selection as the motion to operate on.
if @mode is 'visual' and operation instanceof Operators.Operator
@opStack.push(new Motions.CurrentSelection(@editor, this))

@processOpStack()
@processOpStack()
finally
@processing = false
@ensureCursorIsWithinLine(cursor) for cursor in @editor.getCursors()

onDidFailToCompose: (fn) ->
@emitter.on('failed-to-compose', fn)
Expand Down Expand Up @@ -654,6 +662,16 @@ class VimState
text = @getRegister(name)?.text
@editor.insertText(text) if text?

ensureCursorIsWithinLine: (cursor) =>
return if @processing or @mode isnt 'normal'

{goalColumn} = cursor
if cursor.isAtEndOfLine() and not cursor.isAtBeginningOfLine()
@processing = true # to ignore the cursor change (and recursion) caused by the next line
cursor.moveLeft()
@processing = false
cursor.goalColumn = goalColumn

# This uses private APIs and may break if TextBuffer is refactored.
# Package authors - copy and paste this code at your own risk.
getChangesSinceCheckpoint = (buffer, checkpoint) ->
Expand Down
28 changes: 7 additions & 21 deletions spec/motions-spec.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -144,30 +144,21 @@ describe "Motions", ->
keydown('w')
expect(editor.getCursorScreenPosition()).toEqual [2, 0]

# FIXME: The definition of Cursor#getEndOfCurrentWordBufferPosition,
# means that the end of the word can't be the current cursor
# position (even though it is when your cursor is on a new line).
#
# Therefore it picks the end of the next word here (which is [3,3])
# to start looking for the next word, which is also the end of the
# buffer so the cursor never advances.
#
# See atom/vim-mode#3
keydown('w')
expect(editor.getCursorScreenPosition()).toEqual [3, 0]

keydown('w')
expect(editor.getCursorScreenPosition()).toEqual [3, 3]
expect(editor.getCursorScreenPosition()).toEqual [3, 2]

# After cursor gets to the EOF, it should stay there.
# When the cursor gets to the EOF, it should stay there.
keydown('w')
expect(editor.getCursorScreenPosition()).toEqual [3, 3]
expect(editor.getCursorScreenPosition()).toEqual [3, 2]

it "moves the cursor to the end of the word if last word in file", ->
editor.setText("abc")
editor.setCursorScreenPosition([0, 0])
keydown('w')
expect(editor.getCursorScreenPosition()).toEqual([0, 3])
expect(editor.getCursorScreenPosition()).toEqual([0, 2])

describe "as a selection", ->
describe "within a word", ->
Expand Down Expand Up @@ -463,7 +454,7 @@ describe "Motions", ->
editor.setCursorScreenPosition([1, 10])
keydown('y')
keydown('B', shift: true)
expect(vimState.getRegister('"').text).toBe 'xyz-123'
expect(vimState.getRegister('"').text).toBe 'xyz-12' # because cursor is on the `3`

it "doesn't go past the beginning of the file", ->
editor.setCursorScreenPosition([0, 0])
Expand Down Expand Up @@ -965,7 +956,7 @@ describe "Motions", ->
beforeEach -> keydown('G', shift: true)

it "moves the cursor to the last line after whitespace", ->
expect(editor.getCursorScreenPosition()).toEqual [3, 1]
expect(editor.getCursorScreenPosition()).toEqual [3, 0]

describe "as a repeated motion", ->
beforeEach ->
Expand Down Expand Up @@ -1262,14 +1253,9 @@ describe "Motions", ->

it "doesn't move cursor unless next match has exact word ending", ->
editor.setText("abc\n@def\nabc\n@def1\n")
# FIXME: I suspect there is a bug laying around
# Cursor#getEndOfCurrentWordBufferPosition, this function
# is returning '@' as a word, instead of returning the whole
# word '@def', this behavior is avoided in this test, when we
# execute the '*' command when cursor is on character after '@'
# (in this particular example, the 'd' char)
editor.setCursorBufferPosition([1, 1])
keydown("*")
# this is because of the default isKeyword value of vim-mode that includes @
expect(editor.getCursorBufferPosition()).toEqual [1, 0]

# FIXME: This behavior is different from the one found in
Expand Down
4 changes: 2 additions & 2 deletions spec/scroll-spec.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ describe "Scrolling", ->
expect(editor.getCursorBufferPosition()).toEqual [0, 1]
pos10 = zsPos(10)
expect(pos10).toEqual(startPosition)
expect(editor.getCursorBufferPosition()).toEqual [0, 5]
expect(editor.getCursorBufferPosition()).toEqual [0, 4]


describe "the ze keybinding", ->
Expand Down Expand Up @@ -227,4 +227,4 @@ describe "Scrolling", ->
expect(editor.getCursorBufferPosition()).toEqual [0, 1]
pos10 = zePos(10)
expect(pos10).toEqual(startPosition)
expect(editor.getCursorBufferPosition()).toEqual [0, 5]
expect(editor.getCursorBufferPosition()).toEqual [0, 4]
3 changes: 1 addition & 2 deletions spec/vim-state-spec.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -154,8 +154,7 @@ describe "VimState", ->
describe "with content", ->
beforeEach -> editor.setText("012345\n\nabcdef")

# FIXME: See atom/vim-mode#2
xdescribe "on a line with content", ->
describe "on a line with content", ->
beforeEach -> editor.setCursorScreenPosition([0, 6])

it "does not allow the cursor to be placed on the \n character", ->
Expand Down