diff --git a/docs/overview.md b/docs/overview.md index 1954e08c..c59176ca 100644 --- a/docs/overview.md +++ b/docs/overview.md @@ -3,6 +3,9 @@ * There are only currently two modes, normal mode and insert mode. * Motions have repeat support, `d3w` will delete three words. * Insert mode can be entered using `i`, `I`, `a`, `A`, `o`, or `O`. + * The following commands are supported in insert mode: + * `ctrl-y` to copy the character right above the cursor + * `ctrl-e` to copy the character right below the cursor (**disabled by default**, see note 1 below) * Replace mode can be entered using `R` * Limitations: * If repeating with `.` gets a bit confused (e.g. by multiple cursors or when more than one line was typed), please report it with steps to reproduce if you can. @@ -16,3 +19,13 @@ * What Doesn't Exist: * default buffer doesn't yet save on delete operations. * Setting `wrapLeftRightMotion` acts like VIM's whichwrap=h,l,<,> + + +#### Notes + +1. To enable the VIM key binding `ctrl-e` to copy the character right below the cursor, please put this in your `keymap.cson`: + +``` +'atom-text-editor.vim-mode.insert-mode': + 'ctrl-e': 'vim-mode:copy-from-line-below' +``` diff --git a/keymaps/vim-mode.cson b/keymaps/vim-mode.cson index 1a3d9624..b6ee64b5 100644 --- a/keymaps/vim-mode.cson +++ b/keymaps/vim-mode.cson @@ -13,6 +13,9 @@ 'atom-text-editor.vim-mode.insert-mode': 'ctrl-w': 'editor:delete-to-beginning-of-word' 'ctrl-u': 'editor:delete-to-beginning-of-line' + 'ctrl-y': 'vim-mode:copy-from-line-above' + # disabled for compatibility with the common binding for going to the end of the line + # 'ctrl-e': 'vim-mode:copy-from-line-below' 'ctrl-r a': 'vim-mode:insert-mode-put' 'ctrl-r b': 'vim-mode:insert-mode-put' diff --git a/lib/insert-mode.coffee b/lib/insert-mode.coffee new file mode 100644 index 00000000..d600e7e2 --- /dev/null +++ b/lib/insert-mode.coffee @@ -0,0 +1,19 @@ +copyCharacterFromAbove = (editor, vimState) -> + editor.transact -> + for cursor in editor.getCursors() + {row, column} = cursor.getScreenPosition() + continue if row is 0 + range = [[row-1, column], [row-1, column+1]] + cursor.selection.insertText(editor.getTextInBufferRange(editor.bufferRangeForScreenRange(range))) + +copyCharacterFromBelow = (editor, vimState) -> + editor.transact -> + for cursor in editor.getCursors() + {row, column} = cursor.getScreenPosition() + range = [[row+1, column], [row+1, column+1]] + cursor.selection.insertText(editor.getTextInBufferRange(editor.bufferRangeForScreenRange(range))) + +module.exports = { + copyCharacterFromAbove, + copyCharacterFromBelow +} diff --git a/lib/vim-state.coffee b/lib/vim-state.coffee index 26153cc7..b5c71557 100644 --- a/lib/vim-state.coffee +++ b/lib/vim-state.coffee @@ -7,6 +7,7 @@ settings = require './settings' Operators = require './operators/index' Prefixes = require './prefixes' Motions = require './motions/index' +InsertMode = require './insert-mode' TextObjects = require './text-objects' Utils = require './utils' @@ -73,6 +74,8 @@ class VimState 'undo': (e) => @undo(e) 'replace-mode-backspace': => @replaceModeUndo() 'insert-mode-put': (e) => @insertRegister(@registerName(e)) + 'copy-from-line-above': => InsertMode.copyCharacterFromAbove(@editor, this) + 'copy-from-line-below': => InsertMode.copyCharacterFromBelow(@editor, this) @registerOperationCommands 'activate-insert-mode': => new Operators.Insert(@editor, this) diff --git a/spec/insert-mode-spec.coffee b/spec/insert-mode-spec.coffee new file mode 100644 index 00000000..061cb50e --- /dev/null +++ b/spec/insert-mode-spec.coffee @@ -0,0 +1,73 @@ +helpers = require './spec-helper' + +describe "Insert mode commands", -> + [editor, editorElement, vimState] = [] + + beforeEach -> + vimMode = atom.packages.loadPackage('vim-mode') + vimMode.activateResources() + + helpers.getEditorElement (element) -> + editorElement = element + editor = editorElement.getModel() + vimState = editorElement.vimState + vimState.activateNormalMode() + vimState.resetNormalMode() + + keydown = (key, options={}) -> + options.element ?= editorElement + helpers.keydown(key, options) + + describe "Copy from line above/below", -> + beforeEach -> + editor.setText("12345\n\nabcd\nefghi") + editor.setCursorBufferPosition([1, 0]) + editor.addCursorAtBufferPosition([3, 0]) + keydown 'i' + + describe "the ctrl-y command", -> + it "copies from the line above", -> + keydown 'y', ctrl: true + expect(editor.getText()).toBe '12345\n1\nabcd\naefghi' + + editor.insertText ' ' + keydown 'y', ctrl: true + expect(editor.getText()).toBe '12345\n1 3\nabcd\na cefghi' + + it "does nothing if there's nothing above the cursor", -> + editor.insertText 'fill' + keydown 'y', ctrl: true + expect(editor.getText()).toBe '12345\nfill5\nabcd\nfillefghi' + + keydown 'y', ctrl: true + expect(editor.getText()).toBe '12345\nfill5\nabcd\nfillefghi' + + it "does nothing on the first line", -> + editor.setCursorBufferPosition([0, 2]) + editor.addCursorAtBufferPosition([3, 2]) + editor.insertText 'a' + expect(editor.getText()).toBe '12a345\n\nabcd\nefaghi' + keydown 'y', ctrl: true + expect(editor.getText()).toBe '12a345\n\nabcd\nefadghi' + + describe "the ctrl-e command", -> + beforeEach -> + atom.keymaps.add "test", + 'atom-text-editor.vim-mode.insert-mode': + 'ctrl-e': 'vim-mode:copy-from-line-below' + + it "copies from the line below", -> + keydown 'e', ctrl: true + expect(editor.getText()).toBe '12345\na\nabcd\nefghi' + + editor.insertText ' ' + keydown 'e', ctrl: true + expect(editor.getText()).toBe '12345\na c\nabcd\n efghi' + + it "does nothing if there's nothing below the cursor", -> + editor.insertText 'foo' + keydown 'e', ctrl: true + expect(editor.getText()).toBe '12345\nfood\nabcd\nfooefghi' + + keydown 'e', ctrl: true + expect(editor.getText()).toBe '12345\nfood\nabcd\nfooefghi'