# HG changeset patch # User Bram Moolenaar # Date 1587500104 -7200 # Node ID 252d2bb9039427746fcc4682382a42f1c2b29cb7 # Parent 60a0e7ed241d3b398560ab09063e196de56cbb3d patch 8.2.0614: get ml_get error when deleting a line in 'completefunc' Commit: https://github.com/vim/vim/commit/ff06f283e3e4b3ec43012dd3b83f8454c98f6639 Author: Bram Moolenaar Date: Tue Apr 21 22:01:14 2020 +0200 patch 8.2.0614: get ml_get error when deleting a line in 'completefunc' Problem: Get ml_get error when deleting a line in 'completefunc'. (Yegappan Lakshmanan) Solution: Lock the text while evaluating 'completefunc'. diff --git a/runtime/doc/insert.txt b/runtime/doc/insert.txt --- a/runtime/doc/insert.txt +++ b/runtime/doc/insert.txt @@ -666,8 +666,10 @@ Note: The keys that are valid in CTRL-X ends CTRL-X mode (any key that is not a valid CTRL-X mode command) is mapped. Also, when doing completion with 'complete' mappings apply as usual. -Note: While completion is active Insert mode can't be used recursively. -Mappings that somehow invoke ":normal i.." will generate an E523 error. + *E565* +Note: While completion is active Insert mode can't be used recursively and +buffer text cannot be changed. Mappings that somehow invoke ":normal i.." +will generate an E565 error. The following mappings are suggested to make typing the completion commands a bit easier (although they will hide other commands): > diff --git a/src/edit.c b/src/edit.c --- a/src/edit.c +++ b/src/edit.c @@ -175,16 +175,10 @@ edit( #endif // Don't allow changes in the buffer while editing the cmdline. The // caller of getcmdline() may get confused. - if (textlock != 0) + // Don't allow recursive insert mode when busy with completion. + if (textlock != 0 || ins_compl_active() || compl_busy || pum_visible()) { - emsg(_(e_secure)); - return FALSE; - } - - // Don't allow recursive insert mode when busy with completion. - if (ins_compl_active() || compl_busy || pum_visible()) - { - emsg(_(e_secure)); + emsg(_(e_textlock)); return FALSE; } ins_compl_clear(); // clear stuff for CTRL-X mode diff --git a/src/ex_getln.c b/src/ex_getln.c --- a/src/ex_getln.c +++ b/src/ex_getln.c @@ -2576,7 +2576,7 @@ get_text_locked_msg(void) if (cmdwin_type != 0) return e_cmdwin; #endif - return e_secure; + return e_textlock; } /* diff --git a/src/globals.h b/src/globals.h --- a/src/globals.h +++ b/src/globals.h @@ -1678,9 +1678,10 @@ EXTERN char e_letunexp[] INIT(= N_("E18: EXTERN char e_readerrf[] INIT(= N_("E47: Error while reading errorfile")); #endif #ifdef HAVE_SANDBOX -EXTERN char e_sandbox[] INIT(= N_("E48: Not allowed in sandbox")); +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_secure[] INIT(= N_("E523: Not allowed here")); +EXTERN char e_textlock[] INIT(= N_("E565: Not allowed to change text here")); #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/insexpand.c b/src/insexpand.c --- a/src/insexpand.c +++ b/src/insexpand.c @@ -2217,6 +2217,8 @@ expand_by_function( pos = curwin->w_cursor; curwin_save = curwin; curbuf_save = curbuf; + // Lock the text to avoid weird things from happening. + ++textlock; // Call a function, which returns a list or dict. if (call_vim_function(funcname, 2, args, &rettv) == OK) @@ -2239,6 +2241,7 @@ expand_by_function( break; } } + --textlock; if (curwin_save != curwin || curbuf_save != curbuf) { @@ -2431,6 +2434,7 @@ set_completion(colnr_T startcol, list_T f_complete(typval_T *argvars, typval_T *rettv UNUSED) { int startcol; + int save_textlock = textlock; if ((State & INSERT) == 0) { @@ -2438,22 +2442,24 @@ f_complete(typval_T *argvars, typval_T * return; } + // "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 // the line was already saved for undo and this check isn't done. if (!undo_allowed()) return; if (argvars[1].v_type != VAR_LIST || argvars[1].vval.v_list == NULL) + emsg(_(e_invarg)); + else { - emsg(_(e_invarg)); - return; + startcol = (int)tv_get_number_chk(&argvars[0], NULL); + if (startcol > 0) + set_completion(startcol - 1, argvars[1].vval.v_list); } - - startcol = (int)tv_get_number_chk(&argvars[0], NULL); - if (startcol <= 0) - return; - - set_completion(startcol - 1, argvars[1].vval.v_list); + textlock = save_textlock; } /* 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 @@ -915,6 +915,23 @@ func Test_edit_CTRL_U() bw! endfunc +func Test_edit_completefunc_delete() + func CompleteFunc(findstart, base) + if a:findstart == 1 + return col('.') - 1 + endif + normal dd + return ['a', 'b'] + endfunc + new + set completefunc=CompleteFunc + call setline(1, ['', 'abcd', '']) + 2d + call assert_fails("normal 2G$a\\", 'E565:') + bwipe! +endfunc + + func Test_edit_CTRL_Z() " Ctrl-Z when insertmode is not set inserts it literally new @@ -1240,7 +1257,7 @@ func Test_edit_forbidden() try call feedkeys("ix\", 'tnix') call assert_fails(1, 'textlock') - catch /^Vim\%((\a\+)\)\=:E523/ " catch E523: not allowed here + catch /^Vim\%((\a\+)\)\=:E565/ " catch E565: not allowed here endtry " TODO: Might be a bug: should x really be inserted here call assert_equal(['xa'], getline(1, '$')) @@ -1264,7 +1281,7 @@ func Test_edit_forbidden() try call feedkeys("i\\\", 'tnix') call assert_fails(1, 'change in complete function') - catch /^Vim\%((\a\+)\)\=:E523/ " catch E523 + catch /^Vim\%((\a\+)\)\=:E565/ " catch E565 endtry delfu Complete set completefunc= diff --git a/src/testdir/test_ex_mode.vim b/src/testdir/test_ex_mode.vim --- a/src/testdir/test_ex_mode.vim +++ b/src/testdir/test_ex_mode.vim @@ -158,13 +158,13 @@ endfunc func Test_ex_mode_errors() " Not allowed to enter ex mode when text is locked au InsertCharPre normal! gQ - let caught_e523 = 0 + let caught_e565 = 0 try call feedkeys("ix\", 'xt') - catch /^Vim\%((\a\+)\)\=:E523/ " catch E523 - let caught_e523 = 1 + catch /^Vim\%((\a\+)\)\=:E565/ " catch E565 + let caught_e565 = 1 endtry - call assert_equal(1, caught_e523) + call assert_equal(1, caught_e565) au! InsertCharPre endfunc diff --git a/src/testdir/test_excmd.vim b/src/testdir/test_excmd.vim --- a/src/testdir/test_excmd.vim +++ b/src/testdir/test_excmd.vim @@ -354,15 +354,15 @@ endfunc func Test_run_excmd_with_text_locked() " :quit let cmd = ":\eexecute('quit')\\" - call assert_fails("call feedkeys(cmd, 'xt')", 'E523:') + call assert_fails("call feedkeys(cmd, 'xt')", 'E565:') " :qall let cmd = ":\eexecute('qall')\\" - call assert_fails("call feedkeys(cmd, 'xt')", 'E523:') + call assert_fails("call feedkeys(cmd, 'xt')", 'E565:') " :exit let cmd = ":\eexecute('exit')\\" - call assert_fails("call feedkeys(cmd, 'xt')", 'E523:') + call assert_fails("call feedkeys(cmd, 'xt')", 'E565:') " :close - should be ignored new @@ -370,7 +370,7 @@ func Test_run_excmd_with_text_locked() call assert_equal(2, winnr('$')) close - call assert_fails("call feedkeys(\":\=execute('bnext')\\", 'xt')", 'E523:') + call assert_fails("call feedkeys(\":\=execute('bnext')\\", 'xt')", 'E565:') endfunc " Test for the :verbose command diff --git a/src/testdir/test_gf.vim b/src/testdir/test_gf.vim --- a/src/testdir/test_gf.vim +++ b/src/testdir/test_gf.vim @@ -134,13 +134,13 @@ func Test_gf_error() " gf is not allowed when text is locked au InsertCharPre normal! gF - let caught_e523 = 0 + let caught_e565 = 0 try call feedkeys("ix\", 'xt') - catch /^Vim\%((\a\+)\)\=:E523/ " catch E523 - let caught_e523 = 1 + catch /^Vim\%((\a\+)\)\=:E565/ " catch E565 + let caught_e565 = 1 endtry - call assert_equal(1, caught_e523) + call assert_equal(1, caught_e565) au! InsertCharPre bwipe! 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 @@ -334,19 +334,17 @@ func DummyCompleteOne(findstart, base) endif endfunc -" Test that nothing happens if the 'completefunc' opens -" a new window (no completion, no crash) +" Test that nothing happens if the 'completefunc' tries to open +" a new window (fails to open window, continues) func Test_completefunc_opens_new_window_one() new let winid = win_getid() setlocal completefunc=DummyCompleteOne call setline(1, 'one') /^one - call assert_fails('call feedkeys("A\\\\", "x")', 'E839:') - call assert_notequal(winid, win_getid()) - q! + call assert_fails('call feedkeys("A\\\\", "x")', 'E565:') call assert_equal(winid, win_getid()) - call assert_equal('', getline(1)) + call assert_equal('oneDEF', getline(1)) q! endfunc diff --git a/src/undo.c b/src/undo.c --- a/src/undo.c +++ b/src/undo.c @@ -333,7 +333,7 @@ undo_allowed(void) // caller of getcmdline() may get confused. if (textlock != 0) { - emsg(_(e_secure)); + 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 */ /**/ + 614, +/**/ 613, /**/ 612,