From 42047a5c16adcf26d636292053ae577c72114486 Mon Sep 17 00:00:00 2001 From: Jacek Kopecky Date: Fri, 5 Jun 2015 20:27:01 +0100 Subject: [PATCH 1/3] fix #565 : cursor motion in insert mode should affect `.` --- keymaps/vim-mode.cson | 4 ++++ lib/operators/input.coffee | 10 ++++++++++ lib/vim-state.coffee | 15 +++++++++++++++ spec/operators-spec.coffee | 23 +++++++++++++++++++++++ 4 files changed, 52 insertions(+) diff --git a/keymaps/vim-mode.cson b/keymaps/vim-mode.cson index e90de203..8f19e319 100644 --- a/keymaps/vim-mode.cson +++ b/keymaps/vim-mode.cson @@ -13,6 +13,10 @@ 'atom-text-editor.vim-mode.insert-mode': 'ctrl-w': 'editor:delete-to-beginning-of-word' 'ctrl-u': 'editor:delete-to-beginning-of-line' + 'left': 'vim-mode:move-left-insert' + 'right': 'vim-mode:move-right-insert' + 'up': 'vim-mode:move-up-insert' + 'down': 'vim-mode:move-down-insert' 'atom-text-editor.vim-mode:not(.insert-mode)': 'h': 'vim-mode:move-left' diff --git a/lib/operators/input.coffee b/lib/operators/input.coffee index 3e5db4a3..746274f1 100644 --- a/lib/operators/input.coffee +++ b/lib/operators/input.coffee @@ -28,6 +28,15 @@ class Insert extends Operator inputOperator: -> true +# an insert operation following cursor motion in insert mode can be cancelled +# and forgotten like it never happened +class InsertCancellable extends Insert + + confirmTransaction: (transaction) -> + super + if @typedText?.length is 0 + @vimState.history.shift() if @vimState.history[0] is this + class InsertAfter extends Insert execute: -> @editor.moveRight() unless @editor.getLastCursor().isAtEndOfLine() @@ -189,6 +198,7 @@ module.exports = { InsertAtBeginningOfLine, InsertAboveWithNewline, InsertBelowWithNewline, + InsertCancellable, Change, Substitute, SubstituteLine diff --git a/lib/vim-state.coffee b/lib/vim-state.coffee index 1c2ad45f..9ac83981 100644 --- a/lib/vim-state.coffee +++ b/lib/vim-state.coffee @@ -102,6 +102,10 @@ class VimState 'move-up': => new Motions.MoveUp(@editor, this) 'move-down': => new Motions.MoveDown(@editor, this) 'move-right': => new Motions.MoveRight(@editor, this) + 'move-left-insert': => @interruptInsertMode(); [new Motions.MoveLeft(@editor, this), new Operators.InsertCancellable(@editor, this)] + 'move-up-insert': => @interruptInsertMode(); [new Motions.MoveUp(@editor, this), new Operators.InsertCancellable(@editor, this)] + 'move-down-insert': => @interruptInsertMode(); [new Motions.MoveDown(@editor, this), new Operators.InsertCancellable(@editor, this)] + 'move-right-insert': => @interruptInsertMode(); [new Motions.MoveRight(@editor, this), new Operators.InsertCancellable(@editor, this)] 'move-to-next-word': => new Motions.MoveToNextWord(@editor, this) 'move-to-next-whole-word': => new Motions.MoveToNextWholeWord(@editor, this) 'move-to-end-of-word': => new Motions.MoveToEndOfWord(@editor, this) @@ -412,6 +416,17 @@ class VimState for cursor in @editor.getCursors() cursor.moveLeft() unless cursor.isAtBeginningOfLine() + interruptInsertMode: -> + return unless @mode is 'insert' + @editor.groupChangesSinceCheckpoint(@insertionCheckpoint) + changes = getChangesSinceCheckpoint(@editor.buffer, @insertionCheckpoint) + item = @inputOperator(@history[0]) + @insertionCheckpoint = null + if item? + item.confirmChanges(changes) + @setInsertionCheckpoint() + + deactivateVisualMode: -> return unless @mode is 'visual' for selection in @editor.getSelections() diff --git a/spec/operators-spec.coffee b/spec/operators-spec.coffee index 93a040f7..e4ffc913 100644 --- a/spec/operators-spec.coffee +++ b/spec/operators-spec.coffee @@ -1573,6 +1573,17 @@ describe "Operators", -> keydown '.' expect(editor.getText()).toBe "abababccc123\nabababccc4567" + it "stores for repeating only the last batch of characters", -> + keydown 'i' + editor.insertText("abc") + atom.commands.dispatch editorElement, 'vim-mode:move-left-insert' + editor.insertText("de") + keydown 'escape' + expect(editor.getText()).toBe "abdec123\nabdec4567" + + keydown '.' + expect(editor.getText()).toBe "abddeec123\nabddeec4567" + describe 'the a keybinding', -> beforeEach -> editor.setText('') @@ -1596,6 +1607,18 @@ describe "Operators", -> expect(editor.getText()).toBe "abcabc" expect(editor.getCursorScreenPosition()).toEqual [0, 5] + it "stores for repeating only the last batch of characters, repeats as insert", -> + keydown 'a' + editor.insertText("abc") + atom.commands.dispatch editorElement, 'vim-mode:move-left-insert' + editor.insertText("de") + keydown 'escape' + expect(editor.getText()).toBe "abdec" + expect(editor.getCursorScreenPosition()).toEqual [0, 3] + keydown '.' + expect(editor.getText()).toBe "abddeec" + expect(editor.getCursorScreenPosition()).toEqual [0, 4] + describe "the ctrl-a/ctrl-x keybindings", -> beforeEach -> editor.setText('123\nab45\ncd-67ef\nab-5\na-bcdef') From 8e2cc8dd9c5c1fcd4e6a459fa2b845a5e70c7075 Mon Sep 17 00:00:00 2001 From: Jacek Kopecky Date: Tue, 7 Jul 2015 23:23:54 +0100 Subject: [PATCH 2/3] fix bronson/vim-mode-next#4 --- lib/motions/general-motions.coffee | 9 ++--- spec/operators-spec.coffee | 57 ++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 4 deletions(-) diff --git a/lib/motions/general-motions.coffee b/lib/motions/general-motions.coffee index 47a3cc78..fe9d5215 100644 --- a/lib/motions/general-motions.coffee +++ b/lib/motions/general-motions.coffee @@ -89,7 +89,7 @@ class Motion selection.modifySelection => @moveCursor(selection.cursor, count, options) ensureCursorIsWithinLine: (cursor) -> - return if @vimState.mode is 'visual' or not cursor.selection.isEmpty() + return if @vimState.mode is 'visual' or @vimState.mode is 'insert' or not cursor.selection.isEmpty() {goalColumn} = cursor {row, column} = cursor.getBufferPosition() lastColumn = cursor.getCurrentLineBufferRange().end.column @@ -153,9 +153,10 @@ class MoveRight extends Motion _.times count, => wrapToNextLine = settings.wrapLeftRightMotion() - # when the motion is combined with an operator, we will only wrap to the next line - # if we are already at the end of the line (after the last character) - wrapToNextLine = false if @vimState.mode is 'operator-pending' and not cursor.isAtEndOfLine() + # when the motion is in insert mode or is combined with an operator, + # we will only wrap to the next line if we are already + # at the end of the line (after the last character) + wrapToNextLine = false if (@vimState.mode is 'insert' or @vimState.mode is 'operator-pending') and not cursor.isAtEndOfLine() cursor.moveRight() unless cursor.isAtEndOfLine() cursor.moveRight() if wrapToNextLine and cursor.isAtEndOfLine() diff --git a/spec/operators-spec.coffee b/spec/operators-spec.coffee index e4ffc913..5da8e6a2 100644 --- a/spec/operators-spec.coffee +++ b/spec/operators-spec.coffee @@ -1584,6 +1584,63 @@ describe "Operators", -> keydown '.' expect(editor.getText()).toBe "abddeec123\nabddeec4567" + describe "without wrapLeftRightMotion", -> + it "handles right motions correctly", -> + editor.setCursorBufferPosition [0, 0] + keydown 'i' + atom.commands.dispatch editorElement, 'vim-mode:move-right-insert' + expect(editor.getCursorBufferPosition()).toEqual [0, 1] + atom.commands.dispatch editorElement, 'vim-mode:move-right-insert' + expect(editor.getCursorBufferPosition()).toEqual [0, 2] + atom.commands.dispatch editorElement, 'vim-mode:move-right-insert' + expect(editor.getCursorBufferPosition()).toEqual [0, 3] + atom.commands.dispatch editorElement, 'vim-mode:move-right-insert' + expect(editor.getCursorBufferPosition()).toEqual [0, 3] + editor.insertText "de" + expect(editor.getText()).toBe "123de\n4567" + + it "handles left motions correctly", -> + editor.setCursorBufferPosition [1, 1] + keydown 'i' + atom.commands.dispatch editorElement, 'vim-mode:move-left-insert' + expect(editor.getCursorBufferPosition()).toEqual [1, 0] + atom.commands.dispatch editorElement, 'vim-mode:move-left-insert' + expect(editor.getCursorBufferPosition()).toEqual [1, 0] + editor.insertText "de" + expect(editor.getText()).toBe "123\nde4567" + + describe "with wrapLeftRightMotion", -> + beforeEach -> + atom.config.set('vim-mode.wrapLeftRightMotion', true) + + it "handles right motions correctly", -> + editor.setCursorBufferPosition [0, 0] + keydown 'i' + atom.commands.dispatch editorElement, 'vim-mode:move-right-insert' + expect(editor.getCursorBufferPosition()).toEqual [0, 1] + atom.commands.dispatch editorElement, 'vim-mode:move-right-insert' + expect(editor.getCursorBufferPosition()).toEqual [0, 2] + atom.commands.dispatch editorElement, 'vim-mode:move-right-insert' + expect(editor.getCursorBufferPosition()).toEqual [0, 3] + atom.commands.dispatch editorElement, 'vim-mode:move-right-insert' + expect(editor.getCursorBufferPosition()).toEqual [1, 0] + editor.insertText "de" + expect(editor.getText()).toBe "123\nde4567" + + it "handles left motions correctly", -> + editor.setCursorBufferPosition [1, 2] + keydown 'i' + atom.commands.dispatch editorElement, 'vim-mode:move-left-insert' + expect(editor.getCursorBufferPosition()).toEqual [1, 1] + atom.commands.dispatch editorElement, 'vim-mode:move-left-insert' + expect(editor.getCursorBufferPosition()).toEqual [1, 0] + atom.commands.dispatch editorElement, 'vim-mode:move-left-insert' + expect(editor.getCursorBufferPosition()).toEqual [0, 3] + atom.commands.dispatch editorElement, 'vim-mode:move-left-insert' + expect(editor.getCursorBufferPosition()).toEqual [0, 2] + editor.insertText "de" + expect(editor.getText()).toBe "12de3\n4567" + describe 'the a keybinding', -> beforeEach -> editor.setText('') From 00b321e1daa124901a78561a23377a54dc8d5d98 Mon Sep 17 00:00:00 2001 From: Jacek Kopecky Date: Fri, 10 Jul 2015 19:48:33 +0100 Subject: [PATCH 3/3] fixed interaction with autocomplete --- keymaps/vim-mode.cson | 2 ++ 1 file changed, 2 insertions(+) diff --git a/keymaps/vim-mode.cson b/keymaps/vim-mode.cson index 8f19e319..cc96af99 100644 --- a/keymaps/vim-mode.cson +++ b/keymaps/vim-mode.cson @@ -15,6 +15,8 @@ 'ctrl-u': 'editor:delete-to-beginning-of-line' 'left': 'vim-mode:move-left-insert' 'right': 'vim-mode:move-right-insert' + +'atom-text-editor.vim-mode.insert-mode:not(.autocomplete-active)': 'up': 'vim-mode:move-up-insert' 'down': 'vim-mode:move-down-insert'