Skip to content

Commit 58eb03d

Browse files
committed
fix :'a,.y ex command
1 parent 71919db commit 58eb03d

2 files changed

Lines changed: 114 additions & 27 deletions

File tree

src/vim.js

Lines changed: 95 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,7 @@ export function initVim(CM) {
297297
{ name: 'startinsert', shortName: 'start' },
298298
{ name: 'nohlsearch', shortName: 'noh' },
299299
{ name: 'yank', shortName: 'y' },
300+
{ name: 'put', shortName: 'pu' },
300301
{ name: 'delmarks', shortName: 'delm' },
301302
{ name: 'marks', excludeFromCommandHistory: true },
302303
{ name: 'registers', shortName: 'reg', excludeFromCommandHistory: true },
@@ -1983,6 +1984,12 @@ export function initVim(CM) {
19831984
if (vim.visualMode) {
19841985
promptOptions.value = '\'<,\'>';
19851986
promptOptions.selectValueOnOpen = false;
1987+
} else {
1988+
var repeat = vim.inputState.getRepeat();
1989+
if (repeat > 1) {
1990+
promptOptions.value = '.,.+' + (repeat - 1);
1991+
promptOptions.selectValueOnOpen = false;
1992+
}
19861993
}
19871994
showPrompt(cm, promptOptions);
19881995
}
@@ -2901,6 +2908,9 @@ export function initVim(CM) {
29012908
vimGlobalState.registerController.pushText(
29022909
args.registerName, 'yank',
29032910
text, args.linewise, vim.visualBlock);
2911+
2912+
var lineCount = Math.abs(cm.getCursor("end").line - cm.getCursor("start").line) || 1;
2913+
showConfirm(cm, lineCount + ' lines yanked' + (args.registerName ? ' into "' + args.registerName : ''), false, 1500);
29042914
return endPos;
29052915
},
29062916
rot13: function(cm, args, ranges, oldAnchor, newHead) {
@@ -3291,7 +3301,7 @@ export function initVim(CM) {
32913301
if (actionArgs.repeat > 1) {
32923302
text = Array(actionArgs.repeat + 1).join(text);
32933303
}
3294-
var linewise = register.linewise;
3304+
var linewise = actionArgs.linewise == undefined ? register.linewise : actionArgs.linewise;
32953305
var blockwise = register.blockwise;
32963306
var textLines = blockwise ? text.split('\n') : undefined;
32973307
if (textLines) {
@@ -5288,8 +5298,8 @@ export function initVim(CM) {
52885298
return n;
52895299
}
52905300

5291-
/** @arg {CodeMirror} cm @arg {any} template @arg {boolean} [long]*/
5292-
function showConfirm(cm, template, long) {
5301+
/** @arg {CodeMirror} cm @arg {any} template @arg {boolean} [long] @arg {number} [duration]*/
5302+
function showConfirm(cm, template, long, duration) {
52935303
var pre = dom('div', {$color: 'red', $whiteSpace: 'pre', class: 'cm-vim-message'}, template);
52945304
if (cm.openNotification) {
52955305
if (long) {
@@ -5299,9 +5309,9 @@ export function initVim(CM) {
52995309
}
53005310
cm.state.closeVimNotification = cm.openNotification(pre, {bottom: true, duration: 0});
53015311
} else {
5302-
cm.openNotification(pre, {bottom: true, duration: 15000});
5312+
cm.openNotification(pre, {bottom: true, duration: duration || 15000});
53035313
}
5304-
} else {
5314+
} else if (!duration) {
53055315
alert(pre.innerText);
53065316
}
53075317
}
@@ -5628,7 +5638,7 @@ export function initVim(CM) {
56285638
this.parseInput_(cm, inputStream, params);
56295639
} catch(e) {
56305640
showConfirm(cm, e + "");
5631-
throw e;
5641+
return;
56325642
}
56335643

56345644
if (vim.visualMode) {
@@ -5689,6 +5699,12 @@ export function initVim(CM) {
56895699
if (inputStream.eat('%')) {
56905700
result.line = cm.firstLine();
56915701
result.lineEnd = cm.lastLine();
5702+
} else if (inputStream.eat('*')) {
5703+
var lastSelection = cm.state.vim.lastSelection;
5704+
var anchor = lastSelection?.anchorMark.find()?.line || 0;
5705+
var head = lastSelection?.headMark.find()?.line || 0;
5706+
result.line = Math.max(anchor, head);
5707+
result.lineEnd = Math.min(anchor, head);
56925708
} else {
56935709
result.line = this.parseLineSpec_(cm, inputStream);
56945710
if (result.line !== undefined && inputStream.eat(',')) {
@@ -5708,6 +5724,7 @@ export function initVim(CM) {
57085724
result.selectionLineEnd = result.lineEnd;
57095725
}
57105726

5727+
inputStream.eatSpace();
57115728
// Parse command name.
57125729
var commandMatch = inputStream.match(/^(\w+|!!|@@|[!#&*<=>@~])/);
57135730
if (commandMatch) {
@@ -5723,43 +5740,68 @@ export function initVim(CM) {
57235740
* @param {import("@codemirror/language").StringStream} inputStream
57245741
*/
57255742
parseLineSpec_(cm, inputStream) {
5726-
var numberMatch = inputStream.match(/^(\d+)/);
5743+
var numberMatch = inputStream.match(/^([\d]+)/);
57275744
if (numberMatch) {
5728-
// Absolute line number plus offset (N+M or N-M) is probably a typo,
5729-
// not something the user actually wanted. (NB: vim does allow this.)
5730-
return parseInt(numberMatch[1], 10) - 1;
5745+
return this.parseLineSpecOffset_(cm, inputStream, parseInt(numberMatch[1], 10) - 1);
57315746
}
57325747
switch (inputStream.next()) {
57335748
case '.':
5734-
return this.parseLineSpecOffset_(inputStream, cm.getCursor().line);
5749+
return this.parseLineSpecOffset_(cm, inputStream, cm.getCursor().line);
57355750
case '$':
5736-
return this.parseLineSpecOffset_(inputStream, cm.lastLine());
5751+
return this.parseLineSpecOffset_(cm, inputStream, cm.lastLine());
57375752
case '\'':
57385753
var markName = inputStream.next() || "";
57395754
var markPos = getMarkPos(cm, cm.state.vim, markName);
57405755
if (!markPos) throw new Error('Mark not set');
5741-
return this.parseLineSpecOffset_(inputStream, markPos.line);
5756+
return this.parseLineSpecOffset_(cm, inputStream, markPos.line);
57425757
case '-':
57435758
case '+':
5759+
case '/':
5760+
case '?':
57445761
inputStream.backUp(1);
57455762
// Offset is relative to current line if not otherwise specified.
5746-
return this.parseLineSpecOffset_(inputStream, cm.getCursor().line);
5763+
return this.parseLineSpecOffset_(cm, inputStream, cm.getCursor().line);
57475764
default:
57485765
inputStream.backUp(1);
57495766
return undefined;
57505767
}
57515768
}
57525769
/**
5770+
* @param {CodeMirrorV} cm
57535771
* @param {string | import("@codemirror/language").StringStream} inputStream
57545772
* @param {number} line
57555773
*/
5756-
parseLineSpecOffset_(inputStream, line) {
5757-
var offsetMatch = inputStream.match(/^([+-])?(\d+)/);
5758-
if (offsetMatch) {
5759-
var offset = parseInt(offsetMatch[2], 10);
5760-
if (offsetMatch[1] == "-") {
5761-
line -= offset;
5774+
parseLineSpecOffset_(cm, inputStream, line) {
5775+
while (true) {
5776+
var offsetMatch = inputStream.match(/^([\/\?]|\\[\?\/])|([+-]?)(\d*)/)
5777+
if (!offsetMatch || !offsetMatch[0])
5778+
break;
5779+
5780+
if (offsetMatch[1]) {
5781+
var queryString = "";
5782+
var forward = !offsetMatch[1].endsWith("?");
5783+
if (offsetMatch[1].length == 1) {
5784+
var queryMatch = inputStream.match(forward ? /^([^\/\\]|\\\/)*/ : /^([^\/\?]|\\\?)*/);
5785+
inputStream.match(forward ? /^\/?/ : /^\??/);
5786+
queryString = queryMatch && queryMatch[0];
5787+
}
5788+
if (!queryString) {
5789+
queryString = vimGlobalState.registerController.getRegister('/')?.toString() || "";
5790+
}
5791+
var query = new RegExp(queryString);
5792+
var cursor = cm.getSearchCursor(query, new Pos(line + (forward ? 1 : 0), 0));
5793+
if (forward) {
5794+
cursor.findNext();
5795+
} else {
5796+
cursor.findPrevious();
5797+
}
5798+
var nextPos = cursor.from();
5799+
if (!nextPos) {
5800+
throw new Error("Pattern not found" + query);
5801+
}
5802+
line = nextPos.line;
57625803
} else {
5804+
var offset = parseInt(offsetMatch[2] + (offsetMatch[3] || '1'), 10);
57635805
line += offset;
57645806
}
57655807
}
@@ -6365,13 +6407,40 @@ export function initVim(CM) {
63656407
nohlsearch: function(cm) {
63666408
clearSearchHighlight(cm);
63676409
},
6368-
/** @arg {CodeMirrorV} cm */
6369-
yank: function (cm) {
6370-
var cur = copyCursor(cm.getCursor());
6371-
var line = cur.line;
6372-
var lineText = cm.getLine(line);
6410+
/** @arg {CodeMirrorV} cm @arg {ExParams} params */
6411+
yank: function (cm, params) {
6412+
var line = params.selectionLine;
6413+
var lineEnd = isNaN(params.selectionLineEnd) ? line : params.selectionLineEnd;
6414+
if (lineEnd < line) {
6415+
var tmp = lineEnd;
6416+
lineEnd = line;
6417+
line = tmp;
6418+
}
6419+
var text = cm.getRange(new Pos(line, 0), new Pos(lineEnd + 1, 0));
6420+
var registerName = params.args && params.args[0] ? params.args[0] : '0';
63736421
vimGlobalState.registerController.pushText(
6374-
'0', 'yank', lineText, true, true);
6422+
registerName, 'yank', text, true, false);
6423+
showConfirm(cm, (lineEnd + 1 - line) + ' lines yanked' + (registerName ? ' into "' + registerName : ''), false, 1500);
6424+
},
6425+
/** @arg {CodeMirrorV} cm @arg {ExParams} params @arg {boolean} [matchIndent]*/
6426+
put: function(cm, params, matchIndent) {
6427+
var actionArgs = { after: true, isEdit: true, matchIndent: !!matchIndent, repeat: 1, lineWise: true, registerName: '' };
6428+
var args = params.args || [];
6429+
if (args[0] == "!") {
6430+
actionArgs.after = false;
6431+
args.shift();
6432+
}
6433+
if (args[0]) {
6434+
actionArgs.registerName = args[0];
6435+
}
6436+
var line = params.selectionLine;
6437+
if (line != undefined)
6438+
cm.setCursor(new Pos(line, 0));
6439+
actions.paste(cm, actionArgs, cm.state.vim);
6440+
},
6441+
/** @arg {CodeMirrorV} cm @arg {ExParams} params*/
6442+
iput: function(cm, params) {
6443+
this.put(cm, params, true);
63756444
},
63766445
/** @arg {CodeMirrorV} cm @arg {ExParams} params*/
63776446
delete: function(cm, params) {

test/vim_test.js

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4216,6 +4216,8 @@ testVim('ex_go_to_line', function(cm, vim, helpers) {
42164216
cm.setCursor(0, 0);
42174217
helpers.doEx('4');
42184218
helpers.assertCursorAt(3, 0);
4219+
helpers.doEx('4-1');
4220+
helpers.assertCursorAt(2, 0);
42194221
}, { value: 'a\nb\nc\nd\ne\n'});
42204222
testVim('ex_go_to_mark', function(cm, vim, helpers) {
42214223
cm.setCursor(3, 0);
@@ -4246,6 +4248,22 @@ testVim('ex_go_to_mark_offset', function(cm, vim, helpers) {
42464248
helpers.doEx('\'a+2');
42474249
helpers.assertCursorAt(4, 0);
42484250
}, { value: 'a\nb\nc\nd\ne\n'});
4251+
testVim('ex_advanced_range_syntax', function(cm, vim, helpers) {
4252+
helpers.doEx('0/m');
4253+
helpers.assertCursorAt(3, 0);
4254+
helpers.doKeys('m', 'a');
4255+
helpers.doEx('/m//m/');
4256+
helpers.assertCursorAt(8, 0);
4257+
helpers.doEx("'a,.y x");
4258+
helpers.doEx('10?m??m?put! x');
4259+
eq("1\n2\n3\n4m\n5\n6\n4m\n5\n6\n7m\n8\n9m\n7m\n8\n9m\n10\n", cm.getValue());
4260+
helpers.doEx('1+++2+/m/,1/m//m/+2-/m/+2d');
4261+
eq("1\n2\n3\n4m\n5\n6\n7m\n8\n9m\n10\n", cm.getValue());
4262+
helpers.doKeys('3', ':');
4263+
eq(cm.getWrapperElement().querySelector("input").value, ".,.+2");
4264+
helpers.doKeys('d', '\n');
4265+
eq("1\n2\n3\n4m\n5\n6\n10\n", cm.getValue());
4266+
}, {value: "1\n2\n3\n4m\n5\n6\n7m\n8\n9m\n10\n"});
42494267
testVim('ex_write', function(cm, vim, helpers) {
42504268
var tmp = CodeMirror.commands.save;
42514269
var written;
@@ -5180,7 +5198,7 @@ testVim('ex_api_test', function(cm, vim, helpers) {
51805198
// Testing ex-commands with non-alpha names.
51815199
testVim('ex_special_names', function(cm, vim, helpers) {
51825200
var ran,val;
5183-
var cmds = ['!','!!','#','&','*','<','=','>','@','@@','~','regtest1','RT2'];
5201+
var cmds = ['!','!!','#','&','<','=','>','@','@@','~','regtest1','RT2'];
51845202
cmds.forEach(function(name){
51855203
CodeMirror.Vim.defineEx(name,'',function(cm,params){
51865204
ran=params.commandName;

0 commit comments

Comments
 (0)