vim-patch:8.2.4018: ml_get error when win_execute redraws with Visual selection

Problem:    ml_get error when win_execute redraws with Visual selection.
Solution:   Disable Visual area temporarily. (closes vim/vim#9479)
18f4740f04

{switch_to/restore}_win_for_buf is N/A (marked as such in v8.0.0860; currently
only used in Vim's if_py).

Add a modeline to test_execute_func.vim.
This commit is contained in:
Sean Dewar
2022-01-06 13:48:37 +00:00
parent f5c4c1d768
commit f326c9a77d
8 changed files with 79 additions and 59 deletions

View File

@@ -989,18 +989,16 @@ Object copy_object(Object obj)
static void set_option_value_for(char *key, int numval, char *stringval, int opt_flags, static void set_option_value_for(char *key, int numval, char *stringval, int opt_flags,
int opt_type, void *from, Error *err) int opt_type, void *from, Error *err)
{ {
win_T *save_curwin = NULL; switchwin_T switchwin;
tabpage_T *save_curtab = NULL;
aco_save_T aco; aco_save_T aco;
try_start(); try_start();
switch (opt_type) switch (opt_type)
{ {
case SREQ_WIN: case SREQ_WIN:
if (switch_win_noblock(&save_curwin, &save_curtab, (win_T *)from, if (switch_win_noblock(&switchwin, (win_T *)from, win_find_tabpage((win_T *)from), true)
win_find_tabpage((win_T *)from), true)
== FAIL) { == FAIL) {
restore_win_noblock(save_curwin, save_curtab, true); restore_win_noblock(&switchwin, true);
if (try_end(err)) { if (try_end(err)) {
return; return;
} }
@@ -1010,7 +1008,7 @@ static void set_option_value_for(char *key, int numval, char *stringval, int opt
return; return;
} }
set_option_value_err(key, numval, stringval, opt_flags, err); set_option_value_err(key, numval, stringval, opt_flags, err);
restore_win_noblock(save_curwin, save_curtab, true); restore_win_noblock(&switchwin, true);
break; break;
case SREQ_BUF: case SREQ_BUF:
aucmd_prepbuf(&aco, (buf_T *)from); aucmd_prepbuf(&aco, (buf_T *)from);

View File

@@ -455,17 +455,14 @@ Object nvim_win_call(Window window, LuaRef fun, Error *err)
} }
tabpage_T *tabpage = win_find_tabpage(win); tabpage_T *tabpage = win_find_tabpage(win);
win_T *save_curwin;
tabpage_T *save_curtab;
try_start(); try_start();
Object res = OBJECT_INIT; Object res = OBJECT_INIT;
if (switch_win_noblock(&save_curwin, &save_curtab, win, tabpage, true) == switchwin_T switchwin;
OK) { if (switch_win_noblock(&switchwin, win, tabpage, true) == OK) {
Array args = ARRAY_DICT_INIT; Array args = ARRAY_DICT_INIT;
res = nlua_call_ref(fun, NULL, args, true, err); res = nlua_call_ref(fun, NULL, args, true, err);
} }
restore_win_noblock(save_curwin, save_curtab, true); restore_win_noblock(&switchwin, true);
try_end(err); try_end(err);
return res; return res;
} }

View File

@@ -6963,10 +6963,9 @@ win_T *find_tabwin(typval_T *wvp, typval_T *tvp)
/// @param off 1 for gettabwinvar() /// @param off 1 for gettabwinvar()
void getwinvar(typval_T *argvars, typval_T *rettv, int off) void getwinvar(typval_T *argvars, typval_T *rettv, int off)
{ {
win_T *win, *oldcurwin; win_T *win;
dictitem_T *v; dictitem_T *v;
tabpage_T *tp = NULL; tabpage_T *tp = NULL;
tabpage_T *oldtabpage = NULL;
bool done = false; bool done = false;
if (off == 1) { if (off == 1) {
@@ -6986,8 +6985,8 @@ void getwinvar(typval_T *argvars, typval_T *rettv, int off)
// otherwise the window is not valid. Only do this when needed, // otherwise the window is not valid. Only do this when needed,
// autocommands get blocked. // autocommands get blocked.
bool need_switch_win = tp != curtab || win != curwin; bool need_switch_win = tp != curtab || win != curwin;
if (!need_switch_win switchwin_T switchwin;
|| switch_win(&oldcurwin, &oldtabpage, win, tp, true) == OK) { if (!need_switch_win || switch_win(&switchwin, win, tp, true) == OK) {
if (*varname == '&') { if (*varname == '&') {
if (varname[1] == NUL) { if (varname[1] == NUL) {
// get all window-local options in a dict // get all window-local options in a dict
@@ -7015,7 +7014,7 @@ void getwinvar(typval_T *argvars, typval_T *rettv, int off)
if (need_switch_win) { if (need_switch_win) {
// restore previous notion of curwin // restore previous notion of curwin
restore_win(oldcurwin, oldtabpage, true); restore_win(&switchwin, true);
} }
} }
emsg_off--; emsg_off--;
@@ -7517,11 +7516,9 @@ void setwinvar(typval_T *argvars, typval_T *rettv, int off)
typval_T *varp = &argvars[off + 2]; typval_T *varp = &argvars[off + 2];
if (win != NULL && varname != NULL && varp != NULL) { if (win != NULL && varname != NULL && varp != NULL) {
win_T *save_curwin;
tabpage_T *save_curtab;
bool need_switch_win = tp != curtab || win != curwin; bool need_switch_win = tp != curtab || win != curwin;
if (!need_switch_win switchwin_T switchwin;
|| switch_win(&save_curwin, &save_curtab, win, tp, true) == OK) { if (!need_switch_win || switch_win(&switchwin, win, tp, true) == OK) {
if (*varname == '&') { if (*varname == '&') {
long numval; long numval;
bool error = false; bool error = false;
@@ -7543,7 +7540,7 @@ void setwinvar(typval_T *argvars, typval_T *rettv, int off)
} }
} }
if (need_switch_win) { if (need_switch_win) {
restore_win(save_curwin, save_curtab, true); restore_win(&switchwin, true);
} }
} }
} }

View File

@@ -2176,20 +2176,18 @@ static void f_win_execute(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{ {
tabpage_T *tp; tabpage_T *tp;
win_T *wp = win_id2wp_tp(argvars, &tp); win_T *wp = win_id2wp_tp(argvars, &tp);
win_T *save_curwin;
tabpage_T *save_curtab;
// Return an empty string if something fails. // Return an empty string if something fails.
rettv->v_type = VAR_STRING; rettv->v_type = VAR_STRING;
rettv->vval.v_string = NULL; rettv->vval.v_string = NULL;
if (wp != NULL && tp != NULL) { if (wp != NULL && tp != NULL) {
pos_T curpos = wp->w_cursor; pos_T curpos = wp->w_cursor;
if (switch_win_noblock(&save_curwin, &save_curtab, wp, tp, true) == switchwin_T switchwin;
OK) { if (switch_win_noblock(&switchwin, wp, tp, true) == OK) {
check_cursor(); check_cursor();
execute_common(argvars, rettv, fptr, 1); execute_common(argvars, rettv, fptr, 1);
} }
restore_win_noblock(save_curwin, save_curtab, true); restore_win_noblock(&switchwin, true);
// Update the status line if the cursor moved. // Update the status line if the cursor moved.
if (win_valid(wp) && !equalpos(curpos, wp->w_cursor)) { if (win_valid(wp) && !equalpos(curpos, wp->w_cursor)) {
@@ -4031,8 +4029,6 @@ static void f_gettabinfo(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/ */
static void f_gettabvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_gettabvar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{ {
win_T *oldcurwin;
tabpage_T *oldtabpage;
bool done = false; bool done = false;
rettv->v_type = VAR_STRING; rettv->v_type = VAR_STRING;
@@ -4046,7 +4042,8 @@ static void f_gettabvar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
win_T *const window = tp == curtab || tp->tp_firstwin == NULL win_T *const window = tp == curtab || tp->tp_firstwin == NULL
? firstwin ? firstwin
: tp->tp_firstwin; : tp->tp_firstwin;
if (switch_win(&oldcurwin, &oldtabpage, window, tp, true) == OK) { switchwin_T switchwin;
if (switch_win(&switchwin, window, tp, true) == OK) {
// look up the variable // look up the variable
// Let gettabvar({nr}, "") return the "t:" dictionary. // Let gettabvar({nr}, "") return the "t:" dictionary.
const dictitem_T *const v = find_var_in_ht(&tp->tp_vars->dv_hashtab, 't', const dictitem_T *const v = find_var_in_ht(&tp->tp_vars->dv_hashtab, 't',
@@ -4059,7 +4056,7 @@ static void f_gettabvar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
} }
// restore previous notion of curwin // restore previous notion of curwin
restore_win(oldcurwin, oldtabpage, true); restore_win(&switchwin, true);
} }
if (!done && argvars[2].v_type != VAR_UNKNOWN) { if (!done && argvars[2].v_type != VAR_UNKNOWN) {
@@ -5881,18 +5878,16 @@ static void f_line(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (argvars[1].v_type != VAR_UNKNOWN) { if (argvars[1].v_type != VAR_UNKNOWN) {
tabpage_T *tp; tabpage_T *tp;
win_T *save_curwin;
tabpage_T *save_curtab;
// use window specified in the second argument // use window specified in the second argument
win_T *wp = win_id2wp_tp(&argvars[1], &tp); win_T *wp = win_id2wp_tp(&argvars[1], &tp);
if (wp != NULL && tp != NULL) { if (wp != NULL && tp != NULL) {
if (switch_win_noblock(&save_curwin, &save_curtab, wp, tp, true) switchwin_T switchwin;
== OK) { if (switch_win_noblock(&switchwin, wp, tp, true) == OK) {
check_cursor(); check_cursor();
fp = var2fpos(&argvars[0], true, &fnum); fp = var2fpos(&argvars[0], true, &fnum);
} }
restore_win_noblock(save_curwin, save_curtab, true); restore_win_noblock(&switchwin, true);
} }
} else { } else {
// use current window // use current window

View File

@@ -346,10 +346,10 @@ void update_topline(win_T *wp)
*/ */
void update_topline_win(win_T *win) void update_topline_win(win_T *win)
{ {
win_T *save_curwin; switchwin_T switchwin;
switch_win(&save_curwin, NULL, win, NULL, true); switch_win(&switchwin, win, NULL, true);
update_topline(curwin); update_topline(curwin);
restore_win(save_curwin, NULL, true); restore_win(&switchwin, true);
} }
/* /*

View File

@@ -147,3 +147,15 @@ func Test_win_execute_other_tab()
tabclose tabclose
unlet xyz unlet xyz
endfunc endfunc
func Test_win_execute_visual_redraw()
call setline(1, ['a', 'b', 'c'])
new
wincmd p
call feedkeys("G\<C-V>", 'txn')
call win_execute(winnr('#')->win_getid(), 'redraw')
bwipe!
bwipe!
endfunc
" vim: shiftwidth=2 sts=2 expandtab

View File

@@ -577,18 +577,19 @@ static void cmd_with_count(char *cmd, char_u *bufp, size_t bufsize, int64_t Pren
void win_set_buf(Window window, Buffer buffer, bool noautocmd, Error *err) void win_set_buf(Window window, Buffer buffer, bool noautocmd, Error *err)
{ {
win_T *win = find_window_by_handle(window, err), *save_curwin = curwin; win_T *win = find_window_by_handle(window, err);
buf_T *buf = find_buffer_by_handle(buffer, err); buf_T *buf = find_buffer_by_handle(buffer, err);
tabpage_T *tab = win_find_tabpage(win), *save_curtab = curtab; tabpage_T *tab = win_find_tabpage(win);
if (!win || !buf) { if (!win || !buf) {
return; return;
} }
if (noautocmd) { if (noautocmd) {
block_autocmds(); block_autocmds();
} }
if (switch_win_noblock(&save_curwin, &save_curtab, win, tab, false) == FAIL) {
switchwin_T switchwin;
if (switch_win_noblock(&switchwin, win, tab, false) == FAIL) {
api_set_error(err, api_set_error(err,
kErrorTypeException, kErrorTypeException,
"Failed to switch to window %d", "Failed to switch to window %d",
@@ -608,7 +609,7 @@ void win_set_buf(Window window, Buffer buffer, bool noautocmd, Error *err)
// So do it now. // So do it now.
validate_cursor(); validate_cursor();
restore_win_noblock(save_curwin, save_curtab, false); restore_win_noblock(&switchwin, false);
if (noautocmd) { if (noautocmd) {
unblock_autocmds(); unblock_autocmds();
} }
@@ -6631,20 +6632,27 @@ static win_T *get_snapshot_focus(int idx)
/// triggered, another tabpage access is limited. /// triggered, another tabpage access is limited.
/// ///
/// @return FAIL if switching to "win" failed. /// @return FAIL if switching to "win" failed.
int switch_win(win_T **save_curwin, tabpage_T **save_curtab, win_T *win, tabpage_T *tp, int switch_win(switchwin_T *switchwin, win_T *win, tabpage_T *tp, bool no_display)
bool no_display)
{ {
block_autocmds(); block_autocmds();
return switch_win_noblock(save_curwin, save_curtab, win, tp, no_display); return switch_win_noblock(switchwin, win, tp, no_display);
} }
// As switch_win() but without blocking autocommands. // As switch_win() but without blocking autocommands.
int switch_win_noblock(win_T **save_curwin, tabpage_T **save_curtab, win_T *win, tabpage_T *tp, int switch_win_noblock(switchwin_T *switchwin, win_T *win, tabpage_T *tp, bool no_display)
bool no_display)
{ {
*save_curwin = curwin; memset(switchwin, 0, sizeof(switchwin_T));
switchwin->sw_curwin = curwin;
if (win == curwin) {
switchwin->sw_same_win = true;
} else {
// Disable Visual selection, because redrawing may fail.
switchwin->sw_visual_active = VIsual_active;
VIsual_active = false;
}
if (tp != NULL) { if (tp != NULL) {
*save_curtab = curtab; switchwin->sw_curtab = curtab;
if (no_display) { if (no_display) {
curtab->tp_firstwin = firstwin; curtab->tp_firstwin = firstwin;
curtab->tp_lastwin = lastwin; curtab->tp_lastwin = lastwin;
@@ -6666,28 +6674,33 @@ int switch_win_noblock(win_T **save_curwin, tabpage_T **save_curtab, win_T *win,
// Restore current tabpage and window saved by switch_win(), if still valid. // Restore current tabpage and window saved by switch_win(), if still valid.
// When "no_display" is true the display won't be affected, no redraw is // When "no_display" is true the display won't be affected, no redraw is
// triggered. // triggered.
void restore_win(win_T *save_curwin, tabpage_T *save_curtab, bool no_display) void restore_win(switchwin_T *switchwin, bool no_display)
{ {
restore_win_noblock(save_curwin, save_curtab, no_display); restore_win_noblock(switchwin, no_display);
unblock_autocmds(); unblock_autocmds();
} }
// As restore_win() but without unblocking autocommands. // As restore_win() but without unblocking autocommands.
void restore_win_noblock(win_T *save_curwin, tabpage_T *save_curtab, bool no_display) void restore_win_noblock(switchwin_T *switchwin, bool no_display)
{ {
if (save_curtab != NULL && valid_tabpage(save_curtab)) { if (switchwin->sw_curtab != NULL && valid_tabpage(switchwin->sw_curtab)) {
if (no_display) { if (no_display) {
curtab->tp_firstwin = firstwin; curtab->tp_firstwin = firstwin;
curtab->tp_lastwin = lastwin; curtab->tp_lastwin = lastwin;
curtab = save_curtab; curtab = switchwin->sw_curtab;
firstwin = curtab->tp_firstwin; firstwin = curtab->tp_firstwin;
lastwin = curtab->tp_lastwin; lastwin = curtab->tp_lastwin;
} else { } else {
goto_tabpage_tp(save_curtab, false, false); goto_tabpage_tp(switchwin->sw_curtab, false, false);
} }
} }
if (win_valid(save_curwin)) {
curwin = save_curwin; if (!switchwin->sw_same_win) {
VIsual_active = switchwin->sw_visual_active;
}
if (win_valid(switchwin->sw_curwin)) {
curwin = switchwin->sw_curwin;
curbuf = curwin->w_buffer; curbuf = curwin->w_buffer;
} }
// If called by win_execute() and executing the command changed the // If called by win_execute() and executing the command changed the

View File

@@ -32,6 +32,14 @@
#define MIN_COLUMNS 12 // minimal columns for screen #define MIN_COLUMNS 12 // minimal columns for screen
#define MIN_LINES 2 // minimal lines for screen #define MIN_LINES 2 // minimal lines for screen
/// Structure used by switch_win() to pass values to restore_win()
typedef struct {
win_T *sw_curwin;
tabpage_T *sw_curtab;
bool sw_same_win; ///< VIsual_active was not reset
bool sw_visual_active;
} switchwin_T;
#ifdef INCLUDE_GENERATED_DECLARATIONS #ifdef INCLUDE_GENERATED_DECLARATIONS
# include "window.h.generated.h" # include "window.h.generated.h"
#endif #endif