1
0
forked from aniani/vim

patch 8.2.3980: if 'operatorfunc' invokes an operator Visual mode is changed

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)
This commit is contained in:
Bram Moolenaar
2022-01-02 13:05:45 +00:00
parent a6feb163f0
commit b3bd1d39e6
3 changed files with 58 additions and 27 deletions

View File

@@ -3492,6 +3492,15 @@ get_op_vcol(
oap->start = curwin->w_cursor; 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. * Handle an operator after Visual mode or when the movement is finished.
* "gui_yank" is true when yanking text for the clipboard. * "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 #endif
// The visual area is remembered for redo // The visual area is remembered for redo
static int redo_VIsual_mode = NUL; // 'v', 'V', or Ctrl-V static redo_VIsual_T redo_VIsual = {NUL, 0, 0, 0,0};
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
int include_line_break = FALSE; int include_line_break = FALSE;
#if defined(FEAT_CLIPBOARD) #if defined(FEAT_CLIPBOARD)
@@ -3621,24 +3627,24 @@ do_pending_operator(cmdarg_T *cap, int old_col, int gui_yank)
if (redo_VIsual_busy) if (redo_VIsual_busy)
{ {
// Redo of an operation on a Visual area. Use the same size from // 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; 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) if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count)
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; VIsual_mode = redo_VIsual.rv_mode;
if (redo_VIsual_vcol == MAXCOL || VIsual_mode == 'v') if (redo_VIsual.rv_vcol == MAXCOL || VIsual_mode == 'v')
{ {
if (VIsual_mode == 'v') if (VIsual_mode == 'v')
{ {
if (redo_VIsual_line_count <= 1) if (redo_VIsual.rv_line_count <= 1)
{ {
validate_virtcol(); validate_virtcol();
curwin->w_curswant = curwin->w_curswant =
curwin->w_virtcol + redo_VIsual_vcol - 1; curwin->w_virtcol + redo_VIsual.rv_vcol - 1;
} }
else else
curwin->w_curswant = redo_VIsual_vcol; curwin->w_curswant = redo_VIsual.rv_vcol;
} }
else else
{ {
@@ -3646,9 +3652,9 @@ do_pending_operator(cmdarg_T *cap, int old_col, int gui_yank)
} }
coladvance(curwin->w_curswant); coladvance(curwin->w_curswant);
} }
cap->count0 = redo_VIsual_count; cap->count0 = redo_VIsual.rv_count;
if (redo_VIsual_count != 0) if (redo_VIsual.rv_count != 0)
cap->count1 = redo_VIsual_count; cap->count1 = redo_VIsual.rv_count;
else else
cap->count1 = 1; 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) 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) 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) if (!redo_VIsual_busy)
{ {
redo_VIsual_mode = resel_VIsual_mode; redo_VIsual.rv_mode = resel_VIsual_mode;
redo_VIsual_vcol = resel_VIsual_vcol; redo_VIsual.rv_vcol = resel_VIsual_vcol;
redo_VIsual_line_count = resel_VIsual_line_count; redo_VIsual.rv_line_count = resel_VIsual_line_count;
redo_VIsual_count = cap->count0; redo_VIsual.rv_count = cap->count0;
redo_VIsual_arg = cap->arg; redo_VIsual.rv_arg = cap->arg;
} }
} }
@@ -4114,13 +4120,22 @@ do_pending_operator(cmdarg_T *cap, int old_col, int gui_yank)
break; break;
case OP_FUNCTION: case OP_FUNCTION:
{
redo_VIsual_T save_redo_VIsual = redo_VIsual;
#ifdef FEAT_LINEBREAK #ifdef FEAT_LINEBREAK
// Restore linebreak, so that when the user edits it looks as // Restore linebreak, so that when the user edits it looks as
// before. // before.
curwin->w_p_lbr = lbr_saved; curwin->w_p_lbr = lbr_saved;
#endif #endif
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; break;
}
case OP_INSERT: case OP_INSERT:
case OP_APPEND: case OP_APPEND:
@@ -4216,7 +4231,7 @@ do_pending_operator(cmdarg_T *cap, int old_col, int gui_yank)
#ifdef FEAT_LINEBREAK #ifdef FEAT_LINEBREAK
curwin->w_p_lbr = lbr_saved; curwin->w_p_lbr = lbr_saved;
#endif #endif
op_addsub(oap, cap->count1, redo_VIsual_arg); op_addsub(oap, cap->count1, redo_VIsual.rv_arg);
VIsual_active = FALSE; VIsual_active = FALSE;
} }
check_cursor_col(); check_cursor_col();

View File

@@ -464,6 +464,10 @@ func OperatorfuncRedo(_)
let g:opfunc_count = v:count let g:opfunc_count = v:count
endfunc endfunc
func Underscorize(_)
normal! '[V']r_
endfunc
func Test_normal09c_operatorfunc() func Test_normal09c_operatorfunc()
" Test redoing operatorfunc " Test redoing operatorfunc
new new
@@ -477,6 +481,16 @@ func Test_normal09c_operatorfunc()
bw! bw!
unlet g:opfunc_count 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= set operatorfunc=
endfunc endfunc

View File

@@ -750,6 +750,8 @@ static char *(features[]) =
static int included_patches[] = static int included_patches[] =
{ /* Add new patch number below this line */ { /* Add new patch number below this line */
/**/
3980,
/**/ /**/
3979, 3979,
/**/ /**/