X-Git-Url: https://git.r.bdr.sh/rbdr/dotfiles/blobdiff_plain/24c7594d62d8d7fbbcdb64b11ce4adc5d8e6991a..f32a7c8693a80f2c04c0512eda9ec92fc67d890b:/atom/packages/vim-mode/lib/motions/general-motions.coffee?ds=sidebyside diff --git a/atom/packages/vim-mode/lib/motions/general-motions.coffee b/atom/packages/vim-mode/lib/motions/general-motions.coffee index 47a3cc7..b5cd627 100644 --- a/atom/packages/vim-mode/lib/motions/general-motions.coffee +++ b/atom/packages/vim-mode/lib/motions/general-motions.coffee @@ -11,7 +11,7 @@ class MotionError @name = 'Motion Error' class Motion - operatesInclusively: true + operatesInclusively: false operatesLinewise: false constructor: (@editor, @vimState) -> @@ -20,7 +20,9 @@ class Motion value = for selection in @editor.getSelections() if @isLinewise() @moveSelectionLinewise(selection, count, options) - else if @isInclusive() + else if @vimState.mode is 'visual' + @moveSelectionVisual(selection, count, options) + else if @operatesInclusively @moveSelectionInclusively(selection, count, options) else @moveSelection(selection, count, options) @@ -61,10 +63,27 @@ class Motion selection.setBufferRange([[newStartRow, 0], [newEndRow + 1, 0]]) moveSelectionInclusively: (selection, count, options) -> + return @moveSelectionVisual(selection, count, options) unless selection.isEmpty() + + selection.modifySelection => + @moveCursor(selection.cursor, count, options) + return if selection.isEmpty() + + if selection.isReversed() + # for backward motion, add the original starting character of the motion + {start, end} = selection.getBufferRange() + selection.setBufferRange([start, [end.row, end.column + 1]]) + else + # for forward motion, add the ending character of the motion + selection.cursor.moveRight() + + moveSelectionVisual: (selection, count, options) -> selection.modifySelection => range = selection.getBufferRange() [oldStart, oldEnd] = [range.start, range.end] + # in visual mode, atom cursor is after the last selected character, + # so here put cursor in the expected place for the following motion wasEmpty = selection.isEmpty() wasReversed = selection.isReversed() unless wasEmpty or wasReversed @@ -72,6 +91,7 @@ class Motion @moveCursor(selection.cursor, count, options) + # put cursor back after the last character so it is also selected isEmpty = selection.isEmpty() isReversed = selection.isReversed() unless isEmpty or isReversed @@ -80,23 +100,25 @@ class Motion range = selection.getBufferRange() [newStart, newEnd] = [range.start, range.end] + # if we reversed or emptied a normal selection + # we need to select again the last character deselected above the motion if (isReversed or isEmpty) and not (wasReversed or wasEmpty) selection.setBufferRange([newStart, [newEnd.row, oldStart.column + 1]]) + + # if we re-reversed a reversed non-empty selection, + # we need to keep the last character of the old selection selected if wasReversed and not wasEmpty and not isReversed - selection.setBufferRange([[newStart.row, oldEnd.column - 1], newEnd]) + selection.setBufferRange([[oldEnd.row, oldEnd.column - 1], newEnd]) + + # keep a single-character selection non-reversed + range = selection.getBufferRange() + [newStart, newEnd] = [range.start, range.end] + if selection.isReversed() and newStart.row is newEnd.row and newStart.column + 1 is newEnd.column + selection.setBufferRange(range, reversed: false) 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 @@ -107,21 +129,41 @@ class Motion else @operatesLinewise - isInclusive: -> - @vimState.mode is 'visual' or @operatesInclusively - class CurrentSelection extends Motion constructor: (@editor, @vimState) -> super(@editor, @vimState) - @selection = @editor.getSelectedBufferRanges() + @lastSelectionRange = @editor.getSelectedBufferRange() + @wasLinewise = @isLinewise() execute: (count=1) -> _.times(count, -> true) select: (count=1) -> - @editor.setSelectedBufferRanges(@selection) + # in visual mode, the current selections are already there + # if we're not in visual mode, we are repeating some operation and need to re-do the selections + unless @vimState.mode is 'visual' + if @wasLinewise + @selectLines() + else + @selectCharacters() + _.times(count, -> true) + selectLines: -> + lastSelectionExtent = @lastSelectionRange.getExtent() + for selection in @editor.getSelections() + cursor = selection.cursor.getBufferPosition() + selection.setBufferRange [[cursor.row, 0], [cursor.row + lastSelectionExtent.row, 0]] + return + + selectCharacters: -> + lastSelectionExtent = @lastSelectionRange.getExtent() + for selection in @editor.getSelections() + {start} = selection.getBufferRange() + newEnd = start.traverse(lastSelectionExtent) + selection.setBufferRange([start, newEnd]) + return + # Public: Generic class for motions that require extra input class MotionWithInput extends Motion constructor: (@editor, @vimState) -> @@ -139,16 +181,11 @@ class MotionWithInput extends Motion @complete = true 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 - moveCursor: (cursor, count=1) -> _.times count, => wrapToNextLine = settings.wrapLeftRightMotion() @@ -159,16 +196,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 @@ -177,18 +212,13 @@ class MoveDown extends Motion _.times count, => unless cursor.getScreenRow() is @editor.getLastScreenRow() cursor.moveDown() - @ensureCursorIsWithinLine(cursor) class MoveToPreviousWord extends Motion - operatesInclusively: false - moveCursor: (cursor, count=1) -> _.times count, -> cursor.moveToBeginningOfWord() class MoveToPreviousWholeWord extends Motion - operatesInclusively: false - moveCursor: (cursor, count=1) -> _.times count, => cursor.moveToBeginningOfWord() @@ -205,7 +235,6 @@ class MoveToPreviousWholeWord extends Motion class MoveToNextWord extends Motion wordRegex: null - operatesInclusively: false moveCursor: (cursor, count=1, options) -> _.times count, => @@ -236,6 +265,7 @@ class MoveToNextWholeWord extends MoveToNextWord wordRegex: WholeWordOrEmptyLineRegex class MoveToEndOfWord extends Motion + operatesInclusively: true wordRegex: null moveCursor: (cursor, count=1) -> @@ -260,8 +290,6 @@ class MoveToEndOfWholeWord extends MoveToEndOfWord wordRegex: WholeWordRegex class MoveToNextParagraph extends Motion - operatesInclusively: false - moveCursor: (cursor, count=1) -> _.times count, -> cursor.moveToBeginningOfNextParagraph() @@ -284,8 +312,6 @@ class MoveToAbsoluteLine extends MoveToLine cursor.moveToEndOfLine() if cursor.getBufferColumn() is 0 class MoveToRelativeLine extends MoveToLine - operatesLinewise: true - moveCursor: (cursor, count=1) -> {row, column} = cursor.getBufferPosition() cursor.setBufferPosition([row + (count - 1), 0]) @@ -300,15 +326,11 @@ class MoveToScreenLine extends MoveToLine cursor.setScreenPosition([@getDestinationRow(count), 0]) class MoveToBeginningOfLine extends Motion - operatesInclusively: false - moveCursor: (cursor, count=1) -> _.times count, -> cursor.moveToBeginningOfLine() class MoveToFirstCharacterOfLine extends Motion - operatesInclusively: false - moveCursor: (cursor, count=1) -> _.times count, -> cursor.moveToBeginningOfLine() @@ -316,22 +338,18 @@ class MoveToFirstCharacterOfLine extends Motion class MoveToFirstCharacterOfLineAndDown extends Motion operatesLinewise: true - operatesInclusively: true - moveCursor: (cursor, count=0) -> + moveCursor: (cursor, count=1) -> _.times count-1, -> cursor.moveDown() cursor.moveToBeginningOfLine() cursor.moveToFirstCharacterOfLine() 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 @@ -354,7 +372,6 @@ class MoveToLastNonblankCharacterOfLineAndDown extends Motion class MoveToFirstCharacterOfLineUp extends Motion operatesLinewise: true - operatesInclusively: true moveCursor: (cursor, count=1) -> _.times count, -> @@ -398,7 +415,7 @@ class MoveToBottomOfScreen extends MoveToScreenLine lastScreenRow - offset class MoveToMiddleOfScreen extends MoveToScreenLine - getDestinationRow: (count) -> + getDestinationRow: -> firstScreenRow = @editorElement.getFirstVisibleScreenRow() lastScreenRow = @editorElement.getLastVisibleScreenRow() height = lastScreenRow - firstScreenRow @@ -428,7 +445,7 @@ class ScrollKeepingCursor extends MoveToLine {row, column} = @editor.getCursorScreenPosition() @currentFirstScreenRow - @previousFirstScreenRow + row - scrollScreen: (count = 1) -> + scrollScreen: (count=1) -> @previousFirstScreenRow = @editorElement.getFirstVisibleScreenRow() destination = @scrollDestination(count) @editor.setScrollTop(destination)