aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorzeertzjq <zeertzjq@outlook.com>2022-07-05 18:48:26 +0800
committerGitHub <noreply@github.com>2022-07-05 18:48:26 +0800
commit2536bde6c9ef5f0a94c0d3789a18a14de6a0a666 (patch)
treea6318dfe0cd8a43ca6c4b6e57d4c68454285e77f /src
parentd0835617facc98daf79318e26d41669bb2ce1a6b (diff)
parent785422ad54b6ce97cdec994862725b2846775e88 (diff)
downloadrneovim-2536bde6c9ef5f0a94c0d3789a18a14de6a0a666.tar.gz
rneovim-2536bde6c9ef5f0a94c0d3789a18a14de6a0a666.tar.bz2
rneovim-2536bde6c9ef5f0a94c0d3789a18a14de6a0a666.zip
Merge pull request #19232 from zeertzjq/vim-8.2.2904
vim-patch:8.2.{2904,3644.3980,3990}: three Normal mode fixes
Diffstat (limited to 'src')
-rw-r--r--src/nvim/normal.c25
-rw-r--r--src/nvim/ops.c70
-rw-r--r--src/nvim/testdir/test_normal.vim67
3 files changed, 129 insertions, 33 deletions
diff --git a/src/nvim/normal.c b/src/nvim/normal.c
index c7f7b569e7..ed624c0c9f 100644
--- a/src/nvim/normal.c
+++ b/src/nvim/normal.c
@@ -2494,21 +2494,30 @@ static void prep_redo_cmd(cmdarg_T *cap)
/// Note that only the last argument can be a multi-byte char.
void prep_redo(int regname, long num, int cmd1, int cmd2, int cmd3, int cmd4, int cmd5)
{
+ prep_redo_num2(regname, num, cmd1, cmd2, 0L, cmd3, cmd4, cmd5);
+}
+
+/// Prepare for redo of any command with extra count after "cmd2".
+void prep_redo_num2(int regname, long num1, int cmd1, int cmd2, long num2, int cmd3, int cmd4,
+ int cmd5)
+{
ResetRedobuff();
if (regname != 0) { // yank from specified buffer
AppendCharToRedobuff('"');
AppendCharToRedobuff(regname);
}
- if (num) {
- AppendNumberToRedobuff(num);
+ if (num1 != 0) {
+ AppendNumberToRedobuff(num1);
}
-
if (cmd1 != NUL) {
AppendCharToRedobuff(cmd1);
}
if (cmd2 != NUL) {
AppendCharToRedobuff(cmd2);
}
+ if (num2 != 0) {
+ AppendNumberToRedobuff(num2);
+ }
if (cmd3 != NUL) {
AppendCharToRedobuff(cmd3);
}
@@ -6157,6 +6166,16 @@ static void nv_g_cmd(cmdarg_T *cap)
i = curwin->w_leftcol + curwin->w_width_inner - col_off - 1;
coladvance((colnr_T)i);
+ // if the character doesn't fit move one back
+ if (curwin->w_cursor.col > 0 && utf_ptr2cells((const char *)get_cursor_pos_ptr()) > 1) {
+ colnr_T vcol;
+
+ getvvcol(curwin, &curwin->w_cursor, NULL, NULL, &vcol);
+ if (vcol >= curwin->w_leftcol + curwin->w_width - col_off) {
+ curwin->w_cursor.col--;
+ }
+ }
+
// Make sure we stick in this column.
validate_virtcol();
curwin->w_curswant = curwin->w_virtcol;
diff --git a/src/nvim/ops.c b/src/nvim/ops.c
index f2859bf707..2f29e94ff1 100644
--- a/src/nvim/ops.c
+++ b/src/nvim/ops.c
@@ -6234,6 +6234,15 @@ static void get_op_vcol(oparg_T *oap, colnr_T redo_VIsual_vcol, bool initial)
oap->start = curwin->w_cursor;
}
+/// Information for redoing the previous Visual selection.
+typedef struct {
+ int rv_mode; ///< 'v', 'V', or Ctrl-V
+ linenr_T rv_line_count; ///< number of lines
+ colnr_T rv_vcol; ///< number of cols or end column
+ long rv_count; ///< count for Visual operator
+ int rv_arg; ///< extra argument
+} redo_VIsual_T;
+
/// Handle an operator after Visual mode or when the movement is finished.
/// "gui_yank" is true when yanking text for the clipboard.
void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
@@ -6245,11 +6254,8 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
int lbr_saved = curwin->w_p_lbr;
// The visual area is remembered for redo
- static int redo_VIsual_mode = NUL; // 'v', 'V', or Ctrl-V
- static linenr_T redo_VIsual_line_count; // number of lines
- static colnr_T redo_VIsual_vcol; // number of cols or end column
- static long redo_VIsual_count; // count for Visual operator
- static int redo_VIsual_arg; // extra argument
+ static redo_VIsual_T redo_VIsual = { NUL, 0, 0, 0, 0 };
+
bool include_line_break = false;
old_cursor = curwin->w_cursor;
@@ -6332,28 +6338,27 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
if (redo_VIsual_busy) {
// Redo of an operation on a Visual area. Use the same size from
- // redo_VIsual_line_count and redo_VIsual_vcol.
+ // redo_VIsual.rv_line_count and redo_VIsual.rv_vcol.
oap->start = curwin->w_cursor;
- curwin->w_cursor.lnum += redo_VIsual_line_count - 1;
+ curwin->w_cursor.lnum += redo_VIsual.rv_line_count - 1;
if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count) {
curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
}
- VIsual_mode = redo_VIsual_mode;
- if (redo_VIsual_vcol == MAXCOL || VIsual_mode == 'v') {
+ VIsual_mode = redo_VIsual.rv_mode;
+ if (redo_VIsual.rv_vcol == MAXCOL || VIsual_mode == 'v') {
if (VIsual_mode == 'v') {
- if (redo_VIsual_line_count <= 1) {
+ if (redo_VIsual.rv_line_count <= 1) {
validate_virtcol();
- curwin->w_curswant =
- curwin->w_virtcol + redo_VIsual_vcol - 1;
+ curwin->w_curswant = curwin->w_virtcol + redo_VIsual.rv_vcol - 1;
} else {
- curwin->w_curswant = redo_VIsual_vcol;
+ curwin->w_curswant = redo_VIsual.rv_vcol;
}
} else {
curwin->w_curswant = MAXCOL;
}
coladvance(curwin->w_curswant);
}
- cap->count0 = redo_VIsual_count;
+ cap->count0 = redo_VIsual.rv_count;
cap->count1 = (cap->count0 == 0 ? 1 : cap->count0);
} else if (VIsual_active) {
if (!gui_yank) {
@@ -6440,7 +6445,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
virtual_op = virtual_active();
if (VIsual_active || redo_VIsual_busy) {
- get_op_vcol(oap, redo_VIsual_vcol, true);
+ get_op_vcol(oap, redo_VIsual.rv_vcol, true);
if (!redo_VIsual_busy && !gui_yank) {
// Prepare to reselect and redo Visual: this is based on the
@@ -6485,6 +6490,8 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
get_op_char(oap->op_type), get_extra_op_char(oap->op_type),
oap->motion_force, cap->cmdchar, cap->nchar);
} else if (cap->cmdchar != ':' && cap->cmdchar != K_COMMAND) {
+ int opchar = get_op_char(oap->op_type);
+ int extra_opchar = get_extra_op_char(oap->op_type);
int nchar = oap->op_type == OP_REPLACE ? cap->nchar : NUL;
// reverse what nv_replace() did
@@ -6493,15 +6500,20 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
} else if (nchar == REPLACE_NL_NCHAR) {
nchar = NL;
}
- prep_redo(oap->regname, 0L, NUL, 'v', get_op_char(oap->op_type),
- get_extra_op_char(oap->op_type), nchar);
+
+ if (opchar == 'g' && extra_opchar == '@') {
+ // also repeat the count for 'operatorfunc'
+ prep_redo_num2(oap->regname, 0L, NUL, 'v', cap->count0, opchar, extra_opchar, nchar);
+ } else {
+ prep_redo(oap->regname, 0L, NUL, 'v', opchar, extra_opchar, nchar);
+ }
}
if (!redo_VIsual_busy) {
- redo_VIsual_mode = resel_VIsual_mode;
- redo_VIsual_vcol = resel_VIsual_vcol;
- redo_VIsual_line_count = resel_VIsual_line_count;
- redo_VIsual_count = cap->count0;
- redo_VIsual_arg = cap->arg;
+ redo_VIsual.rv_mode = resel_VIsual_mode;
+ redo_VIsual.rv_vcol = resel_VIsual_vcol;
+ redo_VIsual.rv_line_count = resel_VIsual_line_count;
+ redo_VIsual.rv_count = cap->count0;
+ redo_VIsual.rv_arg = cap->arg;
}
}
@@ -6751,12 +6763,20 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
op_format(oap, true); // use internal function
break;
- case OP_FUNCTION:
+ case OP_FUNCTION: {
+ redo_VIsual_T save_redo_VIsual = redo_VIsual;
+
// Restore linebreak, so that when the user edits it looks as
// before.
curwin->w_p_lbr = lbr_saved;
- op_function(oap); // call 'operatorfunc'
+ // call 'operatorfunc'
+ op_function(oap);
+
+ // Restore the info for redoing Visual mode, the function may
+ // invoke another operator and unintentionally change it.
+ redo_VIsual = save_redo_VIsual;
break;
+ }
case OP_INSERT:
case OP_APPEND:
@@ -6837,7 +6857,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
} else {
VIsual_active = true;
curwin->w_p_lbr = lbr_saved;
- op_addsub(oap, (linenr_T)cap->count1, redo_VIsual_arg);
+ op_addsub(oap, (linenr_T)cap->count1, redo_VIsual.rv_arg);
VIsual_active = false;
}
check_cursor_col();
diff --git a/src/nvim/testdir/test_normal.vim b/src/nvim/testdir/test_normal.vim
index 789ca747b4..82ba2cfd48 100644
--- a/src/nvim/testdir/test_normal.vim
+++ b/src/nvim/testdir/test_normal.vim
@@ -345,7 +345,7 @@ func Test_normal08_fold()
bw!
endfunc
-func Test_normal09_operatorfunc()
+func Test_normal09a_operatorfunc()
" Test operatorfunc
call Setup_NewWindow()
" Add some spaces for counting
@@ -375,7 +375,7 @@ func Test_normal09_operatorfunc()
bw!
endfunc
-func Test_normal09a_operatorfunc()
+func Test_normal09b_operatorfunc()
" Test operatorfunc
call Setup_NewWindow()
" Add some spaces for counting
@@ -397,10 +397,45 @@ func Test_normal09a_operatorfunc()
" clean up
unmap <buffer> ,,
set opfunc=
+ call assert_fails('normal Vg@', 'E774:')
bw!
unlet! g:opt
endfunc
+func OperatorfuncRedo(_)
+ let g:opfunc_count = v:count
+endfunc
+
+func Underscorize(_)
+ normal! '[V']r_
+endfunc
+
+func Test_normal09c_operatorfunc()
+ " Test redoing operatorfunc
+ new
+ call setline(1, 'some text')
+ set operatorfunc=OperatorfuncRedo
+ normal v3g@
+ call assert_equal(3, g:opfunc_count)
+ let g:opfunc_count = 0
+ normal .
+ call assert_equal(3, g:opfunc_count)
+
+ bw!
+ unlet g:opfunc_count
+
+ " Test redoing Visual mode
+ set operatorfunc=Underscorize
+ new
+ call setline(1, ['first', 'first', 'third', 'third', 'second'])
+ normal! 1GVjg@
+ normal! 5G.
+ normal! 3G.
+ call assert_equal(['_____', '_____', '_____', '_____', '______'], getline(1, '$'))
+ bwipe!
+ set operatorfunc=
+endfunc
+
func Test_normal10_expand()
" Test for expand()
10new
@@ -1784,9 +1819,9 @@ fun! Test_normal33_g_cmd2()
%d
15vsp
set wrap listchars= sbr=
- let lineA='abcdefghijklmnopqrstuvwxyz'
- let lineB='0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
- let lineC='0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234567890123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
+ let lineA = 'abcdefghijklmnopqrstuvwxyz'
+ let lineB = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+ let lineC = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234567890123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
$put =lineA
$put =lineB
@@ -1821,6 +1856,28 @@ fun! Test_normal33_g_cmd2()
call assert_equal('l', getreg(0))
call assert_beeps('normal 5g$')
+ " Test for g$ with double-width character half displayed
+ vsplit
+ 9wincmd |
+ setlocal nowrap nonumber
+ call setline(2, 'asdfasdfヨ')
+ 2
+ normal 0g$
+ call assert_equal(8, col('.'))
+ 10wincmd |
+ normal 0g$
+ call assert_equal(9, col('.'))
+
+ setlocal signcolumn=yes
+ 11wincmd |
+ normal 0g$
+ call assert_equal(8, col('.'))
+ 12wincmd |
+ normal 0g$
+ call assert_equal(9, col('.'))
+
+ close
+
" Test for g_
call assert_beeps('normal! 100g_')
call setline(2, [' foo ', ' foobar '])