diff --git a/keymap/vim.js b/keymap/vim.js index 6ff5edce4c578d86db712c77abeca5903372e6e9..ab61e17afb5b42ddf41855f00ff8ea93ddd89501 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -1858,6 +1858,8 @@ }, // delete is a javascript keyword. 'delete': function(cm, operatorArgs, vim, curStart, curEnd) { + // Save the '>' mark before cm.replaceRange clears it. + var selectionEnd = vim.visualMode ? vim.marks['>'].find() : null; // If the ending line is past the last line, inclusive, instead of // including the trailing \n, include the \n before the starting line if (operatorArgs.linewise && @@ -1876,6 +1878,10 @@ } else { cm.replaceRange('', curStart, curEnd); } + // restore the saved bookmark + if (selectionEnd) { + vim.marks['>'] = cm.setBookmark(selectionEnd); + } if (operatorArgs.linewise) { cm.setCursor(motions.moveToFirstNonWhiteSpaceCharacter(cm)); } else { @@ -2085,7 +2091,6 @@ curStart = cm.getCursor('anchor'); curEnd = cm.getCursor('head'); if (vim.visualLine) { - vim.visualLine = false; if (actionArgs.blockwise) { // This means Ctrl-V pressed in linewise visual vim.visualBlock = true; @@ -2097,8 +2102,8 @@ } else { exitVisualMode(cm); } + vim.visualLine = false; } else if (vim.visualBlock) { - vim.visualBlock = false; if (actionArgs.linewise) { // Shift-V pressed in blockwise visual mode vim.visualLine = true; @@ -2118,6 +2123,7 @@ } else { exitVisualMode(cm); } + vim.visualBlock = false; } else if (actionArgs.linewise) { // Shift-V pressed in characterwise visual mode. Switch to linewise // visual mode instead of exiting visual mode. @@ -2142,27 +2148,40 @@ : curStart); }, reselectLastSelection: function(cm, _actionArgs, vim) { + var curStart = vim.marks['<'].find(); + var curEnd = vim.marks['>'].find(); var lastSelection = vim.lastSelection; if (lastSelection) { - var curStart = lastSelection.curStartMark.find(); - var curEnd = lastSelection.curEndMark.find(); - cm.setSelection(curStart, curEnd); + // Set the selections as per last selection + var selectionStart = lastSelection.curStartMark.find(); + var selectionEnd = lastSelection.curEndMark.find(); + var blockwise = lastSelection.visualBlock; + // update last selection + updateLastSelection(cm, vim, curStart, curEnd); + if (blockwise) { + cm.setCursor(selectionStart); + selectionStart = selectBlock(cm, selectionEnd); + } else { + cm.setSelection(selectionStart, selectionEnd); + selectionStart = cm.getCursor('anchor'); + selectionEnd = cm.getCursor('head'); + } if (vim.visualMode) { - updateLastSelection(cm, vim); - var selectionStart = cm.getCursor('anchor'); - var selectionEnd = cm.getCursor('head'); updateMark(cm, vim, '<', cursorIsBefore(selectionStart, selectionEnd) ? selectionStart - : selectionEnd); + : selectionEnd); updateMark(cm, vim, '>', cursorIsBefore(selectionStart, selectionEnd) ? selectionEnd - : selectionStart); + : selectionStart); } + // Last selection is updated now + vim.visualMode = true; if (lastSelection.visualLine) { - vim.visualMode = true; vim.visualLine = true; - } - else { - vim.visualMode = true; + vim.visualBlock = false; + } else if (lastSelection.visualBlock) { vim.visualLine = false; + vim.visualBlock = true; + } else { + vim.visualBlock = vim.visualLine = false; } CodeMirror.signal(cm, "vim-mode-change", {mode: "visual", subMode: vim.visualLine ? "linewise" : ""}); } @@ -2552,6 +2571,10 @@ } start++; } + // Update selectionEnd and selectionStart + // after selection crossing + selectionEnd.ch = selections[0].head.ch; + selectionStart.ch = selections[0].anchor.ch; cm.setSelections(selections, primIndex); return selectionStart; } @@ -2586,22 +2609,27 @@ } return [selectionStart, selectionEnd]; } - function updateLastSelection(cm, vim) { - // We need the vim mark '<' to get the selection in case of yank and put - var selectionStart = vim.marks['<'].find() || cm.getCursor('anchor'); - var selectionEnd = vim.marks['>'].find() ||cm.getCursor('head'); - // To accommodate the effect lastPastedText in the last selection + function updateLastSelection(cm, vim, selectionStart, selectionEnd) { + if (!selectionStart || !selectionEnd) { + selectionStart = vim.marks['<'].find() || cm.getCursor('anchor'); + selectionEnd = vim.marks['>'].find() || cm.getCursor('head'); + } + // To accommodate the effect of lastPastedText in the last selection if (vim.lastPastedText) { - selectionEnd = cm.posFromIndex(cm.indexFromPos(selectionStart) + vim.lastPastedText.length-1); + selectionEnd = cm.posFromIndex(cm.indexFromPos(selectionStart) + vim.lastPastedText.length); vim.lastPastedText = null; } + var ranges = cm.listSelections(); + // This check ensures to set the cursor + // position where we left off in previous selection + var swap = getIndex(ranges, selectionStart) > -1; // can't use selection state here because yank has already reset its cursor // Also, Bookmarks make the visual selections robust to edit operations - vim.lastSelection = {'curStartMark': cm.setBookmark(selectionStart), 'curEndMark': cm.setBookmark(selectionEnd), 'visualMode': vim.visualMode, 'visualLine': vim.visualLine}; - if (cursorIsBefore(selectionEnd, selectionStart)) { - vim.lastSelection.curStartMark = cm.setBookmark(selectionEnd); - vim.lastSelection.curEndMark = cm.setBookmark(selectionStart); - } + vim.lastSelection = {'curStartMark': cm.setBookmark(swap ? selectionEnd : selectionStart), + 'curEndMark': cm.setBookmark(swap ? selectionStart : selectionEnd), + 'visualMode': vim.visualMode, + 'visualLine': vim.visualLine, + 'visualBlock': vim.visualBlock}; } function exitVisualMode(cm) { diff --git a/test/vim_test.js b/test/vim_test.js index b09d730d50a19e6469e59cb617a23c9d366f962d..1c9e8c3c9a13d6dc2f4df46c621994428205bdcc 100644 --- a/test/vim_test.js +++ b/test/vim_test.js @@ -1655,7 +1655,8 @@ testVim('reselect_visual', function(cm, vim, helpers) { eq('123456\n2345\nbar', cm.getValue()); cm.setCursor(0, 0); helpers.doKeys('g', 'v'); - helpers.assertCursorAt(1, 3); + // here the fake cursor is at (1, 3) + helpers.assertCursorAt(2, 0); eqPos(makeCursor(1, 0), cm.getCursor('anchor')); helpers.doKeys('v'); cm.setCursor(2, 0); @@ -1669,17 +1670,32 @@ testVim('reselect_visual', function(cm, vim, helpers) { }, { value: '123456\nfoo\nbar' }); testVim('reselect_visual_line', function(cm, vim, helpers) { helpers.doKeys('l', 'V', 'j', 'j', 'V', 'g', 'v', 'd'); - eq('\nfoo\nand\nbar', cm.getValue()); + eq('foo\nand\nbar', cm.getValue()); cm.setCursor(1, 0); helpers.doKeys('V', 'y', 'j'); helpers.doKeys('V', 'p' , 'g', 'v', 'd'); - eq('\nfoo\nbar', cm.getValue()); + eq('foo\nand', cm.getValue()); }, { value: 'hello\nthis\nis\nfoo\nand\nbar' }); +testVim('reselect_visual_block', function(cm, vim, helpers) { + cm.setCursor(1, 2); + helpers.doKeys('<C-v>', 'k', 'h', '<C-v>'); + cm.setCursor(2, 1); + helpers.doKeys('v', 'l', 'g', 'v'); + helpers.assertCursorAt(0, 1); + // Ensure selection is done with visual block mode rather than one + // continuous range. + eq(cm.getSelections().join(''), '23oo') + helpers.doKeys('g', 'v'); + helpers.assertCursorAt(2, 3); + // Ensure selection of deleted range + cm.setCursor(1, 1); + helpers.doKeys('v', '<C-v>', 'j', 'd', 'g', 'v'); + eq(cm.getSelections().join(''), 'or'); +}, { value: '123456\nfoo\nbar' }); testVim('s_normal', function(cm, vim, helpers) { cm.setCursor(0, 1); helpers.doKeys('s'); helpers.doInsertModeKeys('Esc'); - helpers.assertCursorAt(0, 0); eq('ac', cm.getValue()); }, { value: 'abc'}); testVim('s_visual', function(cm, vim, helpers) {