]> git.r.bdr.sh - rbdr/dotfiles/blob - atom/packages/vim-mode/spec/vim-state-spec.coffee
Remove vim-unimpaired plugin
[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.activateNormalMode()
18 vimState.resetNormalMode()
19
20 keydown = (key, options={}) ->
21 options.element ?= editorElement
22 helpers.keydown(key, options)
23
24 normalModeInputKeydown = (key, opts = {}) ->
25 editor.normalModeInputView.editorElement.getModel().setText(key)
26
27 describe "initialization", ->
28 it "puts the editor in normal-mode initially by default", ->
29 expect(editorElement.classList.contains('vim-mode')).toBe(true)
30 expect(editorElement.classList.contains('normal-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("normal-mode")).toBeTruthy()
45 vimState.destroy()
46 expect(editorElement.classList.contains("normal-mode")).toBeFalsy()
47
48 it "is a noop when the editor is already destroyed", ->
49 editorElement.getModel().destroy()
50 vimState.destroy()
51
52 describe "normal-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 ->
90 editor.setText("012345\nabcdef")
91 editor.setCursorScreenPosition([0, 0])
92 keydown('v')
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'
97 expect(editorElement.classList.contains('normal-mode')).toBe(false)
98
99 it "selects the current character", ->
100 expect(editor.getLastSelection().getText()).toEqual '0'
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'
111 expect(editorElement.classList.contains('normal-mode')).toBe(false)
112
113 it "selects the current line", ->
114 expect(editor.getLastSelection().getText()).toEqual '012345\n'
115
116 describe "the ctrl-v keybinding", ->
117 beforeEach ->
118 editor.setText("012345\nabcdef")
119 editor.setCursorScreenPosition([0, 0])
120 keydown('v', ctrl: true)
121
122 it "puts the editor into visual blockwise mode", ->
123 expect(editorElement.classList.contains('visual-mode')).toBe(true)
124 expect(vimState.submode).toEqual 'blockwise'
125 expect(editorElement.classList.contains('normal-mode')).toBe(false)
126
127 describe "selecting text", ->
128 beforeEach ->
129 editor.setText("abc def")
130 editor.setCursorScreenPosition([0, 0])
131
132 it "puts the editor into visual mode", ->
133 expect(vimState.mode).toEqual 'normal'
134 atom.commands.dispatch(editorElement, "core:select-right")
135
136 expect(vimState.mode).toEqual 'visual'
137 expect(vimState.submode).toEqual 'characterwise'
138 expect(editor.getSelectedBufferRanges()).toEqual([[[0, 0], [0, 1]]])
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
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
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)
155 expect(editorElement.classList.contains('normal-mode')).toBe(false)
156
157 describe "the R keybinding", ->
158 beforeEach -> keydown('R', shift: true)
159
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)
164
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")
173 expect(editor.getCursorScreenPosition()).toEqual [0, 5]
174
175 describe "on an empty line", ->
176 beforeEach ->
177 editor.setCursorScreenPosition([1, 0])
178 atom.commands.dispatch(editorElement, "editor:move-to-end-of-line")
179
180 it "allows the cursor to be placed on the \\n character", ->
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')
189 expect(vimState.mode).toBe 'normal'
190 expect(vimState.opStack.length).toBe 0
191 atom.commands.dispatch(editor.normalModeInputView.editorElement, "core:cancel")
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", ->
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")
267
268 it "allows the cursor to be placed on the \\n character", ->
269 expect(editor.getCursorScreenPosition()).toEqual [0, 6]
270
271 it "puts the editor into normal mode when <escape> is pressed", ->
272 keydown('R', shift: true)
273 keydown('escape')
274
275 expect(editorElement.classList.contains('normal-mode')).toBe(true)
276 expect(editorElement.classList.contains('insert-mode')).toBe(false)
277 expect(editorElement.classList.contains('replace-mode')).toBe(false)
278 expect(editorElement.classList.contains('visual-mode')).toBe(false)
279
280 it "puts the editor into normal mode when <ctrl-c> is pressed", ->
281 keydown('R', shift: true)
282 helpers.mockPlatform(editorElement, 'platform-darwin')
283 keydown('c', ctrl: true)
284 helpers.unmockPlatform(editorElement)
285
286 expect(editorElement.classList.contains('normal-mode')).toBe(true)
287 expect(editorElement.classList.contains('insert-mode')).toBe(false)
288 expect(editorElement.classList.contains('replace-mode')).toBe(false)
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
301 it "puts the editor into normal mode when <escape> is pressed", ->
302 keydown('escape')
303
304 expect(editor.getCursorBufferPositions()).toEqual [[0, 4]]
305 expect(editorElement.classList.contains('normal-mode')).toBe(true)
306 expect(editorElement.classList.contains('visual-mode')).toBe(false)
307
308 it "puts the editor into normal mode when <escape> is pressed on selection is reversed", ->
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')
315 expect(editorElement.classList.contains('normal-mode')).toBe(true)
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
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
343 describe "returning to normal-mode", ->
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
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
404 describe "activate visualmode witin visualmode", ->
405 beforeEach ->
406 keydown('escape')
407 expect(vimState.mode).toEqual 'normal'
408 expect(editorElement.classList.contains('normal-mode')).toBe(true)
409
410 it "activateVisualMode with same type puts the editor into normal mode", ->
411 keydown('v')
412 expect(editorElement.classList.contains('visual-mode')).toBe(true)
413 expect(vimState.submode).toEqual 'characterwise'
414 expect(editorElement.classList.contains('normal-mode')).toBe(false)
415
416 keydown('v')
417 expect(vimState.mode).toEqual 'normal'
418 expect(editorElement.classList.contains('normal-mode')).toBe(true)
419
420 keydown('V', shift: true)
421 expect(editorElement.classList.contains('visual-mode')).toBe(true)
422 expect(vimState.submode).toEqual 'linewise'
423 expect(editorElement.classList.contains('normal-mode')).toBe(false)
424
425 keydown('V', shift: true)
426 expect(vimState.mode).toEqual 'normal'
427 expect(editorElement.classList.contains('normal-mode')).toBe(true)
428
429 keydown('v', ctrl: true)
430 expect(editorElement.classList.contains('visual-mode')).toBe(true)
431 expect(vimState.submode).toEqual 'blockwise'
432 expect(editorElement.classList.contains('normal-mode')).toBe(false)
433
434 keydown('v', ctrl: true)
435 expect(vimState.mode).toEqual 'normal'
436 expect(editorElement.classList.contains('normal-mode')).toBe(true)
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'
448 expect(editorElement.classList.contains('normal-mode')).toBe(false)
449
450 keydown('V', shift: true)
451 expect(editorElement.classList.contains('visual-mode')).toBe(true)
452 expect(vimState.submode).toEqual 'linewise'
453 expect(editorElement.classList.contains('normal-mode')).toBe(false)
454
455 keydown('v', ctrl: true)
456 expect(editorElement.classList.contains('visual-mode')).toBe(true)
457 expect(vimState.submode).toEqual 'blockwise'
458 expect(editorElement.classList.contains('normal-mode')).toBe(false)
459
460 keydown('v')
461 expect(editorElement.classList.contains('visual-mode')).toBe(true)
462 expect(vimState.submode).toEqual 'characterwise'
463 expect(editorElement.classList.contains('normal-mode')).toBe(false)
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')
491 normalModeInputKeydown('t')
492 expect(editor.getText()).toEqual "text in line 1\ntext in line 2\ntext in line 3"
493 editor.setCursorScreenPosition([2, 2])
494 keydown('`')
495 normalModeInputKeydown('t')
496 expect(editor.getCursorScreenPosition()).toEqual [1, 1]
497
498 it "real (tracking) marking functionality", ->
499 editor.setCursorScreenPosition([2, 2])
500 keydown('m')
501 normalModeInputKeydown('q')
502 editor.setCursorScreenPosition([1, 2])
503 keydown('o')
504 keydown('escape')
505 keydown('`')
506 normalModeInputKeydown('q')
507 expect(editor.getCursorScreenPosition()).toEqual [3, 2]
508
509 it "real (tracking) marking functionality", ->
510 editor.setCursorScreenPosition([2, 2])
511 keydown('m')
512 normalModeInputKeydown('q')
513 editor.setCursorScreenPosition([1, 2])
514 keydown('d')
515 keydown('d')
516 keydown('escape')
517 keydown('`')
518 normalModeInputKeydown('q')
519 expect(editor.getCursorScreenPosition()).toEqual [1, 2]