# HG changeset patch # User Bram Moolenaar # Date 1588279504 -7200 # Node ID 06a1dd50463e3fe258e9b97dd9a7140d43fcb780 # Parent 4fb1da5a503169b64503deaf87c5bef4e346004f patch 8.2.0670: cannot change window when evaluating 'completefunc' Commit: https://github.com/vim/vim/commit/6adb9ea0a6ca01414f4b591f379b0f829a8273c0 Author: Bram Moolenaar Date: Thu Apr 30 22:31:18 2020 +0200 patch 8.2.0670: cannot change window when evaluating 'completefunc' Problem: Cannot change window when evaluating 'completefunc'. Solution: Make a difference between not changing text or buffers and also not changing window. diff --git a/src/beval.c b/src/beval.c --- a/src/beval.c +++ b/src/beval.c @@ -282,7 +282,7 @@ general_beval_cb(BalloonEval *beval, int curbuf = save_curbuf; if (use_sandbox) ++sandbox; - ++textlock; + ++textwinlock; vim_free(result); result = eval_to_string(bexpr, NULL, TRUE); @@ -299,7 +299,7 @@ general_beval_cb(BalloonEval *beval, int if (use_sandbox) --sandbox; - --textlock; + --textwinlock; set_vim_var_string(VV_BEVAL_TEXT, NULL, -1); if (result != NULL && result[0] != NUL) diff --git a/src/change.c b/src/change.c --- a/src/change.c +++ b/src/change.c @@ -382,7 +382,7 @@ invoke_listeners(buf_T *buf) argv[4].v_type = VAR_LIST; argv[4].vval.v_list = buf->b_recorded_changes; - ++textlock; + ++textwinlock; for (lnr = buf->b_listener; lnr != NULL; lnr = lnr->lr_next) { @@ -390,7 +390,7 @@ invoke_listeners(buf_T *buf) clear_tv(&rettv); } - --textlock; + --textwinlock; list_unref(buf->b_recorded_changes); buf->b_recorded_changes = NULL; diff --git a/src/edit.c b/src/edit.c --- a/src/edit.c +++ b/src/edit.c @@ -176,9 +176,10 @@ edit( // Don't allow changes in the buffer while editing the cmdline. The // caller of getcmdline() may get confused. // Don't allow recursive insert mode when busy with completion. - if (textlock != 0 || ins_compl_active() || compl_busy || pum_visible()) + if (textwinlock != 0 || textlock != 0 + || ins_compl_active() || compl_busy || pum_visible()) { - emsg(_(e_textlock)); + emsg(_(e_textwinlock)); return FALSE; } ins_compl_clear(); // clear stuff for CTRL-X mode @@ -5944,7 +5945,7 @@ do_insert_char_pre(int c) } // Lock the text to avoid weird things from happening. - ++textlock; + ++textwinlock; set_vim_var_string(VV_CHAR, buf, -1); // set v:char res = NULL; @@ -5958,7 +5959,7 @@ do_insert_char_pre(int c) } set_vim_var_string(VV_CHAR, NULL, -1); // clear v:char - --textlock; + --textwinlock; // Restore the State, it may have been changed. State = save_State; diff --git a/src/eval.c b/src/eval.c --- a/src/eval.c +++ b/src/eval.c @@ -393,7 +393,7 @@ eval_to_string( /* * Call eval_to_string() without using current local variables and using - * textlock. When "use_sandbox" is TRUE use the sandbox. + * textwinlock. When "use_sandbox" is TRUE use the sandbox. */ char_u * eval_to_string_safe( @@ -407,11 +407,11 @@ eval_to_string_safe( save_funccal(&funccal_entry); if (use_sandbox) ++sandbox; - ++textlock; + ++textwinlock; retval = eval_to_string(arg, nextcmd, FALSE); if (use_sandbox) --sandbox; - --textlock; + --textwinlock; restore_funccal(); return retval; } @@ -576,7 +576,7 @@ eval_foldexpr(char_u *arg, int *cp) ++emsg_off; if (use_sandbox) ++sandbox; - ++textlock; + ++textwinlock; *cp = NUL; if (eval0(arg, &tv, NULL, TRUE) == FAIL) retval = 0; @@ -601,7 +601,7 @@ eval_foldexpr(char_u *arg, int *cp) --emsg_off; if (use_sandbox) --sandbox; - --textlock; + --textwinlock; return (int)retval; } diff --git a/src/ex_docmd.c b/src/ex_docmd.c --- a/src/ex_docmd.c +++ b/src/ex_docmd.c @@ -5749,7 +5749,7 @@ handle_drop( handle_any_postponed_drop(void) { if (!drop_busy && drop_filev != NULL - && !text_locked() && !curbuf_locked() && !updating_screen) + && !text_locked() && !curbuf_locked() && !updating_screen) handle_drop_internal(); } #endif diff --git a/src/ex_getln.c b/src/ex_getln.c --- a/src/ex_getln.c +++ b/src/ex_getln.c @@ -1318,12 +1318,12 @@ getcmdline_int( c = get_expr_register(); if (c == '=') { - // Need to save and restore ccline. And set "textlock" + // Need to save and restore ccline. And set "textwinlock" // to avoid nasty things like going to another buffer when // evaluating an expression. - ++textlock; + ++textwinlock; p = get_expr_line(); - --textlock; + --textwinlock; if (p != NULL) { @@ -2548,17 +2548,17 @@ check_opt_wim(void) /* * Return TRUE when the text must not be changed and we can't switch to - * another window or buffer. Used when editing the command line, evaluating + * another window or buffer. TRUE when editing the command line, evaluating * 'balloonexpr', etc. */ int -text_locked(void) +text_and_win_locked(void) { #ifdef FEAT_CMDWIN if (cmdwin_type != 0) return TRUE; #endif - return textlock != 0; + return textwinlock != 0; } /* @@ -2578,10 +2578,22 @@ get_text_locked_msg(void) if (cmdwin_type != 0) return e_cmdwin; #endif + if (textwinlock != 0) + return e_textwinlock; return e_textlock; } /* + * Return TRUE when the text must not be changed and/or we cannot switch to + * another window. TRUE while evaluating 'completefunc'. + */ + int +text_locked(void) +{ + return text_and_win_locked() || textlock != 0; +} + +/* * Check if "curbuf_lock" or "allbuf_lock" is set and return TRUE when it is * and give an error message. */ @@ -3560,11 +3572,11 @@ cmdline_paste( regname = may_get_selection(regname); #endif - // Need to set "textlock" to avoid nasty things like going to another + // Need to set "textwinlock" to avoid nasty things like going to another // buffer when evaluating an expression. - ++textlock; + ++textwinlock; i = get_spec_reg(regname, &arg, &allocated, TRUE); - --textlock; + --textwinlock; if (i) { diff --git a/src/globals.h b/src/globals.h --- a/src/globals.h +++ b/src/globals.h @@ -798,9 +798,15 @@ EXTERN int secure INIT(= FALSE); // allowed, e.g. when sourcing .exrc or .vimrc // in current directory -EXTERN int textlock INIT(= 0); +EXTERN int textwinlock INIT(= 0); // non-zero when changing text and jumping to - // another window or buffer is not allowed + // another window or editing another buffer is + // not allowed + +EXTERN int textlock INIT(= 0); + // non-zero when changing text is not allowed, + // jumping to another window is allowed, + // editing another buffer is not allowed. EXTERN int curbuf_lock INIT(= 0); // non-zero when the current buffer can't be @@ -1681,7 +1687,8 @@ EXTERN char e_readerrf[] INIT(= N_("E47: EXTERN char e_sandbox[] INIT(= N_("E48: Not allowed in sandbox")); #endif EXTERN char e_secure[] INIT(= N_("E523: Not allowed here")); -EXTERN char e_textlock[] INIT(= N_("E565: Not allowed to change text here")); +EXTERN char e_textlock[] INIT(= N_("E578: Not allowed to change text here")); +EXTERN char e_textwinlock[] INIT(= N_("E565: Not allowed to change text or change window")); #if defined(AMIGA) || defined(MACOS_X) || defined(MSWIN) \ || defined(UNIX) || defined(VMS) EXTERN char e_screenmode[] INIT(= N_("E359: Screen mode setting not supported")); diff --git a/src/indent.c b/src/indent.c --- a/src/indent.c +++ b/src/indent.c @@ -1760,7 +1760,7 @@ get_expr_indent(void) set_vim_var_nr(VV_LNUM, curwin->w_cursor.lnum); if (use_sandbox) ++sandbox; - ++textlock; + ++textwinlock; // Need to make a copy, the 'indentexpr' option could be changed while // evaluating it. @@ -1773,7 +1773,7 @@ get_expr_indent(void) if (use_sandbox) --sandbox; - --textlock; + --textwinlock; // Restore the cursor position so that 'indentexpr' doesn't need to. // Pretend to be in Insert mode, allow cursor past end of line for "o" diff --git a/src/insexpand.c b/src/insexpand.c --- a/src/insexpand.c +++ b/src/insexpand.c @@ -989,9 +989,9 @@ trigger_complete_changed_event(int cur) dict_set_items_ro(v_event); recursive = TRUE; - textlock++; + textwinlock++; apply_autocmds(EVENT_COMPLETECHANGED, NULL, NULL, FALSE, curbuf); - textlock--; + textwinlock--; recursive = FALSE; dict_free_contents(v_event); @@ -2217,7 +2217,8 @@ expand_by_function( pos = curwin->w_cursor; curwin_save = curwin; curbuf_save = curbuf; - // Lock the text to avoid weird things from happening. + // Lock the text to avoid weird things from happening. Do allow switching + // to another window temporarily. ++textlock; // Call a function, which returns a list or dict. @@ -2442,8 +2443,8 @@ f_complete(typval_T *argvars, typval_T * return; } - // "textlock" is set when evaluating 'completefunc' but we can change text - // here. + // "textlock" is set when evaluating 'completefunc' but we can change + // text here. textlock = 0; // Check for undo allowed here, because if something was already inserted diff --git a/src/map.c b/src/map.c --- a/src/map.c +++ b/src/map.c @@ -1570,14 +1570,14 @@ eval_map_expr( // Forbid changing text or using ":normal" to avoid most of the bad side // effects. Also restore the cursor position. - ++textlock; + ++textwinlock; ++ex_normal_lock; set_vim_var_char(c); // set v:char to the typed character save_cursor = curwin->w_cursor; save_msg_col = msg_col; save_msg_row = msg_row; p = eval_to_string(expr, NULL, FALSE); - --textlock; + --textwinlock; --ex_normal_lock; curwin->w_cursor = save_cursor; msg_col = save_msg_col; diff --git a/src/proto/ex_getln.pro b/src/proto/ex_getln.pro --- a/src/proto/ex_getln.pro +++ b/src/proto/ex_getln.pro @@ -3,9 +3,10 @@ void cmdline_init(void); char_u *getcmdline(int firstc, long count, int indent, int do_concat); char_u *getcmdline_prompt(int firstc, char_u *prompt, int attr, int xp_context, char_u *xp_arg); int check_opt_wim(void); -int text_locked(void); +int text_and_win_locked(void); void text_locked_msg(void); char *get_text_locked_msg(void); +int text_locked(void); int curbuf_locked(void); int allbuf_locked(void); char_u *getexline(int c, void *cookie, int indent, int do_concat); diff --git a/src/register.c b/src/register.c --- a/src/register.c +++ b/src/register.c @@ -932,9 +932,9 @@ yank_do_autocmd(oparg_T *oap, yankreg_T dict_set_items_ro(v_event); recursive = TRUE; - textlock++; + textwinlock++; apply_autocmds(EVENT_TEXTYANKPOST, NULL, NULL, FALSE, curbuf); - textlock--; + textwinlock--; recursive = FALSE; // Empty the dictionary, v:event is still valid diff --git a/src/testdir/test_edit.vim b/src/testdir/test_edit.vim --- a/src/testdir/test_edit.vim +++ b/src/testdir/test_edit.vim @@ -927,7 +927,7 @@ func Test_edit_completefunc_delete() set completefunc=CompleteFunc call setline(1, ['', 'abcd', '']) 2d - call assert_fails("normal 2G$a\\", 'E565:') + call assert_fails("normal 2G$a\\", 'E578:') bwipe! endfunc diff --git a/src/testdir/test_ins_complete.vim b/src/testdir/test_ins_complete.vim --- a/src/testdir/test_ins_complete.vim +++ b/src/testdir/test_ins_complete.vim @@ -491,7 +491,7 @@ func Test_completefunc_error() endfunc set completefunc=CompleteFunc2 call setline(1, ['', 'abcd', '']) - call assert_fails('exe "normal 2G$a\\"', 'E565:') + call assert_fails('exe "normal 2G$a\\"', 'E578:') set completefunc& delfunc CompleteFunc diff --git a/src/testdir/test_popup.vim b/src/testdir/test_popup.vim --- a/src/testdir/test_popup.vim +++ b/src/testdir/test_popup.vim @@ -342,7 +342,7 @@ func Test_completefunc_opens_new_window_ setlocal completefunc=DummyCompleteOne call setline(1, 'one') /^one - call assert_fails('call feedkeys("A\\\\", "x")', 'E565:') + call assert_fails('call feedkeys("A\\\\", "x")', 'E578:') call assert_equal(winid, win_getid()) call assert_equal('oneDEF', getline(1)) q! diff --git a/src/undo.c b/src/undo.c --- a/src/undo.c +++ b/src/undo.c @@ -331,7 +331,7 @@ undo_allowed(void) // Don't allow changes in the buffer while editing the cmdline. The // caller of getcmdline() may get confused. - if (textlock != 0) + if (textwinlock != 0 || textlock != 0) { emsg(_(e_textlock)); return FALSE; diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -747,6 +747,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 670, +/**/ 669, /**/ 668, diff --git a/src/window.c b/src/window.c --- a/src/window.c +++ b/src/window.c @@ -4370,7 +4370,7 @@ win_goto(win_T *wp) return; } #endif - if (text_locked()) + if (text_and_win_locked()) { beep_flush(); text_locked_msg();