]> git.r.bdr.sh - rbdr/dotfiles/blob - atom/packages/vim-mode/spec/vim-state-spec.coffee
741c3712e7efd6e5b4573f54cc6834897e0d33f3
[rbdr/dotfiles] / atom / packages / vim-mode / spec / vim-state-spec.coffee
1 _ = require 'underscore-plus'
2 helpers = require './spec-helper'
3 VimState = require '../lib/vim-state'
4 StatusBarManager = require '../lib/status-bar-manager'
5
6 describe "VimState", ->
7 [editor, editorElement, vimState] = []
8
9 beforeEach ->
10 vimMode = atom.packages.loadPackage('vim-mode')
11 vimMode.activateResources()
12
13 helpers.getEditorElement (element) ->
14 editorElement = element
15 editor = editorElement.getModel()
16 vimState = editorElement.vimState
17 vimState.activateCommandMode()
18 vimState.resetCommandMode()
19
20 keydown = (key, options={}) ->
21 options.element ?= editorElement
22 helpers.keydown(key, options)
23
24 commandModeInputKeydown = (key, opts = {}) ->
25 editor.commandModeInputView.editorElement.getModel().setText(key)
26
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)
31
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)
36
37 describe "::destroy", ->
38 it "re-enables text input on the editor", ->
39 expect(editorElement.component.isInputEnabled()).toBeFalsy()
40 vimState.destroy()
41 expect(editorElement.component.isInputEnabled()).toBeTruthy()
42
43 it "removes the mode classes from the editor", ->
44 expect(editorElement.classList.contains("command-mode")).toBeTruthy()
45 vimState.destroy()
46 expect(editorElement.classList.contains("command-mode")).toBeFalsy()
47
48 it "is a noop when the editor is already destroyed", ->
49 editorElement.getModel().destroy()
50 vimState.destroy()
51
52 describe "command-mode", ->
53 describe "when entering an insertable character", ->
54 beforeEach -> keydown('\\')
55
56 it "stops propagation", ->
57 expect(editor.getText()).toEqual ''
58
59 describe "when entering an operator", ->
60 beforeEach -> keydown('d')
61
62 describe "with an operator that can't be composed", ->
63 beforeEach -> keydown('x')
64
65 it "clears the operator stack", ->
66 expect(vimState.opStack.length).toBe 0
67
68 describe "the escape keybinding", ->
69 beforeEach -> keydown('escape')
70
71 it "clears the operator stack", ->
72 expect(vimState.opStack.length).toBe 0
73
74 describe "the ctrl-c keybinding", ->
75 beforeEach -> keydown('c', ctrl: true)
76
77 it "clears the operator stack", ->
78 expect(vimState.opStack.length).toBe 0
79
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
85 keydown('escape')
86 expect(editor.getCursors().length).toBe 1
87
88 describe "the v keybinding", ->
89 beforeEach -> keydown('v')
90
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)
95
96 describe "the V keybinding", ->
97 beforeEach ->
98 editor.setText("012345\nabcdef")
99 editor.setCursorScreenPosition([0, 0])
100 keydown('V', shift: true)
101
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)
106
107 it "selects the current line", ->
108 expect(editor.getLastSelection().getText()).toEqual '012345\n'
109
110 describe "the ctrl-v keybinding", ->
111 beforeEach -> keydown('v', ctrl: true)
112
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)
117
118 describe "selecting text", ->
119 beforeEach ->
120 spyOn(_._, "now").andCallFake -> window.now
121 editor.setText("abc def")
122
123 it "puts the editor into visual mode", ->
124 expect(vimState.mode).toEqual 'command'
125 editor.setSelectedBufferRanges([[[0, 0], [0, 3]]])
126
127 advanceClock(100)
128
129 expect(vimState.mode).toEqual 'visual'
130 expect(vimState.submode).toEqual 'characterwise'
131 expect(editor.getSelectedBufferRanges()).toEqual([[[0, 0], [0, 3]]])
132
133 it "handles the editor being destroyed shortly after selecting text", ->
134 editor.setSelectedBufferRanges([[[0, 0], [0, 3]]])
135 editor.destroy()
136 vimState.destroy()
137 advanceClock(100)
138
139 describe "the i keybinding", ->
140 beforeEach -> keydown('i')
141
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)
145
146 describe "with content", ->
147 beforeEach -> editor.setText("012345\n\nabcdef")
148
149 # FIXME: See atom/vim-mode#2
150 xdescribe "on a line with content", ->
151 beforeEach -> editor.setCursorScreenPosition([0, 6])
152
153 it "does not allow the cursor to be placed on the \n character", ->
154 expect(editor.getCursorScreenPosition()).toEqual [0, 5]
155
156 describe "on an empty line", ->
157 beforeEach -> editor.setCursorScreenPosition([1, 0])
158
159 it "allows the cursor to be placed on the \n character", ->
160 expect(editor.getCursorScreenPosition()).toEqual [1, 0]
161
162 describe 'with character-input operations', ->
163 beforeEach -> editor.setText('012345\nabcdef')
164
165 it 'properly clears the opStack', ->
166 keydown('d')
167 keydown('r')
168 expect(vimState.mode).toBe 'command'
169 expect(vimState.opStack.length).toBe 0
170 atom.commands.dispatch(editor.commandModeInputView.editorElement, "core:cancel")
171 keydown('d')
172 expect(editor.getText()).toBe '012345\nabcdef'
173
174 describe "insert-mode", ->
175 beforeEach ->
176 keydown('i')
177
178 describe "with content", ->
179 beforeEach -> editor.setText("012345\n\nabcdef")
180
181 describe "when cursor is in the middle of the line", ->
182 beforeEach -> editor.setCursorScreenPosition([0, 3])
183
184 it "moves the cursor to the left when exiting insert mode", ->
185 keydown('escape')
186 expect(editor.getCursorScreenPosition()).toEqual [0, 2]
187
188 describe "when cursor is at the beginning of line", ->
189 beforeEach -> editor.setCursorScreenPosition([1, 0])
190
191 it "leaves the cursor at the beginning of line", ->
192 keydown('escape')
193 expect(editor.getCursorScreenPosition()).toEqual [1, 0]
194
195 describe "on a line with content", ->
196 beforeEach -> editor.setCursorScreenPosition([0, 6])
197
198 it "allows the cursor to be placed on the \n character", ->
199 expect(editor.getCursorScreenPosition()).toEqual [0, 6]
200
201 it "puts the editor into command mode when <escape> is pressed", ->
202 keydown('escape')
203
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)
207
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)
212
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)
216
217 describe "visual-mode", ->
218 beforeEach ->
219 editor.setText("one two three")
220 editor.setCursorBufferPosition([0, 4])
221 keydown('v')
222
223 it "selects the character under the cursor", ->
224 expect(editor.getSelectedBufferRanges()).toEqual [[[0, 4], [0, 5]]]
225 expect(editor.getSelectedText()).toBe("t")
226
227 it "puts the editor into command mode when <escape> is pressed", ->
228 keydown('escape')
229
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)
233
234 it "puts the editor into command mode when <escape> is pressed on selection is reversed", ->
235 expect(editor.getSelectedText()).toBe("t")
236 keydown("h")
237 keydown("h")
238 expect(editor.getSelectedText()).toBe("e t")
239 expect(editor.getLastSelection().isReversed()).toBe(true)
240 keydown('escape')
241 expect(editorElement.classList.contains('command-mode')).toBe(true)
242 expect(editor.getCursorBufferPositions()).toEqual [[0, 2]]
243
244 describe "motions", ->
245 it "transforms the selection", ->
246 keydown('w')
247 expect(editor.getLastSelection().getText()).toEqual 'two t'
248
249 it "always leaves the initially selected character selected", ->
250 keydown("h")
251 expect(editor.getSelectedText()).toBe(" t")
252
253 keydown("l")
254 expect(editor.getSelectedText()).toBe("t")
255
256 keydown("l")
257 keydown("l")
258 expect(editor.getSelectedText()).toBe("tw")
259
260 describe "operators", ->
261 beforeEach ->
262 editor.setText("012345\n\nabcdef")
263 editor.setCursorScreenPosition([0, 0])
264 editor.selectLinesContainingCursors()
265 keydown('d')
266
267 it "operate on the current selection", ->
268 expect(editor.getText()).toEqual "\nabcdef"
269
270 describe "returning to command-mode", ->
271 beforeEach ->
272 editor.setText("012345\n\nabcdef")
273 editor.selectLinesContainingCursors()
274 keydown('escape')
275
276 it "operate on the current selection", ->
277 expect(editor.getLastSelection().getText()).toEqual ''
278
279 describe "the o keybinding", ->
280 it "reversed each selection", ->
281 editor.addCursorAtBufferPosition([0, Infinity])
282 keydown("i")
283 keydown("w")
284
285 expect(editor.getSelectedBufferRanges()).toEqual([
286 [[0, 4], [0, 7]],
287 [[0, 8], [0, 13]]
288 ])
289 expect(editor.getCursorBufferPositions()).toEqual([
290 [0, 7]
291 [0, 13]
292 ])
293
294 keydown("o")
295
296 expect(editor.getSelectedBufferRanges()).toEqual([
297 [[0, 4], [0, 7]],
298 [[0, 8], [0, 13]]
299 ])
300 expect(editor.getCursorBufferPositions()).toEqual([
301 [0, 4]
302 [0, 8]
303 ])
304
305 describe "activate visualmode witin visualmode", ->
306 beforeEach ->
307 keydown('escape')
308 expect(vimState.mode).toEqual 'command'
309 expect(editorElement.classList.contains('command-mode')).toBe(true)
310
311 it "activateVisualMode with same type puts the editor into command mode", ->
312 keydown('v')
313 expect(editorElement.classList.contains('visual-mode')).toBe(true)
314 expect(vimState.submode).toEqual 'characterwise'
315 expect(editorElement.classList.contains('command-mode')).toBe(false)
316
317 keydown('v')
318 expect(vimState.mode).toEqual 'command'
319 expect(editorElement.classList.contains('command-mode')).toBe(true)
320
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)
325
326 keydown('V', shift: true)
327 expect(vimState.mode).toEqual 'command'
328 expect(editorElement.classList.contains('command-mode')).toBe(true)
329
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)
334
335 keydown('v', ctrl: true)
336 expect(vimState.mode).toEqual 'command'
337 expect(editorElement.classList.contains('command-mode')).toBe(true)
338
339 describe "change submode within visualmode", ->
340 beforeEach ->
341 editor.setText("line one\nline two\nline three\n")
342 editor.setCursorBufferPosition([0, 5])
343 editor.addCursorAtBufferPosition([2, 5])
344
345 it "can change submode within visual mode", ->
346 keydown('v')
347 expect(editorElement.classList.contains('visual-mode')).toBe(true)
348 expect(vimState.submode).toEqual 'characterwise'
349 expect(editorElement.classList.contains('command-mode')).toBe(false)
350
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)
355
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)
360
361 keydown('v')
362 expect(editorElement.classList.contains('visual-mode')).toBe(true)
363 expect(vimState.submode).toEqual 'characterwise'
364 expect(editorElement.classList.contains('command-mode')).toBe(false)
365
366
367 it "recover original range when shift from linewse to characterwise", ->
368 keydown('v')
369 keydown('i')
370 keydown('w')
371
372 expect(_.map(editor.getSelections(), (selection) ->
373 selection.getText())
374 ).toEqual(['one', 'three'])
375
376 keydown('V', shift: true)
377 expect(_.map(editor.getSelections(), (selection) ->
378 selection.getText())
379 ).toEqual(["line one\n", "line three\n"])
380
381 keydown('v', ctrl: true)
382 expect(_.map(editor.getSelections(), (selection) ->
383 selection.getText())
384 ).toEqual(['one', 'three'])
385
386 describe "marks", ->
387 beforeEach -> editor.setText("text in line 1\ntext in line 2\ntext in line 3")
388
389 it "basic marking functionality", ->
390 editor.setCursorScreenPosition([1, 1])
391 keydown('m')
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])
395 keydown('`')
396 commandModeInputKeydown('t')
397 expect(editor.getCursorScreenPosition()).toEqual [1, 1]
398
399 it "real (tracking) marking functionality", ->
400 editor.setCursorScreenPosition([2, 2])
401 keydown('m')
402 commandModeInputKeydown('q')
403 editor.setCursorScreenPosition([1, 2])
404 keydown('o')
405 keydown('escape')
406 keydown('`')
407 commandModeInputKeydown('q')
408 expect(editor.getCursorScreenPosition()).toEqual [3, 2]
409
410 it "real (tracking) marking functionality", ->
411 editor.setCursorScreenPosition([2, 2])
412 keydown('m')
413 commandModeInputKeydown('q')
414 editor.setCursorScreenPosition([1, 2])
415 keydown('d')
416 keydown('d')
417 keydown('escape')
418 keydown('`')
419 commandModeInputKeydown('q')
420 expect(editor.getCursorScreenPosition()).toEqual [1, 2]