Add vim emulation support for `gn` and `gN`.
If we are given the following snippet of text: ``` A green green sky. _ ``` We can search for the word "green" with `/green`, and then use `gn` to select the next occurrence of "green" in visual mode. ``` A green green sky. ----- ``` Alternatively, we can use `cgn` and then enter the word "blue" to change the word "green" to "blue". ``` A blue green sky. ``` Then we can use the `.` operator to repeat the change: ``` A blue blue sky. ``` Addresses #3851.
This commit is contained in:
parent
fd2e32250e
commit
b6da8bf16b
|
@ -56,6 +56,7 @@ int getchar(void)
|
||||||
}
|
}
|
||||||
</textarea></form>
|
</textarea></form>
|
||||||
<div style="font-size: 13px; width: 300px; height: 30px;">Key buffer: <span id="command-display"></span></div>
|
<div style="font-size: 13px; width: 300px; height: 30px;">Key buffer: <span id="command-display"></span></div>
|
||||||
|
<div style="font-size: 13px; width: 300px; height: 30px;">Vim mode: <span id="vim-mode"></span></div>
|
||||||
|
|
||||||
<p>The vim keybindings are enabled by including <code><a
|
<p>The vim keybindings are enabled by including <code><a
|
||||||
href="../keymap/vim.js">keymap/vim.js</a></code> and setting the
|
href="../keymap/vim.js">keymap/vim.js</a></code> and setting the
|
||||||
|
@ -101,12 +102,16 @@ become a complete vim implementation</p>
|
||||||
var keys = '';
|
var keys = '';
|
||||||
CodeMirror.on(editor, 'vim-keypress', function(key) {
|
CodeMirror.on(editor, 'vim-keypress', function(key) {
|
||||||
keys = keys + key;
|
keys = keys + key;
|
||||||
commandDisplay.innerHTML = keys;
|
commandDisplay.innerText = keys;
|
||||||
});
|
});
|
||||||
CodeMirror.on(editor, 'vim-command-done', function(e) {
|
CodeMirror.on(editor, 'vim-command-done', function(e) {
|
||||||
keys = '';
|
keys = '';
|
||||||
commandDisplay.innerHTML = keys;
|
commandDisplay.innerHTML = keys;
|
||||||
});
|
});
|
||||||
|
var vimMode = document.getElementById('vim-mode');
|
||||||
|
CodeMirror.on(editor, 'vim-mode-change', function(e) {
|
||||||
|
vimMode.innerText = JSON.stringify(e);
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
</article>
|
</article>
|
||||||
|
|
149
keymap/vim.js
149
keymap/vim.js
|
@ -141,6 +141,8 @@
|
||||||
{ keys: 'gU', type: 'operator', operator: 'changeCase', operatorArgs: {toLower: false}, isEdit: true },
|
{ keys: 'gU', type: 'operator', operator: 'changeCase', operatorArgs: {toLower: false}, isEdit: true },
|
||||||
{ keys: 'n', type: 'motion', motion: 'findNext', motionArgs: { forward: true, toJumplist: true }},
|
{ keys: 'n', type: 'motion', motion: 'findNext', motionArgs: { forward: true, toJumplist: true }},
|
||||||
{ keys: 'N', type: 'motion', motion: 'findNext', motionArgs: { forward: false, toJumplist: true }},
|
{ keys: 'N', type: 'motion', motion: 'findNext', motionArgs: { forward: false, toJumplist: true }},
|
||||||
|
{ keys: 'gn', type: 'motion', motion: 'findAndSelectNextInclusive', motionArgs: { forward: true }},
|
||||||
|
{ keys: 'gN', type: 'motion', motion: 'findAndSelectNextInclusive', motionArgs: { forward: false }},
|
||||||
// Operator-Motion dual commands
|
// Operator-Motion dual commands
|
||||||
{ keys: 'x', type: 'operatorMotion', operator: 'delete', motion: 'moveByCharacters', motionArgs: { forward: true }, operatorMotionArgs: { visualLine: false }},
|
{ keys: 'x', type: 'operatorMotion', operator: 'delete', motion: 'moveByCharacters', motionArgs: { forward: true }, operatorMotionArgs: { visualLine: false }},
|
||||||
{ keys: 'X', type: 'operatorMotion', operator: 'delete', motion: 'moveByCharacters', motionArgs: { forward: false }, operatorMotionArgs: { visualLine: true }},
|
{ keys: 'X', type: 'operatorMotion', operator: 'delete', motion: 'moveByCharacters', motionArgs: { forward: false }, operatorMotionArgs: { visualLine: true }},
|
||||||
|
@ -1576,7 +1578,7 @@
|
||||||
motionArgs.repeat = repeat;
|
motionArgs.repeat = repeat;
|
||||||
clearInputState(cm);
|
clearInputState(cm);
|
||||||
if (motion) {
|
if (motion) {
|
||||||
var motionResult = motions[motion](cm, origHead, motionArgs, vim);
|
var motionResult = motions[motion](cm, origHead, motionArgs, vim, inputState);
|
||||||
vim.lastMotion = motions[motion];
|
vim.lastMotion = motions[motion];
|
||||||
if (!motionResult) {
|
if (!motionResult) {
|
||||||
return;
|
return;
|
||||||
|
@ -1774,6 +1776,87 @@
|
||||||
highlightSearchMatches(cm, query);
|
highlightSearchMatches(cm, query);
|
||||||
return findNext(cm, prev/** prev */, query, motionArgs.repeat);
|
return findNext(cm, prev/** prev */, query, motionArgs.repeat);
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* Find and select the next occurrence of the search query. If the cursor is currently
|
||||||
|
* within a match, then find and select the current match. Otherwise, find the next occurrence in the
|
||||||
|
* appropriate direction.
|
||||||
|
*
|
||||||
|
* This differs from `findNext` in the following ways:
|
||||||
|
*
|
||||||
|
* 1. Instead of only returning the "from", this returns a "from", "to" range.
|
||||||
|
* 2. If the cursor is currently inside a search match, this selects the current match
|
||||||
|
* instead of the next match.
|
||||||
|
* 3. If there is no associated operator, this will turn on visual mode.
|
||||||
|
*/
|
||||||
|
findAndSelectNextInclusive: function(cm, _head, motionArgs, vim, prevInputState) {
|
||||||
|
var state = getSearchState(cm);
|
||||||
|
var query = state.getQuery();
|
||||||
|
|
||||||
|
if (!query) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var prev = !motionArgs.forward;
|
||||||
|
prev = (state.isReversed()) ? !prev : prev;
|
||||||
|
|
||||||
|
// next: [from, to] | null
|
||||||
|
var next = findNextFromAndToInclusive(cm, prev, query, motionArgs.repeat, vim);
|
||||||
|
|
||||||
|
// No matches.
|
||||||
|
if (!next) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there's an operator that will be executed, return the selection.
|
||||||
|
if (prevInputState.operator) {
|
||||||
|
return next;
|
||||||
|
}
|
||||||
|
|
||||||
|
// At this point, we know that there is no accompanying operator -- let's
|
||||||
|
// deal with visual mode in order to select an appropriate match.
|
||||||
|
|
||||||
|
var from = next[0];
|
||||||
|
// For whatever reason, when we use the "to" as returned by searchcursor.js directly,
|
||||||
|
// the resulting selection is extended by 1 char. Let's shrink it so that only the
|
||||||
|
// match is selected.
|
||||||
|
var to = Pos(next[1].line, next[1].ch - 1);
|
||||||
|
|
||||||
|
if (vim.visualMode) {
|
||||||
|
// If we were in visualLine or visualBlock mode, get out of it.
|
||||||
|
if (vim.visualLine || vim.visualBlock) {
|
||||||
|
vim.visualLine = false;
|
||||||
|
vim.visualBlock = false;
|
||||||
|
CodeMirror.signal(cm, "vim-mode-change", {mode: "visual", subMode: ""});
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we're currently in visual mode, we should extend the selection to include
|
||||||
|
// the search result.
|
||||||
|
var anchor = vim.sel.anchor;
|
||||||
|
if (anchor) {
|
||||||
|
if (state.isReversed()) {
|
||||||
|
if (motionArgs.forward) {
|
||||||
|
return [anchor, from];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [anchor, to];
|
||||||
|
} else {
|
||||||
|
if (motionArgs.forward) {
|
||||||
|
return [anchor, to];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [anchor, from];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Let's turn visual mode on.
|
||||||
|
vim.visualMode = true;
|
||||||
|
vim.visualLine = false;
|
||||||
|
vim.visualBlock = false;
|
||||||
|
CodeMirror.signal(cm, "vim-mode-change", {mode: "visual", subMode: ""});
|
||||||
|
}
|
||||||
|
|
||||||
|
return prev ? [to, from] : [from, to];
|
||||||
|
},
|
||||||
goToMark: function(cm, _head, motionArgs, vim) {
|
goToMark: function(cm, _head, motionArgs, vim) {
|
||||||
var pos = getMarkPos(cm, vim, motionArgs.selectedCharacter);
|
var pos = getMarkPos(cm, vim, motionArgs.selectedCharacter);
|
||||||
if (pos) {
|
if (pos) {
|
||||||
|
@ -1869,8 +1952,8 @@
|
||||||
// move to previous/next line is triggered.
|
// move to previous/next line is triggered.
|
||||||
if (line < first && cur.line == first){
|
if (line < first && cur.line == first){
|
||||||
return this.moveToStartOfLine(cm, head, motionArgs, vim);
|
return this.moveToStartOfLine(cm, head, motionArgs, vim);
|
||||||
}else if (line > last && cur.line == last){
|
} else if (line > last && cur.line == last){
|
||||||
return this.moveToEol(cm, head, motionArgs, vim, true);
|
return moveToEol(cm, head, motionArgs, vim, true);
|
||||||
}
|
}
|
||||||
if (motionArgs.toFirstChar){
|
if (motionArgs.toFirstChar){
|
||||||
endCh=findFirstNonWhiteSpaceCharacter(cm.getLine(line));
|
endCh=findFirstNonWhiteSpaceCharacter(cm.getLine(line));
|
||||||
|
@ -1972,16 +2055,8 @@
|
||||||
vim.lastHSPos = cm.charCoords(head,'div').left;
|
vim.lastHSPos = cm.charCoords(head,'div').left;
|
||||||
return moveToColumn(cm, repeat);
|
return moveToColumn(cm, repeat);
|
||||||
},
|
},
|
||||||
moveToEol: function(cm, head, motionArgs, vim, keepHPos) {
|
moveToEol: function(cm, head, motionArgs, vim) {
|
||||||
var cur = head;
|
return moveToEol(cm, head, motionArgs, vim, false);
|
||||||
var retval= Pos(cur.line + motionArgs.repeat - 1, Infinity);
|
|
||||||
var end=cm.clipPos(retval);
|
|
||||||
end.ch--;
|
|
||||||
if (!keepHPos) {
|
|
||||||
vim.lastHPos = Infinity;
|
|
||||||
vim.lastHSPos = cm.charCoords(end,'div').left;
|
|
||||||
}
|
|
||||||
return retval;
|
|
||||||
},
|
},
|
||||||
moveToFirstNonWhiteSpaceCharacter: function(cm, head) {
|
moveToFirstNonWhiteSpaceCharacter: function(cm, head) {
|
||||||
// Go to the start of the line where the text begins, or the end for
|
// Go to the start of the line where the text begins, or the end for
|
||||||
|
@ -3609,6 +3684,18 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function moveToEol(cm, head, motionArgs, vim, keepHPos) {
|
||||||
|
var cur = head;
|
||||||
|
var retval= Pos(cur.line + motionArgs.repeat - 1, Infinity);
|
||||||
|
var end=cm.clipPos(retval);
|
||||||
|
end.ch--;
|
||||||
|
if (!keepHPos) {
|
||||||
|
vim.lastHPos = Infinity;
|
||||||
|
vim.lastHSPos = cm.charCoords(end,'div').left;
|
||||||
|
}
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
function moveToCharacter(cm, repeat, forward, character) {
|
function moveToCharacter(cm, repeat, forward, character) {
|
||||||
var cur = cm.getCursor();
|
var cur = cm.getCursor();
|
||||||
var start = cur.ch;
|
var start = cur.ch;
|
||||||
|
@ -4350,6 +4437,42 @@
|
||||||
return cursor.from();
|
return cursor.from();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Pretty much the same as `findNext`, except for the following differences:
|
||||||
|
*
|
||||||
|
* 1. Before starting the search, move to the previous search. This way if our cursor is
|
||||||
|
* already inside a match, we should return the current match.
|
||||||
|
* 2. Rather than only returning the cursor's from, we return the cursor's from and to as a tuple.
|
||||||
|
*/
|
||||||
|
function findNextFromAndToInclusive(cm, prev, query, repeat, vim) {
|
||||||
|
if (repeat === undefined) { repeat = 1; }
|
||||||
|
return cm.operation(function() {
|
||||||
|
var pos = cm.getCursor();
|
||||||
|
var cursor = cm.getSearchCursor(query, pos);
|
||||||
|
|
||||||
|
// Go back one result to ensure that if the cursor is currently a match, we keep it.
|
||||||
|
var found = cursor.find(!prev);
|
||||||
|
|
||||||
|
// If we haven't moved, go back one more (similar to if i==0 logic in findNext).
|
||||||
|
if (!vim.visualMode && found && cursorEqual(cursor.from(), pos)) {
|
||||||
|
cursor.find(!prev);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < repeat; i++) {
|
||||||
|
found = cursor.find(prev);
|
||||||
|
if (!found) {
|
||||||
|
// SearchCursor may have returned null because it hit EOF, wrap
|
||||||
|
// around and try again.
|
||||||
|
cursor = cm.getSearchCursor(query,
|
||||||
|
(prev) ? Pos(cm.lastLine()) : Pos(cm.firstLine(), 0) );
|
||||||
|
if (!cursor.find(prev)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return [cursor.from(), cursor.to()];
|
||||||
|
});
|
||||||
|
}
|
||||||
function clearSearchHighlight(cm) {
|
function clearSearchHighlight(cm) {
|
||||||
var state = getSearchState(cm);
|
var state = getSearchState(cm);
|
||||||
cm.removeOverlay(getSearchState(cm).getOverlay());
|
cm.removeOverlay(getSearchState(cm).getOverlay());
|
||||||
|
|
169
test/vim_test.js
169
test/vim_test.js
|
@ -2579,6 +2579,91 @@ testVim('/ and n/N', function(cm, vim, helpers) {
|
||||||
helpers.doKeys('2', '/');
|
helpers.doKeys('2', '/');
|
||||||
helpers.assertCursorAt(1, 6);
|
helpers.assertCursorAt(1, 6);
|
||||||
}, { value: 'match nope match \n nope Match' });
|
}, { value: 'match nope match \n nope Match' });
|
||||||
|
testVim('/ and gn selects the appropriate word', function(cm, vim, helpers) {
|
||||||
|
cm.openDialog = helpers.fakeOpenDialog('match');
|
||||||
|
helpers.doKeys('/');
|
||||||
|
helpers.assertCursorAt(0, 11);
|
||||||
|
|
||||||
|
// gn should highlight the the current word while it is within a match.
|
||||||
|
|
||||||
|
// gn when cursor is in beginning of match
|
||||||
|
helpers.doKeys('gn', '<Esc>');
|
||||||
|
helpers.assertCursorAt(0, 15);
|
||||||
|
|
||||||
|
// gn when cursor is at end of match
|
||||||
|
helpers.doKeys('gn', '<Esc>');
|
||||||
|
helpers.doKeys('<Esc>');
|
||||||
|
helpers.assertCursorAt(0, 15);
|
||||||
|
|
||||||
|
// consecutive gns should extend the selection
|
||||||
|
helpers.doKeys('gn');
|
||||||
|
helpers.assertCursorAt(0, 16);
|
||||||
|
helpers.doKeys('gn');
|
||||||
|
helpers.assertCursorAt(1, 11);
|
||||||
|
|
||||||
|
// we should have selected the second and third "match"
|
||||||
|
helpers.doKeys('d');
|
||||||
|
eq('match nope ', cm.getValue());
|
||||||
|
}, { value: 'match nope match \n nope Match' });
|
||||||
|
testVim('/ and gN selects the appropriate word', function(cm, vim, helpers) {
|
||||||
|
cm.openDialog = helpers.fakeOpenDialog('match');
|
||||||
|
helpers.doKeys('/');
|
||||||
|
helpers.assertCursorAt(0, 11);
|
||||||
|
|
||||||
|
// gN when cursor is at beginning of match
|
||||||
|
helpers.doKeys('gN', '<Esc>');
|
||||||
|
helpers.assertCursorAt(0, 11);
|
||||||
|
|
||||||
|
// gN when cursor is at end of match
|
||||||
|
helpers.doKeys('e', 'gN', '<Esc>');
|
||||||
|
helpers.assertCursorAt(0, 11);
|
||||||
|
|
||||||
|
// consecutive gNs should extend the selection
|
||||||
|
helpers.doKeys('gN');
|
||||||
|
helpers.assertCursorAt(0, 11);
|
||||||
|
helpers.doKeys('gN');
|
||||||
|
helpers.assertCursorAt(0, 0);
|
||||||
|
|
||||||
|
// we should have selected the first and second "match"
|
||||||
|
helpers.doKeys('d');
|
||||||
|
eq(' \n nope Match', cm.getValue());
|
||||||
|
}, { value: 'match nope match \n nope Match' })
|
||||||
|
testVim('/ and gn with an associated operator', function(cm, vim, helpers) {
|
||||||
|
cm.openDialog = helpers.fakeOpenDialog('match');
|
||||||
|
helpers.doKeys('/');
|
||||||
|
helpers.assertCursorAt(0, 11);
|
||||||
|
|
||||||
|
helpers.doKeys('c', 'gn', 'changed', '<Esc>');
|
||||||
|
|
||||||
|
// change the current match.
|
||||||
|
eq('match nope changed \n nope Match', cm.getValue());
|
||||||
|
|
||||||
|
// change the next match.
|
||||||
|
helpers.doKeys('.');
|
||||||
|
eq('match nope changed \n nope changed', cm.getValue());
|
||||||
|
|
||||||
|
// change the final match.
|
||||||
|
helpers.doKeys('.');
|
||||||
|
eq('changed nope changed \n nope changed', cm.getValue());
|
||||||
|
}, { value: 'match nope match \n nope Match' });
|
||||||
|
testVim('/ and gN with an associated operator', function(cm, vim, helpers) {
|
||||||
|
cm.openDialog = helpers.fakeOpenDialog('match');
|
||||||
|
helpers.doKeys('/');
|
||||||
|
helpers.assertCursorAt(0, 11);
|
||||||
|
|
||||||
|
helpers.doKeys('c', 'gN', 'changed', '<Esc>');
|
||||||
|
|
||||||
|
// change the current match.
|
||||||
|
eq('match nope changed \n nope Match', cm.getValue());
|
||||||
|
|
||||||
|
// change the next match.
|
||||||
|
helpers.doKeys('.');
|
||||||
|
eq('changed nope changed \n nope Match', cm.getValue());
|
||||||
|
|
||||||
|
// change the final match.
|
||||||
|
helpers.doKeys('.');
|
||||||
|
eq('changed nope changed \n nope changed', cm.getValue());
|
||||||
|
}, { value: 'match nope match \n nope Match' });
|
||||||
testVim('/_case', function(cm, vim, helpers) {
|
testVim('/_case', function(cm, vim, helpers) {
|
||||||
cm.openDialog = helpers.fakeOpenDialog('Match');
|
cm.openDialog = helpers.fakeOpenDialog('Match');
|
||||||
helpers.doKeys('/');
|
helpers.doKeys('/');
|
||||||
|
@ -2679,6 +2764,90 @@ testVim('? and n/N', function(cm, vim, helpers) {
|
||||||
helpers.doKeys('2', '?');
|
helpers.doKeys('2', '?');
|
||||||
helpers.assertCursorAt(0, 11);
|
helpers.assertCursorAt(0, 11);
|
||||||
}, { value: 'match nope match \n nope Match' });
|
}, { value: 'match nope match \n nope Match' });
|
||||||
|
testVim('? and gn selects the appropriate word', function(cm, vim, helpers) {
|
||||||
|
cm.openDialog = helpers.fakeOpenDialog('match');
|
||||||
|
helpers.doKeys('?', 'n');
|
||||||
|
helpers.assertCursorAt(0, 11);
|
||||||
|
|
||||||
|
// gn should highlight the the current word while it is within a match.
|
||||||
|
|
||||||
|
// gn when cursor is in beginning of match
|
||||||
|
helpers.doKeys('gn', '<Esc>');
|
||||||
|
helpers.assertCursorAt(0, 11);
|
||||||
|
|
||||||
|
// gn when cursor is at end of match
|
||||||
|
helpers.doKeys('e', 'gn', '<Esc>');
|
||||||
|
helpers.assertCursorAt(0, 11);
|
||||||
|
|
||||||
|
// consecutive gns should extend the selection
|
||||||
|
helpers.doKeys('gn');
|
||||||
|
helpers.assertCursorAt(0, 11);
|
||||||
|
helpers.doKeys('gn');
|
||||||
|
helpers.assertCursorAt(0, 0);
|
||||||
|
|
||||||
|
// we should have selected the first and second "match"
|
||||||
|
helpers.doKeys('d');
|
||||||
|
eq(' \n nope Match', cm.getValue());
|
||||||
|
}, { value: 'match nope match \n nope Match' });
|
||||||
|
testVim('? and gN selects the appropriate word', function(cm, vim, helpers) {
|
||||||
|
cm.openDialog = helpers.fakeOpenDialog('match');
|
||||||
|
helpers.doKeys('?', 'n');
|
||||||
|
helpers.assertCursorAt(0, 11);
|
||||||
|
|
||||||
|
// gN when cursor is at beginning of match
|
||||||
|
helpers.doKeys('gN', '<Esc>');
|
||||||
|
helpers.assertCursorAt(0, 15);
|
||||||
|
|
||||||
|
// gN when cursor is at end of match
|
||||||
|
helpers.doKeys('gN', '<Esc>');
|
||||||
|
helpers.assertCursorAt(0, 15);
|
||||||
|
|
||||||
|
// consecutive gNs should extend the selection
|
||||||
|
helpers.doKeys('gN');
|
||||||
|
helpers.assertCursorAt(0, 16);
|
||||||
|
helpers.doKeys('gN');
|
||||||
|
helpers.assertCursorAt(1, 11);
|
||||||
|
|
||||||
|
// we should have selected the second and third "match"
|
||||||
|
helpers.doKeys('d');
|
||||||
|
eq('match nope ', cm.getValue());
|
||||||
|
}, { value: 'match nope match \n nope Match' })
|
||||||
|
testVim('? and gn with an associated operator', function(cm, vim, helpers) {
|
||||||
|
cm.openDialog = helpers.fakeOpenDialog('match');
|
||||||
|
helpers.doKeys('?', 'n');
|
||||||
|
helpers.assertCursorAt(0, 11);
|
||||||
|
|
||||||
|
helpers.doKeys('c', 'gn', 'changed', '<Esc>');
|
||||||
|
|
||||||
|
// change the current match.
|
||||||
|
eq('match nope changed \n nope Match', cm.getValue());
|
||||||
|
|
||||||
|
// change the next match.
|
||||||
|
helpers.doKeys('.');
|
||||||
|
eq('changed nope changed \n nope Match', cm.getValue());
|
||||||
|
|
||||||
|
// change the final match.
|
||||||
|
helpers.doKeys('.');
|
||||||
|
eq('changed nope changed \n nope changed', cm.getValue());
|
||||||
|
}, { value: 'match nope match \n nope Match' });
|
||||||
|
testVim('? and gN with an associated operator', function(cm, vim, helpers) {
|
||||||
|
cm.openDialog = helpers.fakeOpenDialog('match');
|
||||||
|
helpers.doKeys('?', 'n');
|
||||||
|
helpers.assertCursorAt(0, 11);
|
||||||
|
|
||||||
|
helpers.doKeys('c', 'gN', 'changed', '<Esc>');
|
||||||
|
|
||||||
|
// change the current match.
|
||||||
|
eq('match nope changed \n nope Match', cm.getValue());
|
||||||
|
|
||||||
|
// change the next match.
|
||||||
|
helpers.doKeys('.');
|
||||||
|
eq('match nope changed \n nope changed', cm.getValue());
|
||||||
|
|
||||||
|
// change the final match.
|
||||||
|
helpers.doKeys('.');
|
||||||
|
eq('changed nope changed \n nope changed', cm.getValue());
|
||||||
|
}, { value: 'match nope match \n nope Match' });
|
||||||
testVim('*', function(cm, vim, helpers) {
|
testVim('*', function(cm, vim, helpers) {
|
||||||
cm.setCursor(0, 9);
|
cm.setCursor(0, 9);
|
||||||
helpers.doKeys('*');
|
helpers.doKeys('*');
|
||||||
|
|
Loading…
Reference in New Issue