From 24c7594d62d8d7fbbcdb64b11ce4adc5d8e6991a Mon Sep 17 00:00:00 2001 From: Ben Beltran Date: Fri, 10 Jul 2015 11:12:25 -0500 Subject: Adds atom --- atom/packages/vim-mode/spec/operators-spec.coffee | 1748 +++++++++++++++++++++ 1 file changed, 1748 insertions(+) create mode 100644 atom/packages/vim-mode/spec/operators-spec.coffee (limited to 'atom/packages/vim-mode/spec/operators-spec.coffee') diff --git a/atom/packages/vim-mode/spec/operators-spec.coffee b/atom/packages/vim-mode/spec/operators-spec.coffee new file mode 100644 index 0000000..a41edb3 --- /dev/null +++ b/atom/packages/vim-mode/spec/operators-spec.coffee @@ -0,0 +1,1748 @@ +helpers = require './spec-helper' +settings = require '../lib/settings' + +describe "Operators", -> + [editor, editorElement, vimState] = [] + + beforeEach -> + vimMode = atom.packages.loadPackage('vim-mode') + vimMode.activateResources() + + helpers.getEditorElement (element) -> + editorElement = element + editor = editorElement.getModel() + vimState = editorElement.vimState + vimState.activateCommandMode() + vimState.resetCommandMode() + + keydown = (key, options={}) -> + options.element ?= editorElement + helpers.keydown(key, options) + + commandModeInputKeydown = (key, opts = {}) -> + editor.commandModeInputView.editorElement.getModel().setText(key) + + describe "cancelling operations", -> + it "does not throw an error even if no operation is pending", -> + # cancel operation pushes an empty input operation + # doing this without a pending operation throws an exception + expect(-> vimState.pushOperations(new Input(''))).toThrow() + + # make sure commandModeInputView is created + keydown('/') + expect(vimState.isOperatorPending()).toBe true + editor.commandModeInputView.viewModel.cancel() + + expect(vimState.isOperatorPending()).toBe false + expect(-> editor.commandModeInputView.viewModel.cancel()).not.toThrow() + + describe "the x keybinding", -> + describe "on a line with content", -> + describe "without vim-mode.wrapLeftRightMotion", -> + beforeEach -> + editor.setText("abc\n012345\n\nxyz") + editor.setCursorScreenPosition([1, 4]) + + it "deletes a character", -> + keydown('x') + expect(editor.getText()).toBe 'abc\n01235\n\nxyz' + expect(editor.getCursorScreenPosition()).toEqual [1, 4] + expect(vimState.getRegister('"').text).toBe '4' + + keydown('x') + expect(editor.getText()).toBe 'abc\n0123\n\nxyz' + expect(editor.getCursorScreenPosition()).toEqual [1, 3] + expect(vimState.getRegister('"').text).toBe '5' + + keydown('x') + expect(editor.getText()).toBe 'abc\n012\n\nxyz' + expect(editor.getCursorScreenPosition()).toEqual [1, 2] + expect(vimState.getRegister('"').text).toBe '3' + + keydown('x') + expect(editor.getText()).toBe 'abc\n01\n\nxyz' + expect(editor.getCursorScreenPosition()).toEqual [1, 1] + expect(vimState.getRegister('"').text).toBe '2' + + keydown('x') + expect(editor.getText()).toBe 'abc\n0\n\nxyz' + expect(editor.getCursorScreenPosition()).toEqual [1, 0] + expect(vimState.getRegister('"').text).toBe '1' + + keydown('x') + expect(editor.getText()).toBe 'abc\n\n\nxyz' + expect(editor.getCursorScreenPosition()).toEqual [1, 0] + expect(vimState.getRegister('"').text).toBe '0' + + it "deletes multiple characters with a count", -> + keydown('2') + keydown('x') + expect(editor.getText()).toBe 'abc\n0123\n\nxyz' + expect(editor.getCursorScreenPosition()).toEqual [1, 3] + expect(vimState.getRegister('"').text).toBe '45' + + editor.setCursorScreenPosition([0, 1]) + keydown('3') + keydown('x') + expect(editor.getText()).toBe 'a\n0123\n\nxyz' + expect(editor.getCursorScreenPosition()).toEqual [0, 0] + expect(vimState.getRegister('"').text).toBe 'bc' + + describe "with vim-mode.wrapLeftRightMotion", -> + beforeEach -> + editor.setText("abc\n012345\n\nxyz") + editor.setCursorScreenPosition([1, 4]) + atom.config.set('vim-mode.wrapLeftRightMotion', true) + + it "deletes a character", -> + # copy of the earlier test because wrapLeftRightMotion should not affect it + keydown('x') + expect(editor.getText()).toBe 'abc\n01235\n\nxyz' + expect(editor.getCursorScreenPosition()).toEqual [1, 4] + expect(vimState.getRegister('"').text).toBe '4' + + keydown('x') + expect(editor.getText()).toBe 'abc\n0123\n\nxyz' + expect(editor.getCursorScreenPosition()).toEqual [1, 3] + expect(vimState.getRegister('"').text).toBe '5' + + keydown('x') + expect(editor.getText()).toBe 'abc\n012\n\nxyz' + expect(editor.getCursorScreenPosition()).toEqual [1, 2] + expect(vimState.getRegister('"').text).toBe '3' + + keydown('x') + expect(editor.getText()).toBe 'abc\n01\n\nxyz' + expect(editor.getCursorScreenPosition()).toEqual [1, 1] + expect(vimState.getRegister('"').text).toBe '2' + + keydown('x') + expect(editor.getText()).toBe 'abc\n0\n\nxyz' + expect(editor.getCursorScreenPosition()).toEqual [1, 0] + expect(vimState.getRegister('"').text).toBe '1' + + keydown('x') + expect(editor.getText()).toBe 'abc\n\n\nxyz' + expect(editor.getCursorScreenPosition()).toEqual [1, 0] + expect(vimState.getRegister('"').text).toBe '0' + + it "deletes multiple characters and newlines with a count", -> + atom.config.set('vim-mode.wrapLeftRightMotion', true) + keydown('2') + keydown('x') + expect(editor.getText()).toBe 'abc\n0123\n\nxyz' + expect(editor.getCursorScreenPosition()).toEqual [1, 3] + expect(vimState.getRegister('"').text).toBe '45' + + editor.setCursorScreenPosition([0, 1]) + keydown('3') + keydown('x') + expect(editor.getText()).toBe 'a0123\n\nxyz' + expect(editor.getCursorScreenPosition()).toEqual [0, 1] + expect(vimState.getRegister('"').text).toBe 'bc\n' + + keydown('7') + keydown('x') + expect(editor.getText()).toBe 'ayz' + expect(editor.getCursorScreenPosition()).toEqual [0, 1] + expect(vimState.getRegister('"').text).toBe '0123\n\nx' + + + describe "on an empty line", -> + beforeEach -> + editor.setText("abc\n012345\n\nxyz") + editor.setCursorScreenPosition([2, 0]) + + it "deletes nothing on an empty line when vim-mode.wrapLeftRightMotion is false", -> + atom.config.set('vim-mode.wrapLeftRightMotion', false) + keydown('x') + expect(editor.getText()).toBe "abc\n012345\n\nxyz" + expect(editor.getCursorScreenPosition()).toEqual [2, 0] + + it "deletes an empty line when vim-mode.wrapLeftRightMotion is true", -> + atom.config.set('vim-mode.wrapLeftRightMotion', true) + keydown('x') + expect(editor.getText()).toBe "abc\n012345\nxyz" + expect(editor.getCursorScreenPosition()).toEqual [2, 0] + + + describe "the X keybinding", -> + describe "on a line with content", -> + beforeEach -> + editor.setText("ab\n012345") + editor.setCursorScreenPosition([1, 2]) + + it "deletes a character", -> + keydown('X', shift: true) + expect(editor.getText()).toBe 'ab\n02345' + expect(editor.getCursorScreenPosition()).toEqual [1, 1] + expect(vimState.getRegister('"').text).toBe '1' + + keydown('X', shift: true) + expect(editor.getText()).toBe 'ab\n2345' + expect(editor.getCursorScreenPosition()).toEqual [1, 0] + expect(vimState.getRegister('"').text).toBe '0' + + keydown('X', shift: true) + expect(editor.getText()).toBe 'ab\n2345' + expect(editor.getCursorScreenPosition()).toEqual [1, 0] + expect(vimState.getRegister('"').text).toBe '0' + + atom.config.set('vim-mode.wrapLeftRightMotion', true) + keydown('X', shift: true) + expect(editor.getText()).toBe 'ab2345' + expect(editor.getCursorScreenPosition()).toEqual [0, 2] + expect(vimState.getRegister('"').text).toBe '\n' + + + describe "on an empty line", -> + beforeEach -> + editor.setText("012345\n\nabcdef") + editor.setCursorScreenPosition([1, 0]) + + it "deletes nothing when vim-mode.wrapLeftRightMotion is false", -> + atom.config.set('vim-mode.wrapLeftRightMotion', false) + keydown('X', shift: true) + expect(editor.getText()).toBe "012345\n\nabcdef" + expect(editor.getCursorScreenPosition()).toEqual [1, 0] + + it "deletes the newline when wrapLeftRightMotion is true", -> + atom.config.set('vim-mode.wrapLeftRightMotion', true) + keydown('X', shift: true) + expect(editor.getText()).toBe "012345\nabcdef" + expect(editor.getCursorScreenPosition()).toEqual [0, 5] + + describe "the s keybinding", -> + beforeEach -> + editor.setText('012345') + editor.setCursorScreenPosition([0, 1]) + + it "deletes the character to the right and enters insert mode", -> + keydown('s') + expect(editorElement.classList.contains('insert-mode')).toBe(true) + expect(editor.getText()).toBe '02345' + expect(editor.getCursorScreenPosition()).toEqual [0, 1] + expect(vimState.getRegister('"').text).toBe '1' + + it "is repeatable", -> + editor.setCursorScreenPosition([0, 0]) + keydown('3') + keydown('s') + editor.insertText("ab") + keydown('escape') + expect(editor.getText()).toBe 'ab345' + editor.setCursorScreenPosition([0, 2]) + keydown('.') + expect(editor.getText()).toBe 'abab' + + it "is undoable", -> + editor.setCursorScreenPosition([0, 0]) + keydown('3') + keydown('s') + editor.insertText("ab") + keydown('escape') + expect(editor.getText()).toBe 'ab345' + keydown('u') + expect(editor.getText()).toBe '012345' + expect(editor.getSelectedText()).toBe '' + + describe "in visual mode", -> + beforeEach -> + keydown('v') + editor.selectRight() + keydown('s') + + it "deletes the selected characters and enters insert mode", -> + expect(editorElement.classList.contains('insert-mode')).toBe(true) + expect(editor.getText()).toBe '0345' + expect(editor.getCursorScreenPosition()).toEqual [0, 1] + expect(vimState.getRegister('"').text).toBe '12' + + describe "the S keybinding", -> + beforeEach -> + editor.setText("12345\nabcde\nABCDE") + editor.setCursorScreenPosition([1, 3]) + + it "deletes the entire line and enters insert mode", -> + keydown('S', shift: true) + expect(editorElement.classList.contains('insert-mode')).toBe(true) + expect(editor.getText()).toBe "12345\n\nABCDE" + expect(editor.getCursorScreenPosition()).toEqual [1, 0] + expect(vimState.getRegister('"').text).toBe "abcde\n" + expect(vimState.getRegister('"').type).toBe 'linewise' + + it "is repeatable", -> + keydown('S', shift: true) + editor.insertText("abc") + keydown 'escape' + expect(editor.getText()).toBe "12345\nabc\nABCDE" + editor.setCursorScreenPosition([2, 3]) + keydown '.' + expect(editor.getText()).toBe "12345\nabc\nabc\n" + + it "is undoable", -> + keydown('S', shift: true) + editor.insertText("abc") + keydown 'escape' + expect(editor.getText()).toBe "12345\nabc\nABCDE" + keydown 'u' + expect(editor.getText()).toBe "12345\nabcde\nABCDE" + expect(editor.getSelectedText()).toBe '' + + it "works when the cursor's goal column is greater than its current column", -> + editor.setText("\n12345") + editor.setCursorBufferPosition([1, Infinity]) + editor.moveUp() + keydown("S", shift: true) + expect(editor.getText()).toBe("\n12345") + + # Can't be tested without setting grammar of test buffer + xit "respects indentation", -> + + describe "the d keybinding", -> + it "enters operator-pending mode", -> + keydown('d') + expect(editorElement.classList.contains('operator-pending-mode')).toBe(true) + expect(editorElement.classList.contains('command-mode')).toBe(false) + + describe "when followed by a d", -> + it "deletes the current line and exits operator-pending mode", -> + editor.setText("12345\nabcde\n\nABCDE") + editor.setCursorScreenPosition([1, 1]) + + keydown('d') + keydown('d') + + expect(editor.getText()).toBe "12345\n\nABCDE" + expect(editor.getCursorScreenPosition()).toEqual [1, 0] + expect(vimState.getRegister('"').text).toBe "abcde\n" + expect(editorElement.classList.contains('operator-pending-mode')).toBe(false) + expect(editorElement.classList.contains('command-mode')).toBe(true) + + it "deletes the last line", -> + editor.setText("12345\nabcde\nABCDE") + editor.setCursorScreenPosition([2, 1]) + + keydown('d') + keydown('d') + + expect(editor.getText()).toBe "12345\nabcde\n" + expect(editor.getCursorScreenPosition()).toEqual [2, 0] + + it "leaves the cursor on the first nonblank character", -> + editor.setText("12345\n abcde\n") + editor.setCursorScreenPosition([0, 4]) + + keydown('d') + keydown('d') + + expect(editor.getText()).toBe " abcde\n" + expect(editor.getCursorScreenPosition()).toEqual [0, 2] + + describe "undo behavior", -> + beforeEach -> + editor.setText("12345\nabcde\nABCDE\nQWERT") + editor.setCursorScreenPosition([1, 1]) + + keydown('d') + keydown('2') + keydown('d') + + keydown('u') + + it "undoes both lines", -> + expect(editor.getText()).toBe "12345\nabcde\nABCDE\nQWERT" + expect(editor.getSelectedText()).toBe '' + + describe "when followed by a w", -> + it "deletes the next word until the end of the line and exits operator-pending mode", -> + editor.setText("abcd efg\nabc") + editor.setCursorScreenPosition([0, 5]) + + keydown('d') + keydown('w') + + # Incompatibility with VIM. In vim, `w` behaves differently as an + # operator than as a motion; it stops at the end of a line.expect(editor.getText()).toBe "abcd abc" + expect(editor.getText()).toBe "abcd abc" + expect(editor.getCursorScreenPosition()).toEqual [0, 5] + + expect(editorElement.classList.contains('operator-pending-mode')).toBe(false) + expect(editorElement.classList.contains('command-mode')).toBe(true) + + it "deletes to the beginning of the next word", -> + editor.setText('abcd efg') + editor.setCursorScreenPosition([0, 2]) + + keydown('d') + keydown('w') + + expect(editor.getText()).toBe 'abefg' + expect(editor.getCursorScreenPosition()).toEqual [0, 2] + + editor.setText('one two three four') + editor.setCursorScreenPosition([0, 0]) + + keydown('d') + keydown('3') + keydown('w') + + expect(editor.getText()).toBe 'four' + expect(editor.getCursorScreenPosition()).toEqual [0, 0] + + describe "when followed by an iw", -> + it "deletes the containing word", -> + editor.setText("12345 abcde ABCDE") + editor.setCursorScreenPosition([0, 9]) + + keydown('d') + expect(editorElement.classList.contains('operator-pending-mode')).toBe(true) + keydown('i') + keydown('w') + + expect(editor.getText()).toBe "12345 ABCDE" + expect(editor.getCursorScreenPosition()).toEqual [0, 6] + expect(vimState.getRegister('"').text).toBe "abcde" + expect(editorElement.classList.contains('operator-pending-mode')).toBe(false) + expect(editorElement.classList.contains('command-mode')).toBe(true) + + describe "when followed by an j", -> + beforeEach -> + originalText = "12345\nabcde\nABCDE" + editor.setText(originalText) + + describe "on the beginning of the file", -> + editor.setCursorScreenPosition([0, 0]) + it "deletes the next two lines", -> + keydown('d') + keydown('j') + expect(editor.getText()).toBe("ABCDE") + + describe "on the end of the file", -> + editor.setCursorScreenPosition([4, 2]) + it "deletes nothing", -> + keydown('d') + keydown('j') + expect(editor.getText()).toBe(originalText) + + describe "on the middle of second line", -> + editor.setCursorScreenPosition([2, 1]) + it "deletes the last two lines", -> + keydown('d') + keydown('j') + expect(editor.getText()).toBe("12345") + + describe "when followed by an k", -> + beforeEach -> + originalText = "12345\nabcde\nABCDE" + editor.setText(originalText) + + describe "on the end of the file", -> + editor.setCursorScreenPosition([4, 2]) + it "deletes the bottom two lines", -> + keydown('d') + keydown('k') + expect(editor.getText()).toBe("ABCDE") + + describe "on the beginning of the file", -> + editor.setCursorScreenPosition([0, 0]) + it "deletes nothing", -> + keydown('d') + keydown('k') + expect(editor.getText()).toBe(originalText) + + describe "when on the middle of second line", -> + editor.setCursorScreenPosition([2, 1]) + it "deletes the first two lines", -> + keydown('d') + keydown('k') + expect(editor.getText()).toBe("12345") + + describe "when followed by a G", -> + beforeEach -> + originalText = "12345\nabcde\nABCDE" + editor.setText(originalText) + + describe "on the beginning of the second line", -> + it "deletes the bottom two lines", -> + editor.setCursorScreenPosition([1, 0]) + keydown('d') + keydown('G', shift: true) + expect(editor.getText()).toBe("12345\n") + + describe "on the middle of the second line", -> + it "deletes the bottom two lines", -> + editor.setCursorScreenPosition([1, 2]) + keydown('d') + keydown('G', shift: true) + expect(editor.getText()).toBe("12345\n") + + describe "when followed by a goto line G", -> + beforeEach -> + originalText = "12345\nabcde\nABCDE" + editor.setText(originalText) + + describe "on the beginning of the second line", -> + it "deletes the bottom two lines", -> + editor.setCursorScreenPosition([1, 0]) + keydown('d') + keydown('2') + keydown('G', shift: true) + expect(editor.getText()).toBe("12345\nABCDE") + + describe "on the middle of the second line", -> + it "deletes the bottom two lines", -> + editor.setCursorScreenPosition([1, 2]) + keydown('d') + keydown('2') + keydown('G', shift: true) + expect(editor.getText()).toBe("12345\nABCDE") + + describe "when followed by a t)", -> + describe "with the entire line yanked before", -> + beforeEach -> + editor.setText("test (xyz)") + editor.setCursorScreenPosition([0, 6]) + + it "deletes until the closing parenthesis", -> + keydown('y') + keydown('y') + keydown('d') + keydown('t') + commandModeInputKeydown(')') + expect(editor.getText()).toBe("test ()") + expect(editor.getCursorScreenPosition()).toEqual [0, 6] + + describe "with multiple cursors", -> + it "deletes each selection", -> + editor.setText("abcd\n1234\nABCD\n") + editor.setCursorBufferPosition([0, 1]) + editor.addCursorAtBufferPosition([1, 2]) + editor.addCursorAtBufferPosition([2, 3]) + + keydown('d') + keydown('e') + + expect(editor.getText()).toBe "a\n12\nABC" + expect(editor.getCursorBufferPositions()).toEqual [ + [0, 0], + [1, 1], + [2, 2], + ] + + it "doesn't delete empty selections", -> + editor.setText("abcd\nabc\nabd") + editor.setCursorBufferPosition([0, 0]) + editor.addCursorAtBufferPosition([1, 0]) + editor.addCursorAtBufferPosition([2, 0]) + + keydown('d') + keydown('t') + commandModeInputKeydown('d') + + expect(editor.getText()).toBe "d\nabc\nd" + expect(editor.getCursorBufferPositions()).toEqual [ + [0, 0], + [1, 0], + [2, 0], + ] + + describe "the D keybinding", -> + beforeEach -> + editor.getBuffer().setText("012\n") + editor.setCursorScreenPosition([0, 1]) + keydown('D', shift: true) + + it "deletes the contents until the end of the line", -> + expect(editor.getText()).toBe "0\n" + + describe "the c keybinding", -> + beforeEach -> + editor.setText("12345\nabcde\nABCDE") + + describe "when followed by a c", -> + it "deletes the current line and enters insert mode", -> + editor.setCursorScreenPosition([1, 1]) + + keydown('c') + keydown('c') + + expect(editor.getText()).toBe "12345\n\nABCDE" + expect(editor.getCursorScreenPosition()).toEqual [1, 0] + expect(editorElement.classList.contains('command-mode')).toBe(false) + expect(editorElement.classList.contains('insert-mode')).toBe(true) + + describe "when the cursor is on the last line", -> + it "deletes the line's content and enters insert mode on the last line", -> + editor.setCursorScreenPosition([2, 1]) + + keydown('c') + keydown('c') + + expect(editor.getText()).toBe "12345\nabcde\n\n" + expect(editor.getCursorScreenPosition()).toEqual [2, 0] + expect(editorElement.classList.contains('command-mode')).toBe(false) + expect(editorElement.classList.contains('insert-mode')).toBe(true) + + describe "when the cursor is on the only line", -> + it "deletes the line's content and enters insert mode", -> + editor.setText("12345") + editor.setCursorScreenPosition([0, 2]) + + keydown('c') + keydown('c') + + expect(editor.getText()).toBe "\n" + expect(editor.getCursorScreenPosition()).toEqual [0, 0] + expect(editorElement.classList.contains('command-mode')).toBe(false) + expect(editorElement.classList.contains('insert-mode')).toBe(true) + + describe "when followed by i w", -> + it "undo's and redo's completely", -> + editor.setCursorScreenPosition([1, 1]) + + keydown('c') + keydown('i') + keydown('w') + expect(editor.getText()).toBe "12345\n\nABCDE" + expect(editor.getCursorScreenPosition()).toEqual [1, 0] + expect(editorElement.classList.contains('insert-mode')).toBe(true) + + # Just cannot get "typing" to work correctly in test. + editor.setText("12345\nfg\nABCDE") + keydown('escape') + expect(editorElement.classList.contains('command-mode')).toBe(true) + expect(editor.getText()).toBe "12345\nfg\nABCDE" + + keydown('u') + expect(editor.getText()).toBe "12345\nabcde\nABCDE" + keydown('r', ctrl: true) + expect(editor.getText()).toBe "12345\nfg\nABCDE" + + describe "when followed by a w", -> + it "changes the word", -> + editor.setText("word1 word2 word3") + editor.setCursorBufferPosition([0, "word1 w".length]) + + keydown("c") + keydown("w") + keydown("escape") + + expect(editor.getText()).toBe "word1 w word3" + + describe "when followed by a G", -> + beforeEach -> + originalText = "12345\nabcde\nABCDE" + editor.setText(originalText) + + describe "on the beginning of the second line", -> + it "deletes the bottom two lines", -> + editor.setCursorScreenPosition([1, 0]) + keydown('c') + keydown('G', shift: true) + keydown('escape') + expect(editor.getText()).toBe("12345\n\n") + + describe "on the middle of the second line", -> + it "deletes the bottom two lines", -> + editor.setCursorScreenPosition([1, 2]) + keydown('c') + keydown('G', shift: true) + keydown('escape') + expect(editor.getText()).toBe("12345\n\n") + + describe "when followed by a goto line G", -> + beforeEach -> + editor.setText "12345\nabcde\nABCDE" + + describe "on the beginning of the second line", -> + it "deletes all the text on the line", -> + editor.setCursorScreenPosition([1, 0]) + keydown('c') + keydown('2') + keydown('G', shift: true) + keydown('escape') + expect(editor.getText()).toBe("12345\n\nABCDE") + + describe "on the middle of the second line", -> + it "deletes all the text on the line", -> + editor.setCursorScreenPosition([1, 2]) + keydown('c') + keydown('2') + keydown('G', shift: true) + keydown('escape') + expect(editor.getText()).toBe("12345\n\nABCDE") + + describe "the C keybinding", -> + beforeEach -> + editor.getBuffer().setText("012\n") + editor.setCursorScreenPosition([0, 1]) + keydown('C', shift: true) + + it "deletes the contents until the end of the line and enters insert mode", -> + expect(editor.getText()).toBe "0\n" + expect(editor.getCursorScreenPosition()).toEqual [0, 1] + expect(editorElement.classList.contains('command-mode')).toBe(false) + expect(editorElement.classList.contains('insert-mode')).toBe(true) + + describe "the y keybinding", -> + beforeEach -> + editor.getBuffer().setText("012 345\nabc\n") + editor.setCursorScreenPosition([0, 4]) + + describe "when selected lines in visual linewise mode", -> + beforeEach -> + keydown('V', shift: true) + keydown('j') + keydown('y') + + it "is in linewise motion", -> + expect(vimState.getRegister('"').type).toEqual "linewise" + + it "saves the lines to the default register", -> + expect(vimState.getRegister('"').text).toBe "012 345\nabc\n" + + it "places the cursor at the beginning of the selection", -> + expect(editor.getCursorBufferPositions()).toEqual([[0, 0]]) + + describe "when followed by a second y ", -> + beforeEach -> + keydown('y') + keydown('y') + + it "saves the line to the default register", -> + expect(vimState.getRegister('"').text).toBe "012 345\n" + + it "leaves the cursor at the starting position", -> + expect(editor.getCursorScreenPosition()).toEqual [0, 4] + + describe "when useClipboardAsDefaultRegister enabled", -> + it "writes to clipboard", -> + atom.config.set 'vim-mode.useClipboardAsDefaultRegister', true + keydown('y') + keydown('y') + expect(atom.clipboard.read()).toBe '012 345\n' + + describe "when followed with a repeated y", -> + beforeEach -> + keydown('y') + keydown('2') + keydown('y') + + it "copies n lines, starting from the current", -> + expect(vimState.getRegister('"').text).toBe "012 345\nabc\n" + + it "leaves the cursor at the starting position", -> + expect(editor.getCursorScreenPosition()).toEqual [0, 4] + + describe "with a register", -> + beforeEach -> + keydown('"') + keydown('a') + keydown('y') + keydown('y') + + it "saves the line to the a register", -> + expect(vimState.getRegister('a').text).toBe "012 345\n" + + it "appends the line to the A register", -> + keydown('"') + keydown('A', shift: true) + keydown('y') + keydown('y') + expect(vimState.getRegister('a').text).toBe "012 345\n012 345\n" + + describe "with a forward motion", -> + beforeEach -> + keydown('y') + keydown('e') + + it "saves the selected text to the default register", -> + expect(vimState.getRegister('"').text).toBe '345' + + it "leaves the cursor at the starting position", -> + expect(editor.getCursorScreenPosition()).toEqual [0, 4] + + it "does not yank when motion fails", -> + keydown('y') + keydown('t') + commandModeInputKeydown('x') + expect(vimState.getRegister('"').text).toBe '345' + + describe "with a text object", -> + it "moves the cursor to the beginning of the text object", -> + editor.setCursorBufferPosition([0, 5]) + keydown("y") + keydown("i") + keydown("w") + expect(editor.getCursorBufferPositions()).toEqual([[0, 4]]) + + describe "with a left motion", -> + beforeEach -> + keydown('y') + keydown('h') + + it "saves the left letter to the default register", -> + expect(vimState.getRegister('"').text).toBe " " + + it "moves the cursor position to the left", -> + expect(editor.getCursorScreenPosition()).toEqual [0, 3] + + describe "with a down motion", -> + beforeEach -> + keydown 'y' + keydown 'j' + + it "saves both full lines to the default register", -> + expect(vimState.getRegister('"').text).toBe "012 345\nabc\n" + + it "leaves the cursor at the starting position", -> + expect(editor.getCursorScreenPosition()).toEqual [0, 4] + + describe "when followed by a G", -> + beforeEach -> + originalText = "12345\nabcde\nABCDE" + editor.setText(originalText) + + describe "on the beginning of the second line", -> + it "deletes the bottom two lines", -> + editor.setCursorScreenPosition([1, 0]) + keydown('y') + keydown('G', shift: true) + keydown('P', shift: true) + expect(editor.getText()).toBe("12345\nabcde\nABCDE\nabcde\nABCDE") + + describe "on the middle of the second line", -> + it "deletes the bottom two lines", -> + editor.setCursorScreenPosition([1, 2]) + keydown('y') + keydown('G', shift: true) + keydown('P', shift: true) + expect(editor.getText()).toBe("12345\nabcde\nABCDE\nabcde\nABCDE") + + describe "when followed by a goto line G", -> + beforeEach -> + originalText = "12345\nabcde\nABCDE" + editor.setText(originalText) + + describe "on the beginning of the second line", -> + it "deletes the bottom two lines", -> + editor.setCursorScreenPosition([1, 0]) + keydown('y') + keydown('2') + keydown('G', shift: true) + keydown('P', shift: true) + expect(editor.getText()).toBe("12345\nabcde\nabcde\nABCDE") + + describe "on the middle of the second line", -> + it "deletes the bottom two lines", -> + editor.setCursorScreenPosition([1, 2]) + keydown('y') + keydown('2') + keydown('G', shift: true) + keydown('P', shift: true) + expect(editor.getText()).toBe("12345\nabcde\nabcde\nABCDE") + + describe "with multiple cursors", -> + it "moves each cursor and copies the last selection's text", -> + editor.setText " abcd\n 1234" + editor.setCursorBufferPosition([0, 0]) + editor.addCursorAtBufferPosition([1, 5]) + + keydown("y") + keydown("^") + + expect(vimState.getRegister('"').text).toBe '123' + expect(editor.getCursorBufferPositions()).toEqual [[0, 0], [1, 2]] + + describe "the yy keybinding", -> + describe "on a single line file", -> + beforeEach -> + editor.getBuffer().setText "exclamation!\n" + editor.setCursorScreenPosition [0, 0] + + it "copies the entire line and pastes it correctly", -> + keydown('y') + keydown('y') + keydown('p') + + expect(vimState.getRegister('"').text).toBe "exclamation!\n" + expect(editor.getText()).toBe "exclamation!\nexclamation!\n" + + describe "on a single line file with no newline", -> + beforeEach -> + editor.getBuffer().setText "no newline!" + editor.setCursorScreenPosition [0, 0] + + it "copies the entire line and pastes it correctly", -> + keydown('y') + keydown('y') + keydown('p') + + expect(vimState.getRegister('"').text).toBe "no newline!\n" + expect(editor.getText()).toBe "no newline!\nno newline!" + + it "copies the entire line and pastes it respecting count and new lines", -> + keydown('y') + keydown('y') + keydown('2') + keydown('p') + + expect(vimState.getRegister('"').text).toBe "no newline!\n" + expect(editor.getText()).toBe "no newline!\nno newline!\nno newline!" + + describe "the Y keybinding", -> + beforeEach -> + editor.getBuffer().setText "012 345\nabc\n" + editor.setCursorScreenPosition [0, 4] + + it "saves the line to the default register", -> + keydown('Y', shift: true) + + expect(vimState.getRegister('"').text).toBe "012 345\n" + expect(editor.getCursorScreenPosition()).toEqual [0, 4] + + describe "the p keybinding", -> + describe "with character contents", -> + beforeEach -> + editor.getBuffer().setText "012\n" + editor.setCursorScreenPosition [0, 0] + vimState.setRegister('"', text: '345') + vimState.setRegister('a', text: 'a') + atom.clipboard.write "clip" + + describe "from the default register", -> + beforeEach -> keydown('p') + + it "inserts the contents", -> + expect(editor.getText()).toBe "034512\n" + expect(editor.getCursorScreenPosition()).toEqual [0, 3] + + describe "when useClipboardAsDefaultRegister enabled", -> + it "inserts contents from clipboard", -> + atom.config.set 'vim-mode.useClipboardAsDefaultRegister', true + keydown('p') + expect(editor.getText()).toBe "0clip12\n" + + describe "from a specified register", -> + beforeEach -> + keydown('"') + keydown('a') + keydown('p') + + it "inserts the contents of the 'a' register", -> + expect(editor.getText()).toBe "0a12\n" + expect(editor.getCursorScreenPosition()).toEqual [0, 1] + + describe "at the end of a line", -> + it "inserts before the current line's newline", -> + editor.setText("abcde\none two three") + editor.setCursorScreenPosition([1, 4]) + + keydown 'd' + keydown '$' + keydown 'k' + keydown '$' + keydown 'p' + + expect(editor.getText()).toBe "abcdetwo three\none " + + describe "with a selection", -> + beforeEach -> + editor.selectRight() + keydown('p') + + it "replaces the current selection", -> + expect(editor.getText()).toBe "34512\n" + expect(editor.getCursorScreenPosition()).toEqual [0, 2] + + describe "with linewise contents", -> + describe "on a single line", -> + beforeEach -> + editor.getBuffer().setText("012") + editor.setCursorScreenPosition([0, 1]) + vimState.setRegister('"', text: " 345\n", type: 'linewise') + + it "inserts the contents of the default register", -> + keydown('p') + + expect(editor.getText()).toBe "012\n 345" + expect(editor.getCursorScreenPosition()).toEqual [1, 1] + + it "replaces the current selection", -> + editor.selectRight() + keydown('p') + + expect(editor.getText()).toBe "0 345\n2" + expect(editor.getCursorScreenPosition()).toEqual [1, 0] + + describe "on multiple lines", -> + beforeEach -> + editor.getBuffer().setText("012\n 345") + vimState.setRegister('"', text: " 456\n", type: 'linewise') + + it "inserts the contents of the default register at middle line", -> + editor.setCursorScreenPosition([0, 1]) + keydown('p') + + expect(editor.getText()).toBe "012\n 456\n 345" + expect(editor.getCursorScreenPosition()).toEqual [1, 1] + + it "inserts the contents of the default register at end of line", -> + editor.setCursorScreenPosition([1, 1]) + keydown('p') + + expect(editor.getText()).toBe "012\n 345\n 456" + expect(editor.getCursorScreenPosition()).toEqual [2, 1] + + describe "with multiple linewise contents", -> + beforeEach -> + editor.getBuffer().setText("012\nabc") + editor.setCursorScreenPosition([1, 0]) + vimState.setRegister('"', text: " 345\n 678\n", type: 'linewise') + keydown('p') + + it "inserts the contents of the default register", -> + expect(editor.getText()).toBe "012\nabc\n 345\n 678" + expect(editor.getCursorScreenPosition()).toEqual [2, 1] + + describe "pasting twice", -> + beforeEach -> + editor.setText("12345\nabcde\nABCDE\nQWERT") + editor.setCursorScreenPosition([1, 1]) + vimState.setRegister('"', text: '123') + keydown('2') + keydown('p') + + it "inserts the same line twice", -> + expect(editor.getText()).toBe "12345\nab123123cde\nABCDE\nQWERT" + + describe "when undone", -> + beforeEach -> + keydown('u') + + it "removes both lines", -> + expect(editor.getText()).toBe "12345\nabcde\nABCDE\nQWERT" + + describe "the P keybinding", -> + describe "with character contents", -> + beforeEach -> + editor.getBuffer().setText("012\n") + editor.setCursorScreenPosition([0, 0]) + vimState.setRegister('"', text: '345') + vimState.setRegister('a', text: 'a') + keydown('P', shift: true) + + it "inserts the contents of the default register above", -> + expect(editor.getText()).toBe "345012\n" + expect(editor.getCursorScreenPosition()).toEqual [0, 2] + + describe "the O keybinding", -> + beforeEach -> + spyOn(editor, 'shouldAutoIndent').andReturn(true) + spyOn(editor, 'autoIndentBufferRow').andCallFake (line) -> + editor.indent() + + editor.getBuffer().setText(" abc\n 012\n") + editor.setCursorScreenPosition([1, 1]) + + it "switches to insert and adds a newline above the current one", -> + keydown('O', shift: true) + expect(editor.getText()).toBe " abc\n \n 012\n" + expect(editor.getCursorScreenPosition()).toEqual [1, 2] + expect(editorElement.classList.contains('insert-mode')).toBe(true) + + it "is repeatable", -> + editor.getBuffer().setText(" abc\n 012\n 4spaces\n") + editor.setCursorScreenPosition([1, 1]) + keydown('O', shift: true) + editor.insertText "def" + keydown 'escape' + expect(editor.getText()).toBe " abc\n def\n 012\n 4spaces\n" + editor.setCursorScreenPosition([1, 1]) + keydown '.' + expect(editor.getText()).toBe " abc\n def\n def\n 012\n 4spaces\n" + editor.setCursorScreenPosition([4, 1]) + keydown '.' + expect(editor.getText()).toBe " abc\n def\n def\n 012\n def\n 4spaces\n" + + it "is undoable", -> + keydown('O', shift: true) + editor.insertText "def" + keydown 'escape' + expect(editor.getText()).toBe " abc\n def\n 012\n" + keydown 'u' + expect(editor.getText()).toBe " abc\n 012\n" + + describe "the o keybinding", -> + beforeEach -> + spyOn(editor, 'shouldAutoIndent').andReturn(true) + spyOn(editor, 'autoIndentBufferRow').andCallFake (line) -> + editor.indent() + + editor.getBuffer().setText("abc\n 012\n") + editor.setCursorScreenPosition([1, 2]) + + it "switches to insert and adds a newline above the current one", -> + keydown('o') + expect(editor.getText()).toBe "abc\n 012\n \n" + expect(editorElement.classList.contains('insert-mode')).toBe(true) + expect(editor.getCursorScreenPosition()).toEqual [2, 2] + + # This works in practice, but the editor doesn't respect the indentation + # rules without a syntax grammar. Need to set the editor's grammar + # to fix it. + xit "is repeatable", -> + editor.getBuffer().setText(" abc\n 012\n 4spaces\n") + editor.setCursorScreenPosition([1, 1]) + keydown('o') + editor.insertText "def" + keydown 'escape' + expect(editor.getText()).toBe " abc\n 012\n def\n 4spaces\n" + keydown '.' + expect(editor.getText()).toBe " abc\n 012\n def\n def\n 4spaces\n" + editor.setCursorScreenPosition([4, 1]) + keydown '.' + expect(editor.getText()).toBe " abc\n def\n def\n 012\n 4spaces\n def\n" + + it "is undoable", -> + keydown('o') + editor.insertText "def" + keydown 'escape' + expect(editor.getText()).toBe "abc\n 012\n def\n" + keydown 'u' + expect(editor.getText()).toBe "abc\n 012\n" + + describe "the a keybinding", -> + beforeEach -> + editor.getBuffer().setText("012\n") + + describe "at the beginning of the line", -> + beforeEach -> + editor.setCursorScreenPosition([0, 0]) + keydown('a') + + it "switches to insert mode and shifts to the right", -> + expect(editor.getCursorScreenPosition()).toEqual [0, 1] + expect(editorElement.classList.contains('insert-mode')).toBe(true) + + describe "at the end of the line", -> + beforeEach -> + editor.setCursorScreenPosition([0, 3]) + keydown('a') + + it "doesn't linewrap", -> + expect(editor.getCursorScreenPosition()).toEqual [0, 3] + + describe "the A keybinding", -> + beforeEach -> + editor.getBuffer().setText("11\n22\n") + + describe "at the beginning of a line", -> + it "switches to insert mode at the end of the line", -> + editor.setCursorScreenPosition([0, 0]) + keydown('A', shift: true) + + expect(editorElement.classList.contains('insert-mode')).toBe(true) + expect(editor.getCursorScreenPosition()).toEqual [0, 2] + + it "repeats always as insert at the end of the line", -> + editor.setCursorScreenPosition([0, 0]) + keydown('A', shift: true) + editor.insertText("abc") + keydown 'escape' + editor.setCursorScreenPosition([1, 0]) + keydown '.' + + expect(editor.getText()).toBe "11abc\n22abc\n" + expect(editorElement.classList.contains('insert-mode')).toBe(false) + expect(editor.getCursorScreenPosition()).toEqual [1, 4] + + describe "the I keybinding", -> + beforeEach -> + editor.getBuffer().setText("11\n 22\n") + + describe "at the end of a line", -> + it "switches to insert mode at the beginning of the line", -> + editor.setCursorScreenPosition([0, 2]) + keydown('I', shift: true) + + expect(editorElement.classList.contains('insert-mode')).toBe(true) + expect(editor.getCursorScreenPosition()).toEqual [0, 0] + + it "switches to insert mode after leading whitespace", -> + editor.setCursorScreenPosition([1, 4]) + keydown('I', shift: true) + + expect(editorElement.classList.contains('insert-mode')).toBe(true) + expect(editor.getCursorScreenPosition()).toEqual [1, 2] + + it "repeats always as insert at the first character of the line", -> + editor.setCursorScreenPosition([0, 2]) + keydown('I', shift: true) + editor.insertText("abc") + keydown 'escape' + expect(editor.getCursorScreenPosition()).toEqual [0, 2] + editor.setCursorScreenPosition([1, 4]) + keydown '.' + + expect(editor.getText()).toBe "abc11\n abc22\n" + expect(editorElement.classList.contains('insert-mode')).toBe(false) + expect(editor.getCursorScreenPosition()).toEqual [1, 4] + + describe "the J keybinding", -> + beforeEach -> + editor.getBuffer().setText("012\n 456\n") + editor.setCursorScreenPosition([0, 1]) + + describe "without repeating", -> + beforeEach -> keydown('J', shift: true) + + it "joins the contents of the current line with the one below it", -> + expect(editor.getText()).toBe "012 456\n" + + describe "with repeating", -> + beforeEach -> + editor.setText("12345\nabcde\nABCDE\nQWERT") + editor.setCursorScreenPosition([1, 1]) + keydown('2') + keydown('J', shift: true) + + describe "undo behavior", -> + beforeEach -> keydown('u') + + it "handles repeats", -> + expect(editor.getText()).toBe "12345\nabcde\nABCDE\nQWERT" + + describe "the > keybinding", -> + beforeEach -> + editor.setText("12345\nabcde\nABCDE") + + describe "on the last line", -> + beforeEach -> + editor.setCursorScreenPosition([2, 0]) + + describe "when followed by a >", -> + beforeEach -> + keydown('>') + keydown('>') + + it "indents the current line", -> + expect(editor.getText()).toBe "12345\nabcde\n ABCDE" + expect(editor.getCursorScreenPosition()).toEqual [2, 2] + + describe "on the first line", -> + beforeEach -> + editor.setCursorScreenPosition([0, 0]) + + describe "when followed by a >", -> + beforeEach -> + keydown('>') + keydown('>') + + it "indents the current line", -> + expect(editor.getText()).toBe " 12345\nabcde\nABCDE" + expect(editor.getCursorScreenPosition()).toEqual [0, 2] + + describe "when followed by a repeating >", -> + beforeEach -> + keydown('3') + keydown('>') + keydown('>') + + it "indents multiple lines at once", -> + expect(editor.getText()).toBe " 12345\n abcde\n ABCDE" + expect(editor.getCursorScreenPosition()).toEqual [0, 2] + + describe "undo behavior", -> + beforeEach -> keydown('u') + + it "outdents all three lines", -> + expect(editor.getText()).toBe "12345\nabcde\nABCDE" + + describe "in visual mode", -> + beforeEach -> + editor.setCursorScreenPosition([0, 0]) + keydown('v', shift: true) + keydown('>') + + it "indents the current line and remains in visual mode", -> + expect(editorElement.classList.contains('visual-mode')).toBe(true) + expect(editor.getText()).toBe " 12345\nabcde\nABCDE" + expect(editor.getSelectedText()).toBe " 12345\n" + + it "allows repeating the operation", -> + keydown("escape") + keydown(".") + expect(editorElement.classList.contains('command-mode')).toBe(true) + expect(editor.getText()).toBe " 12345\nabcde\nABCDE" + + describe "the < keybinding", -> + beforeEach -> + editor.setText(" 12345\n abcde\nABCDE") + editor.setCursorScreenPosition([0, 0]) + + describe "when followed by a <", -> + beforeEach -> + keydown('<') + keydown('<') + + it "indents the current line", -> + expect(editor.getText()).toBe "12345\n abcde\nABCDE" + expect(editor.getCursorScreenPosition()).toEqual [0, 0] + + describe "when followed by a repeating <", -> + beforeEach -> + keydown('2') + keydown('<') + keydown('<') + + it "indents multiple lines at once", -> + expect(editor.getText()).toBe "12345\nabcde\nABCDE" + expect(editor.getCursorScreenPosition()).toEqual [0, 0] + + describe "undo behavior", -> + beforeEach -> keydown('u') + + it "indents both lines", -> + expect(editor.getText()).toBe " 12345\n abcde\nABCDE" + + describe "in visual mode", -> + beforeEach -> + keydown('v', shift: true) + keydown('<') + + it "indents the current line and remains in visual mode", -> + expect(editorElement.classList.contains('visual-mode')).toBe(true) + expect(editor.getText()).toBe "12345\n abcde\nABCDE" + expect(editor.getSelectedText()).toBe "12345\n" + + describe "the = keybinding", -> + oldGrammar = [] + + beforeEach -> + waitsForPromise -> + atom.packages.activatePackage('language-javascript') + + oldGrammar = editor.getGrammar() + editor.setText("foo\n bar\n baz") + editor.setCursorScreenPosition([1, 0]) + + describe "when used in a scope that supports auto-indent", -> + beforeEach -> + jsGrammar = atom.grammars.grammarForScopeName('source.js') + editor.setGrammar(jsGrammar) + + afterEach -> + editor.setGrammar(oldGrammar) + + describe "when followed by a =", -> + beforeEach -> + keydown('=') + keydown('=') + + it "indents the current line", -> + expect(editor.indentationForBufferRow(1)).toBe 0 + + describe "when followed by a repeating =", -> + beforeEach -> + keydown('2') + keydown('=') + keydown('=') + + it "autoindents multiple lines at once", -> + expect(editor.getText()).toBe "foo\nbar\nbaz" + expect(editor.getCursorScreenPosition()).toEqual [1, 0] + + describe "undo behavior", -> + beforeEach -> keydown('u') + + it "indents both lines", -> + expect(editor.getText()).toBe "foo\n bar\n baz" + + describe "the . keybinding", -> + beforeEach -> + editor.setText("12\n34\n56\n78") + editor.setCursorScreenPosition([0, 0]) + + it "repeats the last operation", -> + keydown '2' + keydown 'd' + keydown 'd' + keydown '.' + + expect(editor.getText()).toBe "" + + it "composes with motions", -> + keydown 'd' + keydown 'd' + keydown '2' + keydown '.' + + expect(editor.getText()).toBe "78" + + describe "the r keybinding", -> + beforeEach -> + editor.setText("12\n34\n\n") + editor.setCursorBufferPosition([0, 0]) + editor.addCursorAtBufferPosition([1, 0]) + + it "replaces a single character", -> + keydown('r') + commandModeInputKeydown('x') + expect(editor.getText()).toBe 'x2\nx4\n\n' + + it "does nothing when cancelled", -> + keydown('r') + expect(editorElement.classList.contains('operator-pending-mode')).toBe(true) + keydown('escape') + expect(editor.getText()).toBe '12\n34\n\n' + expect(editorElement.classList.contains('command-mode')).toBe(true) + + it "replaces a single character with a line break", -> + keydown('r') + atom.commands.dispatch(editor.commandModeInputView.editorElement, 'core:confirm') + expect(editor.getText()).toBe '\n2\n\n4\n\n' + expect(editor.getCursorBufferPositions()).toEqual [[1, 0], [3, 0]] + + it "composes properly with motions", -> + keydown('2') + keydown('r') + commandModeInputKeydown('x') + expect(editor.getText()).toBe 'xx\nxx\n\n' + + it "does nothing on an empty line", -> + editor.setCursorBufferPosition([2, 0]) + keydown('r') + commandModeInputKeydown('x') + expect(editor.getText()).toBe '12\n34\n\n' + + it "does nothing if asked to replace more characters than there are on a line", -> + keydown('3') + keydown('r') + commandModeInputKeydown('x') + expect(editor.getText()).toBe '12\n34\n\n' + + describe "when in visual mode", -> + beforeEach -> + keydown('v') + keydown('e') + + it "replaces the entire selection with the given character", -> + keydown('r') + commandModeInputKeydown('x') + expect(editor.getText()).toBe 'xx\nxx\n\n' + + it "leaves the cursor at the beginning of the selection", -> + keydown('r') + commandModeInputKeydown('x') + expect(editor.getCursorBufferPositions()).toEqual [[0, 0], [1, 0]] + + describe 'the m keybinding', -> + beforeEach -> + editor.setText('12\n34\n56\n') + editor.setCursorBufferPosition([0, 1]) + + it 'marks a position', -> + keydown('m') + commandModeInputKeydown('a') + expect(vimState.getMark('a')).toEqual [0, 1] + + describe 'the ~ keybinding', -> + beforeEach -> + editor.setText('aBc\nXyZ') + editor.setCursorBufferPosition([0, 0]) + editor.addCursorAtBufferPosition([1, 0]) + + it 'toggles the case and moves right', -> + keydown('~') + expect(editor.getText()).toBe 'ABc\nxyZ' + expect(editor.getCursorScreenPositions()).toEqual [[0, 1], [1, 1]] + + keydown('~') + expect(editor.getText()).toBe 'Abc\nxYZ' + expect(editor.getCursorScreenPositions()).toEqual [[0, 2], [1, 2]] + + keydown('~') + expect(editor.getText()).toBe 'AbC\nxYz' + expect(editor.getCursorScreenPositions()).toEqual [[0, 2], [1, 2]] + + it 'takes a count', -> + keydown('4') + keydown('~') + + expect(editor.getText()).toBe 'AbC\nxYz' + expect(editor.getCursorScreenPositions()).toEqual [[0, 2], [1, 2]] + + describe "in visual mode", -> + it "toggles the case of the selected text", -> + editor.setCursorBufferPosition([0, 0]) + keydown("V", shift: true) + keydown("~") + expect(editor.getText()).toBe 'AbC\nXyZ' + + describe "with g and motion", -> + it "toggles the case of text", -> + editor.setCursorBufferPosition([0, 0]) + keydown("g") + keydown("~") + keydown("2") + keydown("l") + expect(editor.getText()).toBe 'Abc\nXyZ' + + describe 'the U keybinding', -> + beforeEach -> + editor.setText('aBc\nXyZ') + editor.setCursorBufferPosition([0, 0]) + + it "makes text uppercase with g and motion", -> + keydown("g") + keydown("U", shift: true) + keydown("l") + expect(editor.getText()).toBe 'ABc\nXyZ' + + keydown("g") + keydown("U", shift: true) + keydown("e") + expect(editor.getText()).toBe 'ABC\nXyZ' + + editor.setCursorBufferPosition([1, 0]) + keydown("g") + keydown("U", shift: true) + keydown("$") + expect(editor.getText()).toBe 'ABC\nXYZ' + expect(editor.getCursorScreenPosition()).toEqual [1, 2] + + it "makes the selected text uppercase in visual mode", -> + keydown("V", shift: true) + keydown("U", shift: true) + expect(editor.getText()).toBe 'ABC\nXyZ' + + describe 'the u keybinding', -> + beforeEach -> + editor.setText('aBc\nXyZ') + editor.setCursorBufferPosition([0, 0]) + + it "makes text lowercase with g and motion", -> + keydown("g") + keydown("u") + keydown("$") + expect(editor.getText()).toBe 'abc\nXyZ' + expect(editor.getCursorScreenPosition()).toEqual [0, 2] + + it "makes the selected text lowercase in visual mode", -> + keydown("V", shift: true) + keydown("u") + expect(editor.getText()).toBe 'abc\nXyZ' + + describe "the i keybinding", -> + beforeEach -> + editor.setText('123\n4567') + editor.setCursorBufferPosition([0, 0]) + editor.addCursorAtBufferPosition([1, 0]) + + it "allows undoing an entire batch of typing", -> + keydown 'i' + editor.insertText("abcXX") + editor.backspace() + editor.backspace() + keydown 'escape' + expect(editor.getText()).toBe "abc123\nabc4567" + + keydown 'i' + editor.insertText "d" + editor.insertText "e" + editor.insertText "f" + keydown 'escape' + expect(editor.getText()).toBe "abdefc123\nabdefc4567" + + keydown 'u' + expect(editor.getText()).toBe "abc123\nabc4567" + + keydown 'u' + expect(editor.getText()).toBe "123\n4567" + + it "allows repeating typing", -> + keydown 'i' + editor.insertText("abcXX") + editor.backspace() + editor.backspace() + keydown 'escape' + expect(editor.getText()).toBe "abc123\nabc4567" + + keydown '.' + expect(editor.getText()).toBe "ababcc123\nababcc4567" + + keydown '.' + expect(editor.getText()).toBe "abababccc123\nabababccc4567" + + describe 'with nonlinear input', -> + beforeEach -> + editor.setText '' + editor.setCursorBufferPosition [0, 0] + + it 'deals with auto-matched brackets', -> + keydown 'i' + # this sequence simulates what the bracket-matcher package does + # when the user types (a)b + editor.insertText '()' + editor.moveLeft() + editor.insertText 'a' + editor.moveRight() + editor.insertText 'b\n' + keydown 'escape' + expect(editor.getCursorScreenPosition()).toEqual [1, 0] + + keydown '.' + expect(editor.getText()).toBe '(a)b\n(a)b\n' + expect(editor.getCursorScreenPosition()).toEqual [2, 0] + + it 'deals with autocomplete', -> + keydown 'i' + # this sequence simulates autocompletion of 'add' to 'addFoo' + editor.insertText 'a' + editor.insertText 'd' + editor.insertText 'd' + editor.setTextInBufferRange [[0, 0], [0, 3]], 'addFoo' + keydown 'escape' + expect(editor.getCursorScreenPosition()).toEqual [0, 5] + expect(editor.getText()).toBe 'addFoo' + + keydown '.' + expect(editor.getText()).toBe 'addFoaddFooo' + expect(editor.getCursorScreenPosition()).toEqual [0, 10] + + describe 'the a keybinding', -> + beforeEach -> + editor.setText('') + editor.setCursorBufferPosition([0, 0]) + + it "can be undone in one go", -> + keydown 'a' + editor.insertText("abc") + keydown 'escape' + expect(editor.getText()).toBe "abc" + keydown 'u' + expect(editor.getText()).toBe "" + + it "repeats correctly", -> + keydown 'a' + editor.insertText("abc") + keydown 'escape' + expect(editor.getText()).toBe "abc" + expect(editor.getCursorScreenPosition()).toEqual [0, 2] + keydown '.' + expect(editor.getText()).toBe "abcabc" + expect(editor.getCursorScreenPosition()).toEqual [0, 5] + + describe "the ctrl-a/ctrl-x keybindings", -> + beforeEach -> + atom.config.set 'vim-mode.numberRegex', settings.config.numberRegex.default + editor.setText('123\nab45\ncd-67ef\nab-5\na-bcdef') + editor.setCursorBufferPosition [0, 0] + editor.addCursorAtBufferPosition [1, 0] + editor.addCursorAtBufferPosition [2, 0] + editor.addCursorAtBufferPosition [3, 3] + editor.addCursorAtBufferPosition [4, 0] + + describe "increasing numbers", -> + it "increases the next number", -> + keydown('a', ctrl: true) + expect(editor.getCursorBufferPositions()).toEqual [[0, 2], [1, 3], [2, 4], [3, 3], [4, 0]] + expect(editor.getText()).toBe '124\nab46\ncd-66ef\nab-4\na-bcdef' + + it "repeats with .", -> + keydown 'a', ctrl: true + keydown '.' + expect(editor.getCursorBufferPositions()).toEqual [[0, 2], [1, 3], [2, 4], [3, 3], [4, 0]] + expect(editor.getText()).toBe '125\nab47\ncd-65ef\nab-3\na-bcdef' + + it "can have a count", -> + keydown '5' + keydown 'a', ctrl: true + expect(editor.getCursorBufferPositions()).toEqual [[0, 2], [1, 3], [2, 4], [3, 2], [4, 0]] + expect(editor.getText()).toBe '128\nab50\ncd-62ef\nab0\na-bcdef' + + it "can make a negative number positive, change number of digits", -> + keydown '9' + keydown '9' + keydown 'a', ctrl: true + expect(editor.getCursorBufferPositions()).toEqual [[0, 2], [1, 4], [2, 3], [3, 3], [4, 0]] + expect(editor.getText()).toBe '222\nab144\ncd32ef\nab94\na-bcdef' + + it "does nothing when cursor is after the number", -> + editor.setCursorBufferPosition [2, 5] + keydown 'a', ctrl: true + expect(editor.getCursorBufferPositions()).toEqual [[2, 5]] + expect(editor.getText()).toBe '123\nab45\ncd-67ef\nab-5\na-bcdef' + + it "does nothing on an empty line", -> + editor.setText('\n') + editor.setCursorBufferPosition [0, 0] + editor.addCursorAtBufferPosition [1, 0] + keydown 'a', ctrl: true + expect(editor.getCursorBufferPositions()).toEqual [[0, 0], [1, 0]] + expect(editor.getText()).toBe '\n' + + it "honours the vim-mode:numberRegex setting", -> + editor.setText('123\nab45\ncd -67ef\nab-5\na-bcdef') + editor.setCursorBufferPosition [0, 0] + editor.addCursorAtBufferPosition [1, 0] + editor.addCursorAtBufferPosition [2, 0] + editor.addCursorAtBufferPosition [3, 3] + editor.addCursorAtBufferPosition [4, 0] + atom.config.set('vim-mode.numberRegex', '(?:\\B-)?[0-9]+') + keydown('a', ctrl: true) + expect(editor.getCursorBufferPositions()).toEqual [[0, 2], [1, 3], [2, 5], [3, 3], [4, 0]] + expect(editor.getText()).toBe '124\nab46\ncd -66ef\nab-6\na-bcdef' + + describe "decreasing numbers", -> + it "decreases the next number", -> + keydown('x', ctrl: true) + expect(editor.getCursorBufferPositions()).toEqual [[0, 2], [1, 3], [2, 4], [3, 3], [4, 0]] + expect(editor.getText()).toBe '122\nab44\ncd-68ef\nab-6\na-bcdef' + + it "repeats with .", -> + keydown 'x', ctrl: true + keydown '.' + expect(editor.getCursorBufferPositions()).toEqual [[0, 2], [1, 3], [2, 4], [3, 3], [4, 0]] + expect(editor.getText()).toBe '121\nab43\ncd-69ef\nab-7\na-bcdef' + + it "can have a count", -> + keydown '5' + keydown 'x', ctrl: true + expect(editor.getCursorBufferPositions()).toEqual [[0, 2], [1, 3], [2, 4], [3, 4], [4, 0]] + expect(editor.getText()).toBe '118\nab40\ncd-72ef\nab-10\na-bcdef' + + it "can make a positive number negative, change number of digits", -> + keydown '9' + keydown '9' + keydown 'x', ctrl: true + expect(editor.getCursorBufferPositions()).toEqual [[0, 1], [1, 4], [2, 5], [3, 5], [4, 0]] + expect(editor.getText()).toBe '24\nab-54\ncd-166ef\nab-104\na-bcdef' + + it "does nothing when cursor is after the number", -> + editor.setCursorBufferPosition [2, 5] + keydown 'x', ctrl: true + expect(editor.getCursorBufferPositions()).toEqual [[2, 5]] + expect(editor.getText()).toBe '123\nab45\ncd-67ef\nab-5\na-bcdef' + + it "does nothing on an empty line", -> + editor.setText('\n') + editor.setCursorBufferPosition [0, 0] + editor.addCursorAtBufferPosition [1, 0] + keydown 'x', ctrl: true + expect(editor.getCursorBufferPositions()).toEqual [[0, 0], [1, 0]] + expect(editor.getText()).toBe '\n' + + it "honours the vim-mode:numberRegex setting", -> + editor.setText('123\nab45\ncd -67ef\nab-5\na-bcdef') + editor.setCursorBufferPosition [0, 0] + editor.addCursorAtBufferPosition [1, 0] + editor.addCursorAtBufferPosition [2, 0] + editor.addCursorAtBufferPosition [3, 3] + editor.addCursorAtBufferPosition [4, 0] + atom.config.set('vim-mode.numberRegex', '(?:\\B-)?[0-9]+') + keydown('x', ctrl: true) + expect(editor.getCursorBufferPositions()).toEqual [[0, 2], [1, 3], [2, 5], [3, 3], [4, 0]] + expect(editor.getText()).toBe '122\nab44\ncd -68ef\nab-4\na-bcdef' -- cgit