Skip to content

Commit 9312c71

Browse files
binnymightyguava
authored andcommitted
[vim] using dot to replay actions and operators
1 parent bce6992 commit 9312c71

File tree

2 files changed

+92
-36
lines changed

2 files changed

+92
-36
lines changed

keymap/vim.js

Lines changed: 76 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1411,6 +1411,7 @@
14111411
if (operator) {
14121412
var inverted = false;
14131413
vim.lastMotion = null;
1414+
var lastSelection = vim.lastSelection;
14141415
operatorArgs.repeat = repeat; // Indent in visual mode needs this.
14151416
if (vim.visualMode) {
14161417
curStart = selectionStart;
@@ -1437,6 +1438,24 @@
14371438
curEnd.line = curStart.line + operatorArgs.selOffset.line;
14381439
if (operatorArgs.selOffset.line) {curEnd.ch = operatorArgs.selOffset.ch; }
14391440
else { curEnd.ch = curStart.ch + operatorArgs.selOffset.ch; }
1441+
// In case of blockwise visual
1442+
if (lastSelection && lastSelection.visualBlock) {
1443+
var block = lastSelection.visualBlock;
1444+
var width = block.width;
1445+
var height = block.height;
1446+
curEnd = Pos(curStart.line + height, curStart.ch + width);
1447+
// selectBlock creates a 'proper' rectangular block.
1448+
// We do not want that in all cases, so we manually set selections.
1449+
var selections = [];
1450+
for (var i = curStart.line; i < curEnd.line; i++) {
1451+
var anchor = Pos(i, curStart.ch);
1452+
var head = Pos(i, curEnd.ch);
1453+
var range = {anchor: anchor, head: head};
1454+
selections.push(range);
1455+
}
1456+
cm.setSelections(selections);
1457+
var blockSelected = true;
1458+
}
14401459
} else if (vim.visualMode) {
14411460
var selOffset = Pos();
14421461
selOffset.line = curEnd.line - curStart.line;
@@ -1457,8 +1476,8 @@
14571476
operatorArgs.registerName = registerName;
14581477
// Keep track of linewise as it affects how paste and change behave.
14591478
operatorArgs.linewise = linewise;
1460-
if (!vim.visualBlock) {
1461-
cm.extendSelection(curStart, curEnd);
1479+
if (!vim.visualBlock && !blockSelected) {
1480+
cm.setSelection(curStart, curEnd);
14621481
}
14631482
operators[operator](cm, operatorArgs, vim, curStart,
14641483
curEnd, curOriginal);
@@ -1838,6 +1857,8 @@
18381857
var curEnd = cursorIsBefore(end.anchor, end.head) ? end.head : end.anchor;
18391858
var text = cm.getSelection();
18401859
var replacement = new Array(selections.length).join('1').split('1');
1860+
// save the selectionEnd mark
1861+
var selectionEnd = vim.marks['>'] ? vim.marks['>'].find() : cm.getCursor('head');
18411862
vimGlobalState.registerController.pushText(
18421863
operatorArgs.registerName, 'change', text,
18431864
operatorArgs.linewise);
@@ -1880,34 +1901,52 @@
18801901
cm.replaceRange('', curStart, curEnd);
18811902
}
18821903
}
1904+
vim.marks['>'] = cm.setBookmark(selectionEnd);
18831905
actions.enterInsertMode(cm, {}, cm.state.vim);
18841906
},
18851907
// delete is a javascript keyword.
1886-
'delete': function(cm, operatorArgs, vim, curStart, curEnd) {
1908+
'delete': function(cm, operatorArgs, vim) {
1909+
var selections = cm.listSelections();
1910+
var start = selections[0], end = selections[selections.length-1];
1911+
var curStart = cursorIsBefore(start.anchor, start.head) ? start.anchor : start.head;
1912+
var curEnd = cursorIsBefore(end.anchor, end.head) ? end.head : end.anchor;
18871913
// Save the '>' mark before cm.replaceRange clears it.
1888-
var selectionEnd = vim.visualMode ? vim.marks['>'].find() : null;
1914+
var selectionEnd, selectionStart;
1915+
if (vim.visualMode) {
1916+
selectionEnd = vim.marks['>'].find();
1917+
selectionStart = vim.marks['<'].find();
1918+
} else if (vim.lastSelection) {
1919+
selectionEnd = vim.lastSelection.curStartMark.find();
1920+
selectionStart = vim.lastSelection.curEndMark.find();
1921+
}
18891922
var text = cm.getSelection();
1923+
vimGlobalState.registerController.pushText(
1924+
operatorArgs.registerName, 'delete', text,
1925+
operatorArgs.linewise);
1926+
var replacement = new Array(selections.length).join('1').split('1');
18901927
// If the ending line is past the last line, inclusive, instead of
18911928
// including the trailing \n, include the \n before the starting line
18921929
if (operatorArgs.linewise &&
1893-
curEnd.line > cm.lastLine() && curStart.line > cm.firstLine()) {
1930+
curEnd.line == cm.lastLine() && curStart.line == curEnd.line) {
1931+
var tmp = copyCursor(curEnd);
18941932
curStart.line--;
18951933
curStart.ch = lineLength(cm, curStart.line);
1896-
}
1897-
vimGlobalState.registerController.pushText(
1898-
operatorArgs.registerName, 'delete', text,
1899-
operatorArgs.linewise);
1900-
if (vim.visualBlock) {
1901-
var selections = cm.listSelections();
1902-
curStart = selections[0].anchor;
1903-
var replacement = new Array(selections.length).join('1').split('1');
1904-
cm.replaceSelections(replacement);
1905-
} else {
1934+
curEnd = tmp;
19061935
cm.replaceRange('', curStart, curEnd);
1936+
} else {
1937+
cm.replaceSelections(replacement);
19071938
}
19081939
// restore the saved bookmark
19091940
if (selectionEnd) {
1910-
vim.marks['>'] = cm.setBookmark(selectionEnd);
1941+
var curStartMark = cm.setBookmark(selectionStart);
1942+
var curEndMark = cm.setBookmark(selectionEnd);
1943+
if (vim.visualMode) {
1944+
vim.marks['<'] = curStartMark;
1945+
vim.marks['>'] = curEndMark;
1946+
} else {
1947+
vim.lastSelection.curStartMark = curStartMark;
1948+
vim.lastSelection.curEndMark = curEndMark;
1949+
}
19111950
}
19121951
if (operatorArgs.linewise) {
19131952
cm.setCursor(motions.moveToFirstNonWhiteSpaceCharacter(cm));
@@ -2645,29 +2684,26 @@
26452684
return [selectionStart, selectionEnd];
26462685
};
26472686
var getLastSelectedAreaRange = function() {
2648-
var start = lastSelection.curStartMark.find();
2649-
var end = lastSelection.curEndMark.find();
26502687
var selectionStart = cm.getCursor();
26512688
var selectionEnd = cm.getCursor();
2652-
if (lastSelection.visualBlock) {
2653-
var anchor = Pos(Math.min(start.line, end.line), Math.min(start.ch, end.ch));
2654-
var head = Pos(Math.max(start.line, end.line), Math.max(start.ch, end.ch));
2655-
var width = head.ch - anchor.ch;
2656-
var height = head.line - anchor.line;
2689+
var block = lastSelection.visualBlock;
2690+
if (block) {
2691+
var width = block.width;
2692+
var height = block.height;
26572693
selectionEnd = Pos(selectionStart.line + height, selectionStart.ch + width);
2658-
var endCh = cm.clipPos(selectionEnd).ch;
2659-
// We do not want selection crossing while selecting here.
2660-
// So, we cut down the selection.
2661-
while (endCh != selectionEnd.ch) {
2662-
if (endCh-1 == selectionStart.ch) {
2663-
break;
2664-
}
2665-
selectionEnd.line--;
2666-
endCh = cm.clipPos(selectionEnd).ch;
2694+
var selections = [];
2695+
// selectBlock creates a 'proper' rectangular block.
2696+
// We do not want that in all cases, so we manually set selections.
2697+
for (var i = selectionStart.line; i < selectionEnd.line; i++) {
2698+
var anchor = Pos(i, selectionStart.ch);
2699+
var head = Pos(i, selectionEnd.ch);
2700+
var range = {anchor: anchor, head: head};
2701+
selections.push(range);
26672702
}
2668-
cm.setCursor(selectionStart);
2669-
selectBlock(cm, selectionEnd);
2703+
cm.setSelections(selections);
26702704
} else {
2705+
var start = lastSelection.curStartMark.find();
2706+
var end = lastSelection.curEndMark.find();
26712707
var line = end.line - start.line;
26722708
var ch = end.ch - start.ch;
26732709
selectionEnd = {line: selectionEnd.line + line, ch: line ? selectionEnd.ch : ch + selectionEnd.ch};
@@ -2700,13 +2736,18 @@
27002736
// This check ensures to set the cursor
27012737
// position where we left off in previous selection
27022738
var swap = getIndex(ranges, selectionStart) > -1;
2739+
if (vim.visualBlock) {
2740+
var height = Math.abs(selectionStart.line - selectionEnd.line)+1;
2741+
var width = Math.abs(selectionStart.ch - selectionEnd.ch);
2742+
var block = {height: height, width: width};
2743+
}
27032744
// can't use selection state here because yank has already reset its cursor
27042745
// Also, Bookmarks make the visual selections robust to edit operations
27052746
vim.lastSelection = {'curStartMark': cm.setBookmark(swap ? selectionEnd : selectionStart),
27062747
'curEndMark': cm.setBookmark(swap ? selectionStart : selectionEnd),
27072748
'visualMode': vim.visualMode,
27082749
'visualLine': vim.visualLine,
2709-
'visualBlock': vim.visualBlock};
2750+
'visualBlock': block};
27102751
}
27112752

27122753
function exitVisualMode(cm) {

test/vim_test.js

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -944,7 +944,22 @@ testVim('visual_block_~', function(cm, vim, helpers) {
944944
helpers.assertCursorAt(2, 0);
945945
eq('hello\nwoRLd\nAbcDe', cm.getValue());
946946
},{value: 'hello\nwOrld\nabcde' });
947-
947+
testVim('._swapCase_visualBlock', function(cm, vim, helpers) {
948+
helpers.doKeys('<C-v>', 'j', 'j', 'l', '~');
949+
cm.setCursor(0, 3);
950+
helpers.doKeys('.');
951+
eq('HelLO\nWorLd\nAbcdE', cm.getValue());
952+
},{value: 'hEllo\nwOrlD\naBcDe' });
953+
testVim('._delete_visualBlock', function(cm, vim, helpers) {
954+
helpers.doKeys('<C-v>', 'j', 'x');
955+
eq('ive\ne\nsome\nsugar', cm.getValue());
956+
helpers.doKeys('.');
957+
eq('ve\n\nsome\nsugar', cm.getValue());
958+
helpers.doKeys('j', 'j', '.');
959+
eq('ve\n\nome\nugar', cm.getValue());
960+
helpers.doKeys('u', '<C-r>', '.');
961+
eq('ve\n\nme\ngar', cm.getValue());
962+
},{value: 'give\nme\nsome\nsugar' });
948963
testVim('>{motion}', function(cm, vim, helpers) {
949964
cm.setCursor(1, 3);
950965
var expectedLineCount = cm.lineCount();

0 commit comments

Comments
 (0)