diff options
Diffstat (limited to 'atom/packages/vim-mode/spec')
| -rw-r--r-- | atom/packages/vim-mode/spec/insert-mode-spec.coffee | 73 | ||||
| -rw-r--r-- | atom/packages/vim-mode/spec/motions-spec.coffee | 371 | ||||
| -rw-r--r-- | atom/packages/vim-mode/spec/operators-spec.coffee | 754 | ||||
| -rw-r--r-- | atom/packages/vim-mode/spec/prefixes-spec.coffee | 38 | ||||
| -rw-r--r-- | atom/packages/vim-mode/spec/scroll-spec.coffee | 112 | ||||
| -rw-r--r-- | atom/packages/vim-mode/spec/spec-helper.coffee | 12 | ||||
| -rw-r--r-- | atom/packages/vim-mode/spec/text-objects-spec.coffee | 430 | ||||
| -rw-r--r-- | atom/packages/vim-mode/spec/vim-mode-spec.coffee | 19 | ||||
| -rw-r--r-- | atom/packages/vim-mode/spec/vim-state-spec.coffee | 231 |
9 files changed, 1690 insertions, 350 deletions
diff --git a/atom/packages/vim-mode/spec/insert-mode-spec.coffee b/atom/packages/vim-mode/spec/insert-mode-spec.coffee new file mode 100644 index 0000000..061cb50 --- /dev/null +++ b/atom/packages/vim-mode/spec/insert-mode-spec.coffee @@ -0,0 +1,73 @@ +helpers = require './spec-helper' + +describe "Insert mode commands", -> + [editor, editorElement, vimState] = [] + + beforeEach -> + vimMode = atom.packages.loadPackage('vim-mode') + vimMode.activateResources() + + helpers.getEditorElement (element) -> + editorElement = element + editor = editorElement.getModel() + vimState = editorElement.vimState + vimState.activateNormalMode() + vimState.resetNormalMode() + + keydown = (key, options={}) -> + options.element ?= editorElement + helpers.keydown(key, options) + + describe "Copy from line above/below", -> + beforeEach -> + editor.setText("12345\n\nabcd\nefghi") + editor.setCursorBufferPosition([1, 0]) + editor.addCursorAtBufferPosition([3, 0]) + keydown 'i' + + describe "the ctrl-y command", -> + it "copies from the line above", -> + keydown 'y', ctrl: true + expect(editor.getText()).toBe '12345\n1\nabcd\naefghi' + + editor.insertText ' ' + keydown 'y', ctrl: true + expect(editor.getText()).toBe '12345\n1 3\nabcd\na cefghi' + + it "does nothing if there's nothing above the cursor", -> + editor.insertText 'fill' + keydown 'y', ctrl: true + expect(editor.getText()).toBe '12345\nfill5\nabcd\nfillefghi' + + keydown 'y', ctrl: true + expect(editor.getText()).toBe '12345\nfill5\nabcd\nfillefghi' + + it "does nothing on the first line", -> + editor.setCursorBufferPosition([0, 2]) + editor.addCursorAtBufferPosition([3, 2]) + editor.insertText 'a' + expect(editor.getText()).toBe '12a345\n\nabcd\nefaghi' + keydown 'y', ctrl: true + expect(editor.getText()).toBe '12a345\n\nabcd\nefadghi' + + describe "the ctrl-e command", -> + beforeEach -> + atom.keymaps.add "test", + 'atom-text-editor.vim-mode.insert-mode': + 'ctrl-e': 'vim-mode:copy-from-line-below' + + it "copies from the line below", -> + keydown 'e', ctrl: true + expect(editor.getText()).toBe '12345\na\nabcd\nefghi' + + editor.insertText ' ' + keydown 'e', ctrl: true + expect(editor.getText()).toBe '12345\na c\nabcd\n efghi' + + it "does nothing if there's nothing below the cursor", -> + editor.insertText 'foo' + keydown 'e', ctrl: true + expect(editor.getText()).toBe '12345\nfood\nabcd\nfooefghi' + + keydown 'e', ctrl: true + expect(editor.getText()).toBe '12345\nfood\nabcd\nfooefghi' diff --git a/atom/packages/vim-mode/spec/motions-spec.coffee b/atom/packages/vim-mode/spec/motions-spec.coffee index f7114ed..9c205ec 100644 --- a/atom/packages/vim-mode/spec/motions-spec.coffee +++ b/atom/packages/vim-mode/spec/motions-spec.coffee @@ -11,20 +11,21 @@ describe "Motions", -> editorElement = element editor = editorElement.getModel() vimState = editorElement.vimState - vimState.activateCommandMode() - vimState.resetCommandMode() + vimState.activateNormalMode() + vimState.resetNormalMode() keydown = (key, options={}) -> options.element ?= editorElement helpers.keydown(key, options) - commandModeInputKeydown = (key, opts = {}) -> - editor.commandModeInputView.editorElement.getModel().setText(key) + normalModeInputKeydown = (key, opts = {}) -> + theEditor = opts.editor or editor + theEditor.normalModeInputView.editorElement.getModel().setText(key) - submitCommandModeInputText = (text) -> - commandEditor = editor.commandModeInputView.editorElement - commandEditor.getModel().setText(text) - atom.commands.dispatch(commandEditor, "core:confirm") + submitNormalModeInputText = (text) -> + inputEditor = editor.normalModeInputView.editorElement + inputEditor.getModel().setText(text) + atom.commands.dispatch(inputEditor, "core:confirm") describe "simple motions", -> beforeEach -> @@ -144,30 +145,21 @@ describe "Motions", -> keydown('w') expect(editor.getCursorScreenPosition()).toEqual [2, 0] - # FIXME: The definition of Cursor#getEndOfCurrentWordBufferPosition, - # means that the end of the word can't be the current cursor - # position (even though it is when your cursor is on a new line). - # - # Therefore it picks the end of the next word here (which is [3,3]) - # to start looking for the next word, which is also the end of the - # buffer so the cursor never advances. - # - # See atom/vim-mode#3 keydown('w') expect(editor.getCursorScreenPosition()).toEqual [3, 0] keydown('w') - expect(editor.getCursorScreenPosition()).toEqual [3, 3] + expect(editor.getCursorScreenPosition()).toEqual [3, 2] - # After cursor gets to the EOF, it should stay there. + # When the cursor gets to the EOF, it should stay there. keydown('w') - expect(editor.getCursorScreenPosition()).toEqual [3, 3] + expect(editor.getCursorScreenPosition()).toEqual [3, 2] it "moves the cursor to the end of the word if last word in file", -> editor.setText("abc") editor.setCursorScreenPosition([0, 0]) keydown('w') - expect(editor.getCursorScreenPosition()).toEqual([0, 3]) + expect(editor.getCursorScreenPosition()).toEqual([0, 2]) describe "as a selection", -> describe "within a word", -> @@ -460,10 +452,10 @@ describe "Motions", -> describe "as a selection", -> it "selects to the beginning of the whole word", -> - editor.setCursorScreenPosition([1, 10]) + editor.setCursorScreenPosition([1, 9]) keydown('y') keydown('B', shift: true) - expect(vimState.getRegister('"').text).toBe 'xyz-123' + expect(vimState.getRegister('"').text).toBe 'xyz-12' it "doesn't go past the beginning of the file", -> editor.setCursorScreenPosition([0, 0]) @@ -850,7 +842,7 @@ describe "Motions", -> editor.setCursorScreenPosition([0, 2]) describe "as a motion", -> - describe "in command mode", -> + describe "in normal mode", -> beforeEach -> keydown('g') keydown('g') @@ -885,7 +877,7 @@ describe "Motions", -> expect(editor.getCursorScreenPosition()).toEqual [0, 1] describe "as a repeated motion", -> - describe "in command mode", -> + describe "in normal mode", -> beforeEach -> keydown('2') keydown('g') @@ -965,7 +957,7 @@ describe "Motions", -> beforeEach -> keydown('G', shift: true) it "moves the cursor to the last line after whitespace", -> - expect(editor.getCursorScreenPosition()).toEqual [3, 1] + expect(editor.getCursorScreenPosition()).toEqual [3, 0] describe "as a repeated motion", -> beforeEach -> @@ -997,61 +989,77 @@ describe "Motions", -> editor.setText("abc\ndef\nabc\ndef\n") editor.setCursorBufferPosition([0, 0]) + # clear search history + vimState.globalVimState.searchHistory = [] + vimState.globalVimState.currentSearch = {} + describe "as a motion", -> + it "beeps when repeating nonexistent last search", -> + keydown '/' + submitNormalModeInputText '' + expect(editor.getCursorBufferPosition()).toEqual [0, 0] + expect(atom.beep).toHaveBeenCalled() + it "moves the cursor to the specified search pattern", -> keydown('/') - submitCommandModeInputText 'def' + submitNormalModeInputText 'def' expect(editor.getCursorBufferPosition()).toEqual [1, 0] expect(pane.activate).toHaveBeenCalled() + expect(atom.beep).not.toHaveBeenCalled() it "loops back around", -> editor.setCursorBufferPosition([3, 0]) keydown('/') - submitCommandModeInputText 'def' + submitNormalModeInputText 'def' expect(editor.getCursorBufferPosition()).toEqual [1, 0] + expect(atom.beep).not.toHaveBeenCalled() it "uses a valid regex as a regex", -> keydown('/') # Cycle through the 'abc' on the first line with a character pattern - submitCommandModeInputText '[abc]' + submitNormalModeInputText '[abc]' expect(editor.getCursorBufferPosition()).toEqual [0, 1] keydown('n') expect(editor.getCursorBufferPosition()).toEqual [0, 2] + expect(atom.beep).not.toHaveBeenCalled() it "uses an invalid regex as a literal string", -> # Go straight to the literal [abc editor.setText("abc\n[abc]\n") keydown('/') - submitCommandModeInputText '[abc' + submitNormalModeInputText '[abc' expect(editor.getCursorBufferPosition()).toEqual [1, 0] keydown('n') expect(editor.getCursorBufferPosition()).toEqual [1, 0] + expect(atom.beep).not.toHaveBeenCalled() it "uses ? as a literal string", -> editor.setText("abc\n[a?c?\n") keydown('/') - submitCommandModeInputText '?' + submitNormalModeInputText '?' expect(editor.getCursorBufferPosition()).toEqual [1, 2] keydown('n') expect(editor.getCursorBufferPosition()).toEqual [1, 4] + expect(atom.beep).not.toHaveBeenCalled() it 'works with selection in visual mode', -> editor.setText('one two three') keydown('v') keydown('/') - submitCommandModeInputText 'th' + submitNormalModeInputText 'th' expect(editor.getCursorBufferPosition()).toEqual [0, 9] keydown('d') expect(editor.getText()).toBe 'hree' + expect(atom.beep).not.toHaveBeenCalled() it 'extends selection when repeating search in visual mode', -> editor.setText('line1\nline2\nline3') keydown('v') keydown('/') - submitCommandModeInputText 'line' + submitNormalModeInputText 'line' {start, end} = editor.getSelectedBufferRange() expect(start.row).toEqual 0 expect(end.row).toEqual 1 @@ -1059,6 +1067,7 @@ describe "Motions", -> {start, end} = editor.getSelectedBufferRange() expect(start.row).toEqual 0 expect(end.row).toEqual 2 + expect(atom.beep).not.toHaveBeenCalled() describe "case sensitivity", -> beforeEach -> @@ -1067,60 +1076,76 @@ describe "Motions", -> keydown('/') it "works in case sensitive mode", -> - submitCommandModeInputText 'ABC' + submitNormalModeInputText 'ABC' expect(editor.getCursorBufferPosition()).toEqual [2, 0] keydown('n') expect(editor.getCursorBufferPosition()).toEqual [2, 0] + expect(atom.beep).not.toHaveBeenCalled() it "works in case insensitive mode", -> - submitCommandModeInputText '\\cAbC' + submitNormalModeInputText '\\cAbC' expect(editor.getCursorBufferPosition()).toEqual [1, 0] keydown('n') expect(editor.getCursorBufferPosition()).toEqual [2, 0] + expect(atom.beep).not.toHaveBeenCalled() it "works in case insensitive mode wherever \\c is", -> - submitCommandModeInputText 'AbC\\c' + submitNormalModeInputText 'AbC\\c' expect(editor.getCursorBufferPosition()).toEqual [1, 0] keydown('n') expect(editor.getCursorBufferPosition()).toEqual [2, 0] + expect(atom.beep).not.toHaveBeenCalled() it "uses case insensitive search if useSmartcaseForSearch is true and searching lowercase", -> atom.config.set 'vim-mode.useSmartcaseForSearch', true - submitCommandModeInputText 'abc' + submitNormalModeInputText 'abc' expect(editor.getCursorBufferPosition()).toEqual [1, 0] keydown('n') expect(editor.getCursorBufferPosition()).toEqual [2, 0] + expect(atom.beep).not.toHaveBeenCalled() it "uses case sensitive search if useSmartcaseForSearch is true and searching uppercase", -> atom.config.set 'vim-mode.useSmartcaseForSearch', true - submitCommandModeInputText 'ABC' + submitNormalModeInputText 'ABC' expect(editor.getCursorBufferPosition()).toEqual [2, 0] keydown('n') expect(editor.getCursorBufferPosition()).toEqual [2, 0] + expect(atom.beep).not.toHaveBeenCalled() describe "repeating", -> it "does nothing with no search history", -> - # This tests that no exception is raised + editor.setCursorBufferPosition([0, 0]) + keydown('n') + expect(editor.getCursorBufferPosition()).toEqual [0, 0] + expect(atom.beep).toHaveBeenCalled() + + editor.setCursorBufferPosition([1, 1]) keydown('n') + expect(editor.getCursorBufferPosition()).toEqual [1, 1] + expect(atom.beep.callCount).toBe 2 + describe "repeating with search history", -> beforeEach -> keydown('/') - submitCommandModeInputText 'def' + submitNormalModeInputText 'def' it "repeats previous search with /<enter>", -> keydown('/') - submitCommandModeInputText('') + submitNormalModeInputText('') expect(editor.getCursorBufferPosition()).toEqual [3, 0] + expect(atom.beep).not.toHaveBeenCalled() it "repeats previous search with //", -> keydown('/') - submitCommandModeInputText('/') + submitNormalModeInputText('/') expect(editor.getCursorBufferPosition()).toEqual [3, 0] + expect(atom.beep).not.toHaveBeenCalled() describe "the n keybinding", -> it "repeats the last search", -> keydown('n') expect(editor.getCursorBufferPosition()).toEqual [3, 0] + expect(atom.beep).not.toHaveBeenCalled() describe "the N keybinding", -> it "repeats the last search backwards", -> @@ -1129,98 +1154,109 @@ describe "Motions", -> expect(editor.getCursorBufferPosition()).toEqual [3, 0] keydown('N', shift: true) expect(editor.getCursorBufferPosition()).toEqual [1, 0] + expect(atom.beep).not.toHaveBeenCalled() describe "composing", -> it "composes with operators", -> keydown('d') keydown('/') - submitCommandModeInputText('def') + submitNormalModeInputText('def') expect(editor.getText()).toEqual "def\nabc\ndef\n" + expect(atom.beep).not.toHaveBeenCalled() it "repeats correctly with operators", -> keydown('d') keydown('/') - submitCommandModeInputText('def') + submitNormalModeInputText('def') keydown('.') expect(editor.getText()).toEqual "def\n" + expect(atom.beep).not.toHaveBeenCalled() describe "when reversed as ?", -> it "moves the cursor backwards to the specified search pattern", -> keydown('?') - submitCommandModeInputText('def') + submitNormalModeInputText('def') expect(editor.getCursorBufferPosition()).toEqual [3, 0] + expect(atom.beep).not.toHaveBeenCalled() it "accepts / as a literal search pattern", -> editor.setText("abc\nd/f\nabc\nd/f\n") editor.setCursorBufferPosition([0, 0]) keydown('?') - submitCommandModeInputText('/') + submitNormalModeInputText('/') expect(editor.getCursorBufferPosition()).toEqual [3, 1] keydown('?') - submitCommandModeInputText('/') + submitNormalModeInputText('/') expect(editor.getCursorBufferPosition()).toEqual [1, 1] + expect(atom.beep).not.toHaveBeenCalled() describe "repeating", -> beforeEach -> keydown('?') - submitCommandModeInputText('def') + submitNormalModeInputText('def') it "repeats previous search as reversed with ?<enter>", -> keydown('?') - submitCommandModeInputText('') + submitNormalModeInputText('') expect(editor.getCursorBufferPosition()).toEqual [1, 0] + expect(atom.beep).not.toHaveBeenCalled() it "repeats previous search as reversed with ??", -> keydown('?') - submitCommandModeInputText('?') + submitNormalModeInputText('?') expect(editor.getCursorBufferPosition()).toEqual [1, 0] + expect(atom.beep).not.toHaveBeenCalled() describe 'the n keybinding', -> it "repeats the last search backwards", -> editor.setCursorBufferPosition([0, 0]) keydown('n') expect(editor.getCursorBufferPosition()).toEqual [3, 0] + expect(atom.beep).not.toHaveBeenCalled() describe 'the N keybinding', -> it "repeats the last search forwards", -> editor.setCursorBufferPosition([0, 0]) keydown('N', shift: true) expect(editor.getCursorBufferPosition()).toEqual [1, 0] + expect(atom.beep).not.toHaveBeenCalled() describe "using search history", -> - commandEditor = null + inputEditor = null beforeEach -> keydown('/') - submitCommandModeInputText('def') + submitNormalModeInputText('def') expect(editor.getCursorBufferPosition()).toEqual [1, 0] keydown('/') - submitCommandModeInputText('abc') + submitNormalModeInputText('abc') expect(editor.getCursorBufferPosition()).toEqual [2, 0] - commandEditor = editor.commandModeInputView.editorElement + inputEditor = editor.normalModeInputView.editorElement it "allows searching history in the search field", -> keydown('/') - atom.commands.dispatch(commandEditor, 'core:move-up') - expect(commandEditor.getModel().getText()).toEqual('abc') - atom.commands.dispatch(commandEditor, 'core:move-up') - expect(commandEditor.getModel().getText()).toEqual('def') - atom.commands.dispatch(commandEditor, 'core:move-up') - expect(commandEditor.getModel().getText()).toEqual('def') + atom.commands.dispatch(inputEditor, 'core:move-up') + expect(inputEditor.getModel().getText()).toEqual('abc') + atom.commands.dispatch(inputEditor, 'core:move-up') + expect(inputEditor.getModel().getText()).toEqual('def') + atom.commands.dispatch(inputEditor, 'core:move-up') + expect(inputEditor.getModel().getText()).toEqual('def') + expect(atom.beep).not.toHaveBeenCalled() it "resets the search field to empty when scrolling back", -> keydown('/') - atom.commands.dispatch(commandEditor, 'core:move-up') - expect(commandEditor.getModel().getText()).toEqual('abc') - atom.commands.dispatch(commandEditor, 'core:move-up') - expect(commandEditor.getModel().getText()).toEqual('def') - atom.commands.dispatch(commandEditor, 'core:move-down') - expect(commandEditor.getModel().getText()).toEqual('abc') - atom.commands.dispatch(commandEditor, 'core:move-down') - expect(commandEditor.getModel().getText()).toEqual '' + atom.commands.dispatch(inputEditor, 'core:move-up') + expect(inputEditor.getModel().getText()).toEqual('abc') + atom.commands.dispatch(inputEditor, 'core:move-up') + expect(inputEditor.getModel().getText()).toEqual('def') + atom.commands.dispatch(inputEditor, 'core:move-down') + expect(inputEditor.getModel().getText()).toEqual('abc') + atom.commands.dispatch(inputEditor, 'core:move-down') + expect(inputEditor.getModel().getText()).toEqual '' + expect(atom.beep).not.toHaveBeenCalled() describe "the * keybinding", -> beforeEach -> @@ -1253,14 +1289,9 @@ describe "Motions", -> it "doesn't move cursor unless next match has exact word ending", -> editor.setText("abc\n@def\nabc\n@def1\n") - # FIXME: I suspect there is a bug laying around - # Cursor#getEndOfCurrentWordBufferPosition, this function - # is returning '@' as a word, instead of returning the whole - # word '@def', this behavior is avoided in this test, when we - # execute the '*' command when cursor is on character after '@' - # (in this particular example, the 'd' char) editor.setCursorBufferPosition([1, 1]) keydown("*") + # this is because of the default isKeyword value of vim-mode that includes @ expect(editor.getCursorBufferPosition()).toEqual [1, 0] # FIXME: This behavior is different from the one found in @@ -1404,58 +1435,58 @@ describe "Motions", -> it 'moves to the beginning of the line of a mark', -> editor.setCursorBufferPosition([1, 1]) keydown('m') - commandModeInputKeydown('a') + normalModeInputKeydown('a') editor.setCursorBufferPosition([0, 0]) keydown('\'') - commandModeInputKeydown('a') + normalModeInputKeydown('a') expect(editor.getCursorBufferPosition()).toEqual [1, 4] it 'moves literally to a mark', -> editor.setCursorBufferPosition([1, 1]) keydown('m') - commandModeInputKeydown('a') + normalModeInputKeydown('a') editor.setCursorBufferPosition([0, 0]) keydown('`') - commandModeInputKeydown('a') + normalModeInputKeydown('a') expect(editor.getCursorBufferPosition()).toEqual [1, 1] it 'deletes to a mark by line', -> editor.setCursorBufferPosition([1, 5]) keydown('m') - commandModeInputKeydown('a') + normalModeInputKeydown('a') editor.setCursorBufferPosition([0, 0]) keydown('d') keydown('\'') - commandModeInputKeydown('a') + normalModeInputKeydown('a') expect(editor.getText()).toEqual '56\n' it 'deletes before to a mark literally', -> editor.setCursorBufferPosition([1, 5]) keydown('m') - commandModeInputKeydown('a') + normalModeInputKeydown('a') editor.setCursorBufferPosition([0, 1]) keydown('d') keydown('`') - commandModeInputKeydown('a') + normalModeInputKeydown('a') expect(editor.getText()).toEqual ' 4\n56\n' it 'deletes after to a mark literally', -> editor.setCursorBufferPosition([1, 5]) keydown('m') - commandModeInputKeydown('a') + normalModeInputKeydown('a') editor.setCursorBufferPosition([2, 1]) keydown('d') keydown('`') - commandModeInputKeydown('a') + normalModeInputKeydown('a') expect(editor.getText()).toEqual ' 12\n 36\n' it 'moves back to previous', -> editor.setCursorBufferPosition([1, 5]) keydown('`') - commandModeInputKeydown('`') + normalModeInputKeydown('`') editor.setCursorBufferPosition([2, 1]) keydown('`') - commandModeInputKeydown('`') + normalModeInputKeydown('`') expect(editor.getCursorBufferPosition()).toEqual [1, 5] describe 'the f/F keybindings', -> @@ -1465,56 +1496,57 @@ describe "Motions", -> it 'moves to the first specified character it finds', -> keydown('f') - commandModeInputKeydown('c') + normalModeInputKeydown('c') expect(editor.getCursorScreenPosition()).toEqual [0, 2] it 'moves backwards to the first specified character it finds', -> editor.setCursorScreenPosition([0, 2]) keydown('F', shift: true) - commandModeInputKeydown('a') + normalModeInputKeydown('a') expect(editor.getCursorScreenPosition()).toEqual [0, 0] it 'respects count forward', -> keydown('2') keydown('f') - commandModeInputKeydown('a') + normalModeInputKeydown('a') expect(editor.getCursorScreenPosition()).toEqual [0, 6] it 'respects count backward', -> editor.setCursorScreenPosition([0, 6]) keydown('2') keydown('F', shift: true) - commandModeInputKeydown('a') + normalModeInputKeydown('a') expect(editor.getCursorScreenPosition()).toEqual [0, 0] it "doesn't move if the character specified isn't found", -> keydown('f') - commandModeInputKeydown('d') + normalModeInputKeydown('d') expect(editor.getCursorScreenPosition()).toEqual [0, 0] + expect(atom.beep).not.toHaveBeenCalled() it "doesn't move if there aren't the specified count of the specified character", -> keydown('1') keydown('0') keydown('f') - commandModeInputKeydown('a') + normalModeInputKeydown('a') expect(editor.getCursorScreenPosition()).toEqual [0, 0] # a bug was making this behaviour depend on the count keydown('1') keydown('1') keydown('f') - commandModeInputKeydown('a') + normalModeInputKeydown('a') expect(editor.getCursorScreenPosition()).toEqual [0, 0] # and backwards now editor.setCursorScreenPosition([0, 6]) keydown('1') keydown('0') keydown('F', shift: true) - commandModeInputKeydown('a') + normalModeInputKeydown('a') expect(editor.getCursorScreenPosition()).toEqual [0, 6] keydown('1') keydown('1') keydown('F', shift: true) - commandModeInputKeydown('a') + normalModeInputKeydown('a') expect(editor.getCursorScreenPosition()).toEqual [0, 6] it "composes with d", -> @@ -1522,9 +1554,47 @@ describe "Motions", -> keydown('d') keydown('2') keydown('f') - commandModeInputKeydown('a') + normalModeInputKeydown('a') expect(editor.getText()).toEqual 'abcbc\n' + it "cancels c when no match found", -> + keydown('c') + keydown('f') + normalModeInputKeydown('d') + expect(editor.getText()).toBe("abcabcabcabc\n") + expect(editor.getCursorScreenPosition()).toEqual [0, 0] + expect(vimState.mode).toBe "normal" + + describe 'with accented characters', -> + buildIMECompositionEvent = (event, {data, target}={}) -> + event = new Event(event) + event.data = data + Object.defineProperty(event, 'target', get: -> target) + event + + buildTextInputEvent = ({data, target}) -> + event = new Event('textInput') + event.data = data + Object.defineProperty(event, 'target', get: -> target) + event + + beforeEach -> + editor.setText("abcébcabcébc\n") + editor.setCursorScreenPosition([0, 0]) + + it 'works with IME composition', -> + keydown('f') + normalModeEditor = editor.normalModeInputView.editorElement + jasmine.attachToDOM(normalModeEditor) + domNode = normalModeEditor.component.domNode + inputNode = domNode.querySelector('.hidden-input') + domNode.dispatchEvent(buildIMECompositionEvent('compositionstart', target: inputNode)) + domNode.dispatchEvent(buildIMECompositionEvent('compositionupdate', data: "´", target: inputNode)) + expect(normalModeEditor.getModel().getText()).toEqual '´' + domNode.dispatchEvent(buildIMECompositionEvent('compositionend', data: "é", target: inputNode)) + domNode.dispatchEvent(buildTextInputEvent(data: 'é', target: inputNode)) + expect(editor.getCursorScreenPosition()).toEqual [0, 3] + describe 'the t/T keybindings', -> beforeEach -> editor.setText("abcabcabcabc\n") @@ -1532,60 +1602,61 @@ describe "Motions", -> it 'moves to the character previous to the first specified character it finds', -> keydown('t') - commandModeInputKeydown('a') + normalModeInputKeydown('a') expect(editor.getCursorScreenPosition()).toEqual [0, 2] # or stays put when it's already there keydown('t') - commandModeInputKeydown('a') + normalModeInputKeydown('a') expect(editor.getCursorScreenPosition()).toEqual [0, 2] it 'moves backwards to the character after the first specified character it finds', -> editor.setCursorScreenPosition([0, 2]) keydown('T', shift: true) - commandModeInputKeydown('a') + normalModeInputKeydown('a') expect(editor.getCursorScreenPosition()).toEqual [0, 1] it 'respects count forward', -> keydown('2') keydown('t') - commandModeInputKeydown('a') + normalModeInputKeydown('a') expect(editor.getCursorScreenPosition()).toEqual [0, 5] it 'respects count backward', -> editor.setCursorScreenPosition([0, 6]) keydown('2') keydown('T', shift: true) - commandModeInputKeydown('a') + normalModeInputKeydown('a') expect(editor.getCursorScreenPosition()).toEqual [0, 1] it "doesn't move if the character specified isn't found", -> keydown('t') - commandModeInputKeydown('d') + normalModeInputKeydown('d') expect(editor.getCursorScreenPosition()).toEqual [0, 0] + expect(atom.beep).not.toHaveBeenCalled() it "doesn't move if there aren't the specified count of the specified character", -> keydown('1') keydown('0') keydown('t') - commandModeInputKeydown('a') + normalModeInputKeydown('a') expect(editor.getCursorScreenPosition()).toEqual [0, 0] # a bug was making this behaviour depend on the count keydown('1') keydown('1') keydown('t') - commandModeInputKeydown('a') + normalModeInputKeydown('a') expect(editor.getCursorScreenPosition()).toEqual [0, 0] # and backwards now editor.setCursorScreenPosition([0, 6]) keydown('1') keydown('0') keydown('T', shift: true) - commandModeInputKeydown('a') + normalModeInputKeydown('a') expect(editor.getCursorScreenPosition()).toEqual [0, 6] keydown('1') keydown('1') keydown('T', shift: true) - commandModeInputKeydown('a') + normalModeInputKeydown('a') expect(editor.getCursorScreenPosition()).toEqual [0, 6] it "composes with d", -> @@ -1593,9 +1664,34 @@ describe "Motions", -> keydown('d') keydown('2') keydown('t') - commandModeInputKeydown('b') + normalModeInputKeydown('b') expect(editor.getText()).toBe 'abcbcabc\n' + it "selects character under cursor even when no movement happens", -> + editor.setCursorBufferPosition([0, 0]) + keydown('d') + keydown('t') + normalModeInputKeydown('b') + expect(editor.getText()).toBe 'bcabcabcabc\n' + + describe 'the v keybinding', -> + beforeEach -> + editor.setText("01\n002\n0003\n00004\n000005\n") + editor.setCursorScreenPosition([1, 1]) + + it "selects down a line", -> + keydown('v') + keydown('j') + keydown('j') + expect(editor.getSelectedText()).toBe "02\n0003\n00" + expect(editor.getSelectedBufferRange().isSingleLine()).toBeFalsy() + + it "selects right", -> + keydown('v') + keydown('l') + expect(editor.getSelectedText()).toBe "02" + expect(editor.getSelectedBufferRange().isSingleLine()).toBeTruthy() + describe 'the V keybinding', -> beforeEach -> editor.setText("01\n002\n0003\n00004\n000005\n") @@ -1603,9 +1699,11 @@ describe "Motions", -> it "selects down a line", -> keydown('V', shift: true) + expect(editor.getSelectedBufferRange().isSingleLine()).toBeFalsy() keydown('j') keydown('j') expect(editor.getSelectedText()).toBe "002\n0003\n00004\n" + expect(editor.getSelectedBufferRange().isSingleLine()).toBeFalsy() it "selects up a line", -> keydown('V', shift: true) @@ -1619,7 +1717,7 @@ describe "Motions", -> it "repeat f in same direction", -> keydown('f') - commandModeInputKeydown('c') + normalModeInputKeydown('c') expect(editor.getCursorScreenPosition()).toEqual [0, 2] keydown(';') expect(editor.getCursorScreenPosition()).toEqual [0, 5] @@ -1629,7 +1727,7 @@ describe "Motions", -> it "repeat F in same direction", -> editor.setCursorScreenPosition([0, 10]) keydown('F', shift: true) - commandModeInputKeydown('c') + normalModeInputKeydown('c') expect(editor.getCursorScreenPosition()).toEqual [0, 8] keydown(';') expect(editor.getCursorScreenPosition()).toEqual [0, 5] @@ -1639,7 +1737,7 @@ describe "Motions", -> it "repeat f in opposite direction", -> editor.setCursorScreenPosition([0, 6]) keydown('f') - commandModeInputKeydown('c') + normalModeInputKeydown('c') expect(editor.getCursorScreenPosition()).toEqual [0, 8] keydown(',') expect(editor.getCursorScreenPosition()).toEqual [0, 5] @@ -1649,7 +1747,7 @@ describe "Motions", -> it "repeat F in opposite direction", -> editor.setCursorScreenPosition([0, 4]) keydown('F', shift: true) - commandModeInputKeydown('c') + normalModeInputKeydown('c') expect(editor.getCursorScreenPosition()).toEqual [0, 2] keydown(',') expect(editor.getCursorScreenPosition()).toEqual [0, 5] @@ -1658,7 +1756,7 @@ describe "Motions", -> it "alternate repeat f in same direction and reverse", -> keydown('f') - commandModeInputKeydown('c') + normalModeInputKeydown('c') expect(editor.getCursorScreenPosition()).toEqual [0, 2] keydown(';') expect(editor.getCursorScreenPosition()).toEqual [0, 5] @@ -1668,7 +1766,7 @@ describe "Motions", -> it "alternate repeat F in same direction and reverse", -> editor.setCursorScreenPosition([0, 10]) keydown('F', shift: true) - commandModeInputKeydown('c') + normalModeInputKeydown('c') expect(editor.getCursorScreenPosition()).toEqual [0, 8] keydown(';') expect(editor.getCursorScreenPosition()).toEqual [0, 5] @@ -1677,7 +1775,7 @@ describe "Motions", -> it "repeat t in same direction", -> keydown('t') - commandModeInputKeydown('c') + normalModeInputKeydown('c') expect(editor.getCursorScreenPosition()).toEqual [0, 1] keydown(';') expect(editor.getCursorScreenPosition()).toEqual [0, 4] @@ -1685,7 +1783,7 @@ describe "Motions", -> it "repeat T in same direction", -> editor.setCursorScreenPosition([0, 10]) keydown('T', shift: true) - commandModeInputKeydown('c') + normalModeInputKeydown('c') expect(editor.getCursorScreenPosition()).toEqual [0, 9] keydown(';') expect(editor.getCursorScreenPosition()).toEqual [0, 6] @@ -1693,7 +1791,7 @@ describe "Motions", -> it "repeat t in opposite direction first, and then reverse", -> editor.setCursorScreenPosition([0, 3]) keydown('t') - commandModeInputKeydown('c') + normalModeInputKeydown('c') expect(editor.getCursorScreenPosition()).toEqual [0, 4] keydown(',') expect(editor.getCursorScreenPosition()).toEqual [0, 3] @@ -1703,7 +1801,7 @@ describe "Motions", -> it "repeat T in opposite direction first, and then reverse", -> editor.setCursorScreenPosition([0, 4]) keydown('T', shift: true) - commandModeInputKeydown('c') + normalModeInputKeydown('c') expect(editor.getCursorScreenPosition()).toEqual [0, 3] keydown(',') expect(editor.getCursorScreenPosition()).toEqual [0, 4] @@ -1713,7 +1811,7 @@ describe "Motions", -> it "repeat with count in same direction", -> editor.setCursorScreenPosition([0, 0]) keydown('f') - commandModeInputKeydown('c') + normalModeInputKeydown('c') expect(editor.getCursorScreenPosition()).toEqual [0, 2] keydown('2') keydown(';') @@ -1722,12 +1820,45 @@ describe "Motions", -> it "repeat with count in reverse direction", -> editor.setCursorScreenPosition([0, 6]) keydown('f') - commandModeInputKeydown('c') + normalModeInputKeydown('c') expect(editor.getCursorScreenPosition()).toEqual [0, 8] keydown('2') keydown(',') expect(editor.getCursorScreenPosition()).toEqual [0, 2] + it "shares the most recent find/till command with other editors", -> + helpers.getEditorElement (otherEditorElement) -> + otherEditor = otherEditorElement.getModel() + + editor.setText("a baz bar\n") + editor.setCursorScreenPosition([0, 0]) + + otherEditor.setText("foo bar baz") + otherEditor.setCursorScreenPosition([0, 0]) + + # by default keyDown and such go in the usual editor + keydown('f') + normalModeInputKeydown('b') + expect(editor.getCursorScreenPosition()).toEqual [0, 2] + expect(otherEditor.getCursorScreenPosition()).toEqual [0, 0] + + # replay same find in the other editor + keydown(';', element: otherEditorElement) + expect(editor.getCursorScreenPosition()).toEqual [0, 2] + expect(otherEditor.getCursorScreenPosition()).toEqual [0, 4] + + # do a till in the other editor + keydown('t', element: otherEditorElement) + normalModeInputKeydown('r', editor: otherEditor) + expect(editor.getCursorScreenPosition()).toEqual [0, 2] + expect(otherEditor.getCursorScreenPosition()).toEqual [0, 5] + + # and replay in the normal editor + keydown(';') + expect(editor.getCursorScreenPosition()).toEqual [0, 7] + expect(otherEditor.getCursorScreenPosition()).toEqual [0, 5] + expect(atom.beep).not.toHaveBeenCalled() + describe 'the % motion', -> beforeEach -> editor.setText("( ( ) )--{ text in here; and a function call(with parameters) }\n") @@ -1780,7 +1911,7 @@ describe "Motions", -> it "does not affect search history", -> keydown('/') - submitCommandModeInputText 'func' + submitNormalModeInputText 'func' expect(editor.getCursorBufferPosition()).toEqual [0, 31] keydown('%') expect(editor.getCursorBufferPosition()).toEqual [0, 60] diff --git a/atom/packages/vim-mode/spec/operators-spec.coffee b/atom/packages/vim-mode/spec/operators-spec.coffee index a41edb3..0dff120 100644 --- a/atom/packages/vim-mode/spec/operators-spec.coffee +++ b/atom/packages/vim-mode/spec/operators-spec.coffee @@ -12,29 +12,30 @@ describe "Operators", -> editorElement = element editor = editorElement.getModel() vimState = editorElement.vimState - vimState.activateCommandMode() - vimState.resetCommandMode() + vimState.activateNormalMode() + vimState.resetNormalMode() keydown = (key, options={}) -> options.element ?= editorElement helpers.keydown(key, options) - commandModeInputKeydown = (key, opts = {}) -> - editor.commandModeInputView.editorElement.getModel().setText(key) + normalModeInputKeydown = (key, opts = {}) -> + editor.normalModeInputView.editorElement.getModel().setText(key) describe "cancelling operations", -> - it "does not throw an error even if no operation is pending", -> + it "throws an error when no operation is pending", -> # cancel operation pushes an empty input operation - # doing this without a pending operation throws an exception + # doing this without a pending operation would throw an exception expect(-> vimState.pushOperations(new Input(''))).toThrow() - # make sure commandModeInputView is created + it "cancels and cleans up properly", -> + # make sure normalModeInputView is created keydown('/') expect(vimState.isOperatorPending()).toBe true - editor.commandModeInputView.viewModel.cancel() + editor.normalModeInputView.viewModel.cancel() expect(vimState.isOperatorPending()).toBe false - expect(-> editor.commandModeInputView.viewModel.cancel()).not.toThrow() + expect(editor.normalModeInputView).toBe undefined describe "the x keybinding", -> describe "on a line with content", -> @@ -88,6 +89,18 @@ describe "Operators", -> expect(editor.getCursorScreenPosition()).toEqual [0, 0] expect(vimState.getRegister('"').text).toBe 'bc' + describe "with multiple cursors", -> + beforeEach -> + editor.setText "abc\n012345\n\nxyz" + editor.setCursorScreenPosition [1, 4] + editor.addCursorAtBufferPosition [0, 1] + + it "is undone as one operation", -> + keydown('x') + expect(editor.getText()).toBe "ac\n01235\n\nxyz" + keydown('u') + expect(editor.getText()).toBe "abc\n012345\n\nxyz" + describe "with vim-mode.wrapLeftRightMotion", -> beforeEach -> editor.setText("abc\n012345\n\nxyz") @@ -147,7 +160,6 @@ describe "Operators", -> expect(editor.getCursorScreenPosition()).toEqual [0, 1] expect(vimState.getRegister('"').text).toBe '0123\n\nx' - describe "on an empty line", -> beforeEach -> editor.setText("abc\n012345\n\nxyz") @@ -165,7 +177,6 @@ describe "Operators", -> expect(editor.getText()).toBe "abc\n012345\nxyz" expect(editor.getCursorScreenPosition()).toEqual [2, 0] - describe "the X keybinding", -> describe "on a line with content", -> beforeEach -> @@ -194,7 +205,6 @@ describe "Operators", -> expect(editor.getCursorScreenPosition()).toEqual [0, 2] expect(vimState.getRegister('"').text).toBe '\n' - describe "on an empty line", -> beforeEach -> editor.setText("012345\n\nabcdef") @@ -303,7 +313,7 @@ describe "Operators", -> it "enters operator-pending mode", -> keydown('d') expect(editorElement.classList.contains('operator-pending-mode')).toBe(true) - expect(editorElement.classList.contains('command-mode')).toBe(false) + expect(editorElement.classList.contains('normal-mode')).toBe(false) describe "when followed by a d", -> it "deletes the current line and exits operator-pending mode", -> @@ -317,7 +327,7 @@ describe "Operators", -> expect(editor.getCursorScreenPosition()).toEqual [1, 0] expect(vimState.getRegister('"').text).toBe "abcde\n" expect(editorElement.classList.contains('operator-pending-mode')).toBe(false) - expect(editorElement.classList.contains('command-mode')).toBe(true) + expect(editorElement.classList.contains('normal-mode')).toBe(true) it "deletes the last line", -> editor.setText("12345\nabcde\nABCDE") @@ -344,16 +354,30 @@ describe "Operators", -> editor.setText("12345\nabcde\nABCDE\nQWERT") editor.setCursorScreenPosition([1, 1]) + it "undoes both lines", -> keydown('d') keydown('2') keydown('d') keydown('u') - it "undoes both lines", -> expect(editor.getText()).toBe "12345\nabcde\nABCDE\nQWERT" expect(editor.getSelectedText()).toBe '' + describe "with multiple cursors", -> + beforeEach -> + editor.setCursorBufferPosition([1, 1]) + editor.addCursorAtBufferPosition([0, 0]) + + it "is undone as one operation", -> + keydown('d') + keydown('l') + + keydown('u') + + expect(editor.getText()).toBe "12345\nabcde\nABCDE\nQWERT" + expect(editor.getSelectedText()).toBe '' + describe "when followed by a w", -> it "deletes the next word until the end of the line and exits operator-pending mode", -> editor.setText("abcd efg\nabc") @@ -368,7 +392,7 @@ describe "Operators", -> expect(editor.getCursorScreenPosition()).toEqual [0, 5] expect(editorElement.classList.contains('operator-pending-mode')).toBe(false) - expect(editorElement.classList.contains('command-mode')).toBe(true) + expect(editorElement.classList.contains('normal-mode')).toBe(true) it "deletes to the beginning of the next word", -> editor.setText('abcd efg') @@ -404,59 +428,61 @@ describe "Operators", -> expect(editor.getCursorScreenPosition()).toEqual [0, 6] expect(vimState.getRegister('"').text).toBe "abcde" expect(editorElement.classList.contains('operator-pending-mode')).toBe(false) - expect(editorElement.classList.contains('command-mode')).toBe(true) + expect(editorElement.classList.contains('normal-mode')).toBe(true) + + describe "when followed by a j", -> + originalText = "12345\nabcde\nABCDE\n" - describe "when followed by an j", -> beforeEach -> - originalText = "12345\nabcde\nABCDE" editor.setText(originalText) - describe "on the beginning of the file", -> + describe "on the beginning of the file", -> + it "deletes the next two lines", -> editor.setCursorScreenPosition([0, 0]) - it "deletes the next two lines", -> - keydown('d') - keydown('j') - expect(editor.getText()).toBe("ABCDE") + keydown('d') + keydown('j') + expect(editor.getText()).toBe("ABCDE\n") - describe "on the end of the file", -> - editor.setCursorScreenPosition([4, 2]) - it "deletes nothing", -> - keydown('d') - keydown('j') - expect(editor.getText()).toBe(originalText) + describe "on the end of the file", -> + it "deletes nothing", -> + editor.setCursorScreenPosition([4, 0]) + keydown('d') + keydown('j') + expect(editor.getText()).toBe(originalText) - describe "on the middle of second line", -> - editor.setCursorScreenPosition([2, 1]) - it "deletes the last two lines", -> - keydown('d') - keydown('j') - expect(editor.getText()).toBe("12345") + describe "on the middle of second line", -> + it "deletes the last two lines", -> + editor.setCursorScreenPosition([1, 2]) + keydown('d') + keydown('j') + expect(editor.getText()).toBe("12345\n") describe "when followed by an k", -> + originalText = "12345\nabcde\nABCDE" + beforeEach -> - originalText = "12345\nabcde\nABCDE" editor.setText(originalText) - describe "on the end of the file", -> - editor.setCursorScreenPosition([4, 2]) - it "deletes the bottom two lines", -> - keydown('d') - keydown('k') - expect(editor.getText()).toBe("ABCDE") + describe "on the end of the file", -> + it "deletes the bottom two lines", -> + editor.setCursorScreenPosition([2, 4]) + keydown('d') + keydown('k') + expect(editor.getText()).toBe("12345\n") - describe "on the beginning of the file", -> + describe "on the beginning of the file", -> + xit "deletes nothing", -> editor.setCursorScreenPosition([0, 0]) - it "deletes nothing", -> - keydown('d') - keydown('k') - expect(editor.getText()).toBe(originalText) + keydown('d') + keydown('k') + expect(editor.getText()).toBe(originalText) - describe "when on the middle of second line", -> - editor.setCursorScreenPosition([2, 1]) - it "deletes the first two lines", -> - keydown('d') - keydown('k') - expect(editor.getText()).toBe("12345") + describe "when on the middle of second line", -> + it "deletes the first two lines", -> + editor.setCursorScreenPosition([1, 2]) + keydown('d') + keydown('k') + expect(editor.getText()).toBe("ABCDE") describe "when followed by a G", -> beforeEach -> @@ -509,7 +535,7 @@ describe "Operators", -> keydown('y') keydown('d') keydown('t') - commandModeInputKeydown(')') + normalModeInputKeydown(')') expect(editor.getText()).toBe("test ()") expect(editor.getCursorScreenPosition()).toEqual [0, 6] @@ -538,7 +564,7 @@ describe "Operators", -> keydown('d') keydown('t') - commandModeInputKeydown('d') + normalModeInputKeydown('d') expect(editor.getText()).toBe "d\nabc\nd" expect(editor.getCursorBufferPositions()).toEqual [ @@ -561,16 +587,45 @@ describe "Operators", -> editor.setText("12345\nabcde\nABCDE") describe "when followed by a c", -> - it "deletes the current line and enters insert mode", -> - editor.setCursorScreenPosition([1, 1]) + describe "with autoindent", -> + beforeEach -> + editor.setText("12345\n abcde\nABCDE") + editor.setCursorScreenPosition([1, 1]) + spyOn(editor, 'shouldAutoIndent').andReturn(true) + spyOn(editor, 'autoIndentBufferRow').andCallFake (line) -> + editor.indent() + spyOn(editor.languageMode, 'suggestedIndentForLineAtBufferRow').andCallFake -> 1 - keydown('c') - keydown('c') + it "deletes the current line and enters insert mode", -> + editor.setCursorScreenPosition([1, 1]) - expect(editor.getText()).toBe "12345\n\nABCDE" - expect(editor.getCursorScreenPosition()).toEqual [1, 0] - expect(editorElement.classList.contains('command-mode')).toBe(false) - expect(editorElement.classList.contains('insert-mode')).toBe(true) + keydown('c') + keydown('c') + + expect(editor.getText()).toBe "12345\n \nABCDE" + expect(editor.getCursorScreenPosition()).toEqual [1, 2] + expect(editorElement.classList.contains('normal-mode')).toBe(false) + expect(editorElement.classList.contains('insert-mode')).toBe(true) + + it "is repeatable", -> + keydown('c') + keydown('c') + editor.insertText("abc") + keydown 'escape' + expect(editor.getText()).toBe "12345\n abc\nABCDE" + editor.setCursorScreenPosition([2, 3]) + keydown '.' + expect(editor.getText()).toBe "12345\n abc\n abc\n" + + it "is undoable", -> + keydown('c') + keydown('c') + editor.insertText("abc") + keydown 'escape' + expect(editor.getText()).toBe "12345\n abc\nABCDE" + keydown 'u' + expect(editor.getText()).toBe "12345\n abcde\nABCDE" + expect(editor.getSelectedText()).toBe '' describe "when the cursor is on the last line", -> it "deletes the line's content and enters insert mode on the last line", -> @@ -581,7 +636,7 @@ describe "Operators", -> expect(editor.getText()).toBe "12345\nabcde\n\n" expect(editor.getCursorScreenPosition()).toEqual [2, 0] - expect(editorElement.classList.contains('command-mode')).toBe(false) + expect(editorElement.classList.contains('normal-mode')).toBe(false) expect(editorElement.classList.contains('insert-mode')).toBe(true) describe "when the cursor is on the only line", -> @@ -592,9 +647,9 @@ describe "Operators", -> keydown('c') keydown('c') - expect(editor.getText()).toBe "\n" + expect(editor.getText()).toBe "" expect(editor.getCursorScreenPosition()).toEqual [0, 0] - expect(editorElement.classList.contains('command-mode')).toBe(false) + expect(editorElement.classList.contains('normal-mode')).toBe(false) expect(editorElement.classList.contains('insert-mode')).toBe(true) describe "when followed by i w", -> @@ -611,7 +666,7 @@ describe "Operators", -> # Just cannot get "typing" to work correctly in test. editor.setText("12345\nfg\nABCDE") keydown('escape') - expect(editorElement.classList.contains('command-mode')).toBe(true) + expect(editorElement.classList.contains('normal-mode')).toBe(true) expect(editor.getText()).toBe "12345\nfg\nABCDE" keydown('u') @@ -651,6 +706,88 @@ describe "Operators", -> keydown('escape') expect(editor.getText()).toBe("12345\n\n") + describe "when followed by a %", -> + beforeEach -> + editor.setText("12345(67)8\nabc(d)e\nA()BCDE") + + describe "before brackets or on the first one", -> + beforeEach -> + editor.setCursorScreenPosition([0, 1]) + editor.addCursorAtScreenPosition([1, 1]) + editor.addCursorAtScreenPosition([2, 1]) + keydown('c') + keydown('%') + editor.insertText('x') + + it "replaces inclusively until matching bracket", -> + expect(editor.getText()).toBe("1x8\naxe\nAxBCDE") + expect(vimState.mode).toBe "insert" + + it "undoes correctly with u", -> + keydown('escape') + expect(vimState.mode).toBe "normal" + keydown 'u' + expect(editor.getText()).toBe("12345(67)8\nabc(d)e\nA()BCDE") + + describe "inside brackets or on the ending one", -> + it "replaces inclusively backwards until matching bracket", -> + editor.setCursorScreenPosition([0, 6]) + editor.addCursorAtScreenPosition([1, 5]) + editor.addCursorAtScreenPosition([2, 2]) + keydown('c') + keydown('%') + editor.insertText('x') + expect(editor.getText()).toBe("12345x7)8\nabcxe\nAxBCDE") + expect(vimState.mode).toBe "insert" + + describe "after or without brackets", -> + it "deletes nothing", -> + editor.setText("12345(67)8\nabc(d)e\nABCDE") + editor.setCursorScreenPosition([0, 9]) + editor.addCursorAtScreenPosition([2, 2]) + keydown('c') + keydown('%') + expect(editor.getText()).toBe("12345(67)8\nabc(d)e\nABCDE") + expect(vimState.mode).toBe "normal" + + describe "repetition with .", -> + beforeEach -> + editor.setCursorScreenPosition([0, 1]) + keydown('c') + keydown('%') + editor.insertText('x') + keydown('escape') + + it "repeats correctly before a bracket", -> + editor.setCursorScreenPosition([1, 0]) + keydown('.') + expect(editor.getText()).toBe("1x8\nxe\nA()BCDE") + expect(vimState.mode).toBe "normal" + + it "repeats correctly on the opening bracket", -> + editor.setCursorScreenPosition([1, 3]) + keydown('.') + expect(editor.getText()).toBe("1x8\nabcxe\nA()BCDE") + expect(vimState.mode).toBe "normal" + + it "repeats correctly inside brackets", -> + editor.setCursorScreenPosition([1, 4]) + keydown('.') + expect(editor.getText()).toBe("1x8\nabcx)e\nA()BCDE") + expect(vimState.mode).toBe "normal" + + it "repeats correctly on the closing bracket", -> + editor.setCursorScreenPosition([1, 5]) + keydown('.') + expect(editor.getText()).toBe("1x8\nabcxe\nA()BCDE") + expect(vimState.mode).toBe "normal" + + it "does nothing when repeated after a bracket", -> + editor.setCursorScreenPosition([2, 3]) + keydown('.') + expect(editor.getText()).toBe("1x8\nabc(d)e\nA()BCDE") + expect(vimState.mode).toBe "normal" + describe "when followed by a goto line G", -> beforeEach -> editor.setText "12345\nabcde\nABCDE" @@ -673,6 +810,130 @@ describe "Operators", -> keydown('escape') expect(editor.getText()).toBe("12345\n\nABCDE") + describe "in visual mode", -> + beforeEach -> + editor.setText "123456789\nabcde\nfghijklmnopq\nuvwxyz" + editor.setCursorScreenPosition [1, 1] + + describe "with characterwise selection on a single line", -> + it "repeats with .", -> + keydown 'v' + keydown '2' + keydown 'l' + keydown 'c' + editor.insertText "ab" + keydown 'escape' + expect(editor.getText()).toBe "123456789\naabe\nfghijklmnopq\nuvwxyz" + + editor.setCursorScreenPosition [0, 1] + keydown '.' + expect(editor.getText()).toBe "1ab56789\naabe\nfghijklmnopq\nuvwxyz" + + it "repeats shortened with . near the end of the line", -> + editor.setCursorScreenPosition [0, 2] + keydown 'v' + keydown '4' + keydown 'l' + keydown 'c' + editor.insertText "ab" + keydown 'escape' + expect(editor.getText()).toBe "12ab89\nabcde\nfghijklmnopq\nuvwxyz" + + editor.setCursorScreenPosition [1, 3] + keydown '.' + expect(editor.getText()).toBe "12ab89\nabcab\nfghijklmnopq\nuvwxyz" + + it "repeats shortened with . near the end of the line regardless of whether motion wrapping is enabled", -> + atom.config.set('vim-mode.wrapLeftRightMotion', true) + editor.setCursorScreenPosition [0, 2] + keydown 'v' + keydown '4' + keydown 'l' + keydown 'c' + editor.insertText "ab" + keydown 'escape' + expect(editor.getText()).toBe "12ab89\nabcde\nfghijklmnopq\nuvwxyz" + + editor.setCursorScreenPosition [1, 3] + keydown '.' + # this differs from VIM, which would eat the \n before fghij... + expect(editor.getText()).toBe "12ab89\nabcab\nfghijklmnopq\nuvwxyz" + + describe "is repeatable with characterwise selection over multiple lines", -> + it "repeats with .", -> + keydown 'v' + keydown 'j' + keydown '3' + keydown 'l' + keydown 'c' + editor.insertText "x" + keydown 'escape' + expect(editor.getText()).toBe "123456789\naxklmnopq\nuvwxyz" + + editor.setCursorScreenPosition [0, 1] + keydown '.' + expect(editor.getText()).toBe "1xnopq\nuvwxyz" + + it "repeats shortened with . near the end of the line", -> + # this behaviour is unlike VIM, see #737 + keydown 'v' + keydown 'j' + keydown '6' + keydown 'l' + keydown 'c' + editor.insertText "x" + keydown 'escape' + expect(editor.getText()).toBe "123456789\naxnopq\nuvwxyz" + + editor.setCursorScreenPosition [0, 1] + keydown '.' + expect(editor.getText()).toBe "1x\nuvwxyz" + + describe "is repeatable with linewise selection", -> + describe "with one line selected", -> + it "repeats with .", -> + keydown 'V', shift: true + keydown 'c' + editor.insertText "x" + keydown 'escape' + expect(editor.getText()).toBe "123456789\nx\nfghijklmnopq\nuvwxyz" + + editor.setCursorScreenPosition [0, 7] + keydown '.' + expect(editor.getText()).toBe "x\nx\nfghijklmnopq\nuvwxyz" + + editor.setCursorScreenPosition [2, 0] + keydown '.' + expect(editor.getText()).toBe "x\nx\nx\nuvwxyz" + + describe "with multiple lines selected", -> + it "repeats with .", -> + keydown 'V', shift: true + keydown 'j' + keydown 'c' + editor.insertText "x" + keydown 'escape' + expect(editor.getText()).toBe "123456789\nx\nuvwxyz" + + editor.setCursorScreenPosition [0, 7] + keydown '.' + expect(editor.getText()).toBe "x\nuvwxyz" + + it "repeats shortened with . near the end of the file", -> + keydown 'V', shift: true + keydown 'j' + keydown 'c' + editor.insertText "x" + keydown 'escape' + expect(editor.getText()).toBe "123456789\nx\nuvwxyz" + + editor.setCursorScreenPosition [1, 7] + keydown '.' + expect(editor.getText()).toBe "123456789\nx\n" + + xdescribe "is repeatable with block selection", -> + # there is no block selection yet + describe "the C keybinding", -> beforeEach -> editor.getBuffer().setText("012\n") @@ -682,13 +943,14 @@ describe "Operators", -> it "deletes the contents until the end of the line and enters insert mode", -> expect(editor.getText()).toBe "0\n" expect(editor.getCursorScreenPosition()).toEqual [0, 1] - expect(editorElement.classList.contains('command-mode')).toBe(false) + expect(editorElement.classList.contains('normal-mode')).toBe(false) expect(editorElement.classList.contains('insert-mode')).toBe(true) describe "the y keybinding", -> beforeEach -> - editor.getBuffer().setText("012 345\nabc\n") + editor.getBuffer().setText("012 345\nabc\ndefg\n") editor.setCursorScreenPosition([0, 4]) + vimState.setRegister('"', text: '345') describe "when selected lines in visual linewise mode", -> beforeEach -> @@ -766,7 +1028,7 @@ describe "Operators", -> it "does not yank when motion fails", -> keydown('y') keydown('t') - commandModeInputKeydown('x') + normalModeInputKeydown('x') expect(vimState.getRegister('"').text).toBe '345' describe "with a text object", -> @@ -782,11 +1044,11 @@ describe "Operators", -> keydown('y') keydown('h') - it "saves the left letter to the default register", -> - expect(vimState.getRegister('"').text).toBe " " + it "saves the left letter to the default register", -> + expect(vimState.getRegister('"').text).toBe " " - it "moves the cursor position to the left", -> - expect(editor.getCursorScreenPosition()).toEqual [0, 3] + it "moves the cursor position to the left", -> + expect(editor.getCursorScreenPosition()).toEqual [0, 3] describe "with a down motion", -> beforeEach -> @@ -799,6 +1061,18 @@ describe "Operators", -> it "leaves the cursor at the starting position", -> expect(editor.getCursorScreenPosition()).toEqual [0, 4] + describe "with an up motion", -> + beforeEach -> + editor.setCursorScreenPosition([2, 2]) + keydown 'y' + keydown 'k' + + it "saves both full lines to the default register", -> + expect(vimState.getRegister('"').text).toBe "abc\ndefg\n" + + it "puts the cursor on the first line and the original column", -> + expect(editor.getCursorScreenPosition()).toEqual [1, 2] + describe "when followed by a G", -> beforeEach -> originalText = "12345\nabcde\nABCDE" @@ -855,6 +1129,47 @@ describe "Operators", -> expect(vimState.getRegister('"').text).toBe '123' expect(editor.getCursorBufferPositions()).toEqual [[0, 0], [1, 2]] + describe "in a long file", -> + beforeEach -> + editor.setHeight(400) + editor.setLineHeightInPixels(10) + editor.setDefaultCharWidth(10) + text = "" + for i in [1..200] + text += "#{i}\n" + editor.setText(text) + + describe "yanking many lines forward", -> + it "does not scroll the window", -> + editor.setCursorBufferPosition [40, 1] + previousScrollTop = editor.getScrollTop() + + # yank many lines + keydown('y') + keydown('1') + keydown('6') + keydown('0') + keydown('G', shift: true) + + expect(editor.getScrollTop()).toEqual(previousScrollTop) + expect(editor.getCursorBufferPosition()).toEqual [40, 1] + expect(vimState.getRegister('"').text.split('\n').length).toBe 121 + + describe "yanking many lines backwards", -> + it "scrolls the window", -> + editor.setCursorBufferPosition [140, 1] + previousScrollTop = editor.getScrollTop() + + # yank many lines + keydown('y') + keydown('6') + keydown('0') + keydown('G', shift: true) + + expect(editor.getScrollTop()).toNotEqual previousScrollTop + expect(editor.getCursorBufferPosition()).toEqual [59, 1] + expect(vimState.getRegister('"').text.split('\n').length).toBe 83 + describe "the yy keybinding", -> describe "on a single line file", -> beforeEach -> @@ -918,6 +1233,15 @@ describe "Operators", -> expect(editor.getText()).toBe "034512\n" expect(editor.getCursorScreenPosition()).toEqual [0, 3] + describe "at the end of a line", -> + beforeEach -> + editor.setCursorScreenPosition [0, 2] + keydown('p') + + it "positions cursor correctly", -> + expect(editor.getText()).toBe "012345\n" + expect(editor.getCursorScreenPosition()).toEqual [0, 5] + describe "when useClipboardAsDefaultRegister enabled", -> it "inserts contents from clipboard", -> atom.config.set 'vim-mode.useClipboardAsDefaultRegister', true @@ -1260,26 +1584,50 @@ describe "Operators", -> it "outdents all three lines", -> expect(editor.getText()).toBe "12345\nabcde\nABCDE" - describe "in visual mode", -> + describe "in visual mode linewise", -> beforeEach -> editor.setCursorScreenPosition([0, 0]) keydown('v', shift: true) - keydown('>') + keydown('j') + + describe "single indent multiple lines", -> + beforeEach -> + keydown('>') + + it "indents both lines once and exits visual mode", -> + expect(editorElement.classList.contains('normal-mode')).toBe(true) + expect(editor.getText()).toBe " 12345\n abcde\nABCDE" + expect(editor.getSelectedBufferRanges()).toEqual [ [[0, 2], [0, 2]] ] - it "indents the current line and remains in visual mode", -> - expect(editorElement.classList.contains('visual-mode')).toBe(true) - expect(editor.getText()).toBe " 12345\nabcde\nABCDE" - expect(editor.getSelectedText()).toBe " 12345\n" + it "allows repeating the operation", -> + keydown('.') + expect(editor.getText()).toBe " 12345\n abcde\nABCDE" - it "allows repeating the operation", -> - keydown("escape") - keydown(".") - expect(editorElement.classList.contains('command-mode')).toBe(true) - expect(editor.getText()).toBe " 12345\nabcde\nABCDE" + describe "multiple indent multiple lines", -> + beforeEach -> + keydown('2') + keydown('>') + + it "indents both lines twice and exits visual mode", -> + expect(editorElement.classList.contains('normal-mode')).toBe(true) + expect(editor.getText()).toBe " 12345\n abcde\nABCDE" + expect(editor.getSelectedBufferRanges()).toEqual [ [[0, 4], [0, 4]] ] + + describe "with multiple selections", -> + beforeEach -> + editor.setCursorScreenPosition([1, 3]) + keydown('v') + keydown('j') + editor.addCursorAtScreenPosition([0, 0]) + + it "indents the lines and keeps the cursors", -> + keydown('>') + expect(editor.getText()).toBe " 12345\n abcde\n ABCDE" + expect(editor.getCursorScreenPositions()).toEqual [[1, 2], [0, 2]] describe "the < keybinding", -> beforeEach -> - editor.setText(" 12345\n abcde\nABCDE") + editor.setText(" 12345\n abcde\nABCDE") editor.setCursorScreenPosition([0, 0]) describe "when followed by a <", -> @@ -1287,9 +1635,9 @@ describe "Operators", -> keydown('<') keydown('<') - it "indents the current line", -> - expect(editor.getText()).toBe "12345\n abcde\nABCDE" - expect(editor.getCursorScreenPosition()).toEqual [0, 0] + it "outdents the current line", -> + expect(editor.getText()).toBe " 12345\n abcde\nABCDE" + expect(editor.getCursorScreenPosition()).toEqual [0, 2] describe "when followed by a repeating <", -> beforeEach -> @@ -1297,25 +1645,43 @@ describe "Operators", -> keydown('<') keydown('<') - it "indents multiple lines at once", -> - expect(editor.getText()).toBe "12345\nabcde\nABCDE" - expect(editor.getCursorScreenPosition()).toEqual [0, 0] + it "outdents multiple lines at once", -> + expect(editor.getText()).toBe " 12345\n abcde\nABCDE" + expect(editor.getCursorScreenPosition()).toEqual [0, 2] describe "undo behavior", -> beforeEach -> keydown('u') it "indents both lines", -> - expect(editor.getText()).toBe " 12345\n abcde\nABCDE" + expect(editor.getText()).toBe " 12345\n abcde\nABCDE" - describe "in visual mode", -> + describe "in visual mode linewise", -> beforeEach -> keydown('v', shift: true) - keydown('<') + keydown('j') + + describe "single outdent multiple lines", -> + beforeEach -> + keydown('<') + + it "outdents the current line and exits visual mode", -> + expect(editorElement.classList.contains('normal-mode')).toBe(true) + expect(editor.getText()).toBe " 12345\n abcde\nABCDE" + expect(editor.getSelectedBufferRanges()).toEqual [ [[0, 2], [0, 2]] ] + + it "allows repeating the operation", -> + keydown('.') + expect(editor.getText()).toBe "12345\nabcde\nABCDE" - it "indents the current line and remains in visual mode", -> - expect(editorElement.classList.contains('visual-mode')).toBe(true) - expect(editor.getText()).toBe "12345\n abcde\nABCDE" - expect(editor.getSelectedText()).toBe "12345\n" + describe "multiple outdent multiple lines", -> + beforeEach -> + keydown('2') + keydown('<') + + it "outdents both lines twice and exits visual mode", -> + expect(editorElement.classList.contains('normal-mode')).toBe(true) + expect(editor.getText()).toBe "12345\nabcde\nABCDE" + expect(editor.getSelectedBufferRanges()).toEqual [ [[0, 0], [0, 0]] ] describe "the = keybinding", -> oldGrammar = [] @@ -1344,6 +1710,16 @@ describe "Operators", -> it "indents the current line", -> expect(editor.indentationForBufferRow(1)).toBe 0 + describe "when followed by a G", -> + beforeEach -> + editor.setCursorScreenPosition([0, 0]) + keydown('=') + keydown('G', shift: true) + + it "uses the default count", -> + expect(editor.indentationForBufferRow(1)).toBe 0 + expect(editor.indentationForBufferRow(2)).toBe 0 + describe "when followed by a repeating =", -> beforeEach -> keydown('2') @@ -1389,7 +1765,7 @@ describe "Operators", -> it "replaces a single character", -> keydown('r') - commandModeInputKeydown('x') + normalModeInputKeydown('x') expect(editor.getText()).toBe 'x2\nx4\n\n' it "does nothing when cancelled", -> @@ -1397,30 +1773,30 @@ describe "Operators", -> expect(editorElement.classList.contains('operator-pending-mode')).toBe(true) keydown('escape') expect(editor.getText()).toBe '12\n34\n\n' - expect(editorElement.classList.contains('command-mode')).toBe(true) + expect(editorElement.classList.contains('normal-mode')).toBe(true) it "replaces a single character with a line break", -> keydown('r') - atom.commands.dispatch(editor.commandModeInputView.editorElement, 'core:confirm') + atom.commands.dispatch(editor.normalModeInputView.editorElement, 'core:confirm') expect(editor.getText()).toBe '\n2\n\n4\n\n' expect(editor.getCursorBufferPositions()).toEqual [[1, 0], [3, 0]] it "composes properly with motions", -> keydown('2') keydown('r') - commandModeInputKeydown('x') + normalModeInputKeydown('x') expect(editor.getText()).toBe 'xx\nxx\n\n' it "does nothing on an empty line", -> editor.setCursorBufferPosition([2, 0]) keydown('r') - commandModeInputKeydown('x') + normalModeInputKeydown('x') expect(editor.getText()).toBe '12\n34\n\n' it "does nothing if asked to replace more characters than there are on a line", -> keydown('3') keydown('r') - commandModeInputKeydown('x') + normalModeInputKeydown('x') expect(editor.getText()).toBe '12\n34\n\n' describe "when in visual mode", -> @@ -1430,14 +1806,42 @@ describe "Operators", -> it "replaces the entire selection with the given character", -> keydown('r') - commandModeInputKeydown('x') + normalModeInputKeydown('x') expect(editor.getText()).toBe 'xx\nxx\n\n' it "leaves the cursor at the beginning of the selection", -> keydown('r') - commandModeInputKeydown('x') + normalModeInputKeydown('x') expect(editor.getCursorBufferPositions()).toEqual [[0, 0], [1, 0]] + describe 'with accented characters', -> + buildIMECompositionEvent = (event, {data, target}={}) -> + event = new Event(event) + event.data = data + Object.defineProperty(event, 'target', get: -> target) + event + + buildTextInputEvent = ({data, target}) -> + event = new Event('textInput') + event.data = data + Object.defineProperty(event, 'target', get: -> target) + event + + it 'works with IME composition', -> + keydown('r') + normalModeEditor = editor.normalModeInputView.editorElement + jasmine.attachToDOM(normalModeEditor) + domNode = normalModeEditor.component.domNode + inputNode = domNode.querySelector('.hidden-input') + domNode.dispatchEvent(buildIMECompositionEvent('compositionstart', target: inputNode)) + domNode.dispatchEvent(buildIMECompositionEvent('compositionupdate', data: 's', target: inputNode)) + expect(normalModeEditor.getModel().getText()).toEqual 's' + domNode.dispatchEvent(buildIMECompositionEvent('compositionupdate', data: 'sd', target: inputNode)) + expect(normalModeEditor.getModel().getText()).toEqual 'sd' + domNode.dispatchEvent(buildIMECompositionEvent('compositionend', target: inputNode)) + domNode.dispatchEvent(buildTextInputEvent(data: '速度', target: inputNode)) + expect(editor.getText()).toBe '速度2\n速度4\n\n' + describe 'the m keybinding', -> beforeEach -> editor.setText('12\n34\n56\n') @@ -1445,7 +1849,7 @@ describe "Operators", -> it 'marks a position', -> keydown('m') - commandModeInputKeydown('a') + normalModeInputKeydown('a') expect(vimState.getMark('a')).toEqual [0, 1] describe 'the ~ keybinding', -> @@ -1490,6 +1894,13 @@ describe "Operators", -> keydown("l") expect(editor.getText()).toBe 'Abc\nXyZ' + it "uses default count", -> + editor.setCursorBufferPosition([0, 0]) + keydown("g") + keydown("~") + keydown("G", shift: true) + expect(editor.getText()).toBe 'AbC\nxYz' + describe 'the U keybinding', -> beforeEach -> editor.setText('aBc\nXyZ') @@ -1513,6 +1924,13 @@ describe "Operators", -> expect(editor.getText()).toBe 'ABC\nXYZ' expect(editor.getCursorScreenPosition()).toEqual [1, 2] + it "uses default count", -> + editor.setCursorBufferPosition([0, 0]) + keydown("g") + keydown("U", shift: true) + keydown("G", shift: true) + expect(editor.getText()).toBe 'ABC\nXYZ' + it "makes the selected text uppercase in visual mode", -> keydown("V", shift: true) keydown("U", shift: true) @@ -1530,6 +1948,13 @@ describe "Operators", -> expect(editor.getText()).toBe 'abc\nXyZ' expect(editor.getCursorScreenPosition()).toEqual [0, 2] + it "uses default count", -> + editor.setCursorBufferPosition([0, 0]) + keydown("g") + keydown("u") + keydown("G", shift: true) + expect(editor.getText()).toBe 'abc\nxyz' + it "makes the selected text lowercase in visual mode", -> keydown("V", shift: true) keydown("u") @@ -1650,18 +2075,21 @@ describe "Operators", -> keydown('a', ctrl: true) expect(editor.getCursorBufferPositions()).toEqual [[0, 2], [1, 3], [2, 4], [3, 3], [4, 0]] expect(editor.getText()).toBe '124\nab46\ncd-66ef\nab-4\na-bcdef' + expect(atom.beep).not.toHaveBeenCalled() it "repeats with .", -> keydown 'a', ctrl: true keydown '.' expect(editor.getCursorBufferPositions()).toEqual [[0, 2], [1, 3], [2, 4], [3, 3], [4, 0]] expect(editor.getText()).toBe '125\nab47\ncd-65ef\nab-3\na-bcdef' + expect(atom.beep).not.toHaveBeenCalled() it "can have a count", -> keydown '5' keydown 'a', ctrl: true expect(editor.getCursorBufferPositions()).toEqual [[0, 2], [1, 3], [2, 4], [3, 2], [4, 0]] expect(editor.getText()).toBe '128\nab50\ncd-62ef\nab0\na-bcdef' + expect(atom.beep).not.toHaveBeenCalled() it "can make a negative number positive, change number of digits", -> keydown '9' @@ -1669,12 +2097,14 @@ describe "Operators", -> keydown 'a', ctrl: true expect(editor.getCursorBufferPositions()).toEqual [[0, 2], [1, 4], [2, 3], [3, 3], [4, 0]] expect(editor.getText()).toBe '222\nab144\ncd32ef\nab94\na-bcdef' + expect(atom.beep).not.toHaveBeenCalled() it "does nothing when cursor is after the number", -> editor.setCursorBufferPosition [2, 5] keydown 'a', ctrl: true expect(editor.getCursorBufferPositions()).toEqual [[2, 5]] expect(editor.getText()).toBe '123\nab45\ncd-67ef\nab-5\na-bcdef' + expect(atom.beep).toHaveBeenCalled() it "does nothing on an empty line", -> editor.setText('\n') @@ -1683,6 +2113,7 @@ describe "Operators", -> keydown 'a', ctrl: true expect(editor.getCursorBufferPositions()).toEqual [[0, 0], [1, 0]] expect(editor.getText()).toBe '\n' + expect(atom.beep).toHaveBeenCalled() it "honours the vim-mode:numberRegex setting", -> editor.setText('123\nab45\ncd -67ef\nab-5\na-bcdef') @@ -1695,24 +2126,28 @@ describe "Operators", -> keydown('a', ctrl: true) expect(editor.getCursorBufferPositions()).toEqual [[0, 2], [1, 3], [2, 5], [3, 3], [4, 0]] expect(editor.getText()).toBe '124\nab46\ncd -66ef\nab-6\na-bcdef' + expect(atom.beep).not.toHaveBeenCalled() describe "decreasing numbers", -> it "decreases the next number", -> keydown('x', ctrl: true) expect(editor.getCursorBufferPositions()).toEqual [[0, 2], [1, 3], [2, 4], [3, 3], [4, 0]] expect(editor.getText()).toBe '122\nab44\ncd-68ef\nab-6\na-bcdef' + expect(atom.beep).not.toHaveBeenCalled() it "repeats with .", -> keydown 'x', ctrl: true keydown '.' expect(editor.getCursorBufferPositions()).toEqual [[0, 2], [1, 3], [2, 4], [3, 3], [4, 0]] expect(editor.getText()).toBe '121\nab43\ncd-69ef\nab-7\na-bcdef' + expect(atom.beep).not.toHaveBeenCalled() it "can have a count", -> keydown '5' keydown 'x', ctrl: true expect(editor.getCursorBufferPositions()).toEqual [[0, 2], [1, 3], [2, 4], [3, 4], [4, 0]] expect(editor.getText()).toBe '118\nab40\ncd-72ef\nab-10\na-bcdef' + expect(atom.beep).not.toHaveBeenCalled() it "can make a positive number negative, change number of digits", -> keydown '9' @@ -1720,12 +2155,14 @@ describe "Operators", -> keydown 'x', ctrl: true expect(editor.getCursorBufferPositions()).toEqual [[0, 1], [1, 4], [2, 5], [3, 5], [4, 0]] expect(editor.getText()).toBe '24\nab-54\ncd-166ef\nab-104\na-bcdef' + expect(atom.beep).not.toHaveBeenCalled() it "does nothing when cursor is after the number", -> editor.setCursorBufferPosition [2, 5] keydown 'x', ctrl: true expect(editor.getCursorBufferPositions()).toEqual [[2, 5]] expect(editor.getText()).toBe '123\nab45\ncd-67ef\nab-5\na-bcdef' + expect(atom.beep).toHaveBeenCalled() it "does nothing on an empty line", -> editor.setText('\n') @@ -1734,6 +2171,7 @@ describe "Operators", -> keydown 'x', ctrl: true expect(editor.getCursorBufferPositions()).toEqual [[0, 0], [1, 0]] expect(editor.getText()).toBe '\n' + expect(atom.beep).toHaveBeenCalled() it "honours the vim-mode:numberRegex setting", -> editor.setText('123\nab45\ncd -67ef\nab-5\na-bcdef') @@ -1746,3 +2184,101 @@ describe "Operators", -> keydown('x', ctrl: true) expect(editor.getCursorBufferPositions()).toEqual [[0, 2], [1, 3], [2, 5], [3, 3], [4, 0]] expect(editor.getText()).toBe '122\nab44\ncd -68ef\nab-4\na-bcdef' + expect(atom.beep).not.toHaveBeenCalled() + + describe 'the R keybinding', -> + beforeEach -> + editor.setText('12345\n67890') + editor.setCursorBufferPosition([0, 2]) + + it "enters replace mode and replaces characters", -> + keydown "R", shift: true + expect(editorElement.classList.contains('insert-mode')).toBe true + expect(editorElement.classList.contains('replace-mode')).toBe true + + editor.insertText "ab" + keydown 'escape' + + expect(editor.getText()).toBe "12ab5\n67890" + expect(editor.getCursorScreenPosition()).toEqual [0, 3] + expect(editorElement.classList.contains('insert-mode')).toBe false + expect(editorElement.classList.contains('replace-mode')).toBe false + expect(editorElement.classList.contains('normal-mode')).toBe true + + it "continues beyond end of line as insert", -> + keydown "R", shift: true + expect(editorElement.classList.contains('insert-mode')).toBe true + expect(editorElement.classList.contains('replace-mode')).toBe true + + editor.insertText "abcde" + keydown 'escape' + + expect(editor.getText()).toBe "12abcde\n67890" + + it "treats backspace as undo", -> + editor.insertText "foo" + keydown "R", shift: true + + editor.insertText "a" + editor.insertText "b" + expect(editor.getText()).toBe "12fooab5\n67890" + + keydown 'backspace', raw: true + expect(editor.getText()).toBe "12fooa45\n67890" + + editor.insertText "c" + + expect(editor.getText()).toBe "12fooac5\n67890" + + keydown 'backspace', raw: true + keydown 'backspace', raw: true + + expect(editor.getText()).toBe "12foo345\n67890" + expect(editor.getSelectedText()).toBe "" + + keydown 'backspace', raw: true + expect(editor.getText()).toBe "12foo345\n67890" + expect(editor.getSelectedText()).toBe "" + + it "can be repeated", -> + keydown "R", shift: true + editor.insertText "ab" + keydown 'escape' + editor.setCursorBufferPosition([1, 2]) + keydown '.' + expect(editor.getText()).toBe "12ab5\n67ab0" + expect(editor.getCursorScreenPosition()).toEqual [1, 3] + + editor.setCursorBufferPosition([0, 4]) + keydown '.' + expect(editor.getText()).toBe "12abab\n67ab0" + expect(editor.getCursorScreenPosition()).toEqual [0, 5] + + it "can be interrupted by arrow keys and behave as insert for repeat", -> + # FIXME don't know how to test this (also, depends on PR #568) + + it "repeats correctly when backspace was used in the text", -> + keydown "R", shift: true + editor.insertText "a" + keydown 'backspace', raw: true + editor.insertText "b" + keydown 'escape' + editor.setCursorBufferPosition([1, 2]) + keydown '.' + expect(editor.getText()).toBe "12b45\n67b90" + expect(editor.getCursorScreenPosition()).toEqual [1, 2] + + editor.setCursorBufferPosition([0, 4]) + keydown '.' + expect(editor.getText()).toBe "12b4b\n67b90" + expect(editor.getCursorScreenPosition()).toEqual [0, 4] + + it "doesn't replace a character if newline is entered", -> + keydown "R", shift: true + expect(editorElement.classList.contains('insert-mode')).toBe true + expect(editorElement.classList.contains('replace-mode')).toBe true + + editor.insertText "\n" + keydown 'escape' + + expect(editor.getText()).toBe "12\n345\n67890" diff --git a/atom/packages/vim-mode/spec/prefixes-spec.coffee b/atom/packages/vim-mode/spec/prefixes-spec.coffee index e782d20..71c4a12 100644 --- a/atom/packages/vim-mode/spec/prefixes-spec.coffee +++ b/atom/packages/vim-mode/spec/prefixes-spec.coffee @@ -11,8 +11,8 @@ describe "Prefixes", -> editorElement = element editor = editorElement.getModel() vimState = editorElement.vimState - vimState.activateCommandMode() - vimState.resetCommandMode() + vimState.activateNormalMode() + vimState.resetNormalMode() keydown = (key, options={}) -> options.element ?= editorElement @@ -146,3 +146,37 @@ describe "Prefixes", -> it "throws away anything written to it", -> vimState.setRegister('%', "new content") expect(vimState.getRegister('%').text).toEqual '/Users/atom/known_value.txt' + + describe "the ctrl-r command in insert mode", -> + beforeEach -> + editor.setText "02\n" + editor.setCursorScreenPosition [0, 0] + vimState.setRegister('"', text: '345') + vimState.setRegister('a', text: 'abc') + atom.clipboard.write "clip" + keydown 'a' + editor.insertText '1' + + it "inserts contents of the unnamed register with \"", -> + keydown 'r', ctrl: true + keydown '"' + expect(editor.getText()).toBe '013452\n' + + describe "when useClipboardAsDefaultRegister enabled", -> + it "inserts contents from clipboard with \"", -> + atom.config.set 'vim-mode.useClipboardAsDefaultRegister', true + keydown 'r', ctrl: true + keydown '"' + expect(editor.getText()).toBe '01clip2\n' + + it "inserts contents of the 'a' register", -> + keydown 'r', ctrl: true + keydown 'a' + expect(editor.getText()).toBe '01abc2\n' + + it "is cancelled with the escape key", -> + keydown 'r', ctrl: true + keydown 'escape' + expect(editor.getText()).toBe '012\n' + expect(vimState.mode).toBe "insert" + expect(editor.getCursorScreenPosition()).toEqual [0, 2] diff --git a/atom/packages/vim-mode/spec/scroll-spec.coffee b/atom/packages/vim-mode/spec/scroll-spec.coffee index 9473193..ebe400a 100644 --- a/atom/packages/vim-mode/spec/scroll-spec.coffee +++ b/atom/packages/vim-mode/spec/scroll-spec.coffee @@ -11,8 +11,8 @@ describe "Scrolling", -> editorElement = element editor = editorElement.getModel() vimState = editorElement.vimState - vimState.activateCommandMode() - vimState.resetCommandMode() + vimState.activateNormalMode() + vimState.resetNormalMode() keydown = (key, options={}) -> options.element ?= editorElement @@ -120,3 +120,111 @@ describe "Scrolling", -> keydown('b') expect(editor.setScrollTop).toHaveBeenCalledWith(860) expect(editor.moveToFirstCharacterOfLine).not.toHaveBeenCalled() + + describe "horizontal scroll cursor keybindings", -> + beforeEach -> + editor.setWidth(600) + editor.setLineHeightInPixels(10) + editor.setDefaultCharWidth(10) + text = "" + for i in [100..199] + text += "#{i} " + editor.setText(text) + editor.setCursorBufferPosition([0, 0]) + + describe "the zs keybinding", -> + zsPos = (pos) -> + editor.setCursorBufferPosition([0, pos]) + keydown('z') + keydown('s') + editor.getScrollLeft() + + startPosition = NaN + + beforeEach -> + startPosition = editor.getScrollLeft() + + it "does nothing near the start of the line", -> + pos1 = zsPos(1) + expect(pos1).toEqual(startPosition) + + it "moves the cursor the nearest it can to the left edge of the editor", -> + pos10 = zsPos(10) + expect(pos10).toBeGreaterThan(startPosition) + + pos11 = zsPos(11) + expect(pos11 - pos10).toEqual(10) + + it "does nothing near the end of the line", -> + posEnd = zsPos(399) + expect(editor.getCursorBufferPosition()).toEqual [0, 399] + + pos390 = zsPos(390) + expect(pos390).toEqual(posEnd) + expect(editor.getCursorBufferPosition()).toEqual [0, 390] + + pos340 = zsPos(340) + expect(pos340).toBeLessThan(posEnd) + pos342 = zsPos(342) + expect(pos342 - pos340).toEqual(20) + + it "does nothing if all lines are short", -> + editor.setText('short') + startPosition = editor.getScrollLeft() + pos1 = zsPos(1) + expect(pos1).toEqual(startPosition) + expect(editor.getCursorBufferPosition()).toEqual [0, 1] + pos10 = zsPos(10) + expect(pos10).toEqual(startPosition) + expect(editor.getCursorBufferPosition()).toEqual [0, 4] + + + describe "the ze keybinding", -> + zePos = (pos) -> + editor.setCursorBufferPosition([0, pos]) + keydown('z') + keydown('e') + editor.getScrollLeft() + + startPosition = NaN + + beforeEach -> + startPosition = editor.getScrollLeft() + + it "does nothing near the start of the line", -> + pos1 = zePos(1) + expect(pos1).toEqual(startPosition) + + pos40 = zePos(40) + expect(pos40).toEqual(startPosition) + + it "moves the cursor the nearest it can to the right edge of the editor", -> + pos110 = zePos(110) + expect(pos110).toBeGreaterThan(startPosition) + + pos109 = zePos(109) + expect(pos110 - pos109).toEqual(10) + + it "does nothing when very near the end of the line", -> + posEnd = zePos(399) + expect(editor.getCursorBufferPosition()).toEqual [0, 399] + + pos397 = zePos(397) + expect(pos397).toEqual(posEnd) + expect(editor.getCursorBufferPosition()).toEqual [0, 397] + + pos380 = zePos(380) + expect(pos380).toBeLessThan(posEnd) + + pos382 = zePos(382) + expect(pos382 - pos380).toEqual(20) + + it "does nothing if all lines are short", -> + editor.setText('short') + startPosition = editor.getScrollLeft() + pos1 = zePos(1) + expect(pos1).toEqual(startPosition) + expect(editor.getCursorBufferPosition()).toEqual [0, 1] + pos10 = zePos(10) + expect(pos10).toEqual(startPosition) + expect(editor.getCursorBufferPosition()).toEqual [0, 4] diff --git a/atom/packages/vim-mode/spec/spec-helper.coffee b/atom/packages/vim-mode/spec/spec-helper.coffee index 88ba4b2..7cff2fc 100644 --- a/atom/packages/vim-mode/spec/spec-helper.coffee +++ b/atom/packages/vim-mode/spec/spec-helper.coffee @@ -3,8 +3,13 @@ GlobalVimState = require '../lib/global-vim-state' VimMode = require '../lib/vim-mode' StatusBarManager = require '../lib/status-bar-manager' +[globalVimState, statusBarManager] = [] + beforeEach -> atom.workspace ||= {} + statusBarManager = null + globalVimState = null + spyOn(atom, 'beep') getEditorElement = (callback) -> textEditor = null @@ -17,11 +22,16 @@ getEditorElement = (callback) -> element = document.createElement("atom-text-editor") element.setModel(textEditor) element.classList.add('vim-mode') - element.vimState = new VimState(element, new StatusBarManager, new GlobalVimState) + statusBarManager ?= new StatusBarManager + globalVimState ?= new GlobalVimState + element.vimState = new VimState(element, statusBarManager, globalVimState) element.addEventListener "keydown", (e) -> atom.keymaps.handleKeyboardEvent(e) + # mock parent element for the text editor + document.createElement('html').appendChild(atom.views.getView(textEditor)) + callback(element) mockPlatform = (editorElement, platform) -> diff --git a/atom/packages/vim-mode/spec/text-objects-spec.coffee b/atom/packages/vim-mode/spec/text-objects-spec.coffee index 6991dc7..92b4a7a 100644 --- a/atom/packages/vim-mode/spec/text-objects-spec.coffee +++ b/atom/packages/vim-mode/spec/text-objects-spec.coffee @@ -11,15 +11,23 @@ describe "TextObjects", -> editorElement = element editor = editorElement.getModel() vimState = editorElement.vimState - vimState.activateCommandMode() - vimState.resetCommandMode() + vimState.activateNormalMode() + vimState.resetNormalMode() keydown = (key, options={}) -> options.element ?= editorElement helpers.keydown(key, options) - commandModeInputKeydown = (key, opts = {}) -> - editor.commandModeInputView.editorElement.getModel().setText(key) + describe "Text Object commands in normal mode not preceded by an operator", -> + beforeEach -> + vimState.activateNormalMode() + + it "selects the appropriate text", -> + editor.setText("<html> text </html>") + editor.setCursorScreenPosition([0, 7]) + # Users could dispatch it via the command palette + atom.commands.dispatch(editorElement, "vim-mode:select-inside-tags") + expect(editor.getSelectedScreenRange()).toEqual [[0, 6], [0, 12]] describe "the 'iw' text object", -> beforeEach -> @@ -35,7 +43,7 @@ describe "TextObjects", -> expect(editor.getCursorScreenPosition()).toEqual [0, 6] expect(vimState.getRegister('"').text).toBe "abcde" expect(editorElement.classList.contains('operator-pending-mode')).toBe(false) - expect(editorElement.classList.contains('command-mode')).toBe(true) + expect(editorElement.classList.contains('normal-mode')).toBe(true) it "selects inside the current word in visual mode", -> keydown('v') @@ -44,6 +52,16 @@ describe "TextObjects", -> expect(editor.getSelectedScreenRange()).toEqual [[0, 6], [0, 11]] + it "expands an existing selection in visual mode", -> + keydown('v') + keydown('l') + keydown('l') + keydown('l') + keydown('i') + keydown('w') + + expect(editor.getSelectedScreenRange()).toEqual [[0, 9], [0, 17]] + it "works with multiple cursors", -> editor.addCursorAtBufferPosition([0, 1]) keydown("v") @@ -54,6 +72,39 @@ describe "TextObjects", -> [[0, 0], [0, 5]] ] + describe "the 'iW' text object", -> + beforeEach -> + editor.setText("12(45 ab'de ABCDE") + editor.setCursorScreenPosition([0, 9]) + + it "applies operators inside the current whole word in operator-pending mode", -> + keydown('d') + keydown('i') + keydown('W', shift: true) + + expect(editor.getText()).toBe "12(45 ABCDE" + expect(editor.getCursorScreenPosition()).toEqual [0, 6] + expect(vimState.getRegister('"').text).toBe "ab'de" + expect(editorElement.classList.contains('operator-pending-mode')).toBe(false) + expect(editorElement.classList.contains('normal-mode')).toBe(true) + + it "selects inside the current whole word in visual mode", -> + keydown('v') + keydown('i') + keydown('W', shift: true) + + expect(editor.getSelectedScreenRange()).toEqual [[0, 6], [0, 11]] + + it "expands an existing selection in visual mode", -> + keydown('v') + keydown('l') + keydown('l') + keydown('l') + keydown('i') + keydown('W', shift: true) + + expect(editor.getSelectedScreenRange()).toEqual [[0, 9], [0, 17]] + describe "the 'i(' text object", -> beforeEach -> editor.setText("( something in here and in (here) )") @@ -66,7 +117,7 @@ describe "TextObjects", -> expect(editor.getText()).toBe "()" expect(editor.getCursorScreenPosition()).toEqual [0, 1] expect(editorElement.classList.contains('operator-pending-mode')).toBe(false) - expect(editorElement.classList.contains('command-mode')).toBe(true) + expect(editorElement.classList.contains('normal-mode')).toBe(true) it "applies operators inside the current word in operator-pending mode (second test)", -> editor.setCursorScreenPosition([0, 29]) @@ -76,7 +127,7 @@ describe "TextObjects", -> expect(editor.getText()).toBe "( something in here and in () )" expect(editor.getCursorScreenPosition()).toEqual [0, 28] expect(editorElement.classList.contains('operator-pending-mode')).toBe(false) - expect(editorElement.classList.contains('command-mode')).toBe(true) + expect(editorElement.classList.contains('normal-mode')).toBe(true) it "works with multiple cursors", -> editor.setText("( a b ) cde ( f g h ) ijk") @@ -92,6 +143,18 @@ describe "TextObjects", -> [[0, 13], [0, 20]] ] + it "expands an existing selection in visual mode", -> + editor.setCursorScreenPosition([0, 25]) + keydown('v') + keydown('l') + keydown('l') + keydown('l') + keydown('l') + keydown('i') + keydown('(') + + expect(editor.getSelectedScreenRange()).toEqual [[0, 25], [0, 32]] + describe "the 'i{' text object", -> beforeEach -> editor.setText("{ something in here and in {here} }") @@ -104,7 +167,7 @@ describe "TextObjects", -> expect(editor.getText()).toBe "{}" expect(editor.getCursorScreenPosition()).toEqual [0, 1] expect(editorElement.classList.contains('operator-pending-mode')).toBe(false) - expect(editorElement.classList.contains('command-mode')).toBe(true) + expect(editorElement.classList.contains('normal-mode')).toBe(true) it "applies operators inside the current word in operator-pending mode (second test)", -> editor.setCursorScreenPosition([0, 29]) @@ -114,7 +177,19 @@ describe "TextObjects", -> expect(editor.getText()).toBe "{ something in here and in {} }" expect(editor.getCursorScreenPosition()).toEqual [0, 28] expect(editorElement.classList.contains('operator-pending-mode')).toBe(false) - expect(editorElement.classList.contains('command-mode')).toBe(true) + expect(editorElement.classList.contains('normal-mode')).toBe(true) + + it "expands an existing selection in visual mode", -> + editor.setCursorScreenPosition([0, 25]) + keydown('v') + keydown('l') + keydown('l') + keydown('l') + keydown('l') + keydown('i') + keydown('{') + + expect(editor.getSelectedScreenRange()).toEqual [[0, 25], [0, 32]] describe "the 'i<' text object", -> beforeEach -> @@ -128,7 +203,7 @@ describe "TextObjects", -> expect(editor.getText()).toBe "<>" expect(editor.getCursorScreenPosition()).toEqual [0, 1] expect(editorElement.classList.contains('operator-pending-mode')).toBe(false) - expect(editorElement.classList.contains('command-mode')).toBe(true) + expect(editorElement.classList.contains('normal-mode')).toBe(true) it "applies operators inside the current word in operator-pending mode (second test)", -> editor.setCursorScreenPosition([0, 29]) @@ -138,7 +213,19 @@ describe "TextObjects", -> expect(editor.getText()).toBe "< something in here and in <> >" expect(editor.getCursorScreenPosition()).toEqual [0, 28] expect(editorElement.classList.contains('operator-pending-mode')).toBe(false) - expect(editorElement.classList.contains('command-mode')).toBe(true) + expect(editorElement.classList.contains('normal-mode')).toBe(true) + + it "expands an existing selection in visual mode", -> + editor.setCursorScreenPosition([0, 25]) + keydown('v') + keydown('l') + keydown('l') + keydown('l') + keydown('l') + keydown('i') + keydown('<') + + expect(editor.getSelectedScreenRange()).toEqual [[0, 25], [0, 32]] describe "the 'it' text object", -> beforeEach -> @@ -152,7 +239,7 @@ describe "TextObjects", -> expect(editor.getText()).toBe "<something>here</something><again>" expect(editor.getCursorScreenPosition()).toEqual [0, 5] expect(editorElement.classList.contains('operator-pending-mode')).toBe(false) - expect(editorElement.classList.contains('command-mode')).toBe(true) + expect(editorElement.classList.contains('normal-mode')).toBe(true) it "applies operators inside the current word in operator-pending mode", -> editor.setCursorScreenPosition([0, 13]) @@ -162,15 +249,24 @@ describe "TextObjects", -> expect(editor.getText()).toBe "<something></something><again>" expect(editor.getCursorScreenPosition()).toEqual [0, 11] expect(editorElement.classList.contains('operator-pending-mode')).toBe(false) - expect(editorElement.classList.contains('command-mode')).toBe(true) + expect(editorElement.classList.contains('normal-mode')).toBe(true) + + it "expands an existing selection in visual mode", -> + editor.setCursorScreenPosition([0, 7]) + keydown('v') + keydown('6') + keydown('l') + keydown('i') + keydown('t') + + expect(editor.getSelectedScreenRange()).toEqual [[0, 7], [0, 15]] describe "the 'ip' text object", -> beforeEach -> editor.setText("\nParagraph-1\nParagraph-1\nParagraph-1\n\n") - editor.setCursorScreenPosition([2, 2]) + editor.setCursorBufferPosition([2, 2]) it "applies operators inside the current paragraph in operator-pending mode", -> - keydown('y') keydown('i') keydown('p') @@ -179,7 +275,7 @@ describe "TextObjects", -> expect(editor.getCursorScreenPosition()).toEqual [1, 0] expect(vimState.getRegister('"').text).toBe "Paragraph-1\nParagraph-1\nParagraph-1\n" expect(editorElement.classList.contains('operator-pending-mode')).toBe(false) - expect(editorElement.classList.contains('command-mode')).toBe(true) + expect(editorElement.classList.contains('normal-mode')).toBe(true) it "selects inside the current paragraph in visual mode", -> keydown('v') @@ -188,30 +284,105 @@ describe "TextObjects", -> expect(editor.getSelectedScreenRange()).toEqual [[1, 0], [4, 0]] + it "selects between paragraphs in visual mode if invoked on a empty line", -> + editor.setText("text\n\n\n\ntext\n") + editor.setCursorBufferPosition([1, 0]) + + keydown('v') + keydown('i') + keydown('p') + + expect(editor.getSelectedScreenRange()).toEqual [[1, 0], [4, 0]] + + it "selects all the lines", -> + editor.setText("text\ntext\ntext\n") + editor.setCursorBufferPosition([0, 0]) + + keydown('v') + keydown('i') + keydown('p') + + expect(editor.getSelectedScreenRange()).toEqual [[0, 0], [3, 0]] + + it "expands an existing selection in visual mode", -> + editor.setText("\nParagraph-1\nParagraph-1\nParagraph-1\n\n\nParagraph-2\nParagraph-2\nParagraph-2\n") + editor.setCursorBufferPosition([2, 2]) + + keydown('v') + keydown('i') + keydown('p') + + keydown('j') + keydown('j') + keydown('j') + keydown('i') + keydown('p') + + expect(editor.getSelectedScreenRange()).toEqual [[1, 0], [9, 0]] + describe "the 'ap' text object", -> beforeEach -> - editor.setText("text\n\nParagraph-1\nParagraph-1\nParagraph-1\n\nmoretext") + editor.setText("text\n\nParagraph-1\nParagraph-1\nParagraph-1\n\n\nmoretext") editor.setCursorScreenPosition([3, 2]) it "applies operators around the current paragraph in operator-pending mode", -> - keydown('y') keydown('a') keydown('p') - expect(editor.getText()).toBe "text\n\nParagraph-1\nParagraph-1\nParagraph-1\n\nmoretext" + expect(editor.getText()).toBe "text\n\nParagraph-1\nParagraph-1\nParagraph-1\n\n\nmoretext" expect(editor.getCursorScreenPosition()).toEqual [2, 0] - expect(vimState.getRegister('"').text).toBe "Paragraph-1\nParagraph-1\nParagraph-1\n\n" + expect(vimState.getRegister('"').text).toBe "Paragraph-1\nParagraph-1\nParagraph-1\n\n\n" expect(editorElement.classList.contains('operator-pending-mode')).toBe(false) - expect(editorElement.classList.contains('command-mode')).toBe(true) + expect(editorElement.classList.contains('normal-mode')).toBe(true) it "selects around the current paragraph in visual mode", -> keydown('v') keydown('a') keydown('p') - expect(editor.getSelectedScreenRange()).toEqual [[2, 0], [6, 0]] - + expect(editor.getSelectedScreenRange()).toEqual [[2, 0], [7, 0]] + + it "applies operators around the next paragraph in operator-pending mode when started from a blank/only-whitespace line", -> + editor.setText("text\n\n\n\nParagraph-1\nParagraph-1\nParagraph-1\n\n\nmoretext") + editor.setCursorBufferPosition([1, 0]) + + keydown('y') + keydown('a') + keydown('p') + + expect(editor.getText()).toBe "text\n\n\n\nParagraph-1\nParagraph-1\nParagraph-1\n\n\nmoretext" + expect(editor.getCursorScreenPosition()).toEqual [1, 0] + expect(vimState.getRegister('"').text).toBe "\n\n\nParagraph-1\nParagraph-1\nParagraph-1\n" + expect(editorElement.classList.contains('operator-pending-mode')).toBe(false) + expect(editorElement.classList.contains('normal-mode')).toBe(true) + + it "selects around the next paragraph in visual mode when started from a blank/only-whitespace line", -> + editor.setText("text\n\n\n\nparagraph-1\nparagraph-1\nparagraph-1\n\n\nmoretext") + editor.setCursorBufferPosition([1, 0]) + + keydown('v') + keydown('a') + keydown('p') + + expect(editor.getSelectedScreenRange()).toEqual [[1, 0], [7, 0]] + + it "expands an existing selection in visual mode", -> + editor.setText("text\n\n\n\nparagraph-1\nparagraph-1\nparagraph-1\n\n\n\nparagraph-2\nparagraph-2\nparagraph-2\n\n\nmoretext") + editor.setCursorBufferPosition([5, 0]) + + keydown('v') + keydown('a') + keydown('p') + + keydown('j') + keydown('j') + keydown('j') + keydown('i') + keydown('p') + + expect(editor.getSelectedScreenRange()).toEqual [[4, 0], [13, 0]] + describe "the 'i[' text object", -> beforeEach -> editor.setText("[ something in here and in [here] ]") @@ -224,7 +395,7 @@ describe "TextObjects", -> expect(editor.getText()).toBe "[]" expect(editor.getCursorScreenPosition()).toEqual [0, 1] expect(editorElement.classList.contains('operator-pending-mode')).toBe(false) - expect(editorElement.classList.contains('command-mode')).toBe(true) + expect(editorElement.classList.contains('normal-mode')).toBe(true) it "applies operators inside the current word in operator-pending mode (second test)", -> editor.setCursorScreenPosition([0, 29]) @@ -234,7 +405,19 @@ describe "TextObjects", -> expect(editor.getText()).toBe "[ something in here and in [] ]" expect(editor.getCursorScreenPosition()).toEqual [0, 28] expect(editorElement.classList.contains('operator-pending-mode')).toBe(false) - expect(editorElement.classList.contains('command-mode')).toBe(true) + expect(editorElement.classList.contains('normal-mode')).toBe(true) + + it "expands an existing selection in visual mode", -> + editor.setCursorScreenPosition([0, 25]) + keydown('v') + keydown('l') + keydown('l') + keydown('l') + keydown('l') + keydown('i') + keydown('[') + + expect(editor.getSelectedScreenRange()).toEqual [[0, 25], [0, 32]] describe "the 'i\'' text object", -> beforeEach -> @@ -248,7 +431,7 @@ describe "TextObjects", -> expect(editor.getText()).toBe "''here' ' and over here" expect(editor.getCursorScreenPosition()).toEqual [0, 1] expect(editorElement.classList.contains('operator-pending-mode')).toBe(false) - expect(editorElement.classList.contains('command-mode')).toBe(true) + expect(editorElement.classList.contains('normal-mode')).toBe(true) it "applies operators inside the next string in operator-pending mode (if not in a string)", -> editor.setCursorScreenPosition([0, 29]) @@ -258,7 +441,7 @@ describe "TextObjects", -> expect(editor.getText()).toBe "' something in here and in 'here'' and over here" expect(editor.getCursorScreenPosition()).toEqual [0, 33] expect(editorElement.classList.contains('operator-pending-mode')).toBe(false) - expect(editorElement.classList.contains('command-mode')).toBe(true) + expect(editorElement.classList.contains('normal-mode')).toBe(true) it "makes no change if past the last string on a line", -> editor.setCursorScreenPosition([0, 39]) @@ -268,7 +451,19 @@ describe "TextObjects", -> expect(editor.getText()).toBe "' something in here and in 'here' ' and over here" expect(editor.getCursorScreenPosition()).toEqual [0, 39] expect(editorElement.classList.contains('operator-pending-mode')).toBe(false) - expect(editorElement.classList.contains('command-mode')).toBe(true) + expect(editorElement.classList.contains('normal-mode')).toBe(true) + + it "expands an existing selection in visual mode", -> + editor.setCursorScreenPosition([0, 25]) + keydown('v') + keydown('l') + keydown('l') + keydown('l') + keydown('l') + keydown('i') + keydown('\'') + + expect(editor.getSelectedScreenRange()).toEqual [[0, 25], [0, 34]] describe "the 'i\"' text object", -> beforeEach -> @@ -282,7 +477,7 @@ describe "TextObjects", -> expect(editor.getText()).toBe "\"\"here\" \" and over here" expect(editor.getCursorScreenPosition()).toEqual [0, 1] expect(editorElement.classList.contains('operator-pending-mode')).toBe(false) - expect(editorElement.classList.contains('command-mode')).toBe(true) + expect(editorElement.classList.contains('normal-mode')).toBe(true) it "applies operators inside the next string in operator-pending mode (if not in a string)", -> editor.setCursorScreenPosition([0, 29]) @@ -292,7 +487,7 @@ describe "TextObjects", -> expect(editor.getText()).toBe "\" something in here and in \"here\"\" and over here" expect(editor.getCursorScreenPosition()).toEqual [0, 33] expect(editorElement.classList.contains('operator-pending-mode')).toBe(false) - expect(editorElement.classList.contains('command-mode')).toBe(true) + expect(editorElement.classList.contains('normal-mode')).toBe(true) it "makes no change if past the last string on a line", -> editor.setCursorScreenPosition([0, 39]) @@ -303,6 +498,18 @@ describe "TextObjects", -> expect(editor.getCursorScreenPosition()).toEqual [0, 39] expect(editorElement.classList.contains('operator-pending-mode')).toBe(false) + it "expands an existing selection in visual mode", -> + editor.setCursorScreenPosition([0, 25]) + keydown('v') + keydown('l') + keydown('l') + keydown('l') + keydown('l') + keydown('i') + keydown('"') + + expect(editor.getSelectedScreenRange()).toEqual [[0, 25], [0, 34]] + describe "the 'aw' text object", -> beforeEach -> editor.setText("12345 abcde ABCDE") @@ -317,7 +524,7 @@ describe "TextObjects", -> expect(editor.getCursorScreenPosition()).toEqual [0, 6] expect(vimState.getRegister('"').text).toBe "abcde " expect(editorElement.classList.contains('operator-pending-mode')).toBe(false) - expect(editorElement.classList.contains('command-mode')).toBe(true) + expect(editorElement.classList.contains('normal-mode')).toBe(true) it "selects from the start of the current word to the start of the next word in visual mode", -> keydown('v') @@ -326,6 +533,17 @@ describe "TextObjects", -> expect(editor.getSelectedScreenRange()).toEqual [[0, 6], [0, 12]] + it "expands an existing selection in visual mode", -> + editor.setCursorScreenPosition([0, 2]) + keydown('v') + keydown('l') + keydown('l') + keydown('l') + keydown('a') + keydown('w') + + expect(editor.getSelectedScreenRange()).toEqual [[0, 2], [0, 12]] + it "doesn't span newlines", -> editor.setText("12345\nabcde ABCDE") editor.setCursorBufferPosition([0, 3]) @@ -336,6 +554,60 @@ describe "TextObjects", -> expect(editor.getSelectedBufferRanges()).toEqual [[[0, 0], [0, 5]]] + it "doesn't span special characters", -> + editor.setText("1(345\nabcde ABCDE") + editor.setCursorBufferPosition([0, 3]) + + keydown("v") + keydown("a") + keydown("w") + + expect(editor.getSelectedBufferRanges()).toEqual [[[0, 2], [0, 5]]] + + describe "the 'aW' text object", -> + beforeEach -> + editor.setText("12(45 ab'de ABCDE") + editor.setCursorScreenPosition([0, 9]) + + it "applies operators from the start of the current whole word to the start of the next whole word in operator-pending mode", -> + keydown('d') + keydown('a') + keydown('W', shift: true) + + expect(editor.getText()).toBe "12(45 ABCDE" + expect(editor.getCursorScreenPosition()).toEqual [0, 6] + expect(vimState.getRegister('"').text).toBe "ab'de " + expect(editorElement.classList.contains('operator-pending-mode')).toBe(false) + expect(editorElement.classList.contains('normal-mode')).toBe(true) + + it "selects from the start of the current whole word to the start of the next whole word in visual mode", -> + keydown('v') + keydown('a') + keydown('W', shift: true) + + expect(editor.getSelectedScreenRange()).toEqual [[0, 6], [0, 12]] + + it "expands an existing selection in visual mode", -> + editor.setCursorScreenPosition([0, 2]) + keydown('v') + keydown('l') + keydown('l') + keydown('l') + keydown('a') + keydown('W', shift: true) + + expect(editor.getSelectedScreenRange()).toEqual [[0, 2], [0, 12]] + + it "doesn't span newlines", -> + editor.setText("12(45\nab'de ABCDE") + editor.setCursorBufferPosition([0, 4]) + + keydown('v') + keydown('a') + keydown('W', shift: true) + + expect(editor.getSelectedBufferRanges()).toEqual [[[0, 0], [0, 5]]] + describe "the 'a(' text object", -> beforeEach -> editor.setText("( something in here and in (here) )") @@ -348,7 +620,7 @@ describe "TextObjects", -> expect(editor.getText()).toBe "" expect(editor.getCursorScreenPosition()).toEqual [0, 0] expect(editorElement.classList.contains('operator-pending-mode')).toBe(false) - expect(editorElement.classList.contains('command-mode')).toBe(true) + expect(editorElement.classList.contains('normal-mode')).toBe(true) it "applies operators around the current parentheses in operator-pending mode (second test)", -> editor.setCursorScreenPosition([0, 29]) @@ -358,7 +630,19 @@ describe "TextObjects", -> expect(editor.getText()).toBe "( something in here and in )" expect(editor.getCursorScreenPosition()).toEqual [0, 27] expect(editorElement.classList.contains('operator-pending-mode')).toBe(false) - expect(editorElement.classList.contains('command-mode')).toBe(true) + expect(editorElement.classList.contains('normal-mode')).toBe(true) + + it "expands an existing selection in visual mode", -> + editor.setCursorScreenPosition([0, 25]) + keydown('v') + keydown('l') + keydown('l') + keydown('l') + keydown('l') + keydown('a') + keydown('(') + + expect(editor.getSelectedScreenRange()).toEqual [[0, 25], [0, 33]] describe "the 'a{' text object", -> beforeEach -> @@ -372,7 +656,7 @@ describe "TextObjects", -> expect(editor.getText()).toBe "" expect(editor.getCursorScreenPosition()).toEqual [0, 0] expect(editorElement.classList.contains('operator-pending-mode')).toBe(false) - expect(editorElement.classList.contains('command-mode')).toBe(true) + expect(editorElement.classList.contains('normal-mode')).toBe(true) it "applies operators around the current curly brackets in operator-pending mode (second test)", -> editor.setCursorScreenPosition([0, 29]) @@ -382,7 +666,19 @@ describe "TextObjects", -> expect(editor.getText()).toBe "{ something in here and in }" expect(editor.getCursorScreenPosition()).toEqual [0, 27] expect(editorElement.classList.contains('operator-pending-mode')).toBe(false) - expect(editorElement.classList.contains('command-mode')).toBe(true) + expect(editorElement.classList.contains('normal-mode')).toBe(true) + + it "expands an existing selection in visual mode", -> + editor.setCursorScreenPosition([0, 25]) + keydown('v') + keydown('l') + keydown('l') + keydown('l') + keydown('l') + keydown('a') + keydown('{') + + expect(editor.getSelectedScreenRange()).toEqual [[0, 25], [0, 33]] describe "the 'a<' text object", -> beforeEach -> @@ -396,7 +692,7 @@ describe "TextObjects", -> expect(editor.getText()).toBe "" expect(editor.getCursorScreenPosition()).toEqual [0, 0] expect(editorElement.classList.contains('operator-pending-mode')).toBe(false) - expect(editorElement.classList.contains('command-mode')).toBe(true) + expect(editorElement.classList.contains('normal-mode')).toBe(true) it "applies operators around the current angle brackets in operator-pending mode (second test)", -> editor.setCursorScreenPosition([0, 29]) @@ -406,7 +702,19 @@ describe "TextObjects", -> expect(editor.getText()).toBe "< something in here and in >" expect(editor.getCursorScreenPosition()).toEqual [0, 27] expect(editorElement.classList.contains('operator-pending-mode')).toBe(false) - expect(editorElement.classList.contains('command-mode')).toBe(true) + expect(editorElement.classList.contains('normal-mode')).toBe(true) + + it "expands an existing selection in visual mode", -> + editor.setCursorScreenPosition([0, 25]) + keydown('v') + keydown('l') + keydown('l') + keydown('l') + keydown('l') + keydown('a') + keydown('<') + + expect(editor.getSelectedScreenRange()).toEqual [[0, 25], [0, 33]] describe "the 'a[' text object", -> beforeEach -> @@ -420,7 +728,7 @@ describe "TextObjects", -> expect(editor.getText()).toBe "" expect(editor.getCursorScreenPosition()).toEqual [0, 0] expect(editorElement.classList.contains('operator-pending-mode')).toBe(false) - expect(editorElement.classList.contains('command-mode')).toBe(true) + expect(editorElement.classList.contains('normal-mode')).toBe(true) it "applies operators around the current square brackets in operator-pending mode (second test)", -> editor.setCursorScreenPosition([0, 29]) @@ -430,7 +738,19 @@ describe "TextObjects", -> expect(editor.getText()).toBe "[ something in here and in ]" expect(editor.getCursorScreenPosition()).toEqual [0, 27] expect(editorElement.classList.contains('operator-pending-mode')).toBe(false) - expect(editorElement.classList.contains('command-mode')).toBe(true) + expect(editorElement.classList.contains('normal-mode')).toBe(true) + + it "expands an existing selection in visual mode", -> + editor.setCursorScreenPosition([0, 25]) + keydown('v') + keydown('l') + keydown('l') + keydown('l') + keydown('l') + keydown('a') + keydown('[') + + expect(editor.getSelectedScreenRange()).toEqual [[0, 25], [0, 33]] describe "the 'a\'' text object", -> beforeEach -> @@ -444,7 +764,7 @@ describe "TextObjects", -> expect(editor.getText()).toBe "here' '" expect(editor.getCursorScreenPosition()).toEqual [0, 0] expect(editorElement.classList.contains('operator-pending-mode')).toBe(false) - expect(editorElement.classList.contains('command-mode')).toBe(true) + expect(editorElement.classList.contains('normal-mode')).toBe(true) it "applies operators around the current single quotes in operator-pending mode (second test)", -> editor.setCursorScreenPosition([0, 29]) @@ -454,7 +774,19 @@ describe "TextObjects", -> expect(editor.getText()).toBe "' something in here and in 'here" expect(editor.getCursorScreenPosition()).toEqual [0, 31] expect(editorElement.classList.contains('operator-pending-mode')).toBe(false) - expect(editorElement.classList.contains('command-mode')).toBe(true) + expect(editorElement.classList.contains('normal-mode')).toBe(true) + + it "expands an existing selection in visual mode", -> + editor.setCursorScreenPosition([0, 25]) + keydown('v') + keydown('l') + keydown('l') + keydown('l') + keydown('l') + keydown('a') + keydown('\'') + + expect(editor.getSelectedScreenRange()).toEqual [[0, 25], [0, 35]] describe "the 'a\"' text object", -> beforeEach -> @@ -468,7 +800,7 @@ describe "TextObjects", -> expect(editor.getText()).toBe 'here" "' expect(editor.getCursorScreenPosition()).toEqual [0, 0] expect(editorElement.classList.contains('operator-pending-mode')).toBe(false) - expect(editorElement.classList.contains('command-mode')).toBe(true) + expect(editorElement.classList.contains('normal-mode')).toBe(true) it "applies operators around the current double quotes in operator-pending mode (second test)", -> editor.setCursorScreenPosition([0, 29]) @@ -478,4 +810,16 @@ describe "TextObjects", -> expect(editor.getText()).toBe "\" something in here and in \"here" expect(editor.getCursorScreenPosition()).toEqual [0, 31] expect(editorElement.classList.contains('operator-pending-mode')).toBe(false) - expect(editorElement.classList.contains('command-mode')).toBe(true) + expect(editorElement.classList.contains('normal-mode')).toBe(true) + + it "expands an existing selection in visual mode", -> + editor.setCursorScreenPosition([0, 25]) + keydown('v') + keydown('l') + keydown('l') + keydown('l') + keydown('l') + keydown('a') + keydown('"') + + expect(editor.getSelectedScreenRange()).toEqual [[0, 25], [0, 35]] diff --git a/atom/packages/vim-mode/spec/vim-mode-spec.coffee b/atom/packages/vim-mode/spec/vim-mode-spec.coffee index 0f4d8b8..280e545 100644 --- a/atom/packages/vim-mode/spec/vim-mode-spec.coffee +++ b/atom/packages/vim-mode/spec/vim-mode-spec.coffee @@ -18,15 +18,20 @@ describe "VimMode", -> editorElement = atom.views.getView(editor) describe ".activate", -> - it "puts the editor in command-mode initially by default", -> + it "puts the editor in normal-mode initially by default", -> expect(editorElement.classList.contains('vim-mode')).toBe(true) - expect(editorElement.classList.contains('command-mode')).toBe(true) + expect(editorElement.classList.contains('normal-mode')).toBe(true) it "shows the current vim mode in the status bar", -> - statusBarTile = workspaceElement.querySelector("#status-bar-vim-mode") - expect(statusBarTile.textContent).toBe("Command") - atom.commands.dispatch(editorElement, "vim-mode:activate-insert-mode") - expect(statusBarTile.textContent).toBe("Insert") + statusBarTile = null + + waitsFor -> + statusBarTile = workspaceElement.querySelector("#status-bar-vim-mode") + + runs -> + expect(statusBarTile.textContent).toBe("Normal") + atom.commands.dispatch(editorElement, "vim-mode:activate-insert-mode") + expect(statusBarTile.textContent).toBe("Insert") it "doesn't register duplicate command listeners for editors", -> editor.setText("12345") @@ -44,7 +49,7 @@ describe "VimMode", -> it "removes the vim classes from the editor", -> atom.packages.deactivatePackage('vim-mode') expect(editorElement.classList.contains("vim-mode")).toBe(false) - expect(editorElement.classList.contains("command-mode")).toBe(false) + expect(editorElement.classList.contains("normal-mode")).toBe(false) it "removes the vim commands from the editor element", -> vimCommands = -> diff --git a/atom/packages/vim-mode/spec/vim-state-spec.coffee b/atom/packages/vim-mode/spec/vim-state-spec.coffee index 741c371..8fd3fb0 100644 --- a/atom/packages/vim-mode/spec/vim-state-spec.coffee +++ b/atom/packages/vim-mode/spec/vim-state-spec.coffee @@ -14,20 +14,20 @@ describe "VimState", -> editorElement = element editor = editorElement.getModel() vimState = editorElement.vimState - vimState.activateCommandMode() - vimState.resetCommandMode() + vimState.activateNormalMode() + vimState.resetNormalMode() keydown = (key, options={}) -> options.element ?= editorElement helpers.keydown(key, options) - commandModeInputKeydown = (key, opts = {}) -> - editor.commandModeInputView.editorElement.getModel().setText(key) + normalModeInputKeydown = (key, opts = {}) -> + editor.normalModeInputView.editorElement.getModel().setText(key) describe "initialization", -> - it "puts the editor in command-mode initially by default", -> + it "puts the editor in normal-mode initially by default", -> expect(editorElement.classList.contains('vim-mode')).toBe(true) - expect(editorElement.classList.contains('command-mode')).toBe(true) + expect(editorElement.classList.contains('normal-mode')).toBe(true) it "puts the editor in insert-mode if startInInsertMode is true", -> atom.config.set 'vim-mode.startInInsertMode', true @@ -41,15 +41,15 @@ describe "VimState", -> expect(editorElement.component.isInputEnabled()).toBeTruthy() it "removes the mode classes from the editor", -> - expect(editorElement.classList.contains("command-mode")).toBeTruthy() + expect(editorElement.classList.contains("normal-mode")).toBeTruthy() vimState.destroy() - expect(editorElement.classList.contains("command-mode")).toBeFalsy() + expect(editorElement.classList.contains("normal-mode")).toBeFalsy() it "is a noop when the editor is already destroyed", -> editorElement.getModel().destroy() vimState.destroy() - describe "command-mode", -> + describe "normal-mode", -> describe "when entering an insertable character", -> beforeEach -> keydown('\\') @@ -86,12 +86,18 @@ describe "VimState", -> expect(editor.getCursors().length).toBe 1 describe "the v keybinding", -> - beforeEach -> keydown('v') + beforeEach -> + editor.setText("012345\nabcdef") + editor.setCursorScreenPosition([0, 0]) + keydown('v') it "puts the editor into visual characterwise mode", -> expect(editorElement.classList.contains('visual-mode')).toBe(true) expect(vimState.submode).toEqual 'characterwise' - expect(editorElement.classList.contains('command-mode')).toBe(false) + expect(editorElement.classList.contains('normal-mode')).toBe(false) + + it "selects the current character", -> + expect(editor.getLastSelection().getText()).toEqual '0' describe "the V keybinding", -> beforeEach -> @@ -102,33 +108,34 @@ describe "VimState", -> it "puts the editor into visual linewise mode", -> expect(editorElement.classList.contains('visual-mode')).toBe(true) expect(vimState.submode).toEqual 'linewise' - expect(editorElement.classList.contains('command-mode')).toBe(false) + expect(editorElement.classList.contains('normal-mode')).toBe(false) it "selects the current line", -> expect(editor.getLastSelection().getText()).toEqual '012345\n' describe "the ctrl-v keybinding", -> - beforeEach -> keydown('v', ctrl: true) + beforeEach -> + editor.setText("012345\nabcdef") + editor.setCursorScreenPosition([0, 0]) + keydown('v', ctrl: true) - it "puts the editor into visual characterwise mode", -> + it "puts the editor into visual blockwise mode", -> expect(editorElement.classList.contains('visual-mode')).toBe(true) expect(vimState.submode).toEqual 'blockwise' - expect(editorElement.classList.contains('command-mode')).toBe(false) + expect(editorElement.classList.contains('normal-mode')).toBe(false) describe "selecting text", -> beforeEach -> - spyOn(_._, "now").andCallFake -> window.now editor.setText("abc def") + editor.setCursorScreenPosition([0, 0]) it "puts the editor into visual mode", -> - expect(vimState.mode).toEqual 'command' - editor.setSelectedBufferRanges([[[0, 0], [0, 3]]]) - - advanceClock(100) + expect(vimState.mode).toEqual 'normal' + atom.commands.dispatch(editorElement, "core:select-right") expect(vimState.mode).toEqual 'visual' expect(vimState.submode).toEqual 'characterwise' - expect(editor.getSelectedBufferRanges()).toEqual([[[0, 0], [0, 3]]]) + expect(editor.getSelectedBufferRanges()).toEqual([[[0, 0], [0, 1]]]) it "handles the editor being destroyed shortly after selecting text", -> editor.setSelectedBufferRanges([[[0, 0], [0, 3]]]) @@ -136,27 +143,41 @@ describe "VimState", -> vimState.destroy() advanceClock(100) + it "handles native selection such as core:select-all", -> + atom.commands.dispatch(editorElement, "core:select-all") + expect(editor.getSelectedBufferRanges()).toEqual([[[0, 0], [0, 7]]]) + describe "the i keybinding", -> beforeEach -> keydown('i') it "puts the editor into insert mode", -> expect(editorElement.classList.contains('insert-mode')).toBe(true) - expect(editorElement.classList.contains('command-mode')).toBe(false) + expect(editorElement.classList.contains('normal-mode')).toBe(false) - describe "with content", -> - beforeEach -> editor.setText("012345\n\nabcdef") + describe "the R keybinding", -> + beforeEach -> keydown('R', shift: true) - # FIXME: See atom/vim-mode#2 - xdescribe "on a line with content", -> - beforeEach -> editor.setCursorScreenPosition([0, 6]) + it "puts the editor into replace mode", -> + expect(editorElement.classList.contains('insert-mode')).toBe(true) + expect(editorElement.classList.contains('replace-mode')).toBe(true) + expect(editorElement.classList.contains('normal-mode')).toBe(false) - it "does not allow the cursor to be placed on the \n character", -> + describe "with content", -> + beforeEach -> + editor.setText("012345\n\nabcdef") + editor.setCursorScreenPosition([0, 0]) + + describe "on a line with content", -> + it "does not allow atom commands to place the cursor on the \\n character", -> + atom.commands.dispatch(editorElement, "editor:move-to-end-of-line") expect(editor.getCursorScreenPosition()).toEqual [0, 5] describe "on an empty line", -> - beforeEach -> editor.setCursorScreenPosition([1, 0]) + beforeEach -> + editor.setCursorScreenPosition([1, 0]) + atom.commands.dispatch(editorElement, "editor:move-to-end-of-line") - it "allows the cursor to be placed on the \n character", -> + it "allows the cursor to be placed on the \\n character", -> expect(editor.getCursorScreenPosition()).toEqual [1, 0] describe 'with character-input operations', -> @@ -165,9 +186,9 @@ describe "VimState", -> it 'properly clears the opStack', -> keydown('d') keydown('r') - expect(vimState.mode).toBe 'command' + expect(vimState.mode).toBe 'normal' expect(vimState.opStack.length).toBe 0 - atom.commands.dispatch(editor.commandModeInputView.editorElement, "core:cancel") + atom.commands.dispatch(editor.normalModeInputView.editorElement, "core:cancel") keydown('d') expect(editor.getText()).toBe '012345\nabcdef' @@ -193,25 +214,78 @@ describe "VimState", -> expect(editor.getCursorScreenPosition()).toEqual [1, 0] describe "on a line with content", -> - beforeEach -> editor.setCursorScreenPosition([0, 6]) + beforeEach -> + editor.setCursorScreenPosition([0, 0]) + atom.commands.dispatch(editorElement, "editor:move-to-end-of-line") + + it "allows the cursor to be placed on the \\n character", -> + expect(editor.getCursorScreenPosition()).toEqual [0, 6] + + it "puts the editor into normal mode when <escape> is pressed", -> + keydown('escape') + + expect(editorElement.classList.contains('normal-mode')).toBe(true) + expect(editorElement.classList.contains('insert-mode')).toBe(false) + expect(editorElement.classList.contains('visual-mode')).toBe(false) + + it "puts the editor into normal mode when <ctrl-c> is pressed", -> + helpers.mockPlatform(editorElement, 'platform-darwin') + keydown('c', ctrl: true) + helpers.unmockPlatform(editorElement) + + expect(editorElement.classList.contains('normal-mode')).toBe(true) + expect(editorElement.classList.contains('insert-mode')).toBe(false) + expect(editorElement.classList.contains('visual-mode')).toBe(false) + + describe "replace-mode", -> + describe "with content", -> + beforeEach -> editor.setText("012345\n\nabcdef") + + describe "when cursor is in the middle of the line", -> + beforeEach -> + editor.setCursorScreenPosition([0, 3]) + keydown('R', shift: true) + + it "moves the cursor to the left when exiting replace mode", -> + keydown('escape') + expect(editor.getCursorScreenPosition()).toEqual [0, 2] + + describe "when cursor is at the beginning of line", -> + beforeEach -> + editor.setCursorScreenPosition([1, 0]) + keydown('R', shift: true) + + it "leaves the cursor at the beginning of line", -> + keydown('escape') + expect(editor.getCursorScreenPosition()).toEqual [1, 0] + + describe "on a line with content", -> + beforeEach -> + keydown('R', shift: true) + editor.setCursorScreenPosition([0, 0]) + atom.commands.dispatch(editorElement, "editor:move-to-end-of-line") - it "allows the cursor to be placed on the \n character", -> + it "allows the cursor to be placed on the \\n character", -> expect(editor.getCursorScreenPosition()).toEqual [0, 6] - it "puts the editor into command mode when <escape> is pressed", -> + it "puts the editor into normal mode when <escape> is pressed", -> + keydown('R', shift: true) keydown('escape') - expect(editorElement.classList.contains('command-mode')).toBe(true) + expect(editorElement.classList.contains('normal-mode')).toBe(true) expect(editorElement.classList.contains('insert-mode')).toBe(false) + expect(editorElement.classList.contains('replace-mode')).toBe(false) expect(editorElement.classList.contains('visual-mode')).toBe(false) - it "puts the editor into command mode when <ctrl-c> is pressed", -> + it "puts the editor into normal mode when <ctrl-c> is pressed", -> + keydown('R', shift: true) helpers.mockPlatform(editorElement, 'platform-darwin') keydown('c', ctrl: true) helpers.unmockPlatform(editorElement) - expect(editorElement.classList.contains('command-mode')).toBe(true) + expect(editorElement.classList.contains('normal-mode')).toBe(true) expect(editorElement.classList.contains('insert-mode')).toBe(false) + expect(editorElement.classList.contains('replace-mode')).toBe(false) expect(editorElement.classList.contains('visual-mode')).toBe(false) describe "visual-mode", -> @@ -224,21 +298,21 @@ describe "VimState", -> expect(editor.getSelectedBufferRanges()).toEqual [[[0, 4], [0, 5]]] expect(editor.getSelectedText()).toBe("t") - it "puts the editor into command mode when <escape> is pressed", -> + it "puts the editor into normal mode when <escape> is pressed", -> keydown('escape') expect(editor.getCursorBufferPositions()).toEqual [[0, 4]] - expect(editorElement.classList.contains('command-mode')).toBe(true) + expect(editorElement.classList.contains('normal-mode')).toBe(true) expect(editorElement.classList.contains('visual-mode')).toBe(false) - it "puts the editor into command mode when <escape> is pressed on selection is reversed", -> + it "puts the editor into normal mode when <escape> is pressed on selection is reversed", -> expect(editor.getSelectedText()).toBe("t") keydown("h") keydown("h") expect(editor.getSelectedText()).toBe("e t") expect(editor.getLastSelection().isReversed()).toBe(true) keydown('escape') - expect(editorElement.classList.contains('command-mode')).toBe(true) + expect(editorElement.classList.contains('normal-mode')).toBe(true) expect(editor.getCursorBufferPositions()).toEqual [[0, 2]] describe "motions", -> @@ -254,7 +328,6 @@ describe "VimState", -> expect(editor.getSelectedText()).toBe("t") keydown("l") - keydown("l") expect(editor.getSelectedText()).toBe("tw") describe "operators", -> @@ -267,7 +340,7 @@ describe "VimState", -> it "operate on the current selection", -> expect(editor.getText()).toEqual "\nabcdef" - describe "returning to command-mode", -> + describe "returning to normal-mode", -> beforeEach -> editor.setText("012345\n\nabcdef") editor.selectLinesContainingCursors() @@ -302,39 +375,65 @@ describe "VimState", -> [0, 8] ]) + it "harmonizes selection directions", -> + keydown("e") + editor.addCursorAtBufferPosition([0, Infinity]) + keydown("h") + keydown("h") + + expect(editor.getSelectedBufferRanges()).toEqual([ + [[0, 4], [0, 5]], + [[0, 11], [0, 13]] + ]) + expect(editor.getCursorBufferPositions()).toEqual([ + [0, 5] + [0, 11] + ]) + + keydown("o") + + expect(editor.getSelectedBufferRanges()).toEqual([ + [[0, 4], [0, 5]], + [[0, 11], [0, 13]] + ]) + expect(editor.getCursorBufferPositions()).toEqual([ + [0, 5] + [0, 13] + ]) + describe "activate visualmode witin visualmode", -> beforeEach -> keydown('escape') - expect(vimState.mode).toEqual 'command' - expect(editorElement.classList.contains('command-mode')).toBe(true) + expect(vimState.mode).toEqual 'normal' + expect(editorElement.classList.contains('normal-mode')).toBe(true) - it "activateVisualMode with same type puts the editor into command mode", -> + it "activateVisualMode with same type puts the editor into normal mode", -> keydown('v') expect(editorElement.classList.contains('visual-mode')).toBe(true) expect(vimState.submode).toEqual 'characterwise' - expect(editorElement.classList.contains('command-mode')).toBe(false) + expect(editorElement.classList.contains('normal-mode')).toBe(false) keydown('v') - expect(vimState.mode).toEqual 'command' - expect(editorElement.classList.contains('command-mode')).toBe(true) + expect(vimState.mode).toEqual 'normal' + expect(editorElement.classList.contains('normal-mode')).toBe(true) keydown('V', shift: true) expect(editorElement.classList.contains('visual-mode')).toBe(true) expect(vimState.submode).toEqual 'linewise' - expect(editorElement.classList.contains('command-mode')).toBe(false) + expect(editorElement.classList.contains('normal-mode')).toBe(false) keydown('V', shift: true) - expect(vimState.mode).toEqual 'command' - expect(editorElement.classList.contains('command-mode')).toBe(true) + expect(vimState.mode).toEqual 'normal' + expect(editorElement.classList.contains('normal-mode')).toBe(true) keydown('v', ctrl: true) expect(editorElement.classList.contains('visual-mode')).toBe(true) expect(vimState.submode).toEqual 'blockwise' - expect(editorElement.classList.contains('command-mode')).toBe(false) + expect(editorElement.classList.contains('normal-mode')).toBe(false) keydown('v', ctrl: true) - expect(vimState.mode).toEqual 'command' - expect(editorElement.classList.contains('command-mode')).toBe(true) + expect(vimState.mode).toEqual 'normal' + expect(editorElement.classList.contains('normal-mode')).toBe(true) describe "change submode within visualmode", -> beforeEach -> @@ -346,22 +445,22 @@ describe "VimState", -> keydown('v') expect(editorElement.classList.contains('visual-mode')).toBe(true) expect(vimState.submode).toEqual 'characterwise' - expect(editorElement.classList.contains('command-mode')).toBe(false) + expect(editorElement.classList.contains('normal-mode')).toBe(false) keydown('V', shift: true) expect(editorElement.classList.contains('visual-mode')).toBe(true) expect(vimState.submode).toEqual 'linewise' - expect(editorElement.classList.contains('command-mode')).toBe(false) + expect(editorElement.classList.contains('normal-mode')).toBe(false) keydown('v', ctrl: true) expect(editorElement.classList.contains('visual-mode')).toBe(true) expect(vimState.submode).toEqual 'blockwise' - expect(editorElement.classList.contains('command-mode')).toBe(false) + expect(editorElement.classList.contains('normal-mode')).toBe(false) keydown('v') expect(editorElement.classList.contains('visual-mode')).toBe(true) expect(vimState.submode).toEqual 'characterwise' - expect(editorElement.classList.contains('command-mode')).toBe(false) + expect(editorElement.classList.contains('normal-mode')).toBe(false) it "recover original range when shift from linewse to characterwise", -> @@ -389,32 +488,32 @@ describe "VimState", -> it "basic marking functionality", -> editor.setCursorScreenPosition([1, 1]) keydown('m') - commandModeInputKeydown('t') + normalModeInputKeydown('t') expect(editor.getText()).toEqual "text in line 1\ntext in line 2\ntext in line 3" editor.setCursorScreenPosition([2, 2]) keydown('`') - commandModeInputKeydown('t') + normalModeInputKeydown('t') expect(editor.getCursorScreenPosition()).toEqual [1, 1] it "real (tracking) marking functionality", -> editor.setCursorScreenPosition([2, 2]) keydown('m') - commandModeInputKeydown('q') + normalModeInputKeydown('q') editor.setCursorScreenPosition([1, 2]) keydown('o') keydown('escape') keydown('`') - commandModeInputKeydown('q') + normalModeInputKeydown('q') expect(editor.getCursorScreenPosition()).toEqual [3, 2] it "real (tracking) marking functionality", -> editor.setCursorScreenPosition([2, 2]) keydown('m') - commandModeInputKeydown('q') + normalModeInputKeydown('q') editor.setCursorScreenPosition([1, 2]) keydown('d') keydown('d') keydown('escape') keydown('`') - commandModeInputKeydown('q') + normalModeInputKeydown('q') expect(editor.getCursorScreenPosition()).toEqual [1, 2] |