Spaces:
Running
Running
var code = '' + | |
' wOrd1 (#%\n' + | |
' word3] \n' + | |
'aopop pop 0 1 2 3 4\n' + | |
' (a) [b] {c} \n' + | |
'int getchar(void) {\n' + | |
' static char buf[BUFSIZ];\n' + | |
' static char *bufp = buf;\n' + | |
' if (n == 0) { /* buffer is empty */\n' + | |
' n = read(0, buf, sizeof buf);\n' + | |
' bufp = buf;\n' + | |
' }\n' + | |
' return (--n >= 0) ? (unsigned char) *bufp++ : EOF;\n' + | |
'}\n'; | |
var lines = (function() { | |
lineText = code.split('\n'); | |
var ret = []; | |
for (var i = 0; i < lineText.length; i++) { | |
ret[i] = { | |
line: i, | |
length: lineText[i].length, | |
lineText: lineText[i], | |
textStart: /^\s*/.exec(lineText[i])[0].length | |
}; | |
} | |
return ret; | |
})(); | |
var endOfDocument = makeCursor(lines.length - 1, | |
lines[lines.length - 1].length); | |
var wordLine = lines[0]; | |
var bigWordLine = lines[1]; | |
var charLine = lines[2]; | |
var bracesLine = lines[3]; | |
var word1 = { | |
start: { line: wordLine.line, ch: 1 }, | |
end: { line: wordLine.line, ch: 5 } | |
}; | |
var word2 = { | |
start: { line: wordLine.line, ch: word1.end.ch + 2 }, | |
end: { line: wordLine.line, ch: word1.end.ch + 4 } | |
}; | |
var word3 = { | |
start: { line: bigWordLine.line, ch: 1 }, | |
end: { line: bigWordLine.line, ch: 5 } | |
}; | |
var bigWord1 = word1; | |
var bigWord2 = word2; | |
var bigWord3 = { | |
start: { line: bigWordLine.line, ch: 1 }, | |
end: { line: bigWordLine.line, ch: 7 } | |
}; | |
var bigWord4 = { | |
start: { line: bigWordLine.line, ch: bigWord1.end.ch + 3 }, | |
end: { line: bigWordLine.line, ch: bigWord1.end.ch + 7 } | |
} | |
var oChars = [ { line: charLine.line, ch: 1 }, | |
{ line: charLine.line, ch: 3 }, | |
{ line: charLine.line, ch: 7 } ]; | |
var pChars = [ { line: charLine.line, ch: 2 }, | |
{ line: charLine.line, ch: 4 }, | |
{ line: charLine.line, ch: 6 }, | |
{ line: charLine.line, ch: 8 } ]; | |
var numChars = [ { line: charLine.line, ch: 10 }, | |
{ line: charLine.line, ch: 12 }, | |
{ line: charLine.line, ch: 14 }, | |
{ line: charLine.line, ch: 16 }, | |
{ line: charLine.line, ch: 18 }]; | |
var parens1 = { | |
start: { line: bracesLine.line, ch: 1 }, | |
end: { line: bracesLine.line, ch: 3 } | |
}; | |
var squares1 = { | |
start: { line: bracesLine.line, ch: 5 }, | |
end: { line: bracesLine.line, ch: 7 } | |
}; | |
var curlys1 = { | |
start: { line: bracesLine.line, ch: 9 }, | |
end: { line: bracesLine.line, ch: 11 } | |
}; | |
function copyCursor(cur) { | |
return { ch: cur.ch, line: cur.line }; | |
} | |
function testVim(name, run, opts, expectedFail) { | |
var vimOpts = { | |
lineNumbers: true, | |
keyMap: 'vim', | |
showCursorWhenSelecting: true, | |
value: code | |
}; | |
for (var prop in opts) { | |
if (opts.hasOwnProperty(prop)) { | |
vimOpts[prop] = opts[prop]; | |
} | |
} | |
return test('vim_' + name, function() { | |
var place = document.getElementById("testground"); | |
var cm = CodeMirror(place, vimOpts); | |
CodeMirror.Vim.maybeInitState(cm); | |
var vim = cm.vimState; | |
function doKeysFn(cm) { | |
return function(args) { | |
if (args instanceof Array) { | |
arguments = args; | |
} | |
for (var i = 0; i < arguments.length; i++) { | |
CodeMirror.Vim.handleKey(cm, arguments[i]); | |
} | |
} | |
} | |
function doExFn(cm) { | |
return function(command) { | |
cm.openDialog = helpers.fakeOpenDialog(command); | |
helpers.doKeys(':'); | |
} | |
} | |
function assertCursorAtFn(cm) { | |
return function(line, ch) { | |
var pos; | |
if (ch == null && typeof line.line == 'number') { | |
pos = line; | |
} else { | |
pos = makeCursor(line, ch); | |
} | |
eqPos(pos, cm.getCursor()); | |
} | |
} | |
function fakeOpenDialog(result) { | |
return function(text, callback) { | |
return callback(result); | |
} | |
} | |
var helpers = { | |
doKeys: doKeysFn(cm), | |
doEx: doExFn(cm), | |
assertCursorAt: assertCursorAtFn(cm), | |
fakeOpenDialog: fakeOpenDialog, | |
getRegisterController: function() { | |
return CodeMirror.Vim.getRegisterController(); | |
} | |
} | |
CodeMirror.Vim.clearVimGlobalState_(); | |
var successful = false; | |
try { | |
run(cm, vim, helpers); | |
successful = true; | |
} finally { | |
if ((debug && !successful) || verbose) { | |
place.style.visibility = "visible"; | |
} else { | |
place.removeChild(cm.getWrapperElement()); | |
} | |
} | |
}, expectedFail); | |
}; | |
/** | |
* @param name Name of the test | |
* @param keys An array of keys or a string with a single key to simulate. | |
* @param endPos The expected end position of the cursor. | |
* @param startPos The position the cursor should start at, defaults to 0, 0. | |
*/ | |
function testMotion(name, keys, endPos, startPos) { | |
testVim(name, function(cm, vim, helpers) { | |
if (!startPos) { | |
startPos = { line: 0, ch: 0 }; | |
} | |
cm.setCursor(startPos); | |
helpers.doKeys(keys); | |
helpers.assertCursorAt(endPos); | |
}); | |
}; | |
function makeCursor(line, ch) { | |
return { line: line, ch: ch }; | |
}; | |
function offsetCursor(cur, offsetLine, offsetCh) { | |
return { line: cur.line + offsetLine, ch: cur.ch + offsetCh }; | |
}; | |
// Motion tests | |
testMotion('|', '|', makeCursor(0, 0), makeCursor(0,4)); | |
testMotion('|_repeat', ['3', '|'], makeCursor(0, 2), makeCursor(0,4)); | |
testMotion('h', 'h', makeCursor(0, 0), word1.start); | |
testMotion('h_repeat', ['3', 'h'], offsetCursor(word1.end, 0, -3), word1.end); | |
testMotion('l', 'l', makeCursor(0, 1)); | |
testMotion('l_repeat', ['2', 'l'], makeCursor(0, 2)); | |
testMotion('j', 'j', offsetCursor(word1.end, 1, 0), word1.end); | |
testMotion('j_repeat', ['2', 'j'], offsetCursor(word1.end, 2, 0), word1.end); | |
testMotion('k', 'k', offsetCursor(word3.end, -1, 0), word3.end); | |
testMotion('k_repeat', ['2', 'k'], makeCursor(0, 4), makeCursor(2, 4)); | |
testMotion('w', 'w', word1.start); | |
testMotion('w_repeat', ['2', 'w'], word2.start); | |
testMotion('w_wrap', ['w'], word3.start, word2.start); | |
testMotion('w_endOfDocument', 'w', endOfDocument, endOfDocument); | |
testMotion('W', 'W', bigWord1.start); | |
testMotion('W_repeat', ['2', 'W'], bigWord3.start, bigWord1.start); | |
testMotion('e', 'e', word1.end); | |
testMotion('e_repeat', ['2', 'e'], word2.end); | |
testMotion('e_wrap', 'e', word3.end, word2.end); | |
testMotion('e_endOfDocument', 'e', endOfDocument, endOfDocument); | |
testMotion('b', 'b', word3.start, word3.end); | |
testMotion('b_repeat', ['2', 'b'], word2.start, word3.end); | |
testMotion('b_wrap', 'b', word2.start, word3.start); | |
testMotion('b_startOfDocument', 'b', makeCursor(0, 0), makeCursor(0, 0)); | |
testMotion('ge', ['g', 'e'], word2.end, word3.end); | |
testMotion('ge_repeat', ['2', 'g', 'e'], word1.end, word3.start); | |
testMotion('ge_wrap', ['g', 'e'], word2.end, word3.start); | |
testMotion('ge_startOfDocument', ['g', 'e'], makeCursor(0, 0), | |
makeCursor(0, 0)); | |
testMotion('gg', ['g', 'g'], makeCursor(lines[0].line, lines[0].textStart), | |
makeCursor(3, 1)); | |
testMotion('gg_repeat', ['3', 'g', 'g'], | |
makeCursor(lines[2].line, lines[2].textStart)); | |
testMotion('G', 'G', | |
makeCursor(lines[lines.length - 1].line, lines[lines.length - 1].textStart), | |
makeCursor(3, 1)); | |
testMotion('G_repeat', ['3', 'G'], makeCursor(lines[2].line, | |
lines[2].textStart)); | |
// TODO: Make the test code long enough to test Ctrl-F and Ctrl-B. | |
testMotion('0', '0', makeCursor(0, 0), makeCursor(0, 8)); | |
testMotion('^', '^', makeCursor(0, lines[0].textStart), makeCursor(0, 8)); | |
testMotion('$', '$', makeCursor(0, lines[0].length - 1), makeCursor(0, 1)); | |
testMotion('$_repeat', ['2', '$'], makeCursor(1, lines[1].length - 1), | |
makeCursor(0, 3)); | |
testMotion('f', ['f', 'p'], pChars[0], makeCursor(charLine.line, 0)); | |
testMotion('f_repeat', ['2', 'f', 'p'], pChars[2], pChars[0]); | |
testMotion('f_num', ['f', '2'], numChars[2], makeCursor(charLine.line, 0)); | |
testMotion('t', ['t','p'], offsetCursor(pChars[0], 0, -1), | |
makeCursor(charLine.line, 0)); | |
testMotion('t_repeat', ['2', 't', 'p'], offsetCursor(pChars[2], 0, -1), | |
pChars[0]); | |
testMotion('F', ['F', 'p'], pChars[0], pChars[1]); | |
testMotion('F_repeat', ['2', 'F', 'p'], pChars[0], pChars[2]); | |
testMotion('T', ['T', 'p'], offsetCursor(pChars[0], 0, 1), pChars[1]); | |
testMotion('T_repeat', ['2', 'T', 'p'], offsetCursor(pChars[0], 0, 1), pChars[2]); | |
testMotion('%_parens', ['%'], parens1.end, parens1.start); | |
testMotion('%_squares', ['%'], squares1.end, squares1.start); | |
testMotion('%_braces', ['%'], curlys1.end, curlys1.start); | |
// Make sure that moving down after going to the end of a line always leaves you | |
// at the end of a line, but preserves the offset in other cases | |
testVim('Changing lines after Eol operation', function(cm, vim, helpers) { | |
var startPos = { line: 0, ch: 0 }; | |
cm.setCursor(startPos); | |
helpers.doKeys(['$']); | |
helpers.doKeys(['j']); | |
// After moving to Eol and then down, we should be at Eol of line 2 | |
helpers.assertCursorAt({ line: 1, ch: lines[1].length - 1 }); | |
helpers.doKeys(['j']); | |
// After moving down, we should be at Eol of line 3 | |
helpers.assertCursorAt({ line: 2, ch: lines[2].length - 1 }); | |
helpers.doKeys(['h']); | |
helpers.doKeys(['j']); | |
// After moving back one space and then down, since line 4 is shorter than line 2, we should | |
// be at Eol of line 2 - 1 | |
helpers.assertCursorAt({ line: 3, ch: lines[3].length - 1 }); | |
helpers.doKeys(['j']); | |
helpers.doKeys(['j']); | |
// After moving down again, since line 3 has enough characters, we should be back to the | |
// same place we were at on line 1 | |
helpers.assertCursorAt({ line: 5, ch: lines[2].length - 2 }); | |
}); | |
//making sure gj and gk recover from clipping | |
testVim('gj_gk_clipping', function(cm,vim,helpers){ | |
cm.setCursor(0, 1); | |
helpers.doKeys('g','j','g','j'); | |
helpers.assertCursorAt(2, 1); | |
helpers.doKeys('g','k','g','k'); | |
helpers.assertCursorAt(0, 1); | |
},{value: 'line 1\n\nline 2'}); | |
//testing a mix of j/k and gj/gk | |
testVim('j_k_and_gj_gk', function(cm,vim,helpers){ | |
cm.setSize(120); | |
cm.setCursor(0, 0); | |
//go to the last character on the first line | |
helpers.doKeys('$'); | |
//move up/down on the column within the wrapped line | |
//side-effect: cursor is not locked to eol anymore | |
helpers.doKeys('g','k'); | |
var cur=cm.getCursor(); | |
eq(cur.line,0); | |
is((cur.ch<176),'gk didn\'t move cursor back (1)'); | |
helpers.doKeys('g','j'); | |
helpers.assertCursorAt(0, 176); | |
//should move to character 177 on line 2 (j/k preserve character index within line) | |
helpers.doKeys('j'); | |
//due to different line wrapping, the cursor can be on a different screen-x now | |
//gj and gk preserve screen-x on movement, much like moveV | |
helpers.doKeys('3','g','k'); | |
cur=cm.getCursor(); | |
eq(cur.line,1); | |
is((cur.ch<176),'gk didn\'t move cursor back (2)'); | |
helpers.doKeys('g','j','2','g','j'); | |
//should return to the same character-index | |
helpers.doKeys('k'); | |
helpers.assertCursorAt(0, 176); | |
},{ lineWrapping:true, value: 'This line is intentially long to test movement of gj and gk over wrapped lines. I will start on the end of this line, then make a step up and back to set the origin for j and k.\nThis line is supposed to be even longer than the previous. I will jump here and make another wiggle with gj and gk, before I jump back to the line above. Both wiggles should not change my cursor\'s target character but both j/k and gj/gk change each other\'s reference position.'}); | |
testVim('}', function(cm, vim, helpers) { | |
cm.setCursor(0, 0); | |
helpers.doKeys('}'); | |
helpers.assertCursorAt(1, 0); | |
cm.setCursor(0, 0); | |
helpers.doKeys('2', '}'); | |
helpers.assertCursorAt(4, 0); | |
cm.setCursor(0, 0); | |
helpers.doKeys('6', '}'); | |
helpers.assertCursorAt(5, 0); | |
}, { value: 'a\n\nb\nc\n\nd' }); | |
testVim('{', function(cm, vim, helpers) { | |
cm.setCursor(5, 0); | |
helpers.doKeys('{'); | |
helpers.assertCursorAt(4, 0); | |
cm.setCursor(5, 0); | |
helpers.doKeys('2', '{'); | |
helpers.assertCursorAt(1, 0); | |
cm.setCursor(5, 0); | |
helpers.doKeys('6', '{'); | |
helpers.assertCursorAt(0, 0); | |
}, { value: 'a\n\nb\nc\n\nd' }); | |
// Operator tests | |
testVim('dl', function(cm, vim, helpers) { | |
var curStart = makeCursor(0, 0); | |
cm.setCursor(curStart); | |
helpers.doKeys('d', 'l'); | |
eq('word1 ', cm.getValue()); | |
var register = helpers.getRegisterController().getRegister(); | |
eq(' ', register.text); | |
is(!register.linewise); | |
eqPos(curStart, cm.getCursor()); | |
}, { value: ' word1 ' }); | |
testVim('dl_eol', function(cm, vim, helpers) { | |
var curStart = makeCursor(0, 6); | |
cm.setCursor(curStart); | |
helpers.doKeys('d', 'l'); | |
eq(' word1', cm.getValue()); | |
var register = helpers.getRegisterController().getRegister(); | |
eq(' ', register.text); | |
is(!register.linewise); | |
helpers.assertCursorAt(0, 6); | |
}, { value: ' word1 ' }); | |
testVim('dl_repeat', function(cm, vim, helpers) { | |
var curStart = makeCursor(0, 0); | |
cm.setCursor(curStart); | |
helpers.doKeys('2', 'd', 'l'); | |
eq('ord1 ', cm.getValue()); | |
var register = helpers.getRegisterController().getRegister(); | |
eq(' w', register.text); | |
is(!register.linewise); | |
eqPos(curStart, cm.getCursor()); | |
}, { value: ' word1 ' }); | |
testVim('dh', function(cm, vim, helpers) { | |
var curStart = makeCursor(0, 3); | |
cm.setCursor(curStart); | |
helpers.doKeys('d', 'h'); | |
eq(' wrd1 ', cm.getValue()); | |
var register = helpers.getRegisterController().getRegister(); | |
eq('o', register.text); | |
is(!register.linewise); | |
eqPos(offsetCursor(curStart, 0 , -1), cm.getCursor()); | |
}, { value: ' word1 ' }); | |
testVim('dj', function(cm, vim, helpers) { | |
var curStart = makeCursor(0, 3); | |
cm.setCursor(curStart); | |
helpers.doKeys('d', 'j'); | |
eq(' word3', cm.getValue()); | |
var register = helpers.getRegisterController().getRegister(); | |
eq(' word1\nword2\n', register.text); | |
is(register.linewise); | |
helpers.assertCursorAt(0, 1); | |
}, { value: ' word1\nword2\n word3' }); | |
testVim('dj_end_of_document', function(cm, vim, helpers) { | |
var curStart = makeCursor(0, 3); | |
cm.setCursor(curStart); | |
helpers.doKeys('d', 'j'); | |
eq(' word1 ', cm.getValue()); | |
var register = helpers.getRegisterController().getRegister(); | |
eq('', register.text); | |
is(!register.linewise); | |
helpers.assertCursorAt(0, 3); | |
}, { value: ' word1 ' }); | |
testVim('dk', function(cm, vim, helpers) { | |
var curStart = makeCursor(1, 3); | |
cm.setCursor(curStart); | |
helpers.doKeys('d', 'k'); | |
eq(' word3', cm.getValue()); | |
var register = helpers.getRegisterController().getRegister(); | |
eq(' word1\nword2\n', register.text); | |
is(register.linewise); | |
helpers.assertCursorAt(0, 1); | |
}, { value: ' word1\nword2\n word3' }); | |
testVim('dk_start_of_document', function(cm, vim, helpers) { | |
var curStart = makeCursor(0, 3); | |
cm.setCursor(curStart); | |
helpers.doKeys('d', 'k'); | |
eq(' word1 ', cm.getValue()); | |
var register = helpers.getRegisterController().getRegister(); | |
eq('', register.text); | |
is(!register.linewise); | |
helpers.assertCursorAt(0, 3); | |
}, { value: ' word1 ' }); | |
testVim('dw_space', function(cm, vim, helpers) { | |
var curStart = makeCursor(0, 0); | |
cm.setCursor(curStart); | |
helpers.doKeys('d', 'w'); | |
eq('word1 ', cm.getValue()); | |
var register = helpers.getRegisterController().getRegister(); | |
eq(' ', register.text); | |
is(!register.linewise); | |
eqPos(curStart, cm.getCursor()); | |
}, { value: ' word1 ' }); | |
testVim('dw_word', function(cm, vim, helpers) { | |
var curStart = makeCursor(0, 1); | |
cm.setCursor(curStart); | |
helpers.doKeys('d', 'w'); | |
eq(' word2', cm.getValue()); | |
var register = helpers.getRegisterController().getRegister(); | |
eq('word1 ', register.text); | |
is(!register.linewise); | |
eqPos(curStart, cm.getCursor()); | |
}, { value: ' word1 word2' }); | |
testVim('dw_only_word', function(cm, vim, helpers) { | |
// Test that if there is only 1 word left, dw deletes till the end of the | |
// line. | |
var curStart = makeCursor(0, 1); | |
cm.setCursor(curStart); | |
helpers.doKeys('d', 'w'); | |
eq(' ', cm.getValue()); | |
var register = helpers.getRegisterController().getRegister(); | |
eq('word1 ', register.text); | |
is(!register.linewise); | |
eqPos(curStart, cm.getCursor()); | |
}, { value: ' word1 ' }); | |
testVim('dw_eol', function(cm, vim, helpers) { | |
// Assert that dw does not delete the newline if last word to delete is at end | |
// of line. | |
var curStart = makeCursor(0, 1); | |
cm.setCursor(curStart); | |
helpers.doKeys('d', 'w'); | |
eq(' \nword2', cm.getValue()); | |
var register = helpers.getRegisterController().getRegister(); | |
eq('word1', register.text); | |
is(!register.linewise); | |
eqPos(curStart, cm.getCursor()); | |
}, { value: ' word1\nword2' }); | |
testVim('dw_repeat', function(cm, vim, helpers) { | |
// Assert that dw does delete newline if it should go to the next line, and | |
// that repeat works properly. | |
var curStart = makeCursor(0, 1); | |
cm.setCursor(curStart); | |
helpers.doKeys('d', '2', 'w'); | |
eq(' ', cm.getValue()); | |
var register = helpers.getRegisterController().getRegister(); | |
eq('word1\nword2', register.text); | |
is(!register.linewise); | |
eqPos(curStart, cm.getCursor()); | |
}, { value: ' word1\nword2' }); | |
testVim('d_inclusive', function(cm, vim, helpers) { | |
// Assert that when inclusive is set, the character the cursor is on gets | |
// deleted too. | |
var curStart = makeCursor(0, 1); | |
cm.setCursor(curStart); | |
helpers.doKeys('d', 'e'); | |
eq(' ', cm.getValue()); | |
var register = helpers.getRegisterController().getRegister(); | |
eq('word1', register.text); | |
is(!register.linewise); | |
eqPos(curStart, cm.getCursor()); | |
}, { value: ' word1 ' }); | |
testVim('d_reverse', function(cm, vim, helpers) { | |
// Test that deleting in reverse works. | |
cm.setCursor(1, 0); | |
helpers.doKeys('d', 'b'); | |
eq(' word2 ', cm.getValue()); | |
var register = helpers.getRegisterController().getRegister(); | |
eq('word1\n', register.text); | |
is(!register.linewise); | |
helpers.assertCursorAt(0, 1); | |
}, { value: ' word1\nword2 ' }); | |
testVim('dd', function(cm, vim, helpers) { | |
cm.setCursor(0, 3); | |
var expectedBuffer = cm.getRange({ line: 0, ch: 0 }, | |
{ line: 1, ch: 0 }); | |
var expectedLineCount = cm.lineCount() - 1; | |
helpers.doKeys('d', 'd'); | |
eq(expectedLineCount, cm.lineCount()); | |
var register = helpers.getRegisterController().getRegister(); | |
eq(expectedBuffer, register.text); | |
is(register.linewise); | |
helpers.assertCursorAt(0, lines[1].textStart); | |
}); | |
testVim('dd_prefix_repeat', function(cm, vim, helpers) { | |
cm.setCursor(0, 3); | |
var expectedBuffer = cm.getRange({ line: 0, ch: 0 }, | |
{ line: 2, ch: 0 }); | |
var expectedLineCount = cm.lineCount() - 2; | |
helpers.doKeys('2', 'd', 'd'); | |
eq(expectedLineCount, cm.lineCount()); | |
var register = helpers.getRegisterController().getRegister(); | |
eq(expectedBuffer, register.text); | |
is(register.linewise); | |
helpers.assertCursorAt(0, lines[2].textStart); | |
}); | |
testVim('dd_motion_repeat', function(cm, vim, helpers) { | |
cm.setCursor(0, 3); | |
var expectedBuffer = cm.getRange({ line: 0, ch: 0 }, | |
{ line: 2, ch: 0 }); | |
var expectedLineCount = cm.lineCount() - 2; | |
helpers.doKeys('d', '2', 'd'); | |
eq(expectedLineCount, cm.lineCount()); | |
var register = helpers.getRegisterController().getRegister(); | |
eq(expectedBuffer, register.text); | |
is(register.linewise); | |
helpers.assertCursorAt(0, lines[2].textStart); | |
}); | |
testVim('dd_multiply_repeat', function(cm, vim, helpers) { | |
cm.setCursor(0, 3); | |
var expectedBuffer = cm.getRange({ line: 0, ch: 0 }, | |
{ line: 6, ch: 0 }); | |
var expectedLineCount = cm.lineCount() - 6; | |
helpers.doKeys('2', 'd', '3', 'd'); | |
eq(expectedLineCount, cm.lineCount()); | |
var register = helpers.getRegisterController().getRegister(); | |
eq(expectedBuffer, register.text); | |
is(register.linewise); | |
helpers.assertCursorAt(0, lines[6].textStart); | |
}); | |
// Yank commands should behave the exact same as d commands, expect that nothing | |
// gets deleted. | |
testVim('yw_repeat', function(cm, vim, helpers) { | |
// Assert that yw does yank newline if it should go to the next line, and | |
// that repeat works properly. | |
var curStart = makeCursor(0, 1); | |
cm.setCursor(curStart); | |
helpers.doKeys('y', '2', 'w'); | |
eq(' word1\nword2', cm.getValue()); | |
var register = helpers.getRegisterController().getRegister(); | |
eq('word1\nword2', register.text); | |
is(!register.linewise); | |
eqPos(curStart, cm.getCursor()); | |
}, { value: ' word1\nword2' }); | |
testVim('yy_multiply_repeat', function(cm, vim, helpers) { | |
var curStart = makeCursor(0, 3); | |
cm.setCursor(curStart); | |
var expectedBuffer = cm.getRange({ line: 0, ch: 0 }, | |
{ line: 6, ch: 0 }); | |
var expectedLineCount = cm.lineCount(); | |
helpers.doKeys('2', 'y', '3', 'y'); | |
eq(expectedLineCount, cm.lineCount()); | |
var register = helpers.getRegisterController().getRegister(); | |
eq(expectedBuffer, register.text); | |
is(register.linewise); | |
eqPos(curStart, cm.getCursor()); | |
}); | |
// Change commands behave like d commands except that it also enters insert | |
// mode. In addition, when the change is linewise, an additional newline is | |
// inserted so that insert mode starts on that line. | |
testVim('cw_repeat', function(cm, vim, helpers) { | |
// Assert that cw does delete newline if it should go to the next line, and | |
// that repeat works properly. | |
var curStart = makeCursor(0, 1); | |
cm.setCursor(curStart); | |
helpers.doKeys('c', '2', 'w'); | |
eq(' ', cm.getValue()); | |
var register = helpers.getRegisterController().getRegister(); | |
eq('word1\nword2', register.text); | |
is(!register.linewise); | |
eqPos(curStart, cm.getCursor()); | |
eq('vim-insert', cm.getOption('keyMap')); | |
}, { value: ' word1\nword2' }); | |
testVim('cc_multiply_repeat', function(cm, vim, helpers) { | |
cm.setCursor(0, 3); | |
var expectedBuffer = cm.getRange({ line: 0, ch: 0 }, | |
{ line: 6, ch: 0 }); | |
var expectedLineCount = cm.lineCount() - 5; | |
helpers.doKeys('2', 'c', '3', 'c'); | |
eq(expectedLineCount, cm.lineCount()); | |
var register = helpers.getRegisterController().getRegister(); | |
eq(expectedBuffer, register.text); | |
is(register.linewise); | |
helpers.assertCursorAt(0, lines[0].textStart); | |
eq('vim-insert', cm.getOption('keyMap')); | |
}); | |
// Swapcase commands edit in place and do not modify registers. | |
testVim('g~w_repeat', function(cm, vim, helpers) { | |
// Assert that dw does delete newline if it should go to the next line, and | |
// that repeat works properly. | |
var curStart = makeCursor(0, 1); | |
cm.setCursor(curStart); | |
helpers.doKeys('g', '~', '2', 'w'); | |
eq(' WORD1\nWORD2', cm.getValue()); | |
var register = helpers.getRegisterController().getRegister(); | |
eq('', register.text); | |
is(!register.linewise); | |
eqPos(curStart, cm.getCursor()); | |
}, { value: ' word1\nword2' }); | |
testVim('g~g~', function(cm, vim, helpers) { | |
var curStart = makeCursor(0, 3); | |
cm.setCursor(curStart); | |
var expectedLineCount = cm.lineCount(); | |
var expectedValue = cm.getValue().toUpperCase(); | |
helpers.doKeys('2', 'g', '~', '3', 'g', '~'); | |
eq(expectedValue, cm.getValue()); | |
var register = helpers.getRegisterController().getRegister(); | |
eq('', register.text); | |
is(!register.linewise); | |
eqPos(curStart, cm.getCursor()); | |
}, { value: ' word1\nword2\nword3\nword4\nword5\nword6' }); | |
testVim('>{motion}', function(cm, vim, helpers) { | |
cm.setCursor(1, 3); | |
var expectedLineCount = cm.lineCount(); | |
var expectedValue = ' word1\n word2\nword3 '; | |
helpers.doKeys('>', 'k'); | |
eq(expectedValue, cm.getValue()); | |
var register = helpers.getRegisterController().getRegister(); | |
eq('', register.text); | |
is(!register.linewise); | |
helpers.assertCursorAt(0, 3); | |
}, { value: ' word1\nword2\nword3 ', indentUnit: 2 }); | |
testVim('>>', function(cm, vim, helpers) { | |
cm.setCursor(0, 3); | |
var expectedLineCount = cm.lineCount(); | |
var expectedValue = ' word1\n word2\nword3 '; | |
helpers.doKeys('2', '>', '>'); | |
eq(expectedValue, cm.getValue()); | |
var register = helpers.getRegisterController().getRegister(); | |
eq('', register.text); | |
is(!register.linewise); | |
helpers.assertCursorAt(0, 3); | |
}, { value: ' word1\nword2\nword3 ', indentUnit: 2 }); | |
testVim('<{motion}', function(cm, vim, helpers) { | |
cm.setCursor(1, 3); | |
var expectedLineCount = cm.lineCount(); | |
var expectedValue = ' word1\nword2\nword3 '; | |
helpers.doKeys('<', 'k'); | |
eq(expectedValue, cm.getValue()); | |
var register = helpers.getRegisterController().getRegister(); | |
eq('', register.text); | |
is(!register.linewise); | |
helpers.assertCursorAt(0, 1); | |
}, { value: ' word1\n word2\nword3 ', indentUnit: 2 }); | |
testVim('<<', function(cm, vim, helpers) { | |
cm.setCursor(0, 3); | |
var expectedLineCount = cm.lineCount(); | |
var expectedValue = ' word1\nword2\nword3 '; | |
helpers.doKeys('2', '<', '<'); | |
eq(expectedValue, cm.getValue()); | |
var register = helpers.getRegisterController().getRegister(); | |
eq('', register.text); | |
is(!register.linewise); | |
helpers.assertCursorAt(0, 1); | |
}, { value: ' word1\n word2\nword3 ', indentUnit: 2 }); | |
// Operator-motion tests | |
testVim('D', function(cm, vim, helpers) { | |
var curStart = makeCursor(0, 3); | |
cm.setCursor(curStart); | |
helpers.doKeys('D'); | |
eq(' wo\nword2\n word3', cm.getValue()); | |
var register = helpers.getRegisterController().getRegister(); | |
eq('rd1', register.text); | |
is(!register.linewise); | |
helpers.assertCursorAt(0, 3); | |
}, { value: ' word1\nword2\n word3' }); | |
testVim('C', function(cm, vim, helpers) { | |
var curStart = makeCursor(0, 3); | |
cm.setCursor(curStart); | |
helpers.doKeys('C'); | |
eq(' wo\nword2\n word3', cm.getValue()); | |
var register = helpers.getRegisterController().getRegister(); | |
eq('rd1', register.text); | |
is(!register.linewise); | |
helpers.assertCursorAt(0, 3); | |
eq('vim-insert', cm.getOption('keyMap')); | |
}, { value: ' word1\nword2\n word3' }); | |
testVim('Y', function(cm, vim, helpers) { | |
var curStart = makeCursor(0, 3); | |
cm.setCursor(curStart); | |
helpers.doKeys('Y'); | |
eq(' word1\nword2\n word3', cm.getValue()); | |
var register = helpers.getRegisterController().getRegister(); | |
eq('rd1', register.text); | |
is(!register.linewise); | |
helpers.assertCursorAt(0, 3); | |
}, { value: ' word1\nword2\n word3' }); | |
// Action tests | |
testVim('a', function(cm, vim, helpers) { | |
cm.setCursor(0, 1); | |
helpers.doKeys('a'); | |
helpers.assertCursorAt(0, 2); | |
eq('vim-insert', cm.getOption('keyMap')); | |
}); | |
testVim('a_eol', function(cm, vim, helpers) { | |
cm.setCursor(0, lines[0].length - 1); | |
helpers.doKeys('a'); | |
helpers.assertCursorAt(0, lines[0].length); | |
eq('vim-insert', cm.getOption('keyMap')); | |
}); | |
testVim('i', function(cm, vim, helpers) { | |
cm.setCursor(0, 1); | |
helpers.doKeys('i'); | |
helpers.assertCursorAt(0, 1); | |
eq('vim-insert', cm.getOption('keyMap')); | |
}); | |
testVim('A', function(cm, vim, helpers) { | |
cm.setCursor(0, 0); | |
helpers.doKeys('A'); | |
helpers.assertCursorAt(0, lines[0].length); | |
eq('vim-insert', cm.getOption('keyMap')); | |
}); | |
testVim('I', function(cm, vim, helpers) { | |
cm.setCursor(0, 4); | |
helpers.doKeys('I'); | |
helpers.assertCursorAt(0, lines[0].textStart); | |
eq('vim-insert', cm.getOption('keyMap')); | |
}); | |
testVim('o', function(cm, vim, helpers) { | |
cm.setCursor(0, 4); | |
helpers.doKeys('o'); | |
eq('word1\n\nword2', cm.getValue()); | |
helpers.assertCursorAt(1, 0); | |
eq('vim-insert', cm.getOption('keyMap')); | |
}, { value: 'word1\nword2' }); | |
testVim('O', function(cm, vim, helpers) { | |
cm.setCursor(0, 4); | |
helpers.doKeys('O'); | |
eq('\nword1\nword2', cm.getValue()); | |
helpers.assertCursorAt(0, 0); | |
eq('vim-insert', cm.getOption('keyMap')); | |
}, { value: 'word1\nword2' }); | |
testVim('J', function(cm, vim, helpers) { | |
cm.setCursor(0, 4); | |
helpers.doKeys('J'); | |
var expectedValue = 'word1 word2\nword3\n word4'; | |
eq(expectedValue, cm.getValue()); | |
helpers.assertCursorAt(0, expectedValue.indexOf('word2') - 1); | |
}, { value: 'word1 \n word2\nword3\n word4' }); | |
testVim('J_repeat', function(cm, vim, helpers) { | |
cm.setCursor(0, 4); | |
helpers.doKeys('3', 'J'); | |
var expectedValue = 'word1 word2 word3\n word4'; | |
eq(expectedValue, cm.getValue()); | |
helpers.assertCursorAt(0, expectedValue.indexOf('word3') - 1); | |
}, { value: 'word1 \n word2\nword3\n word4' }); | |
testVim('p', function(cm, vim, helpers) { | |
cm.setCursor(0, 1); | |
helpers.getRegisterController().pushText('"', 'yank', 'abc\ndef', false); | |
helpers.doKeys('p'); | |
eq('__abc\ndef_', cm.getValue()); | |
helpers.assertCursorAt(1, 2); | |
}, { value: '___' }); | |
testVim('p_register', function(cm, vim, helpers) { | |
cm.setCursor(0, 1); | |
helpers.getRegisterController().getRegister('a').set('abc\ndef', false); | |
helpers.doKeys('"', 'a', 'p'); | |
eq('__abc\ndef_', cm.getValue()); | |
helpers.assertCursorAt(1, 2); | |
}, { value: '___' }); | |
testVim('p_wrong_register', function(cm, vim, helpers) { | |
cm.setCursor(0, 1); | |
helpers.getRegisterController().getRegister('a').set('abc\ndef', false); | |
helpers.doKeys('p'); | |
eq('___', cm.getValue()); | |
helpers.assertCursorAt(0, 1); | |
}, { value: '___' }); | |
testVim('p_line', function(cm, vim, helpers) { | |
cm.setCursor(0, 1); | |
helpers.getRegisterController().pushText('"', 'yank', ' a\nd\n', true); | |
helpers.doKeys('2', 'p'); | |
eq('___\n a\nd\n a\nd', cm.getValue()); | |
helpers.assertCursorAt(1, 2); | |
}, { value: '___' }); | |
testVim('P', function(cm, vim, helpers) { | |
cm.setCursor(0, 1); | |
helpers.getRegisterController().pushText('"', 'yank', 'abc\ndef', false); | |
helpers.doKeys('P'); | |
eq('_abc\ndef__', cm.getValue()); | |
helpers.assertCursorAt(1, 3); | |
}, { value: '___' }); | |
testVim('P_line', function(cm, vim, helpers) { | |
cm.setCursor(0, 1); | |
helpers.getRegisterController().pushText('"', 'yank', ' a\nd\n', true); | |
helpers.doKeys('2', 'P'); | |
eq(' a\nd\n a\nd\n___', cm.getValue()); | |
helpers.assertCursorAt(0, 2); | |
}, { value: '___' }); | |
testVim('r', function(cm, vim, helpers) { | |
cm.setCursor(0, 1); | |
helpers.doKeys('3', 'r', 'u'); | |
eq('wuuuet', cm.getValue()); | |
helpers.assertCursorAt(0, 3); | |
}, { value: 'wordet' }); | |
testVim('mark', function(cm, vim, helpers) { | |
cm.setCursor(2, 2); | |
helpers.doKeys('m', 't'); | |
cm.setCursor(0, 0); | |
helpers.doKeys('\'', 't'); | |
helpers.assertCursorAt(2, 2); | |
cm.setCursor(0, 0); | |
helpers.doKeys('`', 't'); | |
helpers.assertCursorAt(2, 2); | |
}); | |
testVim('visual', function(cm, vim, helpers) { | |
helpers.doKeys('l', 'v', 'l', 'l'); | |
helpers.assertCursorAt(0, 3); | |
eqPos(makeCursor(0, 1), cm.getCursor('anchor')); | |
helpers.doKeys('d'); | |
eq('15', cm.getValue()); | |
}, { value: '12345' }); | |
testVim('visual_line', function(cm, vim, helpers) { | |
helpers.doKeys('l', 'V', 'l', 'j', 'j', 'd'); | |
eq(' 4\n 5', cm.getValue()); | |
}, { value: ' 1\n 2\n 3\n 4\n 5' }); | |
testVim('visual_marks', function(cm, vim, helpers) { | |
helpers.doKeys('l', 'v', 'l', 'l', 'v'); | |
// Test visual mode marks | |
cm.setCursor(0, 0); | |
helpers.doKeys('\'', '<'); | |
helpers.assertCursorAt(0, 1); | |
helpers.doKeys('\'', '>'); | |
helpers.assertCursorAt(0, 3); | |
}); | |
testVim('visual_join', function(cm, vim, helpers) { | |
helpers.doKeys('l', 'V', 'l', 'j', 'j', 'J'); | |
eq(' 1 2 3\n 4\n 5', cm.getValue()); | |
}, { value: ' 1\n 2\n 3\n 4\n 5' }); | |
testVim('/ and n/N', function(cm, vim, helpers) { | |
cm.openDialog = helpers.fakeOpenDialog('match'); | |
helpers.doKeys('/'); | |
helpers.assertCursorAt(0, 11); | |
helpers.doKeys('n'); | |
helpers.assertCursorAt(1, 6); | |
helpers.doKeys('N'); | |
helpers.assertCursorAt(0, 11); | |
cm.setCursor(0, 0); | |
helpers.doKeys('2', '/'); | |
helpers.assertCursorAt(1, 6); | |
}, { value: 'match nope match \n nope Match' }); | |
testVim('/_case', function(cm, vim, helpers) { | |
cm.openDialog = helpers.fakeOpenDialog('Match'); | |
helpers.doKeys('/'); | |
helpers.assertCursorAt(1, 6); | |
}, { value: 'match nope match \n nope Match' }); | |
testVim('? and n/N', function(cm, vim, helpers) { | |
cm.openDialog = helpers.fakeOpenDialog('match'); | |
helpers.doKeys('?'); | |
helpers.assertCursorAt(1, 6); | |
helpers.doKeys('n'); | |
helpers.assertCursorAt(0, 11); | |
helpers.doKeys('N'); | |
helpers.assertCursorAt(1, 6); | |
cm.setCursor(0, 0); | |
helpers.doKeys('2', '?'); | |
helpers.assertCursorAt(0, 11); | |
}, { value: 'match nope match \n nope Match' }); | |
//:noh should clear highlighting of search-results but allow to resume search through n | |
testVim('noh_clearSearchHighlight', function(cm, vim, helpers) { | |
cm.openDialog = helpers.fakeOpenDialog('match'); | |
helpers.doKeys('?'); | |
helpers.doEx('noh'); | |
eq(vim.searchState_.getOverlay(),null,'match-highlighting wasn\'t cleared'); | |
helpers.doKeys('n'); | |
helpers.assertCursorAt(0, 11,'can\'t resume search after clearing highlighting'); | |
}, { value: 'match nope match \n nope Match' }); | |
testVim('*', function(cm, vim, helpers) { | |
cm.setCursor(0, 9); | |
helpers.doKeys('*'); | |
helpers.assertCursorAt(0, 22); | |
cm.setCursor(0, 9); | |
helpers.doKeys('2', '*'); | |
helpers.assertCursorAt(1, 8); | |
}, { value: 'nomatch match nomatch match \nnomatch Match' }); | |
testVim('*_no_word', function(cm, vim, helpers) { | |
cm.setCursor(0, 0); | |
helpers.doKeys('*'); | |
helpers.assertCursorAt(0, 0); | |
}, { value: ' \n match \n' }); | |
testVim('*_symbol', function(cm, vim, helpers) { | |
cm.setCursor(0, 0); | |
helpers.doKeys('*'); | |
helpers.assertCursorAt(1, 0); | |
}, { value: ' /}\n/} match \n' }); | |
testVim('#', function(cm, vim, helpers) { | |
cm.setCursor(0, 9); | |
helpers.doKeys('#'); | |
helpers.assertCursorAt(1, 8); | |
cm.setCursor(0, 9); | |
helpers.doKeys('2', '#'); | |
helpers.assertCursorAt(0, 22); | |
}, { value: 'nomatch match nomatch match \nnomatch Match' }); | |
testVim('*_seek', function(cm, vim, helpers) { | |
// Should skip over space and symbols. | |
cm.setCursor(0, 3); | |
helpers.doKeys('*'); | |
helpers.assertCursorAt(0, 22); | |
}, { value: ' := match nomatch match \nnomatch Match' }); | |
testVim('#', function(cm, vim, helpers) { | |
// Should skip over space and symbols. | |
cm.setCursor(0, 3); | |
helpers.doKeys('#'); | |
helpers.assertCursorAt(1, 8); | |
}, { value: ' := match nomatch match \nnomatch Match' }); | |
testVim('ex_write', function(cm, vim, helpers) { | |
var tmp = CodeMirror.commands.save; | |
var written; | |
var actualCm; | |
CodeMirror.commands.save = function(cm) { | |
written = true; | |
actualCm = cm; | |
}; | |
// Test that w, wr, wri ... write all trigger :write. | |
var command = 'write'; | |
for (var i = 1; i < command.length; i++) { | |
written = false; | |
actualCm = null; | |
helpers.doEx(command.substring(0, i)); | |
eq(written, true); | |
eq(actualCm, cm); | |
} | |
CodeMirror.commands.save = tmp; | |
}); | |
testVim('ex_substitute_same_line', function(cm, vim, helpers) { | |
cm.setCursor(1, 0); | |
helpers.doEx('s/one/two'); | |
eq('one one\n two two', cm.getValue()); | |
}, { value: 'one one\n one one'}); | |
testVim('ex_substitute_global', function(cm, vim, helpers) { | |
cm.setCursor(1, 0); | |
helpers.doEx('%s/one/two'); | |
eq('two two\n two two', cm.getValue()); | |
}, { value: 'one one\n one one'}); | |
testVim('ex_substitute_input_range', function(cm, vim, helpers) { | |
cm.setCursor(1, 0); | |
helpers.doEx('1,3s/\\d/0'); | |
eq('0\n0\n0\n4', cm.getValue()); | |
}, { value: '1\n2\n3\n4' }); | |
testVim('ex_substitute_visual_range', function(cm, vim, helpers) { | |
cm.setCursor(1, 0); | |
// Set last visual mode selection marks '< and '> at lines 2 and 4 | |
helpers.doKeys('V', '2', 'j', 'v'); | |
helpers.doEx('\'<,\'>s/\\d/0'); | |
eq('1\n0\n0\n0\n5', cm.getValue()); | |
}, { value: '1\n2\n3\n4\n5' }); | |
testVim('ex_substitute_capture', function(cm, vim, helpers) { | |
cm.setCursor(1, 0); | |
helpers.doEx('s/(\\d+)/$1$1/') | |
eq('a1111 a1212 a1313', cm.getValue()); | |
}, { value: 'a11 a12 a13' }); | |
testVim('ex_substitute_empty_query', function(cm, vim, helpers) { | |
// If the query is empty, use last query. | |
cm.setCursor(1, 0); | |
cm.openDialog = helpers.fakeOpenDialog('1'); | |
helpers.doKeys('/'); | |
helpers.doEx('s//b'); | |
eq('abb ab2 ab3', cm.getValue()); | |
}, { value: 'a11 a12 a13' }); | |
testVim('ex_substitute_count', function(cm, vim, helpers) { | |
cm.setCursor(1, 0); | |
helpers.doEx('s/\\d/0/i 2'); | |
eq('1\n0\n0\n4', cm.getValue()); | |
}, { value: '1\n2\n3\n4' }); | |
testVim('ex_substitute_count_with_range', function(cm, vim, helpers) { | |
cm.setCursor(1, 0); | |
helpers.doEx('1,3s/\\d/0/ 3'); | |
eq('1\n2\n0\n0', cm.getValue()); | |
}, { value: '1\n2\n3\n4' }); | |
// TODO: Reset key maps after each test. | |
testVim('ex_map_key2key', function(cm, vim, helpers) { | |
helpers.doEx('map a x'); | |
helpers.doKeys('a'); | |
helpers.assertCursorAt(0, 0); | |
eq('bc', cm.getValue()); | |
}, { value: 'abc' }); | |
testVim('ex_map_key2key_to_colon', function(cm, vim, helpers) { | |
helpers.doEx('map ; :'); | |
var dialogOpened = false; | |
cm.openDialog = function() { | |
dialogOpened = true; | |
} | |
helpers.doKeys(';'); | |
eq(dialogOpened, true); | |
}); | |
testVim('ex_map_ex2key:', function(cm, vim, helpers) { | |
helpers.doEx('map :del x'); | |
helpers.doEx('del'); | |
helpers.assertCursorAt(0, 0); | |
eq('bc', cm.getValue()); | |
}, { value: 'abc' }); | |
testVim('ex_map_ex2ex', function(cm, vim, helpers) { | |
helpers.doEx('map :del :w'); | |
var tmp = CodeMirror.commands.save; | |
var written = false; | |
var actualCm; | |
CodeMirror.commands.save = function(cm) { | |
written = true; | |
actualCm = cm; | |
}; | |
helpers.doEx('del'); | |
CodeMirror.commands.save = tmp; | |
eq(written, true); | |
eq(actualCm, cm); | |
}); | |
testVim('ex_map_key2ex', function(cm, vim, helpers) { | |
helpers.doEx('map a :w'); | |
var tmp = CodeMirror.commands.save; | |
var written = false; | |
var actualCm; | |
CodeMirror.commands.save = function(cm) { | |
written = true; | |
actualCm = cm; | |
}; | |
helpers.doKeys('a'); | |
CodeMirror.commands.save = tmp; | |
eq(written, true); | |
eq(actualCm, cm); | |
}); | |
// Testing registration of functions as ex-commands and mapping to <Key>-keys | |
testVim('ex_api_test', function(cm, vim, helpers) { | |
var res=false; | |
var val='from'; | |
CodeMirror.Vim.defineEx('extest','ext',function(cm,params){ | |
if(params.args)val=params.args[0]; | |
else res=true; | |
}); | |
helpers.doEx(':ext to'); | |
eq(val,'to','Defining ex-command failed'); | |
CodeMirror.Vim.map('<C-CR><Space>',':ext'); | |
helpers.doKeys('Ctrl-Enter','Space'); | |
is(res,'Mapping to key failed'); | |
}); | |
// For now, this test needs to be last because it messes up : for future tests. | |
testVim('ex_map_key2key_from_colon', function(cm, vim, helpers) { | |
helpers.doEx('map : x'); | |
helpers.doKeys(':'); | |
helpers.assertCursorAt(0, 0); | |
eq('bc', cm.getValue()); | |
}, { value: 'abc' }); | |