summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2022-01-02 13:05:45 +0000
committerBram Moolenaar <Bram@vim.org>2022-01-02 13:05:45 +0000
commitb3bd1d39e68e2d697c014b9f85482c2c12a3f909 (patch)
tree112d12725a220f2db94d33f237ef6052e0ee551d
parenta6feb163f0295dfefb829d93ba3fa659b9c262b6 (diff)
patch 8.2.3980: if 'operatorfunc' invokes an operator Visual mode is changedv8.2.3980
Problem: If 'operatorfunc' invokes an operator the remembered Visual mode may be changed. (Naohiro Ono) Solution: Save and restore the information for redoing the Visual area. (closes #9455)
-rw-r--r--src/ops.c69
-rw-r--r--src/testdir/test_normal.vim14
-rw-r--r--src/version.c2
3 files changed, 58 insertions, 27 deletions
diff --git a/src/ops.c b/src/ops.c
index 21df1a6bac..0685a06e0e 100644
--- a/src/ops.c
+++ b/src/ops.c
@@ -3492,6 +3492,15 @@ get_op_vcol(
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.
@@ -3508,11 +3517,8 @@ do_pending_operator(cmdarg_T *cap, int old_col, int gui_yank)
#endif
// 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};
+
int include_line_break = FALSE;
#if defined(FEAT_CLIPBOARD)
@@ -3621,24 +3627,24 @@ do_pending_operator(cmdarg_T *cap, int old_col, int 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_virtcol + redo_VIsual.rv_vcol - 1;
}
else
- curwin->w_curswant = redo_VIsual_vcol;
+ curwin->w_curswant = redo_VIsual.rv_vcol;
}
else
{
@@ -3646,9 +3652,9 @@ do_pending_operator(cmdarg_T *cap, int old_col, int gui_yank)
}
coladvance(curwin->w_curswant);
}
- cap->count0 = redo_VIsual_count;
- if (redo_VIsual_count != 0)
- cap->count1 = redo_VIsual_count;
+ cap->count0 = redo_VIsual.rv_count;
+ if (redo_VIsual.rv_count != 0)
+ cap->count1 = redo_VIsual.rv_count;
else
cap->count1 = 1;
}
@@ -3750,7 +3756,7 @@ do_pending_operator(cmdarg_T *cap, int old_col, int gui_yank)
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)
{
@@ -3822,11 +3828,11 @@ do_pending_operator(cmdarg_T *cap, int old_col, int gui_yank)
}
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;
}
}
@@ -4114,13 +4120,22 @@ do_pending_operator(cmdarg_T *cap, int old_col, int gui_yank)
break;
case OP_FUNCTION:
+ {
+ redo_VIsual_T save_redo_VIsual = redo_VIsual;
+
#ifdef FEAT_LINEBREAK
- // Restore linebreak, so that when the user edits it looks as
- // before.
- curwin->w_p_lbr = lbr_saved;
+ // Restore linebreak, so that when the user edits it looks as
+ // before.
+ curwin->w_p_lbr = lbr_saved;
#endif
- op_function(oap); // call 'operatorfunc'
- break;
+ // 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:
@@ -4216,7 +4231,7 @@ do_pending_operator(cmdarg_T *cap, int old_col, int gui_yank)
#ifdef FEAT_LINEBREAK
curwin->w_p_lbr = lbr_saved;
#endif
- op_addsub(oap, cap->count1, redo_VIsual_arg);
+ op_addsub(oap, cap->count1, redo_VIsual.rv_arg);
VIsual_active = FALSE;
}
check_cursor_col();
diff --git a/src/testdir/test_normal.vim b/src/testdir/test_normal.vim
index 1005694e41..90b7f9d9bb 100644
--- a/src/testdir/test_normal.vim
+++ b/src/testdir/test_normal.vim
@@ -464,6 +464,10 @@ func OperatorfuncRedo(_)
let g:opfunc_count = v:count
endfunc
+func Underscorize(_)
+ normal! '[V']r_
+endfunc
+
func Test_normal09c_operatorfunc()
" Test redoing operatorfunc
new
@@ -477,6 +481,16 @@ func Test_normal09c_operatorfunc()
bw!
unlet g:opfunc_count
+
+ " Test redoing Visual mode
+ set operatorfunc=Underscorize
+ new
+ call setline(1, ['first', 'first', 'third', 'third', 'second'])
+ normal! 1GVjr_
+ normal! 5G.
+ normal! 3G.
+ call assert_equal(['_____', '_____', '_____', '_____', '______'], getline(1, '$'))
+ bwipe!
set operatorfunc=
endfunc
diff --git a/src/version.c b/src/version.c
index 8f47ebb4fc..2ca87fec33 100644
--- a/src/version.c
+++ b/src/version.c
@@ -751,6 +751,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 3980,
+/**/
3979,
/**/
3978,