From 0f6b18eefc67002d0a783f84555ee011537a0e62 Mon Sep 17 00:00:00 2001 From: nightwing Date: Sat, 7 Dec 2024 00:11:58 +0400 Subject: [PATCH] fix visual block indent --- src/vim.js | 38 ++++++++++++++++++++++++++++++++++---- test/vim_test.js | 8 ++++++++ test/webtest-vim.js | 2 +- 3 files changed, 43 insertions(+), 5 deletions(-) diff --git a/src/vim.js b/src/vim.js index 958aac0..237858b 100644 --- a/src/vim.js +++ b/src/vim.js @@ -2791,8 +2791,40 @@ export function initVim(CM) { }, indent: function(cm, args, ranges) { var vim = cm.state.vim; - var repeat = (vim.visualMode) ? (args.repeat || 0) : 1; - if (cm.indentMore) { + // In visual mode, n> shifts the selection right n times, instead of + // shifting n lines right once. + var repeat = vim.visualMode ? args.repeat || 1 : 1; + if (vim.visualBlock) { + var tabSize = cm.getOption('tabSize'); + var indent = cm.getOption('indentWithTabs') ? '\t' : ' '.repeat(tabSize); + var cursor; + for (var i = ranges.length - 1; i >= 0; i--) { + cursor = cursorMin(ranges[i].anchor, ranges[i].head); + if (args.indentRight) { + cm.replaceRange(indent.repeat(repeat), cursor, cursor); + } else { + var text = cm.getLine(cursor.line); + var end = 0; + for (var j = 0; j < repeat; j++) { + var ch = text[cursor.ch + end]; + if (ch == '\t') { + end++; + } else if (ch == ' ') { + end++; + for (var k = 1; k < indent.length; k++) { + ch = text[cursor.ch + end]; + if (ch !== ' ') break; + end++; + } + } else { + break + } + } + cm.replaceRange('', cursor, offsetCursor(cursor, 0, end)); + } + } + return cursor; + } else if (cm.indentMore) { for (var j = 0; j < repeat; j++) { if (args.indentRight) cm.indentMore(); else cm.indentLess(); @@ -2802,8 +2834,6 @@ export function initVim(CM) { var endLine = vim.visualBlock ? ranges[ranges.length - 1].anchor.line : ranges[0].head.line; - // In visual mode, n> shifts the selection right n times, instead of - // shifting n lines right once. if (args.linewise) { // The only way to delete a newline is to delete until the start of // the next line, so in linewise mode evalInput will include the next diff --git a/test/vim_test.js b/test/vim_test.js index 43c479f..52a42ca 100644 --- a/test/vim_test.js +++ b/test/vim_test.js @@ -1508,6 +1508,14 @@ testVim('=', function(cm, vim, helpers) { helpers.doKeys('='); eq(expectedValue, cm.getValue()); }, { value: ' word1\n word2\n word3', indentUnit: 2 }); +testVim('>', 'j', 'j'); + helpers.doKeys('4', '>'); + eq(' word 1\n word 2\n word 3', cm.getValue()); + helpers.doKeys('g', 'v', '14', '<'); + eq(' word1\n word2\n word3', cm.getValue()); +}, { value: ' word1\n word2\n word3', indentUnit: 2 }); // Edit tests diff --git a/test/webtest-vim.js b/test/webtest-vim.js index fa19db8..9794d15 100644 --- a/test/webtest-vim.js +++ b/test/webtest-vim.js @@ -49,7 +49,7 @@ describe("Vim extension", () => { vim({}), basicSetup, options.mode == "xml" ? xml() : javascript(), - EditorState.tabSize.of(options.tabSize || 4), + EditorState.tabSize.of(options.tabSize || options.indentUnit || 4), indentUnit.of( options.indentWithTabs ? "\t" : " ".repeat(options.indentUnit || 2) ),