1 _ = require 'underscore-plus'
2 helpers = require './spec-helper'
3 VimState = require '../lib/vim-state'
4 StatusBarManager = require '../lib/status-bar-manager'
6 describe "VimState", ->
7 [editor, editorElement, vimState] = []
10 vimMode = atom.packages.loadPackage('vim-mode')
11 vimMode.activateResources()
13 helpers.getEditorElement (element) ->
14 editorElement = element
15 editor = editorElement.getModel()
16 vimState = editorElement.vimState
17 vimState.activateCommandMode()
18 vimState.resetCommandMode()
20 keydown = (key, options={}) ->
21 options.element ?= editorElement
22 helpers.keydown(key, options)
24 commandModeInputKeydown = (key, opts = {}) ->
25 editor.commandModeInputView.editorElement.getModel().setText(key)
27 describe "initialization", ->
28 it "puts the editor in command-mode initially by default", ->
29 expect(editorElement.classList.contains('vim-mode')).toBe(true)
30 expect(editorElement.classList.contains('command-mode')).toBe(true)
32 it "puts the editor in insert-mode if startInInsertMode is true", ->
33 atom.config.set 'vim-mode.startInInsertMode', true
34 editor.vimState = new VimState(editorElement, new StatusBarManager)
35 expect(editorElement.classList.contains('insert-mode')).toBe(true)
37 describe "::destroy", ->
38 it "re-enables text input on the editor", ->
39 expect(editorElement.component.isInputEnabled()).toBeFalsy()
41 expect(editorElement.component.isInputEnabled()).toBeTruthy()
43 it "removes the mode classes from the editor", ->
44 expect(editorElement.classList.contains("command-mode")).toBeTruthy()
46 expect(editorElement.classList.contains("command-mode")).toBeFalsy()
48 it "is a noop when the editor is already destroyed", ->
49 editorElement.getModel().destroy()
52 describe "command-mode", ->
53 describe "when entering an insertable character", ->
54 beforeEach -> keydown('\\')
56 it "stops propagation", ->
57 expect(editor.getText()).toEqual ''
59 describe "when entering an operator", ->
60 beforeEach -> keydown('d')
62 describe "with an operator that can't be composed", ->
63 beforeEach -> keydown('x')
65 it "clears the operator stack", ->
66 expect(vimState.opStack.length).toBe 0
68 describe "the escape keybinding", ->
69 beforeEach -> keydown('escape')
71 it "clears the operator stack", ->
72 expect(vimState.opStack.length).toBe 0
74 describe "the ctrl-c keybinding", ->
75 beforeEach -> keydown('c', ctrl: true)
77 it "clears the operator stack", ->
78 expect(vimState.opStack.length).toBe 0
80 describe "the escape keybinding", ->
81 it "clears any extra cursors", ->
82 editor.setText("one-two-three")
83 editor.addCursorAtBufferPosition([0, 3])
84 expect(editor.getCursors().length).toBe 2
86 expect(editor.getCursors().length).toBe 1
88 describe "the v keybinding", ->
89 beforeEach -> keydown('v')
91 it "puts the editor into visual characterwise mode", ->
92 expect(editorElement.classList.contains('visual-mode')).toBe(true)
93 expect(vimState.submode).toEqual 'characterwise'
94 expect(editorElement.classList.contains('command-mode')).toBe(false)
96 describe "the V keybinding", ->
98 editor.setText("012345\nabcdef")
99 editor.setCursorScreenPosition([0, 0])
100 keydown('V', shift: true)
102 it "puts the editor into visual linewise mode", ->
103 expect(editorElement.classList.contains('visual-mode')).toBe(true)
104 expect(vimState.submode).toEqual 'linewise'
105 expect(editorElement.classList.contains('command-mode')).toBe(false)
107 it "selects the current line", ->
108 expect(editor.getLastSelection().getText()).toEqual '012345\n'
110 describe "the ctrl-v keybinding", ->
111 beforeEach -> keydown('v', ctrl: true)
113 it "puts the editor into visual characterwise mode", ->
114 expect(editorElement.classList.contains('visual-mode')).toBe(true)
115 expect(vimState.submode).toEqual 'blockwise'
116 expect(editorElement.classList.contains('command-mode')).toBe(false)
118 describe "selecting text", ->
120 spyOn(_._, "now").andCallFake -> window.now
121 editor.setText("abc def")
123 it "puts the editor into visual mode", ->
124 expect(vimState.mode).toEqual 'command'
125 editor.setSelectedBufferRanges([[[0, 0], [0, 3]]])
129 expect(vimState.mode).toEqual 'visual'
130 expect(vimState.submode).toEqual 'characterwise'
131 expect(editor.getSelectedBufferRanges()).toEqual([[[0, 0], [0, 3]]])
133 it "handles the editor being destroyed shortly after selecting text", ->
134 editor.setSelectedBufferRanges([[[0, 0], [0, 3]]])
139 describe "the i keybinding", ->
140 beforeEach -> keydown('i')
142 it "puts the editor into insert mode", ->
143 expect(editorElement.classList.contains('insert-mode')).toBe(true)
144 expect(editorElement.classList.contains('command-mode')).toBe(false)
146 describe "with content", ->
147 beforeEach -> editor.setText("012345\n\nabcdef")
149 # FIXME: See atom/vim-mode#2
150 xdescribe "on a line with content", ->
151 beforeEach -> editor.setCursorScreenPosition([0, 6])
153 it "does not allow the cursor to be placed on the \n character", ->
154 expect(editor.getCursorScreenPosition()).toEqual [0, 5]
156 describe "on an empty line", ->
157 beforeEach -> editor.setCursorScreenPosition([1, 0])
159 it "allows the cursor to be placed on the \n character", ->
160 expect(editor.getCursorScreenPosition()).toEqual [1, 0]
162 describe 'with character-input operations', ->
163 beforeEach -> editor.setText('012345\nabcdef')
165 it 'properly clears the opStack', ->
168 expect(vimState.mode).toBe 'command'
169 expect(vimState.opStack.length).toBe 0
170 atom.commands.dispatch(editor.commandModeInputView.editorElement, "core:cancel")
172 expect(editor.getText()).toBe '012345\nabcdef'
174 describe "insert-mode", ->
178 describe "with content", ->
179 beforeEach -> editor.setText("012345\n\nabcdef")
181 describe "when cursor is in the middle of the line", ->
182 beforeEach -> editor.setCursorScreenPosition([0, 3])
184 it "moves the cursor to the left when exiting insert mode", ->
186 expect(editor.getCursorScreenPosition()).toEqual [0, 2]
188 describe "when cursor is at the beginning of line", ->
189 beforeEach -> editor.setCursorScreenPosition([1, 0])
191 it "leaves the cursor at the beginning of line", ->
193 expect(editor.getCursorScreenPosition()).toEqual [1, 0]
195 describe "on a line with content", ->
196 beforeEach -> editor.setCursorScreenPosition([0, 6])
198 it "allows the cursor to be placed on the \n character", ->
199 expect(editor.getCursorScreenPosition()).toEqual [0, 6]
201 it "puts the editor into command mode when <escape> is pressed", ->
204 expect(editorElement.classList.contains('command-mode')).toBe(true)
205 expect(editorElement.classList.contains('insert-mode')).toBe(false)
206 expect(editorElement.classList.contains('visual-mode')).toBe(false)
208 it "puts the editor into command mode when <ctrl-c> is pressed", ->
209 helpers.mockPlatform(editorElement, 'platform-darwin')
210 keydown('c', ctrl: true)
211 helpers.unmockPlatform(editorElement)
213 expect(editorElement.classList.contains('command-mode')).toBe(true)
214 expect(editorElement.classList.contains('insert-mode')).toBe(false)
215 expect(editorElement.classList.contains('visual-mode')).toBe(false)
217 describe "visual-mode", ->
219 editor.setText("one two three")
220 editor.setCursorBufferPosition([0, 4])
223 it "selects the character under the cursor", ->
224 expect(editor.getSelectedBufferRanges()).toEqual [[[0, 4], [0, 5]]]
225 expect(editor.getSelectedText()).toBe("t")
227 it "puts the editor into command mode when <escape> is pressed", ->
230 expect(editor.getCursorBufferPositions()).toEqual [[0, 4]]
231 expect(editorElement.classList.contains('command-mode')).toBe(true)
232 expect(editorElement.classList.contains('visual-mode')).toBe(false)
234 it "puts the editor into command mode when <escape> is pressed on selection is reversed", ->
235 expect(editor.getSelectedText()).toBe("t")
238 expect(editor.getSelectedText()).toBe("e t")
239 expect(editor.getLastSelection().isReversed()).toBe(true)
241 expect(editorElement.classList.contains('command-mode')).toBe(true)
242 expect(editor.getCursorBufferPositions()).toEqual [[0, 2]]
244 describe "motions", ->
245 it "transforms the selection", ->
247 expect(editor.getLastSelection().getText()).toEqual 'two t'
249 it "always leaves the initially selected character selected", ->
251 expect(editor.getSelectedText()).toBe(" t")
254 expect(editor.getSelectedText()).toBe("t")
258 expect(editor.getSelectedText()).toBe("tw")
260 describe "operators", ->
262 editor.setText("012345\n\nabcdef")
263 editor.setCursorScreenPosition([0, 0])
264 editor.selectLinesContainingCursors()
267 it "operate on the current selection", ->
268 expect(editor.getText()).toEqual "\nabcdef"
270 describe "returning to command-mode", ->
272 editor.setText("012345\n\nabcdef")
273 editor.selectLinesContainingCursors()
276 it "operate on the current selection", ->
277 expect(editor.getLastSelection().getText()).toEqual ''
279 describe "the o keybinding", ->
280 it "reversed each selection", ->
281 editor.addCursorAtBufferPosition([0, Infinity])
285 expect(editor.getSelectedBufferRanges()).toEqual([
289 expect(editor.getCursorBufferPositions()).toEqual([
296 expect(editor.getSelectedBufferRanges()).toEqual([
300 expect(editor.getCursorBufferPositions()).toEqual([
305 describe "activate visualmode witin visualmode", ->
308 expect(vimState.mode).toEqual 'command'
309 expect(editorElement.classList.contains('command-mode')).toBe(true)
311 it "activateVisualMode with same type puts the editor into command mode", ->
313 expect(editorElement.classList.contains('visual-mode')).toBe(true)
314 expect(vimState.submode).toEqual 'characterwise'
315 expect(editorElement.classList.contains('command-mode')).toBe(false)
318 expect(vimState.mode).toEqual 'command'
319 expect(editorElement.classList.contains('command-mode')).toBe(true)
321 keydown('V', shift: true)
322 expect(editorElement.classList.contains('visual-mode')).toBe(true)
323 expect(vimState.submode).toEqual 'linewise'
324 expect(editorElement.classList.contains('command-mode')).toBe(false)
326 keydown('V', shift: true)
327 expect(vimState.mode).toEqual 'command'
328 expect(editorElement.classList.contains('command-mode')).toBe(true)
330 keydown('v', ctrl: true)
331 expect(editorElement.classList.contains('visual-mode')).toBe(true)
332 expect(vimState.submode).toEqual 'blockwise'
333 expect(editorElement.classList.contains('command-mode')).toBe(false)
335 keydown('v', ctrl: true)
336 expect(vimState.mode).toEqual 'command'
337 expect(editorElement.classList.contains('command-mode')).toBe(true)
339 describe "change submode within visualmode", ->
341 editor.setText("line one\nline two\nline three\n")
342 editor.setCursorBufferPosition([0, 5])
343 editor.addCursorAtBufferPosition([2, 5])
345 it "can change submode within visual mode", ->
347 expect(editorElement.classList.contains('visual-mode')).toBe(true)
348 expect(vimState.submode).toEqual 'characterwise'
349 expect(editorElement.classList.contains('command-mode')).toBe(false)
351 keydown('V', shift: true)
352 expect(editorElement.classList.contains('visual-mode')).toBe(true)
353 expect(vimState.submode).toEqual 'linewise'
354 expect(editorElement.classList.contains('command-mode')).toBe(false)
356 keydown('v', ctrl: true)
357 expect(editorElement.classList.contains('visual-mode')).toBe(true)
358 expect(vimState.submode).toEqual 'blockwise'
359 expect(editorElement.classList.contains('command-mode')).toBe(false)
362 expect(editorElement.classList.contains('visual-mode')).toBe(true)
363 expect(vimState.submode).toEqual 'characterwise'
364 expect(editorElement.classList.contains('command-mode')).toBe(false)
367 it "recover original range when shift from linewse to characterwise", ->
372 expect(_.map(editor.getSelections(), (selection) ->
374 ).toEqual(['one', 'three'])
376 keydown('V', shift: true)
377 expect(_.map(editor.getSelections(), (selection) ->
379 ).toEqual(["line one\n", "line three\n"])
381 keydown('v', ctrl: true)
382 expect(_.map(editor.getSelections(), (selection) ->
384 ).toEqual(['one', 'three'])
387 beforeEach -> editor.setText("text in line 1\ntext in line 2\ntext in line 3")
389 it "basic marking functionality", ->
390 editor.setCursorScreenPosition([1, 1])
392 commandModeInputKeydown('t')
393 expect(editor.getText()).toEqual "text in line 1\ntext in line 2\ntext in line 3"
394 editor.setCursorScreenPosition([2, 2])
396 commandModeInputKeydown('t')
397 expect(editor.getCursorScreenPosition()).toEqual [1, 1]
399 it "real (tracking) marking functionality", ->
400 editor.setCursorScreenPosition([2, 2])
402 commandModeInputKeydown('q')
403 editor.setCursorScreenPosition([1, 2])
407 commandModeInputKeydown('q')
408 expect(editor.getCursorScreenPosition()).toEqual [3, 2]
410 it "real (tracking) marking functionality", ->
411 editor.setCursorScreenPosition([2, 2])
413 commandModeInputKeydown('q')
414 editor.setCursorScreenPosition([1, 2])
419 commandModeInputKeydown('q')
420 expect(editor.getCursorScreenPosition()).toEqual [1, 2]