Skip to content

Commit 554831e

Browse files
authored
Merge pull request ajaxorg#4469 from taikamurmeli/master
Fix vim replace in range command where replacement includes line breaks
2 parents 94422a4 + 7fb421b commit 554831e

File tree

2 files changed

+43
-17
lines changed

2 files changed

+43
-17
lines changed

lib/ace/keyboard/vim.js

+23-17
Original file line numberDiff line numberDiff line change
@@ -140,10 +140,10 @@ define(function(require, exports, module) {
140140
TextModeTokenRe.lastIndex = 0;
141141
return TextModeTokenRe.test(ch);
142142
};
143-
143+
144144
(function() {
145145
oop.implement(CodeMirror.prototype, EventEmitter);
146-
146+
147147
this.destroy = function() {
148148
this.ace.off('change', this.onChange);
149149
this.ace.off('changeSelection', this.onSelectionChange);
@@ -263,7 +263,7 @@ define(function(require, exports, module) {
263263
r.cursor = Range.comparePoints(r.start, head) ? r.end : r.start;
264264
return r;
265265
});
266-
266+
267267
if (this.ace.inVirtualSelectionMode) {
268268
this.ace.selection.fromOrientedRange(ranges[0]);
269269
return;
@@ -306,7 +306,7 @@ define(function(require, exports, module) {
306306
var rowShift = (end.row - start.row) * (isInsert ? 1 : -1);
307307
var colShift = (end.column - start.column) * (isInsert ? 1 : -1);
308308
if (isInsert) end = start;
309-
309+
310310
for (var i in this.marks) {
311311
var point = this.marks[i];
312312
var cmp = Range.comparePoints(point, start);
@@ -473,7 +473,7 @@ define(function(require, exports, module) {
473473
if (!e) e = s;
474474
return this.ace.session.replace(new Range(s.line, s.ch, e.line, e.ch), text);
475475
};
476-
this.replaceSelection =
476+
this.replaceSelection =
477477
this.replaceSelections = function(p) {
478478
var sel = this.ace.selection;
479479
if (this.ace.inVirtualSelectionMode) {
@@ -790,12 +790,12 @@ dom.importCssString(".normal-mode .ace_cursor{\
790790
inp.value = newVal;
791791
} else {
792792
if (closed) return;
793-
793+
794794
if (newVal && newVal.type == "blur") {
795795
if (document.activeElement === inp)
796796
return;
797797
}
798-
798+
799799
me.state.dialog = null;
800800
closed = true;
801801
dialog.parentNode.removeChild(dialog);
@@ -869,7 +869,7 @@ dom.importCssString(".normal-mode .ace_cursor{\
869869
});
870870
})();
871871

872-
872+
873873
var defaultKeymap = [
874874
// Key to key mapping. This goes first to make it possible to override
875875
// existing mappings.
@@ -5883,10 +5883,16 @@ dom.importCssString(".normal-mode .ace_cursor{\
58835883
stop();
58845884
});
58855885
}
5886+
function lineBreakCount(text) {
5887+
return (text.match(/[\n\r]/g) || []).length;
5888+
}
58865889
function replace() {
58875890
var text = cm.getRange(searchCursor.from(), searchCursor.to());
58885891
var newText = text.replace(query, replaceWith);
58895892
searchCursor.replace(newText);
5893+
var lineBreakDifference = lineBreakCount(newText) - lineBreakCount(text);
5894+
lineEnd += lineBreakDifference
5895+
lastPos.line += lineBreakDifference
58905896
}
58915897
function next() {
58925898
// The below only loops to skip over multiple occurrences on the same
@@ -6378,7 +6384,7 @@ dom.importCssString(".normal-mode .ace_cursor{\
63786384
} else if (wasMultiselect && vim.visualBlock) {
63796385
vim.wasInVisualBlock = true;
63806386
}
6381-
6387+
63826388
if (key == '<Esc>' && !vim.insertMode && !vim.visualMode && wasMultiselect) {
63836389
cm.ace.exitMultiSelectMode();
63846390
} else if (visualBlock || !wasMultiselect || cm.ace.inVirtualSelectionMode) {
@@ -6397,7 +6403,7 @@ dom.importCssString(".normal-mode .ace_cursor{\
63976403
anchor = offsetCursor(anchor, 0, anchorOffset);
63986404
cm.state.vim.sel.head = head;
63996405
cm.state.vim.sel.anchor = anchor;
6400-
6406+
64016407
isHandled = handleKey(cm, key, origin);
64026408
sel.$desiredColumn = cm.state.vim.lastHPos == -1 ? null : cm.state.vim.lastHPos;
64036409
if (cm.virtualSelectionMode()) {
@@ -6425,12 +6431,12 @@ dom.importCssString(".normal-mode .ace_cursor{\
64256431
var top = pixelPos.top;
64266432
var left = pixelPos.left;
64276433
if (!vim.insertMode) {
6428-
var isbackwards = !sel.cursor
6434+
var isbackwards = !sel.cursor
64296435
? session.selection.isBackwards() || session.selection.isEmpty()
64306436
: Range.comparePoints(sel.cursor, sel.start) <= 0;
64316437
if (!isbackwards && left > w)
64326438
left -= w;
6433-
}
6439+
}
64346440
if (!vim.insertMode && vim.status) {
64356441
h = h / 2;
64366442
top += h;
@@ -6444,7 +6450,7 @@ dom.importCssString(".normal-mode .ace_cursor{\
64446450
var cm = editor.state.cm;
64456451
var vim = getVim(cm);
64466452
if (keyCode == -1) return;
6447-
6453+
64486454
// in non-insert mode we try to find the ascii key corresponding to the text in textarea
64496455
// this is needed because in languages that use latin alphabet we want to get the key that browser sends to the textarea
64506456
// and in non
@@ -6475,7 +6481,7 @@ dom.importCssString(".normal-mode .ace_cursor{\
64756481
data.inputChar = data.inputKey = null;
64766482
}
64776483
}
6478-
6484+
64796485
// ctrl-c is special it both exits mode and copies text
64806486
if (key == "c" && hashId == 1) { // key == "ctrl-c"
64816487
if (!useragent.isMac && editor.getCopyText()) {
@@ -6485,13 +6491,13 @@ dom.importCssString(".normal-mode .ace_cursor{\
64856491
return {command: "null", passEvent: true};
64866492
}
64876493
}
6488-
6494+
64896495
if (key == "esc" && !vim.insertMode && !vim.visualMode && !cm.ace.inMultiSelectMode) {
64906496
var searchState = getSearchState(cm);
64916497
var overlay = searchState.getOverlay();
64926498
if (overlay) cm.removeOverlay(overlay);
64936499
}
6494-
6500+
64956501
if (hashId == -1 || hashId & 1 || hashId === 0 && key.length > 1) {
64966502
var insertMode = vim.insertMode;
64976503
var name = lookupKey(hashId, key, e || {});
@@ -6590,7 +6596,7 @@ dom.importCssString(".normal-mode .ace_cursor{\
65906596
{ keys: 'zA', type: 'action', action: 'fold', actionArgs: { toggle: true, all: true } },
65916597
{ keys: 'zf', type: 'action', action: 'fold', actionArgs: { open: true, all: true } },
65926598
{ keys: 'zd', type: 'action', action: 'fold', actionArgs: { open: true, all: true } },
6593-
6599+
65946600
{ keys: '<C-A-k>', type: 'action', action: 'aceCommand', actionArgs: { name: "addCursorAbove" } },
65956601
{ keys: '<C-A-j>', type: 'action', action: 'aceCommand', actionArgs: { name: "addCursorBelow" } },
65966602
{ keys: '<C-A-S-k>', type: 'action', action: 'aceCommand', actionArgs: { name: "addCursorAboveSkipCurrent" } },

lib/ace/keyboard/vim_test.js

+20
Original file line numberDiff line numberDiff line change
@@ -3969,6 +3969,26 @@ testVim('ex_substitute_alternate_separator', function(cm, vim, helpers) {
39693969
helpers.doEx('s#o/e#two#g');
39703970
eq('o/e o/e\n two two', cm.getValue());
39713971
}, { value: 'o/e o/e\n o/e o/e'});
3972+
testVim('ex_substitute_with_newline (\\n)', function(cm, vim, helpers) {
3973+
cm.setCursor(1, 0);
3974+
helpers.doEx('s/\\.\\s/.\\n/');
3975+
eq('a. b. c.\n1.\n2. 3.', cm.getValue());
3976+
}, { value: 'a. b. c.\n1. 2. 3.'});
3977+
testVim('ex_substitute_all_in_range_with_newline (\\n)', function(cm, vim, helpers) {
3978+
cm.setCursor(1, 0);
3979+
helpers.doEx('1,2s/\\.\\s/.\\n/g');
3980+
eq('a.\nb.\nc.\n1.\n2.\n3.', cm.getValue());
3981+
}, { value: 'a. b. c.\n1. 2. 3.'});
3982+
testVim('ex_substitute_all_in_range_with_newline (\\r)', function(cm, vim, helpers) {
3983+
cm.setCursor(1, 0);
3984+
helpers.doEx('1,2s/\\.\\s/.\\r/g');
3985+
eq('a.\rb.\rc.\r1.\r2.\r3.', cm.getValue());
3986+
}, { value: 'a. b. c.\r1. 2. 3.'});
3987+
testVim('ex_substitute_all_in_range_with_multiple newlines (\\r)', function(cm, vim, helpers) {
3988+
cm.setCursor(1, 0);
3989+
helpers.doEx('1,2s/\\.\\s/.\\r\\r/g');
3990+
eq('a.\r\rb.\r\rc.\r1.\r\r2.\r\r3.', cm.getValue());
3991+
}, { value: 'a. b. c.\r1. 2. 3.'});
39723992
testVim('ex_substitute_full_file', function(cm, vim, helpers) {
39733993
cm.setCursor(1, 0);
39743994
helpers.doEx('%s/one/two/g');

0 commit comments

Comments
 (0)