aboutsummaryrefslogtreecommitdiff
path: root/atom/packages/vim-mode/spec
diff options
context:
space:
mode:
authorBen Beltran <ben@nsovocal.com>2015-07-10 11:12:25 -0500
committerBen Beltran <ben@nsovocal.com>2015-07-10 11:12:25 -0500
commit24c7594d62d8d7fbbcdb64b11ce4adc5d8e6991a (patch)
treeded312222bb108923da1820ba40b04d710d20e5b /atom/packages/vim-mode/spec
parenteb786e82d170e2abc351a432ade616d6ecdeeb6b (diff)
Adds atom
Diffstat (limited to 'atom/packages/vim-mode/spec')
-rw-r--r--atom/packages/vim-mode/spec/motions-spec.coffee1865
-rw-r--r--atom/packages/vim-mode/spec/operators-spec.coffee1748
-rw-r--r--atom/packages/vim-mode/spec/prefixes-spec.coffee148
-rw-r--r--atom/packages/vim-mode/spec/scroll-spec.coffee122
-rw-r--r--atom/packages/vim-mode/spec/spec-helper.coffee66
-rw-r--r--atom/packages/vim-mode/spec/text-objects-spec.coffee481
-rw-r--r--atom/packages/vim-mode/spec/vim-mode-spec.coffee56
-rw-r--r--atom/packages/vim-mode/spec/vim-state-spec.coffee420
8 files changed, 4906 insertions, 0 deletions
diff --git a/atom/packages/vim-mode/spec/motions-spec.coffee b/atom/packages/vim-mode/spec/motions-spec.coffee
new file mode 100644
index 0000000..f7114ed
--- /dev/null
+++ b/atom/packages/vim-mode/spec/motions-spec.coffee
@@ -0,0 +1,1865 @@
+helpers = require './spec-helper'
+
+describe "Motions", ->
+ [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)
+
+ submitCommandModeInputText = (text) ->
+ commandEditor = editor.commandModeInputView.editorElement
+ commandEditor.getModel().setText(text)
+ atom.commands.dispatch(commandEditor, "core:confirm")
+
+ describe "simple motions", ->
+ beforeEach ->
+ editor.setText("12345\nabcd\nABCDE")
+ editor.setCursorScreenPosition([1, 1])
+
+ describe "the h keybinding", ->
+ describe "as a motion", ->
+ it "moves the cursor left, but not to the previous line", ->
+ keydown('h')
+ expect(editor.getCursorScreenPosition()).toEqual [1, 0]
+
+ keydown('h')
+ expect(editor.getCursorScreenPosition()).toEqual [1, 0]
+
+ it "moves the cursor to the previous line if wrapLeftRightMotion is true", ->
+ atom.config.set('vim-mode.wrapLeftRightMotion', true)
+ keydown('h')
+ keydown('h')
+ expect(editor.getCursorScreenPosition()).toEqual [0, 4]
+
+ describe "as a selection", ->
+ it "selects the character to the left", ->
+ keydown('y')
+ keydown('h')
+
+ expect(vimState.getRegister('"').text).toBe 'a'
+ expect(editor.getCursorScreenPosition()).toEqual [1, 0]
+
+ describe "the j keybinding", ->
+ it "moves the cursor down, but not to the end of the last line", ->
+ keydown('j')
+ expect(editor.getCursorScreenPosition()).toEqual [2, 1]
+
+ keydown('j')
+ expect(editor.getCursorScreenPosition()).toEqual [2, 1]
+
+ it "moves the cursor to the end of the line, not past it", ->
+ editor.setCursorScreenPosition([0, 4])
+
+ keydown('j')
+ expect(editor.getCursorScreenPosition()).toEqual [1, 3]
+
+ it "remembers the position it column it was in after moving to shorter line", ->
+ editor.setCursorScreenPosition([0, 4])
+
+ keydown('j')
+ expect(editor.getCursorScreenPosition()).toEqual [1, 3]
+
+ keydown('j')
+ expect(editor.getCursorScreenPosition()).toEqual [2, 4]
+
+ describe "when visual mode", ->
+ beforeEach ->
+ keydown('v')
+ expect(editor.getCursorScreenPosition()).toEqual [1, 2]
+
+ it "moves the cursor down", ->
+ keydown('j')
+ expect(editor.getCursorScreenPosition()).toEqual [2, 2]
+
+ it "doesn't go over after the last line", ->
+ keydown('j')
+ expect(editor.getCursorScreenPosition()).toEqual [2, 2]
+
+ it "selects the text while moving", ->
+ keydown('j')
+ expect(editor.getSelectedText()).toBe "bcd\nAB"
+
+ describe "the k keybinding", ->
+ it "moves the cursor up, but not to the beginning of the first line", ->
+ keydown('k')
+ expect(editor.getCursorScreenPosition()).toEqual [0, 1]
+
+ keydown('k')
+ expect(editor.getCursorScreenPosition()).toEqual [0, 1]
+
+ describe "the l keybinding", ->
+ beforeEach -> editor.setCursorScreenPosition([1, 2])
+
+ it "moves the cursor right, but not to the next line", ->
+ keydown('l')
+ expect(editor.getCursorScreenPosition()).toEqual [1, 3]
+
+ keydown('l')
+ expect(editor.getCursorScreenPosition()).toEqual [1, 3]
+
+ it "moves the cursor to the next line if wrapLeftRightMotion is true", ->
+ atom.config.set('vim-mode.wrapLeftRightMotion', true)
+ keydown('l')
+ keydown('l')
+ expect(editor.getCursorScreenPosition()).toEqual [2, 0]
+
+ describe "on a blank line", ->
+ it "doesn't move the cursor", ->
+ editor.setText("\n\n\n")
+ editor.setCursorBufferPosition([1, 0])
+ keydown('l')
+ expect(editor.getCursorBufferPosition()).toEqual [1, 0]
+
+ describe "the w keybinding", ->
+ beforeEach -> editor.setText("ab cde1+- \n xyz\n\nzip")
+
+ describe "as a motion", ->
+ beforeEach -> editor.setCursorScreenPosition([0, 0])
+
+ it "moves the cursor to the beginning of the next word", ->
+ keydown('w')
+ expect(editor.getCursorScreenPosition()).toEqual [0, 3]
+
+ keydown('w')
+ expect(editor.getCursorScreenPosition()).toEqual [0, 7]
+
+ keydown('w')
+ expect(editor.getCursorScreenPosition()).toEqual [1, 1]
+
+ 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]
+
+ # After cursor gets to the EOF, it should stay there.
+ keydown('w')
+ expect(editor.getCursorScreenPosition()).toEqual [3, 3]
+
+ 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])
+
+ describe "as a selection", ->
+ describe "within a word", ->
+ beforeEach ->
+ editor.setCursorScreenPosition([0, 0])
+ keydown('y')
+ keydown('w')
+
+ it "selects to the end of the word", ->
+ expect(vimState.getRegister('"').text).toBe 'ab '
+
+ describe "between words", ->
+ beforeEach ->
+ editor.setCursorScreenPosition([0, 2])
+ keydown('y')
+ keydown('w')
+
+ it "selects the whitespace", ->
+ expect(vimState.getRegister('"').text).toBe ' '
+
+ describe "the W keybinding", ->
+ beforeEach -> editor.setText("cde1+- ab \n xyz\n\nzip")
+
+ describe "as a motion", ->
+ beforeEach -> editor.setCursorScreenPosition([0, 0])
+
+ it "moves the cursor to the beginning of the next word", ->
+ keydown('W', shift: true)
+ expect(editor.getCursorScreenPosition()).toEqual [0, 7]
+
+ keydown('W', shift: true)
+ expect(editor.getCursorScreenPosition()).toEqual [1, 1]
+
+ keydown('W', shift: true)
+ expect(editor.getCursorScreenPosition()).toEqual [2, 0]
+
+ keydown('W', shift: true)
+ expect(editor.getCursorScreenPosition()).toEqual [3, 0]
+
+ describe "as a selection", ->
+ describe "within a word", ->
+ it "selects to the end of the whole word", ->
+ editor.setCursorScreenPosition([0, 0])
+ keydown('y')
+ keydown('W', shift: true)
+ expect(vimState.getRegister('"').text).toBe 'cde1+- '
+
+ it "continues past blank lines", ->
+ editor.setCursorScreenPosition([2, 0])
+
+ keydown('d')
+ keydown('W', shift: true)
+ expect(editor.getText()).toBe "cde1+- ab \n xyz\nzip"
+ expect(vimState.getRegister('"').text).toBe '\n'
+
+ it "doesn't go past the end of the file", ->
+ editor.setCursorScreenPosition([3, 0])
+
+ keydown('d')
+ keydown('W', shift: true)
+ expect(editor.getText()).toBe "cde1+- ab \n xyz\n\n"
+ expect(vimState.getRegister('"').text).toBe 'zip'
+
+ describe "the e keybinding", ->
+ beforeEach -> editor.setText("ab cde1+- \n xyz\n\nzip")
+
+ describe "as a motion", ->
+ beforeEach -> editor.setCursorScreenPosition([0, 0])
+
+ it "moves the cursor to the end of the current word", ->
+ keydown('e')
+ expect(editor.getCursorScreenPosition()).toEqual [0, 1]
+
+ keydown('e')
+ expect(editor.getCursorScreenPosition()).toEqual [0, 6]
+
+ keydown('e')
+ expect(editor.getCursorScreenPosition()).toEqual [0, 8]
+
+ keydown('e')
+ expect(editor.getCursorScreenPosition()).toEqual [1, 3]
+
+ keydown('e')
+ expect(editor.getCursorScreenPosition()).toEqual [3, 2]
+
+ describe "as selection", ->
+ describe "within a word", ->
+ beforeEach ->
+ editor.setCursorScreenPosition([0, 0])
+ keydown('y')
+ keydown('e')
+
+ it "selects to the end of the current word", ->
+ expect(vimState.getRegister('"').text).toBe 'ab'
+
+ describe "between words", ->
+ beforeEach ->
+ editor.setCursorScreenPosition([0, 2])
+ keydown('y')
+ keydown('e')
+
+ it "selects to the end of the next word", ->
+ expect(vimState.getRegister('"').text).toBe ' cde1'
+
+ describe "the E keybinding", ->
+ beforeEach -> editor.setText("ab cde1+- \n xyz \n\nzip\n")
+
+ describe "as a motion", ->
+ beforeEach -> editor.setCursorScreenPosition([0, 0])
+
+ it "moves the cursor to the end of the current word", ->
+ keydown('E', shift: true)
+ expect(editor.getCursorScreenPosition()).toEqual [0, 1]
+
+ keydown('E', shift: true)
+ expect(editor.getCursorScreenPosition()).toEqual [0, 9]
+
+ keydown('E', shift: true)
+ expect(editor.getCursorScreenPosition()).toEqual [1, 3]
+
+ keydown('E', shift: true)
+ expect(editor.getCursorScreenPosition()).toEqual [3, 2]
+
+ keydown('E', shift: true)
+ expect(editor.getCursorScreenPosition()).toEqual [4, 0]
+
+ describe "as selection", ->
+ describe "within a word", ->
+ beforeEach ->
+ editor.setCursorScreenPosition([0, 0])
+ keydown('y')
+ keydown('E', shift: true)
+
+ it "selects to the end of the current word", ->
+ expect(vimState.getRegister('"').text).toBe 'ab'
+
+ describe "between words", ->
+ beforeEach ->
+ editor.setCursorScreenPosition([0, 2])
+ keydown('y')
+ keydown('E', shift: true)
+
+ it "selects to the end of the next word", ->
+ expect(vimState.getRegister('"').text).toBe ' cde1+-'
+
+ describe "press more than once", ->
+ beforeEach ->
+ editor.setCursorScreenPosition([0, 0])
+ keydown('v')
+ keydown('E', shift: true)
+ keydown('E', shift: true)
+ keydown('y')
+
+ it "selects to the end of the current word", ->
+ expect(vimState.getRegister('"').text).toBe 'ab cde1+-'
+
+ describe "the } keybinding", ->
+ beforeEach ->
+ editor.setText("abcde\n\nfghij\nhijk\n xyz \n\nzip\n\n \nthe end")
+ editor.setCursorScreenPosition([0, 0])
+
+ describe "as a motion", ->
+ it "moves the cursor to the end of the paragraph", ->
+ keydown('}')
+ expect(editor.getCursorScreenPosition()).toEqual [1, 0]
+
+ keydown('}')
+ expect(editor.getCursorScreenPosition()).toEqual [5, 0]
+
+ keydown('}')
+ expect(editor.getCursorScreenPosition()).toEqual [7, 0]
+
+ keydown('}')
+ expect(editor.getCursorScreenPosition()).toEqual [9, 6]
+
+ describe "as a selection", ->
+ beforeEach ->
+ keydown('y')
+ keydown('}')
+
+ it 'selects to the end of the current paragraph', ->
+ expect(vimState.getRegister('"').text).toBe "abcde\n"
+
+ describe "the { keybinding", ->
+ beforeEach ->
+ editor.setText("abcde\n\nfghij\nhijk\n xyz \n\nzip\n\n \nthe end")
+ editor.setCursorScreenPosition([9, 0])
+
+ describe "as a motion", ->
+ it "moves the cursor to the beginning of the paragraph", ->
+ keydown('{')
+ expect(editor.getCursorScreenPosition()).toEqual [7, 0]
+
+ keydown('{')
+ expect(editor.getCursorScreenPosition()).toEqual [5, 0]
+
+ keydown('{')
+ expect(editor.getCursorScreenPosition()).toEqual [1, 0]
+
+ keydown('{')
+ expect(editor.getCursorScreenPosition()).toEqual [0, 0]
+
+ describe "as a selection", ->
+ beforeEach ->
+ editor.setCursorScreenPosition([7, 0])
+ keydown('y')
+ keydown('{')
+
+ it 'selects to the beginning of the current paragraph', ->
+ expect(vimState.getRegister('"').text).toBe "\nzip\n"
+
+ describe "the b keybinding", ->
+ beforeEach -> editor.setText(" ab cde1+- \n xyz\n\nzip }\n last")
+
+ describe "as a motion", ->
+ beforeEach -> editor.setCursorScreenPosition([4, 1])
+
+ it "moves the cursor to the beginning of the previous word", ->
+ keydown('b')
+ expect(editor.getCursorScreenPosition()).toEqual [3, 4]
+
+ keydown('b')
+ expect(editor.getCursorScreenPosition()).toEqual [3, 0]
+
+ keydown('b')
+ expect(editor.getCursorScreenPosition()).toEqual [2, 0]
+
+ keydown('b')
+ expect(editor.getCursorScreenPosition()).toEqual [1, 1]
+
+ keydown('b')
+ expect(editor.getCursorScreenPosition()).toEqual [0, 8]
+
+ keydown('b')
+ expect(editor.getCursorScreenPosition()).toEqual [0, 4]
+
+ keydown('b')
+ expect(editor.getCursorScreenPosition()).toEqual [0, 1]
+
+ # Go to start of the file, after moving past the first word
+ keydown('b')
+ expect(editor.getCursorScreenPosition()).toEqual [0, 0]
+
+ # Stay at the start of the file
+ keydown('b')
+ expect(editor.getCursorScreenPosition()).toEqual [0, 0]
+
+ describe "as a selection", ->
+ describe "within a word", ->
+ beforeEach ->
+ editor.setCursorScreenPosition([0, 2])
+ keydown('y')
+ keydown('b')
+
+ it "selects to the beginning of the current word", ->
+ expect(vimState.getRegister('"').text).toBe 'a'
+ expect(editor.getCursorScreenPosition()).toEqual [0, 1]
+
+ describe "between words", ->
+ beforeEach ->
+ editor.setCursorScreenPosition([0, 4])
+ keydown('y')
+ keydown('b')
+
+ it "selects to the beginning of the last word", ->
+ expect(vimState.getRegister('"').text).toBe 'ab '
+ expect(editor.getCursorScreenPosition()).toEqual [0, 1]
+
+ describe "the B keybinding", ->
+ beforeEach -> editor.setText("cde1+- ab \n\t xyz-123\n\n zip")
+
+ describe "as a motion", ->
+ beforeEach -> editor.setCursorScreenPosition([4, 1])
+
+ it "moves the cursor to the beginning of the previous word", ->
+ keydown('B', shift: true)
+ expect(editor.getCursorScreenPosition()).toEqual [3, 1]
+
+ keydown('B', shift: true)
+ expect(editor.getCursorScreenPosition()).toEqual [2, 0]
+
+ keydown('B', shift: true)
+ expect(editor.getCursorScreenPosition()).toEqual [1, 3]
+
+ keydown('B', shift: true)
+ expect(editor.getCursorScreenPosition()).toEqual [0, 7]
+
+ keydown('B', shift: true)
+ expect(editor.getCursorScreenPosition()).toEqual [0, 0]
+
+ describe "as a selection", ->
+ it "selects to the beginning of the whole word", ->
+ editor.setCursorScreenPosition([1, 10])
+ keydown('y')
+ keydown('B', shift: true)
+ expect(vimState.getRegister('"').text).toBe 'xyz-123'
+
+ it "doesn't go past the beginning of the file", ->
+ editor.setCursorScreenPosition([0, 0])
+ vimState.setRegister('"', text: 'abc')
+ keydown('y')
+ keydown('B', shift: true)
+ expect(vimState.getRegister('"').text).toBe 'abc'
+
+ describe "the ^ keybinding", ->
+ beforeEach ->
+ editor.setText(" abcde")
+
+ describe "from the beginning of the line", ->
+ beforeEach -> editor.setCursorScreenPosition([0, 0])
+
+ describe "as a motion", ->
+ beforeEach -> keydown('^')
+
+ it "moves the cursor to the first character of the line", ->
+ expect(editor.getCursorScreenPosition()).toEqual [0, 2]
+
+ describe "as a selection", ->
+ beforeEach ->
+ keydown('d')
+ keydown('^')
+
+ it 'selects to the first character of the line', ->
+ expect(editor.getText()).toBe 'abcde'
+ expect(editor.getCursorScreenPosition()).toEqual [0, 0]
+
+ describe "from the first character of the line", ->
+ beforeEach -> editor.setCursorScreenPosition([0, 2])
+
+ describe "as a motion", ->
+ beforeEach -> keydown('^')
+
+ it "stays put", ->
+ expect(editor.getCursorScreenPosition()).toEqual [0, 2]
+
+ describe "as a selection", ->
+ beforeEach ->
+ keydown('d')
+ keydown('^')
+
+ it "does nothing", ->
+ expect(editor.getText()).toBe ' abcde'
+ expect(editor.getCursorScreenPosition()).toEqual [0, 2]
+
+ describe "from the middle of a word", ->
+ beforeEach -> editor.setCursorScreenPosition([0, 4])
+
+ describe "as a motion", ->
+ beforeEach -> keydown('^')
+
+ it "moves the cursor to the first character of the line", ->
+ expect(editor.getCursorScreenPosition()).toEqual [0, 2]
+
+ describe "as a selection", ->
+ beforeEach ->
+ keydown('d')
+ keydown('^')
+
+ it 'selects to the first character of the line', ->
+ expect(editor.getText()).toBe ' cde'
+ expect(editor.getCursorScreenPosition()).toEqual [0, 2]
+
+ describe "the 0 keybinding", ->
+ beforeEach ->
+ editor.setText(" abcde")
+ editor.setCursorScreenPosition([0, 4])
+
+ describe "as a motion", ->
+ beforeEach -> keydown('0')
+
+ it "moves the cursor to the first column", ->
+ expect(editor.getCursorScreenPosition()).toEqual [0, 0]
+
+ describe "as a selection", ->
+ beforeEach ->
+ keydown('d')
+ keydown('0')
+
+ it 'selects to the first column of the line', ->
+ expect(editor.getText()).toBe 'cde'
+ expect(editor.getCursorScreenPosition()).toEqual [0, 0]
+
+ describe "the $ keybinding", ->
+ beforeEach ->
+ editor.setText(" abcde\n\n1234567890")
+ editor.setCursorScreenPosition([0, 4])
+
+ describe "as a motion from empty line", ->
+ beforeEach -> editor.setCursorScreenPosition([1, 0])
+
+ it "moves the cursor to the end of the line", ->
+ expect(editor.getCursorScreenPosition()).toEqual [1, 0]
+
+ describe "as a motion", ->
+ beforeEach -> keydown('$')
+
+ # FIXME: See atom/vim-mode#2
+ it "moves the cursor to the end of the line", ->
+ expect(editor.getCursorScreenPosition()).toEqual [0, 6]
+
+ it "should remain in the last column when moving down", ->
+ keydown('j')
+ expect(editor.getCursorScreenPosition()).toEqual [1, 0]
+
+ keydown('j')
+ expect(editor.getCursorScreenPosition()).toEqual [2, 9]
+
+ describe "as a selection", ->
+ beforeEach ->
+ keydown('d')
+ keydown('$')
+
+ it "selects to the beginning of the lines", ->
+ expect(editor.getText()).toBe " ab\n\n1234567890"
+ expect(editor.getCursorScreenPosition()).toEqual [0, 3]
+
+ describe "the 0 keybinding", ->
+ beforeEach ->
+ editor.setText(" a\n")
+ editor.setCursorScreenPosition([0, 2])
+
+ describe "as a motion", ->
+ beforeEach -> keydown('0')
+
+ it "moves the cursor to the beginning of the line", ->
+ expect(editor.getCursorScreenPosition()).toEqual [0, 0]
+
+ describe "the - keybinding", ->
+ beforeEach ->
+ editor.setText("abcdefg\n abc\n abc\n")
+
+ describe "from the middle of a line", ->
+ beforeEach -> editor.setCursorScreenPosition([1, 3])
+
+ describe "as a motion", ->
+ beforeEach -> keydown('-')
+
+ it "moves the cursor to the first character of the previous line", ->
+ expect(editor.getCursorScreenPosition()).toEqual [0, 0]
+
+ describe "as a selection", ->
+ beforeEach ->
+ keydown('d')
+ keydown('-')
+
+ it "deletes the current and previous line", ->
+ expect(editor.getText()).toBe " abc\n"
+ # commented out because the column is wrong due to a bug in `k`; re-enable when `k` is fixed
+ #expect(editor.getCursorScreenPosition()).toEqual [0, 3]
+
+ describe "from the first character of a line indented the same as the previous one", ->
+ beforeEach -> editor.setCursorScreenPosition([2, 2])
+
+ describe "as a motion", ->
+ beforeEach -> keydown('-')
+
+ it "moves to the first character of the previous line (directly above)", ->
+ expect(editor.getCursorScreenPosition()).toEqual [1, 2]
+
+ describe "as a selection", ->
+ beforeEach ->
+ keydown('d')
+ keydown('-')
+
+ it "selects to the first character of the previous line (directly above)", ->
+ expect(editor.getText()).toBe "abcdefg\n"
+ # commented out because the column is wrong due to a bug in `k`; re-enable when `k` is fixed
+ #expect(editor.getCursorScreenPosition()).toEqual [0, 2]
+
+ describe "from the beginning of a line preceded by an indented line", ->
+ beforeEach -> editor.setCursorScreenPosition([2, 0])
+
+ describe "as a motion", ->
+ beforeEach -> keydown('-')
+
+ it "moves the cursor to the first character of the previous line", ->
+ expect(editor.getCursorScreenPosition()).toEqual [1, 2]
+
+ describe "as a selection", ->
+ beforeEach ->
+ keydown('d')
+ keydown('-')
+
+ it "selects to the first character of the previous line", ->
+ expect(editor.getText()).toBe "abcdefg\n"
+ # commented out because the column is wrong due to a bug in `k`; re-enable when `k` is fixed
+ #expect(editor.getCursorScreenPosition()).toEqual [0, 0]
+
+ describe "with a count", ->
+ beforeEach ->
+ editor.setText("1\n2\n3\n4\n5\n6\n")
+ editor.setCursorScreenPosition([4, 0])
+
+ describe "as a motion", ->
+ beforeEach ->
+ keydown('3')
+ keydown('-')
+
+ it "moves the cursor to the first character of that many lines previous", ->
+ expect(editor.getCursorScreenPosition()).toEqual [1, 0]
+
+ describe "as a selection", ->
+ beforeEach ->
+ keydown('d')
+ keydown('3')
+ keydown('-')
+
+ it "deletes the current line plus that many previous lines", ->
+ expect(editor.getText()).toBe "1\n6\n"
+ expect(editor.getCursorScreenPosition()).toEqual [1, 0]
+
+ describe "the + keybinding", ->
+ beforeEach ->
+ editor.setText(" abc\n abc\nabcdefg\n")
+
+ describe "from the middle of a line", ->
+ beforeEach -> editor.setCursorScreenPosition([1, 3])
+
+ describe "as a motion", ->
+ beforeEach -> keydown('+')
+
+ it "moves the cursor to the first character of the next line", ->
+ expect(editor.getCursorScreenPosition()).toEqual [2, 0]
+
+ describe "as a selection", ->
+ beforeEach ->
+ keydown('d')
+ keydown('+')
+
+ it "deletes the current and next line", ->
+ expect(editor.getText()).toBe " abc\n"
+ # commented out because the column is wrong due to a bug in `j`; re-enable when `j` is fixed
+ #expect(editor.getCursorScreenPosition()).toEqual [0, 3]
+
+ describe "from the first character of a line indented the same as the next one", ->
+ beforeEach -> editor.setCursorScreenPosition([0, 2])
+
+ describe "as a motion", ->
+ beforeEach -> keydown('+')
+
+ it "moves to the first character of the next line (directly below)", ->
+ expect(editor.getCursorScreenPosition()).toEqual [1, 2]
+
+ describe "as a selection", ->
+ beforeEach ->
+ keydown('d')
+ keydown('+')
+
+ it "selects to the first character of the next line (directly below)", ->
+ expect(editor.getText()).toBe "abcdefg\n"
+ # commented out because the column is wrong due to a bug in `j`; re-enable when `j` is fixed
+ #expect(editor.getCursorScreenPosition()).toEqual [0, 2]
+
+ describe "from the beginning of a line followed by an indented line", ->
+ beforeEach -> editor.setCursorScreenPosition([0, 0])
+
+ describe "as a motion", ->
+ beforeEach -> keydown('+')
+
+ it "moves the cursor to the first character of the next line", ->
+ expect(editor.getCursorScreenPosition()).toEqual [1, 2]
+
+ describe "as a selection", ->
+ beforeEach ->
+ keydown('d')
+ keydown('+')
+
+ it "selects to the first character of the next line", ->
+ expect(editor.getText()).toBe "abcdefg\n"
+ expect(editor.getCursorScreenPosition()).toEqual [0, 0]
+
+ describe "with a count", ->
+ beforeEach ->
+ editor.setText("1\n2\n3\n4\n5\n6\n")
+ editor.setCursorScreenPosition([1, 0])
+
+ describe "as a motion", ->
+ beforeEach ->
+ keydown('3')
+ keydown('+')
+
+ it "moves the cursor to the first character of that many lines following", ->
+ expect(editor.getCursorScreenPosition()).toEqual [4, 0]
+
+ describe "as a selection", ->
+ beforeEach ->
+ keydown('d')
+ keydown('3')
+ keydown('+')
+
+ it "deletes the current line plus that many following lines", ->
+ expect(editor.getText()).toBe "1\n6\n"
+ expect(editor.getCursorScreenPosition()).toEqual [1, 0]
+
+ describe "the _ keybinding", ->
+ beforeEach ->
+ editor.setText(" abc\n abc\nabcdefg\n")
+
+ describe "from the middle of a line", ->
+ beforeEach -> editor.setCursorScreenPosition([1, 3])
+
+ describe "as a motion", ->
+ beforeEach -> keydown('_')
+
+ it "moves the cursor to the first character of the current line", ->
+ expect(editor.getCursorScreenPosition()).toEqual [1, 2]
+
+ describe "as a selection", ->
+ beforeEach ->
+ keydown('d')
+ keydown('_')
+
+ it "deletes the current line", ->
+ expect(editor.getText()).toBe " abc\nabcdefg\n"
+ expect(editor.getCursorScreenPosition()).toEqual [1, 0]
+
+ describe "with a count", ->
+ beforeEach ->
+ editor.setText("1\n2\n3\n4\n5\n6\n")
+ editor.setCursorScreenPosition([1, 0])
+
+ describe "as a motion", ->
+ beforeEach ->
+ keydown('3')
+ keydown('_')
+
+ it "moves the cursor to the first character of that many lines following", ->
+ expect(editor.getCursorScreenPosition()).toEqual [3, 0]
+
+ describe "as a selection", ->
+ beforeEach ->
+ keydown('d')
+ keydown('3')
+ keydown('_')
+
+ it "deletes the current line plus that many following lines", ->
+ expect(editor.getText()).toBe "1\n5\n6\n"
+ expect(editor.getCursorScreenPosition()).toEqual [1, 0]
+
+ describe "the enter keybinding", ->
+ keydownCodeForEnter = '\r' # 'enter' does not work
+ startingText = " abc\n abc\nabcdefg\n"
+
+ describe "from the middle of a line", ->
+ startingCursorPosition = [1, 3]
+
+ describe "as a motion", ->
+ it "acts the same as the + keybinding", ->
+ # do it with + and save the results
+ editor.setText(startingText)
+ editor.setCursorScreenPosition(startingCursorPosition)
+ keydown('+')
+ referenceCursorPosition = editor.getCursorScreenPosition()
+ # do it again with enter and compare the results
+ editor.setText(startingText)
+ editor.setCursorScreenPosition(startingCursorPosition)
+ keydown(keydownCodeForEnter)
+ expect(editor.getCursorScreenPosition()).toEqual referenceCursorPosition
+
+ describe "as a selection", ->
+ it "acts the same as the + keybinding", ->
+ # do it with + and save the results
+ editor.setText(startingText)
+ editor.setCursorScreenPosition(startingCursorPosition)
+ keydown('d')
+ keydown('+')
+ referenceText = editor.getText()
+ referenceCursorPosition = editor.getCursorScreenPosition()
+ # do it again with enter and compare the results
+ editor.setText(startingText)
+ editor.setCursorScreenPosition(startingCursorPosition)
+ keydown('d')
+ keydown(keydownCodeForEnter)
+ expect(editor.getText()).toEqual referenceText
+ expect(editor.getCursorScreenPosition()).toEqual referenceCursorPosition
+
+ describe "the gg keybinding", ->
+ beforeEach ->
+ editor.setText(" 1abc\n 2\n3\n")
+ editor.setCursorScreenPosition([0, 2])
+
+ describe "as a motion", ->
+ describe "in command mode", ->
+ beforeEach ->
+ keydown('g')
+ keydown('g')
+
+ it "moves the cursor to the beginning of the first line", ->
+ expect(editor.getCursorScreenPosition()).toEqual [0, 0]
+
+ describe "in linewise visual mode", ->
+ beforeEach ->
+ editor.setCursorScreenPosition([1, 0])
+ vimState.activateVisualMode('linewise')
+ keydown('g')
+ keydown('g')
+
+ it "selects to the first line in the file", ->
+ expect(editor.getSelectedText()).toBe " 1abc\n 2\n"
+
+ it "moves the cursor to a specified line", ->
+ expect(editor.getCursorScreenPosition()).toEqual [0, 0]
+
+ describe "in characterwise visual mode", ->
+ beforeEach ->
+ editor.setCursorScreenPosition([1, 1])
+ vimState.activateVisualMode()
+ keydown('g')
+ keydown('g')
+
+ it "selects to the first line in the file", ->
+ expect(editor.getSelectedText()).toBe "1abc\n 2"
+
+ it "moves the cursor to a specified line", ->
+ expect(editor.getCursorScreenPosition()).toEqual [0, 1]
+
+ describe "as a repeated motion", ->
+ describe "in command mode", ->
+ beforeEach ->
+ keydown('2')
+ keydown('g')
+ keydown('g')
+
+ it "moves the cursor to a specified line", ->
+ expect(editor.getCursorScreenPosition()).toEqual [1, 0]
+
+ describe "in linewise visual motion", ->
+ beforeEach ->
+ editor.setCursorScreenPosition([2, 0])
+ vimState.activateVisualMode('linewise')
+ keydown('2')
+ keydown('g')
+ keydown('g')
+
+ it "selects to a specified line", ->
+ expect(editor.getSelectedText()).toBe " 2\n3\n"
+
+ it "moves the cursor to a specified line", ->
+ expect(editor.getCursorScreenPosition()).toEqual [1, 0]
+
+ describe "in characterwise visual motion", ->
+ beforeEach ->
+ editor.setCursorScreenPosition([2, 0])
+ vimState.activateVisualMode()
+ keydown('2')
+ keydown('g')
+ keydown('g')
+
+ it "selects to a first character of specified line", ->
+ expect(editor.getSelectedText()).toBe "2\n3"
+
+ it "moves the cursor to a specified line", ->
+ expect(editor.getCursorScreenPosition()).toEqual [1, 1]
+
+ describe "the g_ keybinding", ->
+ beforeEach ->
+ editor.setText("1 \n 2 \n 3abc\n ")
+
+ describe "as a motion", ->
+ it "moves the cursor to the last nonblank character", ->
+ editor.setCursorScreenPosition([1, 0])
+ keydown('g')
+ keydown('_')
+ expect(editor.getCursorScreenPosition()).toEqual [1, 4]
+
+ it "will move the cursor to the beginning of the line if necessary", ->
+ editor.setCursorScreenPosition([0, 2])
+ keydown('g')
+ keydown('_')
+ expect(editor.getCursorScreenPosition()).toEqual [0, 0]
+
+ describe "as a repeated motion", ->
+ it "moves the cursor downward and outward", ->
+ editor.setCursorScreenPosition([0, 0])
+ keydown('2')
+ keydown('g')
+ keydown('_')
+ expect(editor.getCursorScreenPosition()).toEqual [1, 4]
+
+ describe "as a selection", ->
+ it "selects the current line excluding whitespace", ->
+ editor.setCursorScreenPosition([1, 2])
+ vimState.activateVisualMode()
+ keydown('2')
+ keydown('g')
+ keydown('_')
+ expect(editor.getSelectedText()).toEqual " 2 \n 3abc"
+
+ describe "the G keybinding", ->
+ beforeEach ->
+ editor.setText("1\n 2\n 3abc\n ")
+ editor.setCursorScreenPosition([0, 2])
+
+ describe "as a motion", ->
+ beforeEach -> keydown('G', shift: true)
+
+ it "moves the cursor to the last line after whitespace", ->
+ expect(editor.getCursorScreenPosition()).toEqual [3, 1]
+
+ describe "as a repeated motion", ->
+ beforeEach ->
+ keydown('2')
+ keydown('G', shift: true)
+
+ it "moves the cursor to a specified line", ->
+ expect(editor.getCursorScreenPosition()).toEqual [1, 4]
+
+ describe "as a selection", ->
+ beforeEach ->
+ editor.setCursorScreenPosition([1, 0])
+ vimState.activateVisualMode()
+ keydown('G', shift: true)
+
+ it "selects to the last line in the file", ->
+ expect(editor.getSelectedText()).toBe " 2\n 3abc\n "
+
+ it "moves the cursor to the last line after whitespace", ->
+ expect(editor.getCursorScreenPosition()).toEqual [3, 1]
+
+ describe "the / keybinding", ->
+ pane = null
+
+ beforeEach ->
+ pane = {activate: jasmine.createSpy("activate")}
+ spyOn(atom.workspace, 'getActivePane').andReturn(pane)
+
+ editor.setText("abc\ndef\nabc\ndef\n")
+ editor.setCursorBufferPosition([0, 0])
+
+ describe "as a motion", ->
+ it "moves the cursor to the specified search pattern", ->
+ keydown('/')
+
+ submitCommandModeInputText 'def'
+
+ expect(editor.getCursorBufferPosition()).toEqual [1, 0]
+ expect(pane.activate).toHaveBeenCalled()
+
+ it "loops back around", ->
+ editor.setCursorBufferPosition([3, 0])
+ keydown('/')
+ submitCommandModeInputText 'def'
+
+ expect(editor.getCursorBufferPosition()).toEqual [1, 0]
+
+ it "uses a valid regex as a regex", ->
+ keydown('/')
+ # Cycle through the 'abc' on the first line with a character pattern
+ submitCommandModeInputText '[abc]'
+ expect(editor.getCursorBufferPosition()).toEqual [0, 1]
+ keydown('n')
+ expect(editor.getCursorBufferPosition()).toEqual [0, 2]
+
+ it "uses an invalid regex as a literal string", ->
+ # Go straight to the literal [abc
+ editor.setText("abc\n[abc]\n")
+ keydown('/')
+ submitCommandModeInputText '[abc'
+ expect(editor.getCursorBufferPosition()).toEqual [1, 0]
+ keydown('n')
+ expect(editor.getCursorBufferPosition()).toEqual [1, 0]
+
+ it "uses ? as a literal string", ->
+ editor.setText("abc\n[a?c?\n")
+ keydown('/')
+ submitCommandModeInputText '?'
+ expect(editor.getCursorBufferPosition()).toEqual [1, 2]
+ keydown('n')
+ expect(editor.getCursorBufferPosition()).toEqual [1, 4]
+
+ it 'works with selection in visual mode', ->
+ editor.setText('one two three')
+ keydown('v')
+ keydown('/')
+ submitCommandModeInputText 'th'
+ expect(editor.getCursorBufferPosition()).toEqual [0, 9]
+ keydown('d')
+ expect(editor.getText()).toBe 'hree'
+
+ it 'extends selection when repeating search in visual mode', ->
+ editor.setText('line1\nline2\nline3')
+ keydown('v')
+ keydown('/')
+ submitCommandModeInputText 'line'
+ {start, end} = editor.getSelectedBufferRange()
+ expect(start.row).toEqual 0
+ expect(end.row).toEqual 1
+ keydown('n')
+ {start, end} = editor.getSelectedBufferRange()
+ expect(start.row).toEqual 0
+ expect(end.row).toEqual 2
+
+ describe "case sensitivity", ->
+ beforeEach ->
+ editor.setText("\nabc\nABC\n")
+ editor.setCursorBufferPosition([0, 0])
+ keydown('/')
+
+ it "works in case sensitive mode", ->
+ submitCommandModeInputText 'ABC'
+ expect(editor.getCursorBufferPosition()).toEqual [2, 0]
+ keydown('n')
+ expect(editor.getCursorBufferPosition()).toEqual [2, 0]
+
+ it "works in case insensitive mode", ->
+ submitCommandModeInputText '\\cAbC'
+ expect(editor.getCursorBufferPosition()).toEqual [1, 0]
+ keydown('n')
+ expect(editor.getCursorBufferPosition()).toEqual [2, 0]
+
+ it "works in case insensitive mode wherever \\c is", ->
+ submitCommandModeInputText 'AbC\\c'
+ expect(editor.getCursorBufferPosition()).toEqual [1, 0]
+ keydown('n')
+ expect(editor.getCursorBufferPosition()).toEqual [2, 0]
+
+ it "uses case insensitive search if useSmartcaseForSearch is true and searching lowercase", ->
+ atom.config.set 'vim-mode.useSmartcaseForSearch', true
+ submitCommandModeInputText 'abc'
+ expect(editor.getCursorBufferPosition()).toEqual [1, 0]
+ keydown('n')
+ expect(editor.getCursorBufferPosition()).toEqual [2, 0]
+
+ it "uses case sensitive search if useSmartcaseForSearch is true and searching uppercase", ->
+ atom.config.set 'vim-mode.useSmartcaseForSearch', true
+ submitCommandModeInputText 'ABC'
+ expect(editor.getCursorBufferPosition()).toEqual [2, 0]
+ keydown('n')
+ expect(editor.getCursorBufferPosition()).toEqual [2, 0]
+
+ describe "repeating", ->
+ it "does nothing with no search history", ->
+ # This tests that no exception is raised
+ keydown('n')
+
+ beforeEach ->
+ keydown('/')
+ submitCommandModeInputText 'def'
+
+ it "repeats previous search with /<enter>", ->
+ keydown('/')
+ submitCommandModeInputText('')
+ expect(editor.getCursorBufferPosition()).toEqual [3, 0]
+
+ it "repeats previous search with //", ->
+ keydown('/')
+ submitCommandModeInputText('/')
+ expect(editor.getCursorBufferPosition()).toEqual [3, 0]
+
+ describe "the n keybinding", ->
+ it "repeats the last search", ->
+ keydown('n')
+ expect(editor.getCursorBufferPosition()).toEqual [3, 0]
+
+ describe "the N keybinding", ->
+ it "repeats the last search backwards", ->
+ editor.setCursorBufferPosition([0, 0])
+ keydown('N', shift: true)
+ expect(editor.getCursorBufferPosition()).toEqual [3, 0]
+ keydown('N', shift: true)
+ expect(editor.getCursorBufferPosition()).toEqual [1, 0]
+
+ describe "composing", ->
+ it "composes with operators", ->
+ keydown('d')
+ keydown('/')
+ submitCommandModeInputText('def')
+ expect(editor.getText()).toEqual "def\nabc\ndef\n"
+
+ it "repeats correctly with operators", ->
+ keydown('d')
+ keydown('/')
+ submitCommandModeInputText('def')
+
+ keydown('.')
+ expect(editor.getText()).toEqual "def\n"
+
+ describe "when reversed as ?", ->
+ it "moves the cursor backwards to the specified search pattern", ->
+ keydown('?')
+ submitCommandModeInputText('def')
+ expect(editor.getCursorBufferPosition()).toEqual [3, 0]
+
+ it "accepts / as a literal search pattern", ->
+ editor.setText("abc\nd/f\nabc\nd/f\n")
+ editor.setCursorBufferPosition([0, 0])
+ keydown('?')
+ submitCommandModeInputText('/')
+ expect(editor.getCursorBufferPosition()).toEqual [3, 1]
+ keydown('?')
+ submitCommandModeInputText('/')
+ expect(editor.getCursorBufferPosition()).toEqual [1, 1]
+
+ describe "repeating", ->
+ beforeEach ->
+ keydown('?')
+ submitCommandModeInputText('def')
+
+ it "repeats previous search as reversed with ?<enter>", ->
+ keydown('?')
+ submitCommandModeInputText('')
+ expect(editor.getCursorBufferPosition()).toEqual [1, 0]
+
+ it "repeats previous search as reversed with ??", ->
+ keydown('?')
+ submitCommandModeInputText('?')
+ expect(editor.getCursorBufferPosition()).toEqual [1, 0]
+
+ describe 'the n keybinding', ->
+ it "repeats the last search backwards", ->
+ editor.setCursorBufferPosition([0, 0])
+ keydown('n')
+ expect(editor.getCursorBufferPosition()).toEqual [3, 0]
+
+ describe 'the N keybinding', ->
+ it "repeats the last search forwards", ->
+ editor.setCursorBufferPosition([0, 0])
+ keydown('N', shift: true)
+ expect(editor.getCursorBufferPosition()).toEqual [1, 0]
+
+ describe "using search history", ->
+ commandEditor = null
+
+ beforeEach ->
+ keydown('/')
+ submitCommandModeInputText('def')
+ expect(editor.getCursorBufferPosition()).toEqual [1, 0]
+
+ keydown('/')
+ submitCommandModeInputText('abc')
+ expect(editor.getCursorBufferPosition()).toEqual [2, 0]
+
+ commandEditor = editor.commandModeInputView.editorElement
+
+ it "allows searching history in the search field", ->
+ keydown('/')
+ atom.commands.dispatch(commandEditor, 'core:move-up')
+ expect(commandEditor.getModel().getText()).toEqual('abc')
+ atom.commands.dispatch(commandEditor, 'core:move-up')
+ expect(commandEditor.getModel().getText()).toEqual('def')
+ atom.commands.dispatch(commandEditor, 'core:move-up')
+ expect(commandEditor.getModel().getText()).toEqual('def')
+
+ it "resets the search field to empty when scrolling back", ->
+ keydown('/')
+ atom.commands.dispatch(commandEditor, 'core:move-up')
+ expect(commandEditor.getModel().getText()).toEqual('abc')
+ atom.commands.dispatch(commandEditor, 'core:move-up')
+ expect(commandEditor.getModel().getText()).toEqual('def')
+ atom.commands.dispatch(commandEditor, 'core:move-down')
+ expect(commandEditor.getModel().getText()).toEqual('abc')
+ atom.commands.dispatch(commandEditor, 'core:move-down')
+ expect(commandEditor.getModel().getText()).toEqual ''
+
+ describe "the * keybinding", ->
+ beforeEach ->
+ editor.setText("abd\n@def\nabd\ndef\n")
+ editor.setCursorBufferPosition([0, 0])
+
+ describe "as a motion", ->
+ it "moves cursor to next occurence of word under cursor", ->
+ keydown("*")
+ expect(editor.getCursorBufferPosition()).toEqual [2, 0]
+
+ it "repeats with the n key", ->
+ keydown("*")
+ expect(editor.getCursorBufferPosition()).toEqual [2, 0]
+ keydown("n")
+ expect(editor.getCursorBufferPosition()).toEqual [0, 0]
+
+ it "doesn't move cursor unless next occurence is the exact word (no partial matches)", ->
+ editor.setText("abc\ndef\nghiabc\njkl\nabcdef")
+ editor.setCursorBufferPosition([0, 0])
+ keydown("*")
+ expect(editor.getCursorBufferPosition()).toEqual [0, 0]
+
+ describe "with words that contain 'non-word' characters", ->
+ it "moves cursor to next occurence of word under cursor", ->
+ editor.setText("abc\n@def\nabc\n@def\n")
+ editor.setCursorBufferPosition([1, 0])
+ keydown("*")
+ expect(editor.getCursorBufferPosition()).toEqual [3, 0]
+
+ 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("*")
+ expect(editor.getCursorBufferPosition()).toEqual [1, 0]
+
+ # FIXME: This behavior is different from the one found in
+ # vim. This is because the word boundary match in Javascript
+ # ignores starting 'non-word' characters.
+ # e.g.
+ # in Vim: /\<def\>/.test("@def") => false
+ # in Javascript: /\bdef\b/.test("@def") => true
+ it "moves cursor to the start of valid word char", ->
+ editor.setText("abc\ndef\nabc\n@def\n")
+ editor.setCursorBufferPosition([1, 0])
+ keydown("*")
+ expect(editor.getCursorBufferPosition()).toEqual [3, 1]
+
+ describe "when cursor is on non-word char column", ->
+ it "matches only the non-word char", ->
+ editor.setText("abc\n@def\nabc\n@def\n")
+ editor.setCursorBufferPosition([1, 0])
+ keydown("*")
+ expect(editor.getCursorBufferPosition()).toEqual [3, 0]
+
+ describe "when cursor is not on a word", ->
+ it "does a match with the next word", ->
+ editor.setText("abc\na @def\n abc\n @def")
+ editor.setCursorBufferPosition([1, 1])
+ keydown("*")
+ expect(editor.getCursorBufferPosition()).toEqual [3, 1]
+
+ describe "when cursor is at EOF", ->
+ it "doesn't try to do any match", ->
+ editor.setText("abc\n@def\nabc\n ")
+ editor.setCursorBufferPosition([3, 0])
+ keydown("*")
+ expect(editor.getCursorBufferPosition()).toEqual [3, 0]
+
+ describe "the hash keybinding", ->
+ describe "as a motion", ->
+ it "moves cursor to previous occurence of word under cursor", ->
+ editor.setText("abc\n@def\nabc\ndef\n")
+ editor.setCursorBufferPosition([2, 1])
+ keydown("#")
+ expect(editor.getCursorBufferPosition()).toEqual [0, 0]
+
+ it "repeats with n", ->
+ editor.setText("abc\n@def\nabc\ndef\nabc\n")
+ editor.setCursorBufferPosition([2, 1])
+ keydown("#")
+ expect(editor.getCursorBufferPosition()).toEqual [0, 0]
+ keydown("n")
+ expect(editor.getCursorBufferPosition()).toEqual [4, 0]
+ keydown("n")
+ expect(editor.getCursorBufferPosition()).toEqual [2, 0]
+
+ it "doesn't move cursor unless next occurence is the exact word (no partial matches)", ->
+ editor.setText("abc\ndef\nghiabc\njkl\nabcdef")
+ editor.setCursorBufferPosition([0, 0])
+ keydown("#")
+ expect(editor.getCursorBufferPosition()).toEqual [0, 0]
+
+ describe "with words that containt 'non-word' characters", ->
+ it "moves cursor to next occurence of word under cursor", ->
+ editor.setText("abc\n@def\nabc\n@def\n")
+ editor.setCursorBufferPosition([3, 0])
+ keydown("#")
+ expect(editor.getCursorBufferPosition()).toEqual [1, 0]
+
+ it "moves cursor to the start of valid word char", ->
+ editor.setText("abc\n@def\nabc\ndef\n")
+ editor.setCursorBufferPosition([3, 0])
+ keydown("#")
+ expect(editor.getCursorBufferPosition()).toEqual [1, 1]
+
+ describe "when cursor is on non-word char column", ->
+ it "matches only the non-word char", ->
+ editor.setText("abc\n@def\nabc\n@def\n")
+ editor.setCursorBufferPosition([1, 0])
+ keydown("*")
+ expect(editor.getCursorBufferPosition()).toEqual [3, 0]
+
+ describe "the H keybinding", ->
+ beforeEach ->
+ editor.setText("1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n")
+ editor.setCursorScreenPosition([8, 0])
+ spyOn(editor.getLastCursor(), 'setScreenPosition')
+
+ it "moves the cursor to the first row if visible", ->
+ spyOn(editor, 'getFirstVisibleScreenRow').andReturn(0)
+ keydown('H', shift: true)
+ expect(editor.getLastCursor().setScreenPosition).toHaveBeenCalledWith([0, 0])
+
+ it "moves the cursor to the first visible row plus offset", ->
+ spyOn(editor, 'getFirstVisibleScreenRow').andReturn(2)
+ keydown('H', shift: true)
+ expect(editor.getLastCursor().setScreenPosition).toHaveBeenCalledWith([4, 0])
+
+ it "respects counts", ->
+ spyOn(editor, 'getFirstVisibleScreenRow').andReturn(0)
+ keydown('3')
+ keydown('H', shift: true)
+ expect(editor.getLastCursor().setScreenPosition).toHaveBeenCalledWith([2, 0])
+
+ describe "the L keybinding", ->
+ beforeEach ->
+ editor.setText("1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n")
+ editor.setCursorScreenPosition([8, 0])
+ spyOn(editor.getLastCursor(), 'setScreenPosition')
+
+ it "moves the cursor to the first row if visible", ->
+ spyOn(editor, 'getLastVisibleScreenRow').andReturn(10)
+ keydown('L', shift: true)
+ expect(editor.getLastCursor().setScreenPosition).toHaveBeenCalledWith([10, 0])
+
+ it "moves the cursor to the first visible row plus offset", ->
+ spyOn(editor, 'getLastVisibleScreenRow').andReturn(6)
+ keydown('L', shift: true)
+ expect(editor.getLastCursor().setScreenPosition).toHaveBeenCalledWith([4, 0])
+
+ it "respects counts", ->
+ spyOn(editor, 'getLastVisibleScreenRow').andReturn(10)
+ keydown('3')
+ keydown('L', shift: true)
+ expect(editor.getLastCursor().setScreenPosition).toHaveBeenCalledWith([8, 0])
+
+ describe "the M keybinding", ->
+ beforeEach ->
+ editor.setText("1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n")
+ editor.setCursorScreenPosition([8, 0])
+ spyOn(editor.getLastCursor(), 'setScreenPosition')
+ spyOn(editor, 'getLastVisibleScreenRow').andReturn(10)
+ spyOn(editor, 'getFirstVisibleScreenRow').andReturn(0)
+
+ it "moves the cursor to the first row if visible", ->
+ keydown('M', shift: true)
+ expect(editor.getLastCursor().setScreenPosition).toHaveBeenCalledWith([5, 0])
+
+ describe 'the mark keybindings', ->
+ beforeEach ->
+ editor.setText(' 12\n 34\n56\n')
+ editor.setCursorBufferPosition([0, 1])
+
+ it 'moves to the beginning of the line of a mark', ->
+ editor.setCursorBufferPosition([1, 1])
+ keydown('m')
+ commandModeInputKeydown('a')
+ editor.setCursorBufferPosition([0, 0])
+ keydown('\'')
+ commandModeInputKeydown('a')
+ expect(editor.getCursorBufferPosition()).toEqual [1, 4]
+
+ it 'moves literally to a mark', ->
+ editor.setCursorBufferPosition([1, 1])
+ keydown('m')
+ commandModeInputKeydown('a')
+ editor.setCursorBufferPosition([0, 0])
+ keydown('`')
+ commandModeInputKeydown('a')
+ expect(editor.getCursorBufferPosition()).toEqual [1, 1]
+
+ it 'deletes to a mark by line', ->
+ editor.setCursorBufferPosition([1, 5])
+ keydown('m')
+ commandModeInputKeydown('a')
+ editor.setCursorBufferPosition([0, 0])
+ keydown('d')
+ keydown('\'')
+ commandModeInputKeydown('a')
+ expect(editor.getText()).toEqual '56\n'
+
+ it 'deletes before to a mark literally', ->
+ editor.setCursorBufferPosition([1, 5])
+ keydown('m')
+ commandModeInputKeydown('a')
+ editor.setCursorBufferPosition([0, 1])
+ keydown('d')
+ keydown('`')
+ commandModeInputKeydown('a')
+ expect(editor.getText()).toEqual ' 4\n56\n'
+
+ it 'deletes after to a mark literally', ->
+ editor.setCursorBufferPosition([1, 5])
+ keydown('m')
+ commandModeInputKeydown('a')
+ editor.setCursorBufferPosition([2, 1])
+ keydown('d')
+ keydown('`')
+ commandModeInputKeydown('a')
+ expect(editor.getText()).toEqual ' 12\n 36\n'
+
+ it 'moves back to previous', ->
+ editor.setCursorBufferPosition([1, 5])
+ keydown('`')
+ commandModeInputKeydown('`')
+ editor.setCursorBufferPosition([2, 1])
+ keydown('`')
+ commandModeInputKeydown('`')
+ expect(editor.getCursorBufferPosition()).toEqual [1, 5]
+
+ describe 'the f/F keybindings', ->
+ beforeEach ->
+ editor.setText("abcabcabcabc\n")
+ editor.setCursorScreenPosition([0, 0])
+
+ it 'moves to the first specified character it finds', ->
+ keydown('f')
+ commandModeInputKeydown('c')
+ expect(editor.getCursorScreenPosition()).toEqual [0, 2]
+
+ it 'moves backwards to the first specified character it finds', ->
+ editor.setCursorScreenPosition([0, 2])
+ keydown('F', shift: true)
+ commandModeInputKeydown('a')
+ expect(editor.getCursorScreenPosition()).toEqual [0, 0]
+
+ it 'respects count forward', ->
+ keydown('2')
+ keydown('f')
+ commandModeInputKeydown('a')
+ expect(editor.getCursorScreenPosition()).toEqual [0, 6]
+
+ it 'respects count backward', ->
+ editor.setCursorScreenPosition([0, 6])
+ keydown('2')
+ keydown('F', shift: true)
+ commandModeInputKeydown('a')
+ expect(editor.getCursorScreenPosition()).toEqual [0, 0]
+
+ it "doesn't move if the character specified isn't found", ->
+ keydown('f')
+ commandModeInputKeydown('d')
+ expect(editor.getCursorScreenPosition()).toEqual [0, 0]
+
+ it "doesn't move if there aren't the specified count of the specified character", ->
+ keydown('1')
+ keydown('0')
+ keydown('f')
+ commandModeInputKeydown('a')
+ expect(editor.getCursorScreenPosition()).toEqual [0, 0]
+ # a bug was making this behaviour depend on the count
+ keydown('1')
+ keydown('1')
+ keydown('f')
+ commandModeInputKeydown('a')
+ expect(editor.getCursorScreenPosition()).toEqual [0, 0]
+ # and backwards now
+ editor.setCursorScreenPosition([0, 6])
+ keydown('1')
+ keydown('0')
+ keydown('F', shift: true)
+ commandModeInputKeydown('a')
+ expect(editor.getCursorScreenPosition()).toEqual [0, 6]
+ keydown('1')
+ keydown('1')
+ keydown('F', shift: true)
+ commandModeInputKeydown('a')
+ expect(editor.getCursorScreenPosition()).toEqual [0, 6]
+
+ it "composes with d", ->
+ editor.setCursorScreenPosition([0, 3])
+ keydown('d')
+ keydown('2')
+ keydown('f')
+ commandModeInputKeydown('a')
+ expect(editor.getText()).toEqual 'abcbc\n'
+
+ describe 'the t/T keybindings', ->
+ beforeEach ->
+ editor.setText("abcabcabcabc\n")
+ editor.setCursorScreenPosition([0, 0])
+
+ it 'moves to the character previous to the first specified character it finds', ->
+ keydown('t')
+ commandModeInputKeydown('a')
+ expect(editor.getCursorScreenPosition()).toEqual [0, 2]
+ # or stays put when it's already there
+ keydown('t')
+ commandModeInputKeydown('a')
+ expect(editor.getCursorScreenPosition()).toEqual [0, 2]
+
+ it 'moves backwards to the character after the first specified character it finds', ->
+ editor.setCursorScreenPosition([0, 2])
+ keydown('T', shift: true)
+ commandModeInputKeydown('a')
+ expect(editor.getCursorScreenPosition()).toEqual [0, 1]
+
+ it 'respects count forward', ->
+ keydown('2')
+ keydown('t')
+ commandModeInputKeydown('a')
+ expect(editor.getCursorScreenPosition()).toEqual [0, 5]
+
+ it 'respects count backward', ->
+ editor.setCursorScreenPosition([0, 6])
+ keydown('2')
+ keydown('T', shift: true)
+ commandModeInputKeydown('a')
+ expect(editor.getCursorScreenPosition()).toEqual [0, 1]
+
+ it "doesn't move if the character specified isn't found", ->
+ keydown('t')
+ commandModeInputKeydown('d')
+ expect(editor.getCursorScreenPosition()).toEqual [0, 0]
+
+ it "doesn't move if there aren't the specified count of the specified character", ->
+ keydown('1')
+ keydown('0')
+ keydown('t')
+ commandModeInputKeydown('a')
+ expect(editor.getCursorScreenPosition()).toEqual [0, 0]
+ # a bug was making this behaviour depend on the count
+ keydown('1')
+ keydown('1')
+ keydown('t')
+ commandModeInputKeydown('a')
+ expect(editor.getCursorScreenPosition()).toEqual [0, 0]
+ # and backwards now
+ editor.setCursorScreenPosition([0, 6])
+ keydown('1')
+ keydown('0')
+ keydown('T', shift: true)
+ commandModeInputKeydown('a')
+ expect(editor.getCursorScreenPosition()).toEqual [0, 6]
+ keydown('1')
+ keydown('1')
+ keydown('T', shift: true)
+ commandModeInputKeydown('a')
+ expect(editor.getCursorScreenPosition()).toEqual [0, 6]
+
+ it "composes with d", ->
+ editor.setCursorScreenPosition([0, 3])
+ keydown('d')
+ keydown('2')
+ keydown('t')
+ commandModeInputKeydown('b')
+ expect(editor.getText()).toBe 'abcbcabc\n'
+
+ describe 'the V keybinding', ->
+ beforeEach ->
+ editor.setText("01\n002\n0003\n00004\n000005\n")
+ editor.setCursorScreenPosition([1, 1])
+
+ it "selects down a line", ->
+ keydown('V', shift: true)
+ keydown('j')
+ keydown('j')
+ expect(editor.getSelectedText()).toBe "002\n0003\n00004\n"
+
+ it "selects up a line", ->
+ keydown('V', shift: true)
+ keydown('k')
+ expect(editor.getSelectedText()).toBe "01\n002\n"
+
+ describe 'the ; and , keybindings', ->
+ beforeEach ->
+ editor.setText("abcabcabcabc\n")
+ editor.setCursorScreenPosition([0, 0])
+
+ it "repeat f in same direction", ->
+ keydown('f')
+ commandModeInputKeydown('c')
+ expect(editor.getCursorScreenPosition()).toEqual [0, 2]
+ keydown(';')
+ expect(editor.getCursorScreenPosition()).toEqual [0, 5]
+ keydown(';')
+ expect(editor.getCursorScreenPosition()).toEqual [0, 8]
+
+ it "repeat F in same direction", ->
+ editor.setCursorScreenPosition([0, 10])
+ keydown('F', shift: true)
+ commandModeInputKeydown('c')
+ expect(editor.getCursorScreenPosition()).toEqual [0, 8]
+ keydown(';')
+ expect(editor.getCursorScreenPosition()).toEqual [0, 5]
+ keydown(';')
+ expect(editor.getCursorScreenPosition()).toEqual [0, 2]
+
+ it "repeat f in opposite direction", ->
+ editor.setCursorScreenPosition([0, 6])
+ keydown('f')
+ commandModeInputKeydown('c')
+ expect(editor.getCursorScreenPosition()).toEqual [0, 8]
+ keydown(',')
+ expect(editor.getCursorScreenPosition()).toEqual [0, 5]
+ keydown(',')
+ expect(editor.getCursorScreenPosition()).toEqual [0, 2]
+
+ it "repeat F in opposite direction", ->
+ editor.setCursorScreenPosition([0, 4])
+ keydown('F', shift: true)
+ commandModeInputKeydown('c')
+ expect(editor.getCursorScreenPosition()).toEqual [0, 2]
+ keydown(',')
+ expect(editor.getCursorScreenPosition()).toEqual [0, 5]
+ keydown(',')
+ expect(editor.getCursorScreenPosition()).toEqual [0, 8]
+
+ it "alternate repeat f in same direction and reverse", ->
+ keydown('f')
+ commandModeInputKeydown('c')
+ expect(editor.getCursorScreenPosition()).toEqual [0, 2]
+ keydown(';')
+ expect(editor.getCursorScreenPosition()).toEqual [0, 5]
+ keydown(',')
+ expect(editor.getCursorScreenPosition()).toEqual [0, 2]
+
+ it "alternate repeat F in same direction and reverse", ->
+ editor.setCursorScreenPosition([0, 10])
+ keydown('F', shift: true)
+ commandModeInputKeydown('c')
+ expect(editor.getCursorScreenPosition()).toEqual [0, 8]
+ keydown(';')
+ expect(editor.getCursorScreenPosition()).toEqual [0, 5]
+ keydown(',')
+ expect(editor.getCursorScreenPosition()).toEqual [0, 8]
+
+ it "repeat t in same direction", ->
+ keydown('t')
+ commandModeInputKeydown('c')
+ expect(editor.getCursorScreenPosition()).toEqual [0, 1]
+ keydown(';')
+ expect(editor.getCursorScreenPosition()).toEqual [0, 4]
+
+ it "repeat T in same direction", ->
+ editor.setCursorScreenPosition([0, 10])
+ keydown('T', shift: true)
+ commandModeInputKeydown('c')
+ expect(editor.getCursorScreenPosition()).toEqual [0, 9]
+ keydown(';')
+ expect(editor.getCursorScreenPosition()).toEqual [0, 6]
+
+ it "repeat t in opposite direction first, and then reverse", ->
+ editor.setCursorScreenPosition([0, 3])
+ keydown('t')
+ commandModeInputKeydown('c')
+ expect(editor.getCursorScreenPosition()).toEqual [0, 4]
+ keydown(',')
+ expect(editor.getCursorScreenPosition()).toEqual [0, 3]
+ keydown(';')
+ expect(editor.getCursorScreenPosition()).toEqual [0, 4]
+
+ it "repeat T in opposite direction first, and then reverse", ->
+ editor.setCursorScreenPosition([0, 4])
+ keydown('T', shift: true)
+ commandModeInputKeydown('c')
+ expect(editor.getCursorScreenPosition()).toEqual [0, 3]
+ keydown(',')
+ expect(editor.getCursorScreenPosition()).toEqual [0, 4]
+ keydown(';')
+ expect(editor.getCursorScreenPosition()).toEqual [0, 3]
+
+ it "repeat with count in same direction", ->
+ editor.setCursorScreenPosition([0, 0])
+ keydown('f')
+ commandModeInputKeydown('c')
+ expect(editor.getCursorScreenPosition()).toEqual [0, 2]
+ keydown('2')
+ keydown(';')
+ expect(editor.getCursorScreenPosition()).toEqual [0, 8]
+
+ it "repeat with count in reverse direction", ->
+ editor.setCursorScreenPosition([0, 6])
+ keydown('f')
+ commandModeInputKeydown('c')
+ expect(editor.getCursorScreenPosition()).toEqual [0, 8]
+ keydown('2')
+ keydown(',')
+ expect(editor.getCursorScreenPosition()).toEqual [0, 2]
+
+ describe 'the % motion', ->
+ beforeEach ->
+ editor.setText("( ( ) )--{ text in here; and a function call(with parameters) }\n")
+ editor.setCursorScreenPosition([0, 0])
+
+ it 'matches the correct parenthesis', ->
+ keydown('%')
+ expect(editor.getCursorScreenPosition()).toEqual [0, 6]
+
+ it 'matches the correct brace', ->
+ editor.setCursorScreenPosition([0, 9])
+ keydown('%')
+ expect(editor.getCursorScreenPosition()).toEqual [0, 62]
+
+ it 'composes correctly with d', ->
+ editor.setCursorScreenPosition([0, 9])
+ keydown('d')
+ keydown('%')
+ expect(editor.getText()).toEqual "( ( ) )--\n"
+
+ it 'moves correctly when composed with v going forward', ->
+ keydown('v')
+ keydown('h')
+ keydown('%')
+ expect(editor.getCursorScreenPosition()).toEqual [0, 7]
+
+ it 'moves correctly when composed with v going backward', ->
+ editor.setCursorScreenPosition([0, 5])
+ keydown('v')
+ keydown('%')
+ expect(editor.getCursorScreenPosition()).toEqual [0, 0]
+
+ it 'it moves appropriately to find the nearest matching action', ->
+ editor.setCursorScreenPosition([0, 3])
+ keydown('%')
+ expect(editor.getCursorScreenPosition()).toEqual [0, 2]
+ expect(editor.getText()).toEqual "( ( ) )--{ text in here; and a function call(with parameters) }\n"
+
+ it 'it moves appropriately to find the nearest matching action', ->
+ editor.setCursorScreenPosition([0, 26])
+ keydown('%')
+ expect(editor.getCursorScreenPosition()).toEqual [0, 60]
+ expect(editor.getText()).toEqual "( ( ) )--{ text in here; and a function call(with parameters) }\n"
+
+ it "finds matches across multiple lines", ->
+ editor.setText("...(\n...)")
+ editor.setCursorScreenPosition([0, 0])
+ keydown("%")
+ expect(editor.getCursorScreenPosition()).toEqual([1, 3])
+
+ it "does not affect search history", ->
+ keydown('/')
+ submitCommandModeInputText 'func'
+ expect(editor.getCursorBufferPosition()).toEqual [0, 31]
+ keydown('%')
+ expect(editor.getCursorBufferPosition()).toEqual [0, 60]
+ keydown('n')
+ expect(editor.getCursorBufferPosition()).toEqual [0, 31]
+
+ describe "scrolling screen and keeping cursor in the same screen position", ->
+ beforeEach ->
+ editor.setText([0...80].join("\n"))
+ editor.setHeight(20 * 10)
+ editor.setLineHeightInPixels(10)
+ editor.setScrollTop(40 * 10)
+ editor.setCursorBufferPosition([42, 0])
+
+ describe "the ctrl-u keybinding", ->
+ it "moves the screen down by half screen size and keeps cursor onscreen", ->
+ keydown('u', ctrl: true)
+ expect(editor.getScrollTop()).toEqual 300
+ expect(editor.getCursorBufferPosition()).toEqual [32, 0]
+
+ it "selects on visual mode", ->
+ editor.setCursorBufferPosition([42, 1])
+ vimState.activateVisualMode()
+ keydown('u', ctrl: true)
+ expect(editor.getSelectedText()).toEqual [32..42].join("\n")
+
+ it "selects on linewise mode", ->
+ vimState.activateVisualMode('linewise')
+ keydown('u', ctrl: true)
+ expect(editor.getSelectedText()).toEqual [32..42].join("\n").concat("\n")
+
+ describe "the ctrl-b keybinding", ->
+ it "moves screen up one page", ->
+ keydown('b', ctrl: true)
+ expect(editor.getScrollTop()).toEqual 200
+ expect(editor.getCursorScreenPosition()).toEqual [22, 0]
+
+ it "selects on visual mode", ->
+ editor.setCursorBufferPosition([42, 1])
+ vimState.activateVisualMode()
+ keydown('b', ctrl: true)
+ expect(editor.getSelectedText()).toEqual [22..42].join("\n")
+
+ it "selects on linewise mode", ->
+ vimState.activateVisualMode('linewise')
+ keydown('b', ctrl: true)
+ expect(editor.getSelectedText()).toEqual [22..42].join("\n").concat("\n")
+
+
+ describe "the ctrl-d keybinding", ->
+ it "moves the screen down by half screen size and keeps cursor onscreen", ->
+ keydown('d', ctrl: true)
+ expect(editor.getScrollTop()).toEqual 500
+ expect(editor.getCursorBufferPosition()).toEqual [52, 0]
+
+ it "selects on visual mode", ->
+ editor.setCursorBufferPosition([42, 1])
+ vimState.activateVisualMode()
+ keydown('d', ctrl: true)
+ expect(editor.getSelectedText()).toEqual [42..52].join("\n").slice(1, -1)
+
+ it "selects on linewise mode", ->
+ vimState.activateVisualMode('linewise')
+ keydown('d', ctrl: true)
+ expect(editor.getSelectedText()).toEqual [42..52].join("\n").concat("\n")
+
+ describe "the ctrl-f keybinding", ->
+ it "moves screen down one page", ->
+ keydown('f', ctrl: true)
+ expect(editor.getScrollTop()).toEqual 600
+ expect(editor.getCursorScreenPosition()).toEqual [62, 0]
+
+ it "selects on visual mode", ->
+ editor.setCursorBufferPosition([42, 1])
+ vimState.activateVisualMode()
+ keydown('f', ctrl: true)
+ expect(editor.getSelectedText()).toEqual [42..62].join("\n").slice(1, -1)
+
+ it "selects on linewise mode", ->
+ vimState.activateVisualMode('linewise')
+ keydown('f', ctrl: true)
+ expect(editor.getSelectedText()).toEqual [42..62].join("\n").concat("\n")
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<enter>
+ 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'
diff --git a/atom/packages/vim-mode/spec/prefixes-spec.coffee b/atom/packages/vim-mode/spec/prefixes-spec.coffee
new file mode 100644
index 0000000..e782d20
--- /dev/null
+++ b/atom/packages/vim-mode/spec/prefixes-spec.coffee
@@ -0,0 +1,148 @@
+helpers = require './spec-helper'
+
+describe "Prefixes", ->
+ [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)
+
+ describe "Repeat", ->
+ describe "with operations", ->
+ beforeEach ->
+ editor.setText("123456789abc")
+ editor.setCursorScreenPosition([0, 0])
+
+ it "repeats N times", ->
+ keydown('3')
+ keydown('x')
+
+ expect(editor.getText()).toBe '456789abc'
+
+ it "repeats NN times", ->
+ keydown('1')
+ keydown('0')
+ keydown('x')
+
+ expect(editor.getText()).toBe 'bc'
+
+ describe "with motions", ->
+ beforeEach ->
+ editor.setText('one two three')
+ editor.setCursorScreenPosition([0, 0])
+
+ it "repeats N times", ->
+ keydown('d')
+ keydown('2')
+ keydown('w')
+
+ expect(editor.getText()).toBe 'three'
+
+ describe "in visual mode", ->
+ beforeEach ->
+ editor.setText('one two three')
+ editor.setCursorScreenPosition([0, 0])
+
+ it "repeats movements in visual mode", ->
+ keydown("v")
+ keydown("2")
+ keydown("w")
+
+ expect(editor.getCursorScreenPosition()).toEqual [0, 9]
+
+ describe "Register", ->
+ describe "the a register", ->
+ it "saves a value for future reading", ->
+ vimState.setRegister('a', text: 'new content')
+ expect(vimState.getRegister("a").text).toEqual 'new content'
+
+ it "overwrites a value previously in the register", ->
+ vimState.setRegister('a', text: 'content')
+ vimState.setRegister('a', text: 'new content')
+ expect(vimState.getRegister("a").text).toEqual 'new content'
+
+ describe "the B register", ->
+ it "saves a value for future reading", ->
+ vimState.setRegister('B', text: 'new content')
+ expect(vimState.getRegister("b").text).toEqual 'new content'
+ expect(vimState.getRegister("B").text).toEqual 'new content'
+
+ it "appends to a value previously in the register", ->
+ vimState.setRegister('b', text: 'content')
+ vimState.setRegister('B', text: 'new content')
+ expect(vimState.getRegister("b").text).toEqual 'contentnew content'
+
+ it "appends linewise to a linewise value previously in the register", ->
+ vimState.setRegister('b', {type: 'linewise', text: 'content\n'})
+ vimState.setRegister('B', text: 'new content')
+ expect(vimState.getRegister("b").text).toEqual 'content\nnew content\n'
+
+ it "appends linewise to a character value previously in the register", ->
+ vimState.setRegister('b', text: 'content')
+ vimState.setRegister('B', {type: 'linewise', text: 'new content\n'})
+ expect(vimState.getRegister("b").text).toEqual 'content\nnew content\n'
+
+
+ describe "the * register", ->
+ describe "reading", ->
+ it "is the same the system clipboard", ->
+ expect(vimState.getRegister('*').text).toEqual 'initial clipboard content'
+ expect(vimState.getRegister('*').type).toEqual 'character'
+
+ describe "writing", ->
+ beforeEach ->
+ vimState.setRegister('*', text: 'new content')
+
+ it "overwrites the contents of the system clipboard", ->
+ expect(atom.clipboard.read()).toEqual 'new content'
+
+ # FIXME: once linux support comes out, this needs to read from
+ # the correct clipboard. For now it behaves just like the * register
+ # See :help x11-cut-buffer and :help registers for more details on how these
+ # registers work on an X11 based system.
+ describe "the + register", ->
+ describe "reading", ->
+ it "is the same the system clipboard", ->
+ expect(vimState.getRegister('*').text).toEqual 'initial clipboard content'
+ expect(vimState.getRegister('*').type).toEqual 'character'
+
+ describe "writing", ->
+ beforeEach ->
+ vimState.setRegister('*', text: 'new content')
+
+ it "overwrites the contents of the system clipboard", ->
+ expect(atom.clipboard.read()).toEqual 'new content'
+
+ describe "the _ register", ->
+ describe "reading", ->
+ it "is always the empty string", ->
+ expect(vimState.getRegister("_").text).toEqual ''
+
+ describe "writing", ->
+ it "throws away anything written to it", ->
+ vimState.setRegister('_', text: 'new content')
+ expect(vimState.getRegister("_").text).toEqual ''
+
+ describe "the % register", ->
+ beforeEach ->
+ spyOn(editor, 'getURI').andReturn('/Users/atom/known_value.txt')
+
+ describe "reading", ->
+ it "returns the filename of the current editor", ->
+ expect(vimState.getRegister('%').text).toEqual '/Users/atom/known_value.txt'
+
+ describe "writing", ->
+ it "throws away anything written to it", ->
+ vimState.setRegister('%', "new content")
+ expect(vimState.getRegister('%').text).toEqual '/Users/atom/known_value.txt'
diff --git a/atom/packages/vim-mode/spec/scroll-spec.coffee b/atom/packages/vim-mode/spec/scroll-spec.coffee
new file mode 100644
index 0000000..9473193
--- /dev/null
+++ b/atom/packages/vim-mode/spec/scroll-spec.coffee
@@ -0,0 +1,122 @@
+helpers = require './spec-helper'
+
+describe "Scrolling", ->
+ [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)
+
+ describe "scrolling keybindings", ->
+ beforeEach ->
+ editor.setText("1\n2\n3\n4\n5\n6\n7\n8\n9\n10")
+ spyOn(editor, 'getFirstVisibleScreenRow').andReturn(2)
+ spyOn(editor, 'getLastVisibleScreenRow').andReturn(8)
+ spyOn(editor, 'scrollToScreenPosition')
+
+ describe "the ctrl-e keybinding", ->
+ beforeEach ->
+ spyOn(editor, 'getCursorScreenPosition').andReturn({row: 4, column: 0})
+ spyOn(editor, 'setCursorScreenPosition')
+
+ it "moves the screen down by one and keeps cursor onscreen", ->
+ keydown('e', ctrl: true)
+ expect(editor.scrollToScreenPosition).toHaveBeenCalledWith([7, 0])
+ expect(editor.setCursorScreenPosition).toHaveBeenCalledWith([6, 0])
+
+ describe "the ctrl-y keybinding", ->
+ beforeEach ->
+ spyOn(editor, 'getCursorScreenPosition').andReturn({row: 6, column: 0})
+ spyOn(editor, 'setCursorScreenPosition')
+
+ it "moves the screen up by one and keeps the cursor onscreen", ->
+ keydown('y', ctrl: true)
+ expect(editor.scrollToScreenPosition).toHaveBeenCalledWith([3, 0])
+ expect(editor.setCursorScreenPosition).toHaveBeenCalledWith([4, 0])
+
+ describe "scroll cursor keybindings", ->
+ beforeEach ->
+ text = ""
+ for i in [1..200]
+ text += "#{i}\n"
+ editor.setText(text)
+
+ spyOn(editor, 'moveToFirstCharacterOfLine')
+ spyOn(editor, 'getLineHeightInPixels').andReturn(20)
+ spyOn(editor, 'setScrollTop')
+ spyOn(editor, 'getHeight').andReturn(200)
+ spyOn(editor, 'getFirstVisibleScreenRow').andReturn(90)
+ spyOn(editor, 'getLastVisibleScreenRow').andReturn(110)
+
+ describe "the z<CR> keybinding", ->
+ keydownCodeForEnter = '\r'
+
+ beforeEach ->
+ spyOn(editor, 'pixelPositionForScreenPosition').andReturn({top: 1000, left: 0})
+
+ it "moves the screen to position cursor at the top of the window and moves cursor to first non-blank in the line", ->
+ keydown('z')
+ keydown(keydownCodeForEnter)
+ expect(editor.setScrollTop).toHaveBeenCalledWith(960)
+ expect(editor.moveToFirstCharacterOfLine).toHaveBeenCalled()
+
+ describe "the zt keybinding", ->
+ beforeEach ->
+ spyOn(editor, 'pixelPositionForScreenPosition').andReturn({top: 1000, left: 0})
+
+ it "moves the screen to position cursor at the top of the window and leave cursor in the same column", ->
+ keydown('z')
+ keydown('t')
+ expect(editor.setScrollTop).toHaveBeenCalledWith(960)
+ expect(editor.moveToFirstCharacterOfLine).not.toHaveBeenCalled()
+
+ describe "the z. keybinding", ->
+ beforeEach ->
+ spyOn(editor, 'pixelPositionForScreenPosition').andReturn({top: 1000, left: 0})
+
+ it "moves the screen to position cursor at the center of the window and moves cursor to first non-blank in the line", ->
+ keydown('z')
+ keydown('.')
+ expect(editor.setScrollTop).toHaveBeenCalledWith(900)
+ expect(editor.moveToFirstCharacterOfLine).toHaveBeenCalled()
+
+ describe "the zz keybinding", ->
+ beforeEach ->
+ spyOn(editor, 'pixelPositionForScreenPosition').andReturn({top: 1000, left: 0})
+
+ it "moves the screen to position cursor at the center of the window and leave cursor in the same column", ->
+ keydown('z')
+ keydown('z')
+ expect(editor.setScrollTop).toHaveBeenCalledWith(900)
+ expect(editor.moveToFirstCharacterOfLine).not.toHaveBeenCalled()
+
+ describe "the z- keybinding", ->
+ beforeEach ->
+ spyOn(editor, 'pixelPositionForScreenPosition').andReturn({top: 1000, left: 0})
+
+ it "moves the screen to position cursor at the bottom of the window and moves cursor to first non-blank in the line", ->
+ keydown('z')
+ keydown('-')
+ expect(editor.setScrollTop).toHaveBeenCalledWith(860)
+ expect(editor.moveToFirstCharacterOfLine).toHaveBeenCalled()
+
+ describe "the zb keybinding", ->
+ beforeEach ->
+ spyOn(editor, 'pixelPositionForScreenPosition').andReturn({top: 1000, left: 0})
+
+ it "moves the screen to position cursor at the bottom of the window and leave cursor in the same column", ->
+ keydown('z')
+ keydown('b')
+ expect(editor.setScrollTop).toHaveBeenCalledWith(860)
+ expect(editor.moveToFirstCharacterOfLine).not.toHaveBeenCalled()
diff --git a/atom/packages/vim-mode/spec/spec-helper.coffee b/atom/packages/vim-mode/spec/spec-helper.coffee
new file mode 100644
index 0000000..88ba4b2
--- /dev/null
+++ b/atom/packages/vim-mode/spec/spec-helper.coffee
@@ -0,0 +1,66 @@
+VimState = require '../lib/vim-state'
+GlobalVimState = require '../lib/global-vim-state'
+VimMode = require '../lib/vim-mode'
+StatusBarManager = require '../lib/status-bar-manager'
+
+beforeEach ->
+ atom.workspace ||= {}
+
+getEditorElement = (callback) ->
+ textEditor = null
+
+ waitsForPromise ->
+ atom.project.open().then (e) ->
+ textEditor = e
+
+ runs ->
+ element = document.createElement("atom-text-editor")
+ element.setModel(textEditor)
+ element.classList.add('vim-mode')
+ element.vimState = new VimState(element, new StatusBarManager, new GlobalVimState)
+
+ element.addEventListener "keydown", (e) ->
+ atom.keymaps.handleKeyboardEvent(e)
+
+ callback(element)
+
+mockPlatform = (editorElement, platform) ->
+ wrapper = document.createElement('div')
+ wrapper.className = platform
+ wrapper.appendChild(editorElement)
+
+unmockPlatform = (editorElement) ->
+ editorElement.parentNode.removeChild(editorElement)
+
+dispatchKeyboardEvent = (target, eventArgs...) ->
+ e = document.createEvent('KeyboardEvent')
+ e.initKeyboardEvent(eventArgs...)
+ # 0 is the default, and it's valid ASCII, but it's wrong.
+ Object.defineProperty(e, 'keyCode', get: -> undefined) if e.keyCode is 0
+ target.dispatchEvent e
+
+dispatchTextEvent = (target, eventArgs...) ->
+ e = document.createEvent('TextEvent')
+ e.initTextEvent(eventArgs...)
+ target.dispatchEvent e
+
+keydown = (key, {element, ctrl, shift, alt, meta, raw}={}) ->
+ key = "U+#{key.charCodeAt(0).toString(16)}" unless key is 'escape' or raw?
+ element ||= document.activeElement
+ eventArgs = [
+ true, # bubbles
+ true, # cancelable
+ null, # view
+ key, # key
+ 0, # location
+ ctrl, alt, shift, meta
+ ]
+
+ canceled = not dispatchKeyboardEvent(element, 'keydown', eventArgs...)
+ dispatchKeyboardEvent(element, 'keypress', eventArgs...)
+ if not canceled
+ if dispatchTextEvent(element, 'textInput', eventArgs...)
+ element.value += key
+ dispatchKeyboardEvent(element, 'keyup', eventArgs...)
+
+module.exports = {keydown, getEditorElement, mockPlatform, unmockPlatform}
diff --git a/atom/packages/vim-mode/spec/text-objects-spec.coffee b/atom/packages/vim-mode/spec/text-objects-spec.coffee
new file mode 100644
index 0000000..6991dc7
--- /dev/null
+++ b/atom/packages/vim-mode/spec/text-objects-spec.coffee
@@ -0,0 +1,481 @@
+helpers = require './spec-helper'
+
+describe "TextObjects", ->
+ [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 "the 'iw' text object", ->
+ beforeEach ->
+ editor.setText("12345 abcde ABCDE")
+ editor.setCursorScreenPosition([0, 9])
+
+ it "applies operators inside the current word in operator-pending mode", ->
+ keydown('d')
+ 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)
+
+ it "selects inside the current word in visual mode", ->
+ keydown('v')
+ keydown('i')
+ keydown('w')
+
+ expect(editor.getSelectedScreenRange()).toEqual [[0, 6], [0, 11]]
+
+ it "works with multiple cursors", ->
+ editor.addCursorAtBufferPosition([0, 1])
+ keydown("v")
+ keydown("i")
+ keydown("w")
+ expect(editor.getSelectedBufferRanges()).toEqual [
+ [[0, 6], [0, 11]]
+ [[0, 0], [0, 5]]
+ ]
+
+ describe "the 'i(' text object", ->
+ beforeEach ->
+ editor.setText("( something in here and in (here) )")
+ editor.setCursorScreenPosition([0, 9])
+
+ it "applies operators inside the current word in operator-pending mode", ->
+ keydown('d')
+ keydown('i')
+ keydown('(')
+ expect(editor.getText()).toBe "()"
+ expect(editor.getCursorScreenPosition()).toEqual [0, 1]
+ expect(editorElement.classList.contains('operator-pending-mode')).toBe(false)
+ expect(editorElement.classList.contains('command-mode')).toBe(true)
+
+ it "applies operators inside the current word in operator-pending mode (second test)", ->
+ editor.setCursorScreenPosition([0, 29])
+ keydown('d')
+ keydown('i')
+ keydown('(')
+ expect(editor.getText()).toBe "( something in here and in () )"
+ expect(editor.getCursorScreenPosition()).toEqual [0, 28]
+ expect(editorElement.classList.contains('operator-pending-mode')).toBe(false)
+ expect(editorElement.classList.contains('command-mode')).toBe(true)
+
+ it "works with multiple cursors", ->
+ editor.setText("( a b ) cde ( f g h ) ijk")
+ editor.setCursorBufferPosition([0, 2])
+ editor.addCursorAtBufferPosition([0, 18])
+
+ keydown("v")
+ keydown("i")
+ keydown("(")
+
+ expect(editor.getSelectedBufferRanges()).toEqual [
+ [[0, 1], [0, 6]]
+ [[0, 13], [0, 20]]
+ ]
+
+ describe "the 'i{' text object", ->
+ beforeEach ->
+ editor.setText("{ something in here and in {here} }")
+ editor.setCursorScreenPosition([0, 9])
+
+ it "applies operators inside the current word in operator-pending mode", ->
+ keydown('d')
+ keydown('i')
+ keydown('{')
+ expect(editor.getText()).toBe "{}"
+ expect(editor.getCursorScreenPosition()).toEqual [0, 1]
+ expect(editorElement.classList.contains('operator-pending-mode')).toBe(false)
+ expect(editorElement.classList.contains('command-mode')).toBe(true)
+
+ it "applies operators inside the current word in operator-pending mode (second test)", ->
+ editor.setCursorScreenPosition([0, 29])
+ keydown('d')
+ keydown('i')
+ keydown('{')
+ expect(editor.getText()).toBe "{ something in here and in {} }"
+ expect(editor.getCursorScreenPosition()).toEqual [0, 28]
+ expect(editorElement.classList.contains('operator-pending-mode')).toBe(false)
+ expect(editorElement.classList.contains('command-mode')).toBe(true)
+
+ describe "the 'i<' text object", ->
+ beforeEach ->
+ editor.setText("< something in here and in <here> >")
+ editor.setCursorScreenPosition([0, 9])
+
+ it "applies operators inside the current word in operator-pending mode", ->
+ keydown('d')
+ keydown('i')
+ keydown('<')
+ expect(editor.getText()).toBe "<>"
+ expect(editor.getCursorScreenPosition()).toEqual [0, 1]
+ expect(editorElement.classList.contains('operator-pending-mode')).toBe(false)
+ expect(editorElement.classList.contains('command-mode')).toBe(true)
+
+ it "applies operators inside the current word in operator-pending mode (second test)", ->
+ editor.setCursorScreenPosition([0, 29])
+ keydown('d')
+ keydown('i')
+ keydown('<')
+ expect(editor.getText()).toBe "< something in here and in <> >"
+ expect(editor.getCursorScreenPosition()).toEqual [0, 28]
+ expect(editorElement.classList.contains('operator-pending-mode')).toBe(false)
+ expect(editorElement.classList.contains('command-mode')).toBe(true)
+
+ describe "the 'it' text object", ->
+ beforeEach ->
+ editor.setText("<something>here</something><again>")
+ editor.setCursorScreenPosition([0, 5])
+
+ it "applies only if in the value of a tag", ->
+ keydown('d')
+ keydown('i')
+ keydown('t')
+ expect(editor.getText()).toBe "<something>here</something><again>"
+ expect(editor.getCursorScreenPosition()).toEqual [0, 5]
+ expect(editorElement.classList.contains('operator-pending-mode')).toBe(false)
+ expect(editorElement.classList.contains('command-mode')).toBe(true)
+
+ it "applies operators inside the current word in operator-pending mode", ->
+ editor.setCursorScreenPosition([0, 13])
+ keydown('d')
+ keydown('i')
+ keydown('t')
+ expect(editor.getText()).toBe "<something></something><again>"
+ expect(editor.getCursorScreenPosition()).toEqual [0, 11]
+ expect(editorElement.classList.contains('operator-pending-mode')).toBe(false)
+ expect(editorElement.classList.contains('command-mode')).toBe(true)
+
+ describe "the 'ip' text object", ->
+ beforeEach ->
+ editor.setText("\nParagraph-1\nParagraph-1\nParagraph-1\n\n")
+ editor.setCursorScreenPosition([2, 2])
+
+ it "applies operators inside the current paragraph in operator-pending mode", ->
+
+ keydown('y')
+ keydown('i')
+ keydown('p')
+
+ expect(editor.getText()).toBe "\nParagraph-1\nParagraph-1\nParagraph-1\n\n"
+ expect(editor.getCursorScreenPosition()).toEqual [1, 0]
+ expect(vimState.getRegister('"').text).toBe "Paragraph-1\nParagraph-1\nParagraph-1\n"
+ expect(editorElement.classList.contains('operator-pending-mode')).toBe(false)
+ expect(editorElement.classList.contains('command-mode')).toBe(true)
+
+ it "selects inside the current paragraph in visual mode", ->
+ keydown('v')
+ keydown('i')
+ keydown('p')
+
+ expect(editor.getSelectedScreenRange()).toEqual [[1, 0], [4, 0]]
+
+ describe "the 'ap' text object", ->
+ beforeEach ->
+ editor.setText("text\n\nParagraph-1\nParagraph-1\nParagraph-1\n\nmoretext")
+ editor.setCursorScreenPosition([3, 2])
+
+ it "applies operators around the current paragraph in operator-pending mode", ->
+
+ keydown('y')
+ keydown('a')
+ keydown('p')
+
+ expect(editor.getText()).toBe "text\n\nParagraph-1\nParagraph-1\nParagraph-1\n\nmoretext"
+ expect(editor.getCursorScreenPosition()).toEqual [2, 0]
+ expect(vimState.getRegister('"').text).toBe "Paragraph-1\nParagraph-1\nParagraph-1\n\n"
+ expect(editorElement.classList.contains('operator-pending-mode')).toBe(false)
+ expect(editorElement.classList.contains('command-mode')).toBe(true)
+
+ it "selects around the current paragraph in visual mode", ->
+ keydown('v')
+ keydown('a')
+ keydown('p')
+
+ expect(editor.getSelectedScreenRange()).toEqual [[2, 0], [6, 0]]
+
+ describe "the 'i[' text object", ->
+ beforeEach ->
+ editor.setText("[ something in here and in [here] ]")
+ editor.setCursorScreenPosition([0, 9])
+
+ it "applies operators inside the current word in operator-pending mode", ->
+ keydown('d')
+ keydown('i')
+ keydown('[')
+ expect(editor.getText()).toBe "[]"
+ expect(editor.getCursorScreenPosition()).toEqual [0, 1]
+ expect(editorElement.classList.contains('operator-pending-mode')).toBe(false)
+ expect(editorElement.classList.contains('command-mode')).toBe(true)
+
+ it "applies operators inside the current word in operator-pending mode (second test)", ->
+ editor.setCursorScreenPosition([0, 29])
+ keydown('d')
+ keydown('i')
+ keydown('[')
+ expect(editor.getText()).toBe "[ something in here and in [] ]"
+ expect(editor.getCursorScreenPosition()).toEqual [0, 28]
+ expect(editorElement.classList.contains('operator-pending-mode')).toBe(false)
+ expect(editorElement.classList.contains('command-mode')).toBe(true)
+
+ describe "the 'i\'' text object", ->
+ beforeEach ->
+ editor.setText("' something in here and in 'here' ' and over here")
+ editor.setCursorScreenPosition([0, 9])
+
+ it "applies operators inside the current string in operator-pending mode", ->
+ keydown('d')
+ keydown('i')
+ keydown('\'')
+ expect(editor.getText()).toBe "''here' ' and over here"
+ expect(editor.getCursorScreenPosition()).toEqual [0, 1]
+ expect(editorElement.classList.contains('operator-pending-mode')).toBe(false)
+ expect(editorElement.classList.contains('command-mode')).toBe(true)
+
+ it "applies operators inside the next string in operator-pending mode (if not in a string)", ->
+ editor.setCursorScreenPosition([0, 29])
+ keydown('d')
+ keydown('i')
+ keydown('\'')
+ expect(editor.getText()).toBe "' something in here and in 'here'' and over here"
+ expect(editor.getCursorScreenPosition()).toEqual [0, 33]
+ expect(editorElement.classList.contains('operator-pending-mode')).toBe(false)
+ expect(editorElement.classList.contains('command-mode')).toBe(true)
+
+ it "makes no change if past the last string on a line", ->
+ editor.setCursorScreenPosition([0, 39])
+ keydown('d')
+ keydown('i')
+ keydown('\'')
+ expect(editor.getText()).toBe "' something in here and in 'here' ' and over here"
+ expect(editor.getCursorScreenPosition()).toEqual [0, 39]
+ expect(editorElement.classList.contains('operator-pending-mode')).toBe(false)
+ expect(editorElement.classList.contains('command-mode')).toBe(true)
+
+ describe "the 'i\"' text object", ->
+ beforeEach ->
+ editor.setText("\" something in here and in \"here\" \" and over here")
+ editor.setCursorScreenPosition([0, 9])
+
+ it "applies operators inside the current string in operator-pending mode", ->
+ keydown('d')
+ keydown('i')
+ keydown('"')
+ expect(editor.getText()).toBe "\"\"here\" \" and over here"
+ expect(editor.getCursorScreenPosition()).toEqual [0, 1]
+ expect(editorElement.classList.contains('operator-pending-mode')).toBe(false)
+ expect(editorElement.classList.contains('command-mode')).toBe(true)
+
+ it "applies operators inside the next string in operator-pending mode (if not in a string)", ->
+ editor.setCursorScreenPosition([0, 29])
+ keydown('d')
+ keydown('i')
+ keydown('"')
+ expect(editor.getText()).toBe "\" something in here and in \"here\"\" and over here"
+ expect(editor.getCursorScreenPosition()).toEqual [0, 33]
+ expect(editorElement.classList.contains('operator-pending-mode')).toBe(false)
+ expect(editorElement.classList.contains('command-mode')).toBe(true)
+
+ it "makes no change if past the last string on a line", ->
+ editor.setCursorScreenPosition([0, 39])
+ keydown('d')
+ keydown('i')
+ keydown('"')
+ expect(editor.getText()).toBe "\" something in here and in \"here\" \" and over here"
+ expect(editor.getCursorScreenPosition()).toEqual [0, 39]
+ expect(editorElement.classList.contains('operator-pending-mode')).toBe(false)
+
+ describe "the 'aw' text object", ->
+ beforeEach ->
+ editor.setText("12345 abcde ABCDE")
+ editor.setCursorScreenPosition([0, 9])
+
+ it "applies operators from the start of the current word to the start of the next word in operator-pending mode", ->
+ keydown('d')
+ keydown('a')
+ 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)
+
+ it "selects from the start of the current word to the start of the next word in visual mode", ->
+ keydown('v')
+ keydown('a')
+ keydown('w')
+
+ expect(editor.getSelectedScreenRange()).toEqual [[0, 6], [0, 12]]
+
+ it "doesn't span newlines", ->
+ editor.setText("12345\nabcde ABCDE")
+ editor.setCursorBufferPosition([0, 3])
+
+ keydown("v")
+ keydown("a")
+ keydown("w")
+
+ expect(editor.getSelectedBufferRanges()).toEqual [[[0, 0], [0, 5]]]
+
+ describe "the 'a(' text object", ->
+ beforeEach ->
+ editor.setText("( something in here and in (here) )")
+ editor.setCursorScreenPosition([0, 9])
+
+ it "applies operators around the current parentheses in operator-pending mode", ->
+ keydown('d')
+ keydown('a')
+ keydown('(')
+ expect(editor.getText()).toBe ""
+ expect(editor.getCursorScreenPosition()).toEqual [0, 0]
+ expect(editorElement.classList.contains('operator-pending-mode')).toBe(false)
+ expect(editorElement.classList.contains('command-mode')).toBe(true)
+
+ it "applies operators around the current parentheses in operator-pending mode (second test)", ->
+ editor.setCursorScreenPosition([0, 29])
+ keydown('d')
+ keydown('a')
+ keydown('(')
+ expect(editor.getText()).toBe "( something in here and in )"
+ expect(editor.getCursorScreenPosition()).toEqual [0, 27]
+ expect(editorElement.classList.contains('operator-pending-mode')).toBe(false)
+ expect(editorElement.classList.contains('command-mode')).toBe(true)
+
+ describe "the 'a{' text object", ->
+ beforeEach ->
+ editor.setText("{ something in here and in {here} }")
+ editor.setCursorScreenPosition([0, 9])
+
+ it "applies operators around the current curly brackets in operator-pending mode", ->
+ keydown('d')
+ keydown('a')
+ keydown('{')
+ expect(editor.getText()).toBe ""
+ expect(editor.getCursorScreenPosition()).toEqual [0, 0]
+ expect(editorElement.classList.contains('operator-pending-mode')).toBe(false)
+ expect(editorElement.classList.contains('command-mode')).toBe(true)
+
+ it "applies operators around the current curly brackets in operator-pending mode (second test)", ->
+ editor.setCursorScreenPosition([0, 29])
+ keydown('d')
+ keydown('a')
+ keydown('{')
+ expect(editor.getText()).toBe "{ something in here and in }"
+ expect(editor.getCursorScreenPosition()).toEqual [0, 27]
+ expect(editorElement.classList.contains('operator-pending-mode')).toBe(false)
+ expect(editorElement.classList.contains('command-mode')).toBe(true)
+
+ describe "the 'a<' text object", ->
+ beforeEach ->
+ editor.setText("< something in here and in <here> >")
+ editor.setCursorScreenPosition([0, 9])
+
+ it "applies operators around the current angle brackets in operator-pending mode", ->
+ keydown('d')
+ keydown('a')
+ keydown('<')
+ expect(editor.getText()).toBe ""
+ expect(editor.getCursorScreenPosition()).toEqual [0, 0]
+ expect(editorElement.classList.contains('operator-pending-mode')).toBe(false)
+ expect(editorElement.classList.contains('command-mode')).toBe(true)
+
+ it "applies operators around the current angle brackets in operator-pending mode (second test)", ->
+ editor.setCursorScreenPosition([0, 29])
+ keydown('d')
+ keydown('a')
+ keydown('<')
+ expect(editor.getText()).toBe "< something in here and in >"
+ expect(editor.getCursorScreenPosition()).toEqual [0, 27]
+ expect(editorElement.classList.contains('operator-pending-mode')).toBe(false)
+ expect(editorElement.classList.contains('command-mode')).toBe(true)
+
+ describe "the 'a[' text object", ->
+ beforeEach ->
+ editor.setText("[ something in here and in [here] ]")
+ editor.setCursorScreenPosition([0, 9])
+
+ it "applies operators around the current square brackets in operator-pending mode", ->
+ keydown('d')
+ keydown('a')
+ keydown('[')
+ expect(editor.getText()).toBe ""
+ expect(editor.getCursorScreenPosition()).toEqual [0, 0]
+ expect(editorElement.classList.contains('operator-pending-mode')).toBe(false)
+ expect(editorElement.classList.contains('command-mode')).toBe(true)
+
+ it "applies operators around the current square brackets in operator-pending mode (second test)", ->
+ editor.setCursorScreenPosition([0, 29])
+ keydown('d')
+ keydown('a')
+ keydown('[')
+ expect(editor.getText()).toBe "[ something in here and in ]"
+ expect(editor.getCursorScreenPosition()).toEqual [0, 27]
+ expect(editorElement.classList.contains('operator-pending-mode')).toBe(false)
+ expect(editorElement.classList.contains('command-mode')).toBe(true)
+
+ describe "the 'a\'' text object", ->
+ beforeEach ->
+ editor.setText("' something in here and in 'here' '")
+ editor.setCursorScreenPosition([0, 9])
+
+ it "applies operators around the current single quotes in operator-pending mode", ->
+ keydown('d')
+ keydown('a')
+ keydown('\'')
+ expect(editor.getText()).toBe "here' '"
+ expect(editor.getCursorScreenPosition()).toEqual [0, 0]
+ expect(editorElement.classList.contains('operator-pending-mode')).toBe(false)
+ expect(editorElement.classList.contains('command-mode')).toBe(true)
+
+ it "applies operators around the current single quotes in operator-pending mode (second test)", ->
+ editor.setCursorScreenPosition([0, 29])
+ keydown('d')
+ keydown('a')
+ keydown('\'')
+ expect(editor.getText()).toBe "' something in here and in 'here"
+ expect(editor.getCursorScreenPosition()).toEqual [0, 31]
+ expect(editorElement.classList.contains('operator-pending-mode')).toBe(false)
+ expect(editorElement.classList.contains('command-mode')).toBe(true)
+
+ describe "the 'a\"' text object", ->
+ beforeEach ->
+ editor.setText("\" something in here and in \"here\" \"")
+ editor.setCursorScreenPosition([0, 9])
+
+ it "applies operators around the current double quotes in operator-pending mode", ->
+ keydown('d')
+ keydown('a')
+ keydown('""')
+ expect(editor.getText()).toBe 'here" "'
+ expect(editor.getCursorScreenPosition()).toEqual [0, 0]
+ expect(editorElement.classList.contains('operator-pending-mode')).toBe(false)
+ expect(editorElement.classList.contains('command-mode')).toBe(true)
+
+ it "applies operators around the current double quotes in operator-pending mode (second test)", ->
+ editor.setCursorScreenPosition([0, 29])
+ keydown('d')
+ keydown('a')
+ keydown('"')
+ expect(editor.getText()).toBe "\" something in here and in \"here"
+ expect(editor.getCursorScreenPosition()).toEqual [0, 31]
+ expect(editorElement.classList.contains('operator-pending-mode')).toBe(false)
+ expect(editorElement.classList.contains('command-mode')).toBe(true)
diff --git a/atom/packages/vim-mode/spec/vim-mode-spec.coffee b/atom/packages/vim-mode/spec/vim-mode-spec.coffee
new file mode 100644
index 0000000..0f4d8b8
--- /dev/null
+++ b/atom/packages/vim-mode/spec/vim-mode-spec.coffee
@@ -0,0 +1,56 @@
+describe "VimMode", ->
+ [editor, editorElement, workspaceElement] = []
+
+ beforeEach ->
+ workspaceElement = atom.views.getView(atom.workspace)
+
+ waitsForPromise ->
+ atom.workspace.open()
+
+ waitsForPromise ->
+ atom.packages.activatePackage('vim-mode')
+
+ waitsForPromise ->
+ atom.packages.activatePackage('status-bar')
+
+ runs ->
+ editor = atom.workspace.getActiveTextEditor()
+ editorElement = atom.views.getView(editor)
+
+ describe ".activate", ->
+ it "puts the editor in command-mode initially by default", ->
+ expect(editorElement.classList.contains('vim-mode')).toBe(true)
+ expect(editorElement.classList.contains('command-mode')).toBe(true)
+
+ it "shows the current vim mode in the status bar", ->
+ statusBarTile = workspaceElement.querySelector("#status-bar-vim-mode")
+ expect(statusBarTile.textContent).toBe("Command")
+ atom.commands.dispatch(editorElement, "vim-mode:activate-insert-mode")
+ expect(statusBarTile.textContent).toBe("Insert")
+
+ it "doesn't register duplicate command listeners for editors", ->
+ editor.setText("12345")
+ editor.setCursorBufferPosition([0, 0])
+
+ pane = atom.workspace.getActivePane()
+ newPane = pane.splitRight()
+ pane.removeItem(editor)
+ newPane.addItem(editor)
+
+ atom.commands.dispatch(editorElement, "vim-mode:move-right")
+ expect(editor.getCursorBufferPosition()).toEqual([0, 1])
+
+ describe ".deactivate", ->
+ it "removes the vim classes from the editor", ->
+ atom.packages.deactivatePackage('vim-mode')
+ expect(editorElement.classList.contains("vim-mode")).toBe(false)
+ expect(editorElement.classList.contains("command-mode")).toBe(false)
+
+ it "removes the vim commands from the editor element", ->
+ vimCommands = ->
+ atom.commands.findCommands(target: editorElement).filter (cmd) ->
+ cmd.name.startsWith("vim-mode:")
+
+ expect(vimCommands().length).toBeGreaterThan(0)
+ atom.packages.deactivatePackage('vim-mode')
+ expect(vimCommands().length).toBe(0)
diff --git a/atom/packages/vim-mode/spec/vim-state-spec.coffee b/atom/packages/vim-mode/spec/vim-state-spec.coffee
new file mode 100644
index 0000000..741c371
--- /dev/null
+++ b/atom/packages/vim-mode/spec/vim-state-spec.coffee
@@ -0,0 +1,420 @@
+_ = require 'underscore-plus'
+helpers = require './spec-helper'
+VimState = require '../lib/vim-state'
+StatusBarManager = require '../lib/status-bar-manager'
+
+describe "VimState", ->
+ [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 "initialization", ->
+ it "puts the editor in command-mode initially by default", ->
+ expect(editorElement.classList.contains('vim-mode')).toBe(true)
+ expect(editorElement.classList.contains('command-mode')).toBe(true)
+
+ it "puts the editor in insert-mode if startInInsertMode is true", ->
+ atom.config.set 'vim-mode.startInInsertMode', true
+ editor.vimState = new VimState(editorElement, new StatusBarManager)
+ expect(editorElement.classList.contains('insert-mode')).toBe(true)
+
+ describe "::destroy", ->
+ it "re-enables text input on the editor", ->
+ expect(editorElement.component.isInputEnabled()).toBeFalsy()
+ vimState.destroy()
+ expect(editorElement.component.isInputEnabled()).toBeTruthy()
+
+ it "removes the mode classes from the editor", ->
+ expect(editorElement.classList.contains("command-mode")).toBeTruthy()
+ vimState.destroy()
+ expect(editorElement.classList.contains("command-mode")).toBeFalsy()
+
+ it "is a noop when the editor is already destroyed", ->
+ editorElement.getModel().destroy()
+ vimState.destroy()
+
+ describe "command-mode", ->
+ describe "when entering an insertable character", ->
+ beforeEach -> keydown('\\')
+
+ it "stops propagation", ->
+ expect(editor.getText()).toEqual ''
+
+ describe "when entering an operator", ->
+ beforeEach -> keydown('d')
+
+ describe "with an operator that can't be composed", ->
+ beforeEach -> keydown('x')
+
+ it "clears the operator stack", ->
+ expect(vimState.opStack.length).toBe 0
+
+ describe "the escape keybinding", ->
+ beforeEach -> keydown('escape')
+
+ it "clears the operator stack", ->
+ expect(vimState.opStack.length).toBe 0
+
+ describe "the ctrl-c keybinding", ->
+ beforeEach -> keydown('c', ctrl: true)
+
+ it "clears the operator stack", ->
+ expect(vimState.opStack.length).toBe 0
+
+ describe "the escape keybinding", ->
+ it "clears any extra cursors", ->
+ editor.setText("one-two-three")
+ editor.addCursorAtBufferPosition([0, 3])
+ expect(editor.getCursors().length).toBe 2
+ keydown('escape')
+ expect(editor.getCursors().length).toBe 1
+
+ describe "the v keybinding", ->
+ beforeEach -> keydown('v')
+
+ it "puts the editor into visual characterwise mode", ->
+ expect(editorElement.classList.contains('visual-mode')).toBe(true)
+ expect(vimState.submode).toEqual 'characterwise'
+ expect(editorElement.classList.contains('command-mode')).toBe(false)
+
+ describe "the V keybinding", ->
+ beforeEach ->
+ editor.setText("012345\nabcdef")
+ editor.setCursorScreenPosition([0, 0])
+ keydown('V', shift: true)
+
+ it "puts the editor into visual linewise mode", ->
+ expect(editorElement.classList.contains('visual-mode')).toBe(true)
+ expect(vimState.submode).toEqual 'linewise'
+ expect(editorElement.classList.contains('command-mode')).toBe(false)
+
+ it "selects the current line", ->
+ expect(editor.getLastSelection().getText()).toEqual '012345\n'
+
+ describe "the ctrl-v keybinding", ->
+ beforeEach -> keydown('v', ctrl: true)
+
+ it "puts the editor into visual characterwise mode", ->
+ expect(editorElement.classList.contains('visual-mode')).toBe(true)
+ expect(vimState.submode).toEqual 'blockwise'
+ expect(editorElement.classList.contains('command-mode')).toBe(false)
+
+ describe "selecting text", ->
+ beforeEach ->
+ spyOn(_._, "now").andCallFake -> window.now
+ editor.setText("abc def")
+
+ it "puts the editor into visual mode", ->
+ expect(vimState.mode).toEqual 'command'
+ editor.setSelectedBufferRanges([[[0, 0], [0, 3]]])
+
+ advanceClock(100)
+
+ expect(vimState.mode).toEqual 'visual'
+ expect(vimState.submode).toEqual 'characterwise'
+ expect(editor.getSelectedBufferRanges()).toEqual([[[0, 0], [0, 3]]])
+
+ it "handles the editor being destroyed shortly after selecting text", ->
+ editor.setSelectedBufferRanges([[[0, 0], [0, 3]]])
+ editor.destroy()
+ vimState.destroy()
+ advanceClock(100)
+
+ describe "the i keybinding", ->
+ beforeEach -> keydown('i')
+
+ it "puts the editor into insert mode", ->
+ expect(editorElement.classList.contains('insert-mode')).toBe(true)
+ expect(editorElement.classList.contains('command-mode')).toBe(false)
+
+ describe "with content", ->
+ beforeEach -> editor.setText("012345\n\nabcdef")
+
+ # FIXME: See atom/vim-mode#2
+ xdescribe "on a line with content", ->
+ beforeEach -> editor.setCursorScreenPosition([0, 6])
+
+ it "does not allow the cursor to be placed on the \n character", ->
+ expect(editor.getCursorScreenPosition()).toEqual [0, 5]
+
+ describe "on an empty line", ->
+ beforeEach -> editor.setCursorScreenPosition([1, 0])
+
+ it "allows the cursor to be placed on the \n character", ->
+ expect(editor.getCursorScreenPosition()).toEqual [1, 0]
+
+ describe 'with character-input operations', ->
+ beforeEach -> editor.setText('012345\nabcdef')
+
+ it 'properly clears the opStack', ->
+ keydown('d')
+ keydown('r')
+ expect(vimState.mode).toBe 'command'
+ expect(vimState.opStack.length).toBe 0
+ atom.commands.dispatch(editor.commandModeInputView.editorElement, "core:cancel")
+ keydown('d')
+ expect(editor.getText()).toBe '012345\nabcdef'
+
+ describe "insert-mode", ->
+ beforeEach ->
+ keydown('i')
+
+ describe "with content", ->
+ beforeEach -> editor.setText("012345\n\nabcdef")
+
+ describe "when cursor is in the middle of the line", ->
+ beforeEach -> editor.setCursorScreenPosition([0, 3])
+
+ it "moves the cursor to the left when exiting insert mode", ->
+ keydown('escape')
+ expect(editor.getCursorScreenPosition()).toEqual [0, 2]
+
+ describe "when cursor is at the beginning of line", ->
+ beforeEach -> editor.setCursorScreenPosition([1, 0])
+
+ it "leaves the cursor at the beginning of line", ->
+ keydown('escape')
+ expect(editor.getCursorScreenPosition()).toEqual [1, 0]
+
+ describe "on a line with content", ->
+ beforeEach -> editor.setCursorScreenPosition([0, 6])
+
+ it "allows the cursor to be placed on the \n character", ->
+ expect(editor.getCursorScreenPosition()).toEqual [0, 6]
+
+ it "puts the editor into command mode when <escape> is pressed", ->
+ keydown('escape')
+
+ expect(editorElement.classList.contains('command-mode')).toBe(true)
+ expect(editorElement.classList.contains('insert-mode')).toBe(false)
+ expect(editorElement.classList.contains('visual-mode')).toBe(false)
+
+ it "puts the editor into command mode when <ctrl-c> is pressed", ->
+ helpers.mockPlatform(editorElement, 'platform-darwin')
+ keydown('c', ctrl: true)
+ helpers.unmockPlatform(editorElement)
+
+ expect(editorElement.classList.contains('command-mode')).toBe(true)
+ expect(editorElement.classList.contains('insert-mode')).toBe(false)
+ expect(editorElement.classList.contains('visual-mode')).toBe(false)
+
+ describe "visual-mode", ->
+ beforeEach ->
+ editor.setText("one two three")
+ editor.setCursorBufferPosition([0, 4])
+ keydown('v')
+
+ it "selects the character under the cursor", ->
+ expect(editor.getSelectedBufferRanges()).toEqual [[[0, 4], [0, 5]]]
+ expect(editor.getSelectedText()).toBe("t")
+
+ it "puts the editor into command mode when <escape> is pressed", ->
+ keydown('escape')
+
+ expect(editor.getCursorBufferPositions()).toEqual [[0, 4]]
+ expect(editorElement.classList.contains('command-mode')).toBe(true)
+ expect(editorElement.classList.contains('visual-mode')).toBe(false)
+
+ it "puts the editor into command mode when <escape> is pressed on selection is reversed", ->
+ expect(editor.getSelectedText()).toBe("t")
+ keydown("h")
+ keydown("h")
+ expect(editor.getSelectedText()).toBe("e t")
+ expect(editor.getLastSelection().isReversed()).toBe(true)
+ keydown('escape')
+ expect(editorElement.classList.contains('command-mode')).toBe(true)
+ expect(editor.getCursorBufferPositions()).toEqual [[0, 2]]
+
+ describe "motions", ->
+ it "transforms the selection", ->
+ keydown('w')
+ expect(editor.getLastSelection().getText()).toEqual 'two t'
+
+ it "always leaves the initially selected character selected", ->
+ keydown("h")
+ expect(editor.getSelectedText()).toBe(" t")
+
+ keydown("l")
+ expect(editor.getSelectedText()).toBe("t")
+
+ keydown("l")
+ keydown("l")
+ expect(editor.getSelectedText()).toBe("tw")
+
+ describe "operators", ->
+ beforeEach ->
+ editor.setText("012345\n\nabcdef")
+ editor.setCursorScreenPosition([0, 0])
+ editor.selectLinesContainingCursors()
+ keydown('d')
+
+ it "operate on the current selection", ->
+ expect(editor.getText()).toEqual "\nabcdef"
+
+ describe "returning to command-mode", ->
+ beforeEach ->
+ editor.setText("012345\n\nabcdef")
+ editor.selectLinesContainingCursors()
+ keydown('escape')
+
+ it "operate on the current selection", ->
+ expect(editor.getLastSelection().getText()).toEqual ''
+
+ describe "the o keybinding", ->
+ it "reversed each selection", ->
+ editor.addCursorAtBufferPosition([0, Infinity])
+ keydown("i")
+ keydown("w")
+
+ expect(editor.getSelectedBufferRanges()).toEqual([
+ [[0, 4], [0, 7]],
+ [[0, 8], [0, 13]]
+ ])
+ expect(editor.getCursorBufferPositions()).toEqual([
+ [0, 7]
+ [0, 13]
+ ])
+
+ keydown("o")
+
+ expect(editor.getSelectedBufferRanges()).toEqual([
+ [[0, 4], [0, 7]],
+ [[0, 8], [0, 13]]
+ ])
+ expect(editor.getCursorBufferPositions()).toEqual([
+ [0, 4]
+ [0, 8]
+ ])
+
+ describe "activate visualmode witin visualmode", ->
+ beforeEach ->
+ keydown('escape')
+ expect(vimState.mode).toEqual 'command'
+ expect(editorElement.classList.contains('command-mode')).toBe(true)
+
+ it "activateVisualMode with same type puts the editor into command mode", ->
+ keydown('v')
+ expect(editorElement.classList.contains('visual-mode')).toBe(true)
+ expect(vimState.submode).toEqual 'characterwise'
+ expect(editorElement.classList.contains('command-mode')).toBe(false)
+
+ keydown('v')
+ expect(vimState.mode).toEqual 'command'
+ expect(editorElement.classList.contains('command-mode')).toBe(true)
+
+ keydown('V', shift: true)
+ expect(editorElement.classList.contains('visual-mode')).toBe(true)
+ expect(vimState.submode).toEqual 'linewise'
+ expect(editorElement.classList.contains('command-mode')).toBe(false)
+
+ keydown('V', shift: true)
+ expect(vimState.mode).toEqual 'command'
+ expect(editorElement.classList.contains('command-mode')).toBe(true)
+
+ keydown('v', ctrl: true)
+ expect(editorElement.classList.contains('visual-mode')).toBe(true)
+ expect(vimState.submode).toEqual 'blockwise'
+ expect(editorElement.classList.contains('command-mode')).toBe(false)
+
+ keydown('v', ctrl: true)
+ expect(vimState.mode).toEqual 'command'
+ expect(editorElement.classList.contains('command-mode')).toBe(true)
+
+ describe "change submode within visualmode", ->
+ beforeEach ->
+ editor.setText("line one\nline two\nline three\n")
+ editor.setCursorBufferPosition([0, 5])
+ editor.addCursorAtBufferPosition([2, 5])
+
+ it "can change submode within visual mode", ->
+ keydown('v')
+ expect(editorElement.classList.contains('visual-mode')).toBe(true)
+ expect(vimState.submode).toEqual 'characterwise'
+ expect(editorElement.classList.contains('command-mode')).toBe(false)
+
+ keydown('V', shift: true)
+ expect(editorElement.classList.contains('visual-mode')).toBe(true)
+ expect(vimState.submode).toEqual 'linewise'
+ expect(editorElement.classList.contains('command-mode')).toBe(false)
+
+ keydown('v', ctrl: true)
+ expect(editorElement.classList.contains('visual-mode')).toBe(true)
+ expect(vimState.submode).toEqual 'blockwise'
+ expect(editorElement.classList.contains('command-mode')).toBe(false)
+
+ keydown('v')
+ expect(editorElement.classList.contains('visual-mode')).toBe(true)
+ expect(vimState.submode).toEqual 'characterwise'
+ expect(editorElement.classList.contains('command-mode')).toBe(false)
+
+
+ it "recover original range when shift from linewse to characterwise", ->
+ keydown('v')
+ keydown('i')
+ keydown('w')
+
+ expect(_.map(editor.getSelections(), (selection) ->
+ selection.getText())
+ ).toEqual(['one', 'three'])
+
+ keydown('V', shift: true)
+ expect(_.map(editor.getSelections(), (selection) ->
+ selection.getText())
+ ).toEqual(["line one\n", "line three\n"])
+
+ keydown('v', ctrl: true)
+ expect(_.map(editor.getSelections(), (selection) ->
+ selection.getText())
+ ).toEqual(['one', 'three'])
+
+ describe "marks", ->
+ beforeEach -> editor.setText("text in line 1\ntext in line 2\ntext in line 3")
+
+ it "basic marking functionality", ->
+ editor.setCursorScreenPosition([1, 1])
+ keydown('m')
+ commandModeInputKeydown('t')
+ expect(editor.getText()).toEqual "text in line 1\ntext in line 2\ntext in line 3"
+ editor.setCursorScreenPosition([2, 2])
+ keydown('`')
+ commandModeInputKeydown('t')
+ expect(editor.getCursorScreenPosition()).toEqual [1, 1]
+
+ it "real (tracking) marking functionality", ->
+ editor.setCursorScreenPosition([2, 2])
+ keydown('m')
+ commandModeInputKeydown('q')
+ editor.setCursorScreenPosition([1, 2])
+ keydown('o')
+ keydown('escape')
+ keydown('`')
+ commandModeInputKeydown('q')
+ expect(editor.getCursorScreenPosition()).toEqual [3, 2]
+
+ it "real (tracking) marking functionality", ->
+ editor.setCursorScreenPosition([2, 2])
+ keydown('m')
+ commandModeInputKeydown('q')
+ editor.setCursorScreenPosition([1, 2])
+ keydown('d')
+ keydown('d')
+ keydown('escape')
+ keydown('`')
+ commandModeInputKeydown('q')
+ expect(editor.getCursorScreenPosition()).toEqual [1, 2]