helpers = require './spec-helper' describe "Motions", -> [editor, editorView, vimState] = [] beforeEach -> vimMode = atom.packages.loadPackage('vim-mode') vimMode.activateResources() editorView = helpers.cacheEditor(editorView) editor = editorView.editor vimState = editorView.vimState vimState.activateCommandMode() vimState.resetCommandMode() keydown = (key, options={}) -> options.element ?= editorView[0] helpers.keydown(key, options) commandModeInputKeydown = (key, opts = {}) -> opts.element = editor.commandModeInputView.editor.find('input').get(0) opts.raw = true keydown(key, opts) describe "simple motions", -> beforeEach -> editor.setText("12345\nabcd\nABCDE") editor.setCursorScreenPosition([1, 1]) describe "the h keybinding", -> describe "as a motion", -> it "moves the cursor left, but not to the previous line", -> keydown('h') expect(editor.getCursorScreenPosition()).toEqual [1, 0] keydown('h') expect(editor.getCursorScreenPosition()).toEqual [1, 0] describe "as a selection", -> it "selects the character to the left", -> keydown('y') keydown('h') expect(vimState.getRegister('"').text).toBe 'a' expect(editor.getCursorScreenPosition()).toEqual [1, 0] describe "the j keybinding", -> it "moves the cursor down, but not to the end of the last line", -> keydown('j') expect(editor.getCursorScreenPosition()).toEqual [2, 1] keydown('j') expect(editor.getCursorScreenPosition()).toEqual [2, 1] it "moves the cursor to the end of the line, not past it", -> editor.setCursorScreenPosition([0,4]) keydown('j') expect(editor.getCursorScreenPosition()).toEqual [1, 3] it "remembers the position it column it was in after moving to shorter line", -> editor.setCursorScreenPosition([0,4]) keydown('j') expect(editor.getCursorScreenPosition()).toEqual [1, 3] keydown('j') expect(editor.getCursorScreenPosition()).toEqual [2, 4] describe "when visual mode", -> beforeEach -> keydown('v') it "moves the cursor down", -> keydown('j') expect(editor.getCursorScreenPosition()).toEqual [2, 1] it "don't go over after the last line", -> keydown('j') expect(editor.getCursorScreenPosition()).toEqual [2, 1] it "selects the text while moving", -> keydown('j') expect(editor.getSelectedText()).toBe "bcd\nA" describe "the k keybinding", -> it "moves the cursor up, but not to the beginning of the first line", -> keydown('k') expect(editor.getCursorScreenPosition()).toEqual [0, 1] keydown('k') expect(editor.getCursorScreenPosition()).toEqual [0, 1] describe "the l keybinding", -> beforeEach -> editor.setCursorScreenPosition([1, 2]) it "moves the cursor right, but not to the next line", -> keydown('l') expect(editor.getCursorScreenPosition()).toEqual [1, 3] keydown('l') expect(editor.getCursorScreenPosition()).toEqual [1, 3] describe "the w keybinding", -> beforeEach -> editor.setText("ab cde1+- \n xyz\n\nzip") describe "as a motion", -> beforeEach -> editor.setCursorScreenPosition([0, 0]) it "moves the cursor to the beginning of the next word", -> keydown('w') expect(editor.getCursorScreenPosition()).toEqual [0, 3] keydown('w') expect(editor.getCursorScreenPosition()).toEqual [0, 7] keydown('w') expect(editor.getCursorScreenPosition()).toEqual [1, 1] 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] # After cursor gets to the EOF, it should stay there. keydown('w') expect(editor.getCursorScreenPosition()).toEqual [3, 3] 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]) describe "as a selection", -> describe "within a word", -> beforeEach -> editor.setCursorScreenPosition([0, 0]) keydown('y') keydown('w') it "selects to the end of the word", -> expect(vimState.getRegister('"').text).toBe 'ab ' describe "between words", -> beforeEach -> editor.setCursorScreenPosition([0, 2]) keydown('y') keydown('w') it "selects the whitespace", -> expect(vimState.getRegister('"').text).toBe ' ' describe "the W keybinding", -> beforeEach -> editor.setText("cde1+- ab \n xyz\n\nzip") describe "as a motion", -> beforeEach -> editor.setCursorScreenPosition([0, 0]) it "moves the cursor to the beginning of the next word", -> keydown('W', shift:true) expect(editor.getCursorScreenPosition()).toEqual [0, 7] keydown('W', shift:true) expect(editor.getCursorScreenPosition()).toEqual [1, 1] keydown('W', shift:true) expect(editor.getCursorScreenPosition()).toEqual [2, 0] keydown('W', shift:true) expect(editor.getCursorScreenPosition()).toEqual [2, 0] describe "as a selection", -> describe "within a word", -> it "selects to the end of the whole word", -> editor.setCursorScreenPosition([0, 0]) keydown('y') keydown('W', shift:true) expect(vimState.getRegister('"').text).toBe 'cde1+- ' it "doesn't go past the end of the file", -> editor.setCursorScreenPosition([2, 0]) keydown('y') keydown('W', shift:true) expect(vimState.getRegister('"').text).toBe '' describe "the e keybinding", -> beforeEach -> editor.setText("ab cde1+- \n xyz\n\nzip") describe "as a motion", -> beforeEach -> editor.setCursorScreenPosition([0, 0]) it "moves the cursor to the end of the current word", -> keydown('e') expect(editor.getCursorScreenPosition()).toEqual [0, 1] keydown('e') expect(editor.getCursorScreenPosition()).toEqual [0, 6] keydown('e') expect(editor.getCursorScreenPosition()).toEqual [0, 8] keydown('e') expect(editor.getCursorScreenPosition()).toEqual [1, 3] # INCOMPATIBILITY: vim doesn't stop at [2, 0] it advances immediately # to [3, 2] keydown('e') expect(editor.getCursorScreenPosition()).toEqual [2, 0] keydown('e') expect(editor.getCursorScreenPosition()).toEqual [3, 2] describe "as selection", -> describe "within a word", -> beforeEach -> editor.setCursorScreenPosition([0, 0]) keydown('y') keydown('e') it "selects to the end of the current word", -> expect(vimState.getRegister('"').text).toBe 'ab' describe "between words", -> beforeEach -> editor.setCursorScreenPosition([0, 2]) keydown('y') keydown('e') it "selects to the end of the next word", -> expect(vimState.getRegister('"').text).toBe ' cde1' describe "the E keybinding", -> beforeEach -> editor.setText("ab cde1+- \n xyz \n\nzip\n") describe "as a motion", -> beforeEach -> editor.setCursorScreenPosition([0, 0]) it "moves the cursor to the end of the current word", -> keydown('E', shift: true) expect(editor.getCursorScreenPosition()).toEqual [0, 1] keydown('E', shift: true) expect(editor.getCursorScreenPosition()).toEqual [0, 9] keydown('E', shift: true) expect(editor.getCursorScreenPosition()).toEqual [1, 3] keydown('E', shift: true) expect(editor.getCursorScreenPosition()).toEqual [3, 2] keydown('E', shift: true) expect(editor.getCursorScreenPosition()).toEqual [4, 0] describe "as selection", -> describe "within a word", -> beforeEach -> editor.setCursorScreenPosition([0, 0]) keydown('y') keydown('E', shift: true) it "selects to the end of the current word", -> expect(vimState.getRegister('"').text).toBe 'ab' describe "between words", -> beforeEach -> editor.setCursorScreenPosition([0, 2]) keydown('y') keydown('E', shift: true) it "selects to the end of the next word", -> expect(vimState.getRegister('"').text).toBe ' cde1+-' describe "press more than once", -> beforeEach -> editor.setCursorScreenPosition([0, 0]) keydown('v') keydown('E', shift: true) keydown('E', shift: true) keydown('y') it "selects to the end of the current word", -> expect(vimState.getRegister('"').text).toBe 'ab cde1+-' describe "the } keybinding", -> beforeEach -> editor.setText("abcde\n\nfghij\nhijk\n xyz \n\nzip\n\n \nthe end") editor.setCursorScreenPosition([0, 0]) describe "as a motion", -> it "moves the cursor to the end of the paragraph", -> keydown('}') expect(editor.getCursorScreenPosition()).toEqual [1, 0] keydown('}') expect(editor.getCursorScreenPosition()).toEqual [5, 0] keydown('}') expect(editor.getCursorScreenPosition()).toEqual [7, 0] keydown('}') expect(editor.getCursorScreenPosition()).toEqual [9, 6] describe "as a selection", -> beforeEach -> keydown('y') keydown('}') it 'selects to the end of the current paragraph', -> expect(vimState.getRegister('"').text).toBe "abcde\n" describe "the { keybinding", -> beforeEach -> editor.setText("abcde\n\nfghij\nhijk\n xyz \n\nzip\n\n \nthe end") editor.setCursorScreenPosition([9, 0]) describe "as a motion", -> it "moves the cursor to the beginning of the paragraph", -> keydown('{') expect(editor.getCursorScreenPosition()).toEqual [7, 0] keydown('{') expect(editor.getCursorScreenPosition()).toEqual [5, 0] keydown('{') expect(editor.getCursorScreenPosition()).toEqual [1, 0] keydown('{') expect(editor.getCursorScreenPosition()).toEqual [0, 0] describe "as a selection", -> beforeEach -> editor.setCursorScreenPosition([7, 0]) keydown('y') keydown('{') it 'selects to the beginning of the current paragraph', -> expect(vimState.getRegister('"').text).toBe "\nzip\n" describe "the b keybinding", -> beforeEach -> editor.setText(" ab cde1+- \n xyz\n\nzip }\n last") describe "as a motion", -> beforeEach -> editor.setCursorScreenPosition([4,1]) it "moves the cursor to the beginning of the previous word", -> keydown('b') expect(editor.getCursorScreenPosition()).toEqual [3, 4] keydown('b') expect(editor.getCursorScreenPosition()).toEqual [3, 0] keydown('b') expect(editor.getCursorScreenPosition()).toEqual [2, 0] keydown('b') expect(editor.getCursorScreenPosition()).toEqual [1, 1] keydown('b') expect(editor.getCursorScreenPosition()).toEqual [0, 8] keydown('b') expect(editor.getCursorScreenPosition()).toEqual [0, 4] keydown('b') expect(editor.getCursorScreenPosition()).toEqual [0, 1] # Go to start of the file, after moving past the first word keydown('b') expect(editor.getCursorScreenPosition()).toEqual [0, 0] # Stay at the start of the file keydown('b') expect(editor.getCursorScreenPosition()).toEqual [0, 0] describe "as a selection", -> describe "within a word", -> beforeEach -> editor.setCursorScreenPosition([0, 2]) keydown('y') keydown('b') it "selects to the beginning of the current word", -> expect(vimState.getRegister('"').text).toBe 'a' expect(editor.getCursorScreenPosition()).toEqual [0, 1] describe "between words", -> beforeEach -> editor.setCursorScreenPosition([0, 4]) keydown('y') keydown('b') it "selects to the beginning of the last word", -> expect(vimState.getRegister('"').text).toBe 'ab ' expect(editor.getCursorScreenPosition()).toEqual [0, 1] describe "the B keybinding", -> beforeEach -> editor.setText("cde1+- ab \n xyz-123\n\n zip") describe "as a motion", -> beforeEach -> editor.setCursorScreenPosition([4, 1]) it "moves the cursor to the beginning of the previous word", -> keydown('B', shift:true) expect(editor.getCursorScreenPosition()).toEqual [3, 1] keydown('B', shift:true) expect(editor.getCursorScreenPosition()).toEqual [2, 0] keydown('B', shift:true) expect(editor.getCursorScreenPosition()).toEqual [1, 1] keydown('B', shift:true) expect(editor.getCursorScreenPosition()).toEqual [0, 7] keydown('B', shift:true) expect(editor.getCursorScreenPosition()).toEqual [0, 0] describe "as a selection", -> it "selects to the beginning of the whole word", -> editor.setCursorScreenPosition([1, 8]) keydown('y') keydown('B', shift:true) expect(vimState.getRegister('"').text).toBe 'xyz-123' it "doesn't go past the beginning of the file", -> editor.setCursorScreenPosition([0, 0]) keydown('y') keydown('B', shift:true) expect(vimState.getRegister('"').text).toBe '' describe "the ^ keybinding", -> beforeEach -> editor.setText(" abcde") describe "from the beginning of the line", -> beforeEach -> editor.setCursorScreenPosition([0, 0]) describe "as a motion", -> beforeEach -> keydown('^') it "moves the cursor to the first character of the line", -> expect(editor.getCursorScreenPosition()).toEqual [0, 2] describe "as a selection", -> beforeEach -> keydown('d') keydown('^') it 'selects to the first character of the line', -> expect(editor.getText()).toBe 'abcde' expect(editor.getCursorScreenPosition()).toEqual [0, 0] describe "from the first character of the line", -> beforeEach -> editor.setCursorScreenPosition([0, 2]) describe "as a motion", -> beforeEach -> keydown('^') it "stays put", -> expect(editor.getCursorScreenPosition()).toEqual [0, 2] describe "as a selection", -> beforeEach -> keydown('d') keydown('^') it "does nothing", -> expect(editor.getText()).toBe ' abcde' expect(editor.getCursorScreenPosition()).toEqual [0, 2] describe "from the middle of a word", -> beforeEach -> editor.setCursorScreenPosition([0, 4]) describe "as a motion", -> beforeEach -> keydown('^') it "moves the cursor to the first character of the line", -> expect(editor.getCursorScreenPosition()).toEqual [0, 2] describe "as a selection", -> beforeEach -> keydown('d') keydown('^') it 'selects to the first character of the line', -> expect(editor.getText()).toBe ' cde' expect(editor.getCursorScreenPosition()).toEqual [0, 2] describe "the 0 keybinding", -> beforeEach -> editor.setText(" abcde") editor.setCursorScreenPosition([0, 4]) describe "as a motion", -> beforeEach -> keydown('0') it "moves the cursor to the first column", -> expect(editor.getCursorScreenPosition()).toEqual [0, 0] describe "as a selection", -> beforeEach -> keydown('d') keydown('0') it 'selects to the first column of the line', -> expect(editor.getText()).toBe 'cde' expect(editor.getCursorScreenPosition()).toEqual [0, 0] describe "the $ keybinding", -> beforeEach -> editor.setText(" abcde\n\n1234567890") editor.setCursorScreenPosition([0, 4]) describe "as a motion from empty line", -> beforeEach -> editor.setCursorScreenPosition([1, 0]) it "moves the cursor to the end of the line", -> expect(editor.getCursorScreenPosition()).toEqual [1, 0] describe "as a motion", -> beforeEach -> keydown('$') # FIXME: See atom/vim-mode#2 it "moves the cursor to the end of the line", -> expect(editor.getCursorScreenPosition()).toEqual [0, 6] it "should remain in the last column when moving down", -> keydown('j') expect(editor.getCursorScreenPosition()).toEqual [1, 0] keydown('j') expect(editor.getCursorScreenPosition()).toEqual [2, 9] describe "as a selection", -> beforeEach -> keydown('d') keydown('$') it "selects to the beginning of the lines", -> expect(editor.getText()).toBe " ab\n\n1234567890" expect(editor.getCursorScreenPosition()).toEqual [0, 3] # FIXME: this doesn't work as we can't determine if this is a motion # or part of a repeat prefix. xdescribe "the 0 keybinding", -> beforeEach -> editor.setText(" a\n") editor.setCursorScreenPosition([0, 2]) describe "as a motion", -> beforeEach -> keydown('0') it "moves the cursor to the beginning of the line", -> expect(editor.getCursorScreenPosition()).toEqual [0,0] describe "the - keybinding", -> beforeEach -> editor.setText("abcdefg\n abc\n abc\n") describe "from the middle of a line", -> beforeEach -> editor.setCursorScreenPosition([1, 3]) describe "as a motion", -> beforeEach -> keydown('-') it "moves the cursor to the first character of the previous line", -> expect(editor.getCursorScreenPosition()).toEqual [0, 0] describe "as a selection", -> beforeEach -> keydown('d') keydown('-') it "deletes the current and previous line", -> expect(editor.getText()).toBe " abc\n" # commented out because the column is wrong due to a bug in `k`; re-enable when `k` is fixed #expect(editor.getCursorScreenPosition()).toEqual [0, 3] describe "from the first character of a line indented the same as the previous one", -> beforeEach -> editor.setCursorScreenPosition([2, 2]) describe "as a motion", -> beforeEach -> keydown('-') it "moves to the first character of the previous line (directly above)", -> expect(editor.getCursorScreenPosition()).toEqual [1, 2] describe "as a selection", -> beforeEach -> keydown('d') keydown('-') it "selects to the first character of the previous line (directly above)", -> expect(editor.getText()).toBe "abcdefg\n" # commented out because the column is wrong due to a bug in `k`; re-enable when `k` is fixed #expect(editor.getCursorScreenPosition()).toEqual [0, 2] describe "from the beginning of a line preceded by an indented line", -> beforeEach -> editor.setCursorScreenPosition([2, 0]) describe "as a motion", -> beforeEach -> keydown('-') it "moves the cursor to the first character of the previous line", -> expect(editor.getCursorScreenPosition()).toEqual [1, 2] describe "as a selection", -> beforeEach -> keydown('d') keydown('-') it "selects to the first character of the previous line", -> expect(editor.getText()).toBe "abcdefg\n" # commented out because the column is wrong due to a bug in `k`; re-enable when `k` is fixed #expect(editor.getCursorScreenPosition()).toEqual [0, 0] describe "with a count", -> beforeEach -> editor.setText("1\n2\n3\n4\n5\n6\n") editor.setCursorScreenPosition([4, 0]) describe "as a motion", -> beforeEach -> keydown('3') keydown('-') it "moves the cursor to the first character of that many lines previous", -> expect(editor.getCursorScreenPosition()).toEqual [1, 0] describe "as a selection", -> beforeEach -> keydown('d') keydown('3') keydown('-') it "deletes the current line plus that many previous lines", -> expect(editor.getText()).toBe "1\n6\n" # commented out because the column is wrong due to a bug in `k`; re-enable when `k` is fixed #expect(editor.getCursorScreenPosition()).toEqual [1, 0] describe "the + keybinding", -> beforeEach -> editor.setText(" abc\n abc\nabcdefg\n") describe "from the middle of a line", -> beforeEach -> editor.setCursorScreenPosition([1, 3]) describe "as a motion", -> beforeEach -> keydown('+') it "moves the cursor to the first character of the next line", -> expect(editor.getCursorScreenPosition()).toEqual [2, 0] describe "as a selection", -> beforeEach -> keydown('d') keydown('+') it "deletes the current and next line", -> expect(editor.getText()).toBe " abc\n" # commented out because the column is wrong due to a bug in `j`; re-enable when `j` is fixed #expect(editor.getCursorScreenPosition()).toEqual [0, 3] describe "from the first character of a line indented the same as the next one", -> beforeEach -> editor.setCursorScreenPosition([0, 2]) describe "as a motion", -> beforeEach -> keydown('+') it "moves to the first character of the next line (directly below)", -> expect(editor.getCursorScreenPosition()).toEqual [1, 2] describe "as a selection", -> beforeEach -> keydown('d') keydown('+') it "selects to the first character of the next line (directly below)", -> expect(editor.getText()).toBe "abcdefg\n" # commented out because the column is wrong due to a bug in `j`; re-enable when `j` is fixed #expect(editor.getCursorScreenPosition()).toEqual [0, 2] describe "from the beginning of a line followed by an indented line", -> beforeEach -> editor.setCursorScreenPosition([0, 0]) describe "as a motion", -> beforeEach -> keydown('+') it "moves the cursor to the first character of the next line", -> expect(editor.getCursorScreenPosition()).toEqual [1, 2] describe "as a selection", -> beforeEach -> keydown('d') keydown('+') it "selects to the first character of the next line", -> expect(editor.getText()).toBe "abcdefg\n" # commented out because the column is wrong due to a bug in `j`; re-enable when `j` is fixed #expect(editor.getCursorScreenPosition()).toEqual [0, 0] describe "with a count", -> beforeEach -> editor.setText("1\n2\n3\n4\n5\n6\n") editor.setCursorScreenPosition([1, 0]) describe "as a motion", -> beforeEach -> keydown('3') keydown('+') it "moves the cursor to the first character of that many lines following", -> expect(editor.getCursorScreenPosition()).toEqual [4, 0] describe "as a selection", -> beforeEach -> keydown('d') keydown('3') keydown('+') it "deletes the current line plus that many following lines", -> expect(editor.getText()).toBe "1\n6\n" # commented out because the column is wrong due to a bug in `j`; re-enable when `j` is fixed #expect(editor.getCursorScreenPosition()).toEqual [1, 0] describe "the enter keybinding", -> keydownCodeForEnter = '\r' # 'enter' does not work startingText = " abc\n abc\nabcdefg\n" describe "from the middle of a line", -> startingCursorPosition = [1, 3] describe "as a motion", -> it "acts the same as the + keybinding", -> # do it with + and save the results editor.setText(startingText) editor.setCursorScreenPosition(startingCursorPosition) keydown('+') referenceCursorPosition = editor.getCursorScreenPosition() # do it again with enter and compare the results editor.setText(startingText) editor.setCursorScreenPosition(startingCursorPosition) keydown(keydownCodeForEnter) expect(editor.getCursorScreenPosition()).toEqual referenceCursorPosition describe "as a selection", -> it "acts the same as the + keybinding", -> # do it with + and save the results editor.setText(startingText) editor.setCursorScreenPosition(startingCursorPosition) keydown('d') keydown('+') referenceText = editor.getText() referenceCursorPosition = editor.getCursorScreenPosition() # do it again with enter and compare the results editor.setText(startingText) editor.setCursorScreenPosition(startingCursorPosition) keydown('d') keydown(keydownCodeForEnter) expect(editor.getText()).toEqual referenceText expect(editor.getCursorScreenPosition()).toEqual referenceCursorPosition describe "the gg keybinding", -> beforeEach -> editor.setText(" 1abc\n 2\n3\n") editor.setCursorScreenPosition([0, 2]) describe "as a motion", -> describe "in command mode", -> beforeEach -> keydown('g') keydown('g') it "moves the cursor to the beginning of the first line", -> expect(editor.getCursorScreenPosition()).toEqual [0, 1] describe "in linewise visual mode", -> beforeEach -> editor.setCursorScreenPosition([1, 0]) vimState.activateVisualMode('linewise') keydown('g') keydown('g') it "selects to the first line in the file", -> expect(editor.getSelectedText()).toBe " 1abc\n 2\n" it "moves the cursor to a specified line", -> expect(editor.getCursorScreenPosition()).toEqual [0, 0] describe "in characterwise visual mode", -> beforeEach -> editor.setCursorScreenPosition([1, 1]) vimState.activateVisualMode() keydown('g') keydown('g') it "selects to the first line in the file", -> expect(editor.getSelectedText()).toBe "1abc\n 2" it "moves the cursor to a specified line", -> expect(editor.getCursorScreenPosition()).toEqual [0, 1] describe "as a repeated motion", -> describe "in command mode", -> beforeEach -> keydown('2') keydown('g') keydown('g') it "moves the cursor to a specified line", -> expect(editor.getCursorScreenPosition()).toEqual [1, 1] describe "in linewise visual motion", -> beforeEach -> editor.setCursorScreenPosition([2, 0]) vimState.activateVisualMode('linewise') keydown('2') keydown('g') keydown('g') it "selects to a specified line", -> expect(editor.getSelectedText()).toBe " 2\n3\n" it "moves the cursor to a specified line", -> expect(editor.getCursorScreenPosition()).toEqual [1, 0] describe "in characterwise visual motion", -> beforeEach -> editor.setCursorScreenPosition([2, 0]) vimState.activateVisualMode() keydown('2') keydown('g') keydown('g') it "selects to a first character of specified line", -> expect(editor.getSelectedText()).toBe "2\n3" it "moves the cursor to a specified line", -> expect(editor.getCursorScreenPosition()).toEqual [1, 1] describe "the G keybinding", -> beforeEach -> editor.setText("1\n 2\n 3abc\n ") editor.setCursorScreenPosition([0, 2]) describe "as a motion", -> beforeEach -> keydown('G', shift: true) it "moves the cursor to the last line after whitespace", -> expect(editor.getCursorScreenPosition()).toEqual [3, 1] describe "as a repeated motion", -> beforeEach -> keydown('2') keydown('G', shift: true) it "moves the cursor to a specified line", -> expect(editor.getCursorScreenPosition()).toEqual [1, 4] describe "as a selection", -> beforeEach -> editor.setCursorScreenPosition([1, 0]) vimState.activateVisualMode() keydown('G', shift: true) it "selects to the last line in the file", -> expect(editor.getSelectedText()).toBe " 2\n 3abc\n " it "moves the cursor to the last line after whitespace", -> expect(editor.getCursorScreenPosition()).toEqual [3,1] describe "the / keybinding", -> beforeEach -> editor.setText("abc\ndef\nabc\ndef\n") editor.setCursorBufferPosition([0, 0]) describe "as a motion", -> it "moves the cursor to the specified search pattern", -> keydown('/') editor.commandModeInputView.editor.setText 'def' editor.commandModeInputView.editor.trigger 'core:confirm' expect(editor.getCursorBufferPosition()).toEqual [1, 0] it "loops back around", -> editor.setCursorBufferPosition([3, 0]) keydown('/') editor.commandModeInputView.editor.setText 'def' editor.commandModeInputView.editor.trigger 'core:confirm' expect(editor.getCursorBufferPosition()).toEqual [1, 0] it "uses a valid regex as a regex", -> keydown('/') # Cycle through the 'abc' on the first line with a character pattern editor.commandModeInputView.editor.setText '[abc]' editor.commandModeInputView.editor.trigger 'core:confirm' expect(editor.getCursorBufferPosition()).toEqual [0, 1] keydown('n') expect(editor.getCursorBufferPosition()).toEqual [0, 2] it "uses an invalid regex as a literal string", -> # Go straight to the literal [abc editor.setText("abc\n[abc]\n") keydown('/') editor.commandModeInputView.editor.setText '[abc' editor.commandModeInputView.editor.trigger 'core:confirm' expect(editor.getCursorBufferPosition()).toEqual [1, 0] keydown('n') expect(editor.getCursorBufferPosition()).toEqual [1, 0] it 'works with selection in visual mode', -> editor.setText('one two three') keydown('v') keydown('/') editor.commandModeInputView.editor.setText 'th' editor.commandModeInputView.editor.trigger 'core:confirm' expect(editor.getCursorBufferPosition()).toEqual [0, 8] keydown('d') expect(editor.getText()).toBe 'three' it 'extends selection when repeating search in visual mode', -> editor.setText('line1\nline2\nline3') keydown('v') keydown('/') editor.commandModeInputView.editor.setText 'line' editor.commandModeInputView.editor.trigger 'core:confirm' {start, end} = editor.getSelectedBufferRange() expect(start.row).toEqual 0 expect(end.row).toEqual 1 keydown('n') {start,end} = editor.getSelectedBufferRange() expect(start.row).toEqual 0 expect(end.row).toEqual 2 describe "repeating", -> it "does nothing with no search history", -> # This tests that no exception is raised keydown('n') beforeEach -> keydown('/') editor.commandModeInputView.editor.setText 'def' editor.commandModeInputView.editor.trigger 'core:confirm' describe "the n keybinding", -> it "repeats the last search", -> keydown('n') expect(editor.getCursorBufferPosition()).toEqual [3, 0] describe "the N keybinding", -> it "repeats the last search backwards", -> editor.setCursorBufferPosition([0, 0]) keydown('N', shift: true) expect(editor.getCursorBufferPosition()).toEqual [3, 0] keydown('N', shift: true) expect(editor.getCursorBufferPosition()).toEqual [1, 0] describe "composing", -> it "composes with operators", -> keydown('d') keydown('/') editor.commandModeInputView.editor.setText('def') editor.commandModeInputView.editor.trigger('core:confirm') expect(editor.getText()).toEqual "def\nabc\ndef\n" it "repeats correctly with operators", -> keydown('d') keydown('/') editor.commandModeInputView.editor.setText('def') editor.commandModeInputView.editor.trigger('core:confirm') keydown('.') expect(editor.getText()).toEqual "def\n" describe "when reversed as ?", -> it "moves the cursor backwards to the specified search pattern", -> keydown('?') editor.commandModeInputView.editor.setText('def') editor.commandModeInputView.editor.trigger('core:confirm') expect(editor.getCursorBufferPosition()).toEqual [3, 0] describe "repeating", -> beforeEach -> keydown('?') editor.commandModeInputView.editor.setText('def') editor.commandModeInputView.editor.trigger('core:confirm') describe 'the n keybinding', -> it "repeats the last search backwards", -> editor.setCursorBufferPosition([0, 0]) keydown('n') expect(editor.getCursorBufferPosition()).toEqual [3, 0] describe 'the N keybinding', -> it "repeats the last search forwards", -> editor.setCursorBufferPosition([0, 0]) keydown('N', shift: true) expect(editor.getCursorBufferPosition()).toEqual [1, 0] describe "using search history", -> beforeEach -> keydown('/') editor.commandModeInputView.editor.setText('def') editor.commandModeInputView.editor.trigger('core:confirm') expect(editor.getCursorBufferPosition()).toEqual [1, 0] keydown('/') editor.commandModeInputView.editor.setText('abc') editor.commandModeInputView.editor.trigger('core:confirm') expect(editor.getCursorBufferPosition()).toEqual [2, 0] it "allows searching history in the search field", -> keydown('/') editor.commandModeInputView.editor.trigger('core:move-up') expect(editor.commandModeInputView.editor.getText()).toEqual('abc') editor.commandModeInputView.editor.trigger('core:move-up') expect(editor.commandModeInputView.editor.getText()).toEqual('def') editor.commandModeInputView.editor.trigger('core:move-up') expect(editor.commandModeInputView.editor.getText()).toEqual('def') it "resets the search field to empty when scrolling back", -> keydown('/') editor.commandModeInputView.editor.trigger('core:move-up') expect(editor.commandModeInputView.editor.getText()).toEqual('abc') editor.commandModeInputView.editor.trigger('core:move-up') expect(editor.commandModeInputView.editor.getText()).toEqual('def') editor.commandModeInputView.editor.trigger('core:move-down') expect(editor.commandModeInputView.editor.getText()).toEqual('abc') editor.commandModeInputView.editor.trigger('core:move-down') expect(editor.commandModeInputView.editor.getText()).toEqual '' describe "the * keybinding", -> beforeEach -> editor.setText("abc\n@def\nabc\ndef\n") editor.setCursorBufferPosition([0, 0]) describe "as a motion", -> it "moves cursor to next occurence of word under cursor", -> keydown("*") expect(editor.getCursorBufferPosition()).toEqual [2, 0] it "doesn't move cursor unless next occurence is the exact word (no partial matches)", -> editor.setText("abc\ndef\nghiabc\njkl\nabcdef") editor.setCursorBufferPosition([0, 0]) keydown("*") expect(editor.getCursorBufferPosition()).toEqual [0, 0] describe "with words that contain 'non-word' characters", -> it "moves cursor to next occurence of word under cursor", -> editor.setText("abc\n@def\nabc\n@def\n") editor.setCursorBufferPosition([1, 0]) keydown("*") expect(editor.getCursorBufferPosition()).toEqual [3, 0] 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("*") expect(editor.getCursorBufferPosition()).toEqual [1, 0] # FIXME: This behavior is different from the one found in # vim. This is because the word boundary match in Javascript # ignores starting 'non-word' characters. # e.g. # in Vim: /\/.test("@def") => false # in Javascript: /\bdef\b/.test("@def") => true it "moves cursor to the start of valid word char", -> editor.setText("abc\ndef\nabc\n@def\n") editor.setCursorBufferPosition([1, 0]) keydown("*") expect(editor.getCursorBufferPosition()).toEqual [3, 1] describe "when cursor is on non-word char column", -> it "matches only the non-word char", -> editor.setText("abc\n@def\nabc\n@def\n") editor.setCursorBufferPosition([1, 0]) keydown("*") expect(editor.getCursorBufferPosition()).toEqual [3, 0] describe "when cursor is not on a word", -> it "does a match with the next word", -> editor.setText("abc\n @def\n abc\n @def") editor.setCursorBufferPosition([1, 0]) keydown("*") expect(editor.getCursorBufferPosition()).toEqual [3, 1] describe "when cursor is at EOF", -> it "doesn't try to do any match", -> editor.setText("abc\n@def\nabc\n ") editor.setCursorBufferPosition([3, 0]) keydown("*") expect(editor.getCursorBufferPosition()).toEqual [3, 1] describe "the hash keybinding", -> describe "as a motion", -> it "moves cursor to next occurence of word under cursor", -> editor.setText("abc\n@def\nabc\ndef\n") editor.setCursorBufferPosition([2, 0]) keydown("#") expect(editor.getCursorBufferPosition()).toEqual [0, 0] it "doesn't move cursor unless next occurence is the exact word (no partial matches)", -> editor.setText("abc\ndef\nghiabc\njkl\nabcdef") editor.setCursorBufferPosition([0, 0]) keydown("#") expect(editor.getCursorBufferPosition()).toEqual [0, 0] describe "with words that containt 'non-word' characters", -> it "moves cursor to next occurence of word under cursor", -> editor.setText("abc\n@def\nabc\n@def\n") editor.setCursorBufferPosition([3, 0]) keydown("#") expect(editor.getCursorBufferPosition()).toEqual [1, 0] it "moves cursor to the start of valid word char", -> editor.setText("abc\n@def\nabc\ndef\n") editor.setCursorBufferPosition([3, 0]) keydown("#") expect(editor.getCursorBufferPosition()).toEqual [1, 1] describe "when cursor is on non-word char column", -> it "matches only the non-word char", -> editor.setText("abc\n@def\nabc\n@def\n") editor.setCursorBufferPosition([1, 0]) keydown("*") expect(editor.getCursorBufferPosition()).toEqual [3, 0] describe "the H keybinding", -> beforeEach -> editor.setText("1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n") editor.setCursorScreenPosition([8, 0]) spyOn(editor, 'setCursorScreenPosition') it "moves the cursor to the first row if visible", -> spyOn(editorView, 'getFirstVisibleScreenRow').andReturn(0) keydown('H', shift: true) expect(editor.setCursorScreenPosition).toHaveBeenCalledWith([0, 0]) it "moves the cursor to the first visible row plus offset", -> spyOn(editorView, 'getFirstVisibleScreenRow').andReturn(2) keydown('H', shift: true) expect(editor.setCursorScreenPosition).toHaveBeenCalledWith([4, 0]) it "respects counts", -> spyOn(editorView, 'getFirstVisibleScreenRow').andReturn(0) keydown('3') keydown('H', shift: true) expect(editor.setCursorScreenPosition).toHaveBeenCalledWith([2, 0]) describe "the L keybinding", -> beforeEach -> editor.setText("1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n") editor.setCursorScreenPosition([8, 0]) spyOn(editor, 'setCursorScreenPosition') it "moves the cursor to the first row if visible", -> spyOn(editorView, 'getLastVisibleScreenRow').andReturn(10) keydown('L', shift: true) expect(editor.setCursorScreenPosition).toHaveBeenCalledWith([10, 0]) it "moves the cursor to the first visible row plus offset", -> spyOn(editorView, 'getLastVisibleScreenRow').andReturn(6) keydown('L', shift: true) expect(editor.setCursorScreenPosition).toHaveBeenCalledWith([4, 0]) it "respects counts", -> spyOn(editorView, 'getLastVisibleScreenRow').andReturn(10) keydown('3') keydown('L', shift: true) expect(editor.setCursorScreenPosition).toHaveBeenCalledWith([8, 0]) describe "the M keybinding", -> beforeEach -> editor.setText("1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n") editor.setCursorScreenPosition([8, 0]) spyOn(editor, 'setCursorScreenPosition') spyOn(editorView, 'getLastVisibleScreenRow').andReturn(10) spyOn(editorView, 'getFirstVisibleScreenRow').andReturn(0) it "moves the cursor to the first row if visible", -> keydown('M', shift: true) expect(editor.setCursorScreenPosition).toHaveBeenCalledWith([5, 0]) describe 'the mark keybindings', -> beforeEach -> editor.setText(' 12\n 34\n56\n') editor.setCursorBufferPosition([0,1]) it 'moves to the beginning of the line of a mark', -> editor.setCursorBufferPosition([1,1]) keydown('m') commandModeInputKeydown('a') editor.setCursorBufferPosition([0,0]) keydown('\'') commandModeInputKeydown('a') expect(editor.getCursorBufferPosition()).toEqual [1,4] it 'moves literally to a mark', -> editor.setCursorBufferPosition([1,1]) keydown('m') commandModeInputKeydown('a') editor.setCursorBufferPosition([0,0]) keydown('`') commandModeInputKeydown('a') expect(editor.getCursorBufferPosition()).toEqual [1,1] it 'deletes to a mark by line', -> editor.setCursorBufferPosition([1,5]) keydown('m') commandModeInputKeydown('a') editor.setCursorBufferPosition([0,0]) keydown('d') keydown('\'') commandModeInputKeydown('a') expect(editor.getText()).toEqual '\n56\n' it 'deletes before to a mark literally', -> editor.setCursorBufferPosition([1,5]) keydown('m') commandModeInputKeydown('a') editor.setCursorBufferPosition([0,1]) keydown('d') keydown('`') commandModeInputKeydown('a') expect(editor.getText()).toEqual ' 4\n56\n' it 'deletes after to a mark literally', -> editor.setCursorBufferPosition([1,5]) keydown('m') commandModeInputKeydown('a') editor.setCursorBufferPosition([2,1]) keydown('d') keydown('`') commandModeInputKeydown('a') expect(editor.getText()).toEqual ' 12\n 36\n' it 'moves back to previous', -> editor.setCursorBufferPosition([1,5]) keydown('`') commandModeInputKeydown('`') editor.setCursorBufferPosition([2,1]) keydown('`') commandModeInputKeydown('`') expect(editor.getCursorBufferPosition()).toEqual [1,5] describe 'the f/F keybindings', -> beforeEach -> editor.setText("abcabcabcabc\n") editor.setCursorScreenPosition([0, 0]) it 'moves to the first specified character it finds', -> keydown('f') commandModeInputKeydown('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') expect(editor.getCursorScreenPosition()).toEqual [0, 0] it 'respects count forward', -> keydown('2') keydown('f') commandModeInputKeydown('a') expect(editor.getCursorScreenPosition()).toEqual [0, 6] it 'respects count backward', -> editor.setCursorScreenPosition([0, 6]) keydown('2') keydown('F', shift: true) commandModeInputKeydown('a') expect(editor.getCursorScreenPosition()).toEqual [0, 0] it "doesn't move if the character specified isn't found", -> keydown('f') commandModeInputKeydown('d') expect(editor.getCursorScreenPosition()).toEqual [0, 0] it "doesn't move if there aren't the specified count of the specified character", -> keydown('1') keydown('0') keydown('f') commandModeInputKeydown('a') expect(editor.getCursorScreenPosition()).toEqual [0, 0] it "composes with d", -> editor.setCursorScreenPosition([0,3]) keydown('d') keydown('2') keydown('f') commandModeInputKeydown('a') expect(editor.getText()).toEqual 'abcbc\n' describe 'the t/T keybindings', -> beforeEach -> editor.setText("abcabcabcabc\n") editor.setCursorScreenPosition([0, 0]) it 'moves to the character previous to the first specified character it finds', -> keydown('t') commandModeInputKeydown('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') expect(editor.getCursorScreenPosition()).toEqual [0, 1] it 'respects count forward', -> keydown('2') keydown('t') commandModeInputKeydown('a') expect(editor.getCursorScreenPosition()).toEqual [0, 5] it 'respects count backward', -> editor.setCursorScreenPosition([0, 6]) keydown('2') keydown('T', shift: true) commandModeInputKeydown('a') expect(editor.getCursorScreenPosition()).toEqual [0, 1] it "doesn't move if the character specified isn't found", -> keydown('t') commandModeInputKeydown('d') expect(editor.getCursorScreenPosition()).toEqual [0, 0] it "doesn't move if there aren't the specified count of the specified character", -> keydown('1') keydown('0') keydown('t') commandModeInputKeydown('a') expect(editor.getCursorScreenPosition()).toEqual [0, 0] it "composes with d", -> editor.setCursorScreenPosition([0,3]) keydown('d') keydown('2') keydown('t') commandModeInputKeydown('a') expect(editor.getText()).toEqual 'abcabc\n' describe 'the % motion', -> beforeEach -> editor.setText("( ( ) )--{ text in here; and a function call(with parameters) }\n") editor.setCursorScreenPosition([0, 0]) it 'matches the correct parenthesis', -> keydown('%') expect(editor.getCursorScreenPosition()).toEqual [0, 6] it 'matches the correct brace', -> editor.setCursorScreenPosition([0, 9]) keydown('%') expect(editor.getCursorScreenPosition()).toEqual [0, 62] it 'composes correctly with d', -> editor.setCursorScreenPosition([0, 9]) keydown('d') keydown('%') expect(editor.getText()).toEqual "( ( ) )--\n" it 'moves correctly when composed with v going forward', -> keydown('v') keydown('%') expect(editor.getCursorScreenPosition()).toEqual [0, 0] it 'moves correctly when composed with v going backward', -> editor.setCursorScreenPosition([0, 6]) keydown('v') keydown('%') expect(editor.getCursorScreenPosition()).toEqual [0, 0] it 'it moves appropriately to find the nearest matching action', -> editor.setCursorScreenPosition([0, 3]) keydown('%') expect(editor.getCursorScreenPosition()).toEqual [0, 2] expect(editor.getText()).toEqual "( ( ) )--{ text in here; and a function call(with parameters) }\n" it 'it moves appropriately to find the nearest matching action', -> editor.setCursorScreenPosition([0, 26]) keydown('%') expect(editor.getCursorScreenPosition()).toEqual [0, 60] expect(editor.getText()).toEqual "( ( ) )--{ text in here; and a function call(with parameters) }\n"