]> git.r.bdr.sh - rbdr/dotfiles/blame - atom/packages/vim-mode/spec/vim-state-spec.coffee
Update atoms
[rbdr/dotfiles] / atom / packages / vim-mode / spec / vim-state-spec.coffee
CommitLineData
24c7594d
BB
1_ = require 'underscore-plus'
2helpers = require './spec-helper'
3VimState = require '../lib/vim-state'
4StatusBarManager = require '../lib/status-bar-manager'
5
6describe "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
455f099b
BB
17 vimState.activateNormalMode()
18 vimState.resetNormalMode()
24c7594d
BB
19
20 keydown = (key, options={}) ->
21 options.element ?= editorElement
22 helpers.keydown(key, options)
23
455f099b
BB
24 normalModeInputKeydown = (key, opts = {}) ->
25 editor.normalModeInputView.editorElement.getModel().setText(key)
24c7594d
BB
26
27 describe "initialization", ->
455f099b 28 it "puts the editor in normal-mode initially by default", ->
24c7594d 29 expect(editorElement.classList.contains('vim-mode')).toBe(true)
455f099b 30 expect(editorElement.classList.contains('normal-mode')).toBe(true)
24c7594d
BB
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", ->
455f099b 44 expect(editorElement.classList.contains("normal-mode")).toBeTruthy()
24c7594d 45 vimState.destroy()
455f099b 46 expect(editorElement.classList.contains("normal-mode")).toBeFalsy()
24c7594d
BB
47
48 it "is a noop when the editor is already destroyed", ->
49 editorElement.getModel().destroy()
50 vimState.destroy()
51
455f099b 52 describe "normal-mode", ->
24c7594d
BB
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", ->
455f099b
BB
89 beforeEach ->
90 editor.setText("012345\nabcdef")
91 editor.setCursorScreenPosition([0, 0])
92 keydown('v')
24c7594d
BB
93
94 it "puts the editor into visual characterwise mode", ->
95 expect(editorElement.classList.contains('visual-mode')).toBe(true)
96 expect(vimState.submode).toEqual 'characterwise'
455f099b
BB
97 expect(editorElement.classList.contains('normal-mode')).toBe(false)
98
99 it "selects the current character", ->
100 expect(editor.getLastSelection().getText()).toEqual '0'
24c7594d
BB
101
102 describe "the V keybinding", ->
103 beforeEach ->
104 editor.setText("012345\nabcdef")
105 editor.setCursorScreenPosition([0, 0])
106 keydown('V', shift: true)
107
108 it "puts the editor into visual linewise mode", ->
109 expect(editorElement.classList.contains('visual-mode')).toBe(true)
110 expect(vimState.submode).toEqual 'linewise'
455f099b 111 expect(editorElement.classList.contains('normal-mode')).toBe(false)
24c7594d
BB
112
113 it "selects the current line", ->
114 expect(editor.getLastSelection().getText()).toEqual '012345\n'
115
116 describe "the ctrl-v keybinding", ->
455f099b
BB
117 beforeEach ->
118 editor.setText("012345\nabcdef")
119 editor.setCursorScreenPosition([0, 0])
120 keydown('v', ctrl: true)
24c7594d 121
455f099b 122 it "puts the editor into visual blockwise mode", ->
24c7594d
BB
123 expect(editorElement.classList.contains('visual-mode')).toBe(true)
124 expect(vimState.submode).toEqual 'blockwise'
455f099b 125 expect(editorElement.classList.contains('normal-mode')).toBe(false)
24c7594d
BB
126
127 describe "selecting text", ->
128 beforeEach ->
24c7594d 129 editor.setText("abc def")
455f099b 130 editor.setCursorScreenPosition([0, 0])
24c7594d
BB
131
132 it "puts the editor into visual mode", ->
455f099b
BB
133 expect(vimState.mode).toEqual 'normal'
134 atom.commands.dispatch(editorElement, "core:select-right")
24c7594d
BB
135
136 expect(vimState.mode).toEqual 'visual'
137 expect(vimState.submode).toEqual 'characterwise'
455f099b 138 expect(editor.getSelectedBufferRanges()).toEqual([[[0, 0], [0, 1]]])
24c7594d
BB
139
140 it "handles the editor being destroyed shortly after selecting text", ->
141 editor.setSelectedBufferRanges([[[0, 0], [0, 3]]])
142 editor.destroy()
143 vimState.destroy()
144 advanceClock(100)
145
455f099b
BB
146 it "handles native selection such as core:select-all", ->
147 atom.commands.dispatch(editorElement, "core:select-all")
148 expect(editor.getSelectedBufferRanges()).toEqual([[[0, 0], [0, 7]]])
149
24c7594d
BB
150 describe "the i keybinding", ->
151 beforeEach -> keydown('i')
152
153 it "puts the editor into insert mode", ->
154 expect(editorElement.classList.contains('insert-mode')).toBe(true)
455f099b 155 expect(editorElement.classList.contains('normal-mode')).toBe(false)
24c7594d 156
455f099b
BB
157 describe "the R keybinding", ->
158 beforeEach -> keydown('R', shift: true)
24c7594d 159
455f099b
BB
160 it "puts the editor into replace mode", ->
161 expect(editorElement.classList.contains('insert-mode')).toBe(true)
162 expect(editorElement.classList.contains('replace-mode')).toBe(true)
163 expect(editorElement.classList.contains('normal-mode')).toBe(false)
24c7594d 164
455f099b
BB
165 describe "with content", ->
166 beforeEach ->
167 editor.setText("012345\n\nabcdef")
168 editor.setCursorScreenPosition([0, 0])
169
170 describe "on a line with content", ->
171 it "does not allow atom commands to place the cursor on the \\n character", ->
172 atom.commands.dispatch(editorElement, "editor:move-to-end-of-line")
24c7594d
BB
173 expect(editor.getCursorScreenPosition()).toEqual [0, 5]
174
175 describe "on an empty line", ->
455f099b
BB
176 beforeEach ->
177 editor.setCursorScreenPosition([1, 0])
178 atom.commands.dispatch(editorElement, "editor:move-to-end-of-line")
24c7594d 179
455f099b 180 it "allows the cursor to be placed on the \\n character", ->
24c7594d
BB
181 expect(editor.getCursorScreenPosition()).toEqual [1, 0]
182
183 describe 'with character-input operations', ->
184 beforeEach -> editor.setText('012345\nabcdef')
185
186 it 'properly clears the opStack', ->
187 keydown('d')
188 keydown('r')
455f099b 189 expect(vimState.mode).toBe 'normal'
24c7594d 190 expect(vimState.opStack.length).toBe 0
455f099b 191 atom.commands.dispatch(editor.normalModeInputView.editorElement, "core:cancel")
24c7594d
BB
192 keydown('d')
193 expect(editor.getText()).toBe '012345\nabcdef'
194
195 describe "insert-mode", ->
196 beforeEach ->
197 keydown('i')
198
199 describe "with content", ->
200 beforeEach -> editor.setText("012345\n\nabcdef")
201
202 describe "when cursor is in the middle of the line", ->
203 beforeEach -> editor.setCursorScreenPosition([0, 3])
204
205 it "moves the cursor to the left when exiting insert mode", ->
206 keydown('escape')
207 expect(editor.getCursorScreenPosition()).toEqual [0, 2]
208
209 describe "when cursor is at the beginning of line", ->
210 beforeEach -> editor.setCursorScreenPosition([1, 0])
211
212 it "leaves the cursor at the beginning of line", ->
213 keydown('escape')
214 expect(editor.getCursorScreenPosition()).toEqual [1, 0]
215
216 describe "on a line with content", ->
455f099b
BB
217 beforeEach ->
218 editor.setCursorScreenPosition([0, 0])
219 atom.commands.dispatch(editorElement, "editor:move-to-end-of-line")
220
221 it "allows the cursor to be placed on the \\n character", ->
222 expect(editor.getCursorScreenPosition()).toEqual [0, 6]
223
224 it "puts the editor into normal mode when <escape> is pressed", ->
225 keydown('escape')
226
227 expect(editorElement.classList.contains('normal-mode')).toBe(true)
228 expect(editorElement.classList.contains('insert-mode')).toBe(false)
229 expect(editorElement.classList.contains('visual-mode')).toBe(false)
230
231 it "puts the editor into normal mode when <ctrl-c> is pressed", ->
232 helpers.mockPlatform(editorElement, 'platform-darwin')
233 keydown('c', ctrl: true)
234 helpers.unmockPlatform(editorElement)
235
236 expect(editorElement.classList.contains('normal-mode')).toBe(true)
237 expect(editorElement.classList.contains('insert-mode')).toBe(false)
238 expect(editorElement.classList.contains('visual-mode')).toBe(false)
239
240 describe "replace-mode", ->
241 describe "with content", ->
242 beforeEach -> editor.setText("012345\n\nabcdef")
243
244 describe "when cursor is in the middle of the line", ->
245 beforeEach ->
246 editor.setCursorScreenPosition([0, 3])
247 keydown('R', shift: true)
248
249 it "moves the cursor to the left when exiting replace mode", ->
250 keydown('escape')
251 expect(editor.getCursorScreenPosition()).toEqual [0, 2]
252
253 describe "when cursor is at the beginning of line", ->
254 beforeEach ->
255 editor.setCursorScreenPosition([1, 0])
256 keydown('R', shift: true)
257
258 it "leaves the cursor at the beginning of line", ->
259 keydown('escape')
260 expect(editor.getCursorScreenPosition()).toEqual [1, 0]
261
262 describe "on a line with content", ->
263 beforeEach ->
264 keydown('R', shift: true)
265 editor.setCursorScreenPosition([0, 0])
266 atom.commands.dispatch(editorElement, "editor:move-to-end-of-line")
24c7594d 267
455f099b 268 it "allows the cursor to be placed on the \\n character", ->
24c7594d
BB
269 expect(editor.getCursorScreenPosition()).toEqual [0, 6]
270
455f099b
BB
271 it "puts the editor into normal mode when <escape> is pressed", ->
272 keydown('R', shift: true)
24c7594d
BB
273 keydown('escape')
274
455f099b 275 expect(editorElement.classList.contains('normal-mode')).toBe(true)
24c7594d 276 expect(editorElement.classList.contains('insert-mode')).toBe(false)
455f099b 277 expect(editorElement.classList.contains('replace-mode')).toBe(false)
24c7594d
BB
278 expect(editorElement.classList.contains('visual-mode')).toBe(false)
279
455f099b
BB
280 it "puts the editor into normal mode when <ctrl-c> is pressed", ->
281 keydown('R', shift: true)
24c7594d
BB
282 helpers.mockPlatform(editorElement, 'platform-darwin')
283 keydown('c', ctrl: true)
284 helpers.unmockPlatform(editorElement)
285
455f099b 286 expect(editorElement.classList.contains('normal-mode')).toBe(true)
24c7594d 287 expect(editorElement.classList.contains('insert-mode')).toBe(false)
455f099b 288 expect(editorElement.classList.contains('replace-mode')).toBe(false)
24c7594d
BB
289 expect(editorElement.classList.contains('visual-mode')).toBe(false)
290
291 describe "visual-mode", ->
292 beforeEach ->
293 editor.setText("one two three")
294 editor.setCursorBufferPosition([0, 4])
295 keydown('v')
296
297 it "selects the character under the cursor", ->
298 expect(editor.getSelectedBufferRanges()).toEqual [[[0, 4], [0, 5]]]
299 expect(editor.getSelectedText()).toBe("t")
300
455f099b 301 it "puts the editor into normal mode when <escape> is pressed", ->
24c7594d
BB
302 keydown('escape')
303
304 expect(editor.getCursorBufferPositions()).toEqual [[0, 4]]
455f099b 305 expect(editorElement.classList.contains('normal-mode')).toBe(true)
24c7594d
BB
306 expect(editorElement.classList.contains('visual-mode')).toBe(false)
307
455f099b 308 it "puts the editor into normal mode when <escape> is pressed on selection is reversed", ->
24c7594d
BB
309 expect(editor.getSelectedText()).toBe("t")
310 keydown("h")
311 keydown("h")
312 expect(editor.getSelectedText()).toBe("e t")
313 expect(editor.getLastSelection().isReversed()).toBe(true)
314 keydown('escape')
455f099b 315 expect(editorElement.classList.contains('normal-mode')).toBe(true)
24c7594d
BB
316 expect(editor.getCursorBufferPositions()).toEqual [[0, 2]]
317
318 describe "motions", ->
319 it "transforms the selection", ->
320 keydown('w')
321 expect(editor.getLastSelection().getText()).toEqual 'two t'
322
323 it "always leaves the initially selected character selected", ->
324 keydown("h")
325 expect(editor.getSelectedText()).toBe(" t")
326
327 keydown("l")
328 expect(editor.getSelectedText()).toBe("t")
329
24c7594d
BB
330 keydown("l")
331 expect(editor.getSelectedText()).toBe("tw")
332
333 describe "operators", ->
334 beforeEach ->
335 editor.setText("012345\n\nabcdef")
336 editor.setCursorScreenPosition([0, 0])
337 editor.selectLinesContainingCursors()
338 keydown('d')
339
340 it "operate on the current selection", ->
341 expect(editor.getText()).toEqual "\nabcdef"
342
455f099b 343 describe "returning to normal-mode", ->
24c7594d
BB
344 beforeEach ->
345 editor.setText("012345\n\nabcdef")
346 editor.selectLinesContainingCursors()
347 keydown('escape')
348
349 it "operate on the current selection", ->
350 expect(editor.getLastSelection().getText()).toEqual ''
351
352 describe "the o keybinding", ->
353 it "reversed each selection", ->
354 editor.addCursorAtBufferPosition([0, Infinity])
355 keydown("i")
356 keydown("w")
357
358 expect(editor.getSelectedBufferRanges()).toEqual([
359 [[0, 4], [0, 7]],
360 [[0, 8], [0, 13]]
361 ])
362 expect(editor.getCursorBufferPositions()).toEqual([
363 [0, 7]
364 [0, 13]
365 ])
366
367 keydown("o")
368
369 expect(editor.getSelectedBufferRanges()).toEqual([
370 [[0, 4], [0, 7]],
371 [[0, 8], [0, 13]]
372 ])
373 expect(editor.getCursorBufferPositions()).toEqual([
374 [0, 4]
375 [0, 8]
376 ])
377
455f099b
BB
378 it "harmonizes selection directions", ->
379 keydown("e")
380 editor.addCursorAtBufferPosition([0, Infinity])
381 keydown("h")
382 keydown("h")
383
384 expect(editor.getSelectedBufferRanges()).toEqual([
385 [[0, 4], [0, 5]],
386 [[0, 11], [0, 13]]
387 ])
388 expect(editor.getCursorBufferPositions()).toEqual([
389 [0, 5]
390 [0, 11]
391 ])
392
393 keydown("o")
394
395 expect(editor.getSelectedBufferRanges()).toEqual([
396 [[0, 4], [0, 5]],
397 [[0, 11], [0, 13]]
398 ])
399 expect(editor.getCursorBufferPositions()).toEqual([
400 [0, 5]
401 [0, 13]
402 ])
403
24c7594d
BB
404 describe "activate visualmode witin visualmode", ->
405 beforeEach ->
406 keydown('escape')
455f099b
BB
407 expect(vimState.mode).toEqual 'normal'
408 expect(editorElement.classList.contains('normal-mode')).toBe(true)
24c7594d 409
455f099b 410 it "activateVisualMode with same type puts the editor into normal mode", ->
24c7594d
BB
411 keydown('v')
412 expect(editorElement.classList.contains('visual-mode')).toBe(true)
413 expect(vimState.submode).toEqual 'characterwise'
455f099b 414 expect(editorElement.classList.contains('normal-mode')).toBe(false)
24c7594d
BB
415
416 keydown('v')
455f099b
BB
417 expect(vimState.mode).toEqual 'normal'
418 expect(editorElement.classList.contains('normal-mode')).toBe(true)
24c7594d
BB
419
420 keydown('V', shift: true)
421 expect(editorElement.classList.contains('visual-mode')).toBe(true)
422 expect(vimState.submode).toEqual 'linewise'
455f099b 423 expect(editorElement.classList.contains('normal-mode')).toBe(false)
24c7594d
BB
424
425 keydown('V', shift: true)
455f099b
BB
426 expect(vimState.mode).toEqual 'normal'
427 expect(editorElement.classList.contains('normal-mode')).toBe(true)
24c7594d
BB
428
429 keydown('v', ctrl: true)
430 expect(editorElement.classList.contains('visual-mode')).toBe(true)
431 expect(vimState.submode).toEqual 'blockwise'
455f099b 432 expect(editorElement.classList.contains('normal-mode')).toBe(false)
24c7594d
BB
433
434 keydown('v', ctrl: true)
455f099b
BB
435 expect(vimState.mode).toEqual 'normal'
436 expect(editorElement.classList.contains('normal-mode')).toBe(true)
24c7594d
BB
437
438 describe "change submode within visualmode", ->
439 beforeEach ->
440 editor.setText("line one\nline two\nline three\n")
441 editor.setCursorBufferPosition([0, 5])
442 editor.addCursorAtBufferPosition([2, 5])
443
444 it "can change submode within visual mode", ->
445 keydown('v')
446 expect(editorElement.classList.contains('visual-mode')).toBe(true)
447 expect(vimState.submode).toEqual 'characterwise'
455f099b 448 expect(editorElement.classList.contains('normal-mode')).toBe(false)
24c7594d
BB
449
450 keydown('V', shift: true)
451 expect(editorElement.classList.contains('visual-mode')).toBe(true)
452 expect(vimState.submode).toEqual 'linewise'
455f099b 453 expect(editorElement.classList.contains('normal-mode')).toBe(false)
24c7594d
BB
454
455 keydown('v', ctrl: true)
456 expect(editorElement.classList.contains('visual-mode')).toBe(true)
457 expect(vimState.submode).toEqual 'blockwise'
455f099b 458 expect(editorElement.classList.contains('normal-mode')).toBe(false)
24c7594d
BB
459
460 keydown('v')
461 expect(editorElement.classList.contains('visual-mode')).toBe(true)
462 expect(vimState.submode).toEqual 'characterwise'
455f099b 463 expect(editorElement.classList.contains('normal-mode')).toBe(false)
24c7594d
BB
464
465
466 it "recover original range when shift from linewse to characterwise", ->
467 keydown('v')
468 keydown('i')
469 keydown('w')
470
471 expect(_.map(editor.getSelections(), (selection) ->
472 selection.getText())
473 ).toEqual(['one', 'three'])
474
475 keydown('V', shift: true)
476 expect(_.map(editor.getSelections(), (selection) ->
477 selection.getText())
478 ).toEqual(["line one\n", "line three\n"])
479
480 keydown('v', ctrl: true)
481 expect(_.map(editor.getSelections(), (selection) ->
482 selection.getText())
483 ).toEqual(['one', 'three'])
484
485 describe "marks", ->
486 beforeEach -> editor.setText("text in line 1\ntext in line 2\ntext in line 3")
487
488 it "basic marking functionality", ->
489 editor.setCursorScreenPosition([1, 1])
490 keydown('m')
455f099b 491 normalModeInputKeydown('t')
24c7594d
BB
492 expect(editor.getText()).toEqual "text in line 1\ntext in line 2\ntext in line 3"
493 editor.setCursorScreenPosition([2, 2])
494 keydown('`')
455f099b 495 normalModeInputKeydown('t')
24c7594d
BB
496 expect(editor.getCursorScreenPosition()).toEqual [1, 1]
497
498 it "real (tracking) marking functionality", ->
499 editor.setCursorScreenPosition([2, 2])
500 keydown('m')
455f099b 501 normalModeInputKeydown('q')
24c7594d
BB
502 editor.setCursorScreenPosition([1, 2])
503 keydown('o')
504 keydown('escape')
505 keydown('`')
455f099b 506 normalModeInputKeydown('q')
24c7594d
BB
507 expect(editor.getCursorScreenPosition()).toEqual [3, 2]
508
509 it "real (tracking) marking functionality", ->
510 editor.setCursorScreenPosition([2, 2])
511 keydown('m')
455f099b 512 normalModeInputKeydown('q')
24c7594d
BB
513 editor.setCursorScreenPosition([1, 2])
514 keydown('d')
515 keydown('d')
516 keydown('escape')
517 keydown('`')
455f099b 518 normalModeInputKeydown('q')
24c7594d 519 expect(editor.getCursorScreenPosition()).toEqual [1, 2]