]>
Commit | Line | Data |
---|---|---|
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] |