diff options
-rw-r--r-- | src/nvim/eval/userfunc.c | 58 | ||||
-rw-r--r-- | src/nvim/testdir/test_timers.vim | 25 |
2 files changed, 57 insertions, 26 deletions
diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c index f6530d0e92..4a62b4bf8d 100644 --- a/src/nvim/eval/userfunc.c +++ b/src/nvim/eval/userfunc.c @@ -1877,6 +1877,36 @@ theend: #define MAX_FUNC_NESTING 50 +/// List functions. +/// +/// @param regmatch When NULL, all of them. +/// Otherwise functions matching "regmatch". +static void list_functions(regmatch_T *regmatch) +{ + const size_t used = func_hashtab.ht_used; + size_t todo = used; + const hashitem_T *const ht_array = func_hashtab.ht_array; + + for (const hashitem_T *hi = ht_array; todo > 0 && !got_int; hi++) { + if (!HASHITEM_EMPTY(hi)) { + ufunc_T *fp = HI2UF(hi); + todo--; + if ((fp->uf_flags & FC_DEAD) == 0 + && (regmatch == NULL + ? (!message_filtered((char *)fp->uf_name) + && !func_name_refcount(fp->uf_name)) + : (!isdigit(*fp->uf_name) + && vim_regexec(regmatch, (char *)fp->uf_name, 0)))) { + list_func_head(fp, false, false); + if (used != func_hashtab.ht_used || ht_array != func_hashtab.ht_array) { + emsg(_("E454: function list was modified")); + return; + } + } + } + } +} + /// ":function" void ex_function(exarg_T *eap) { @@ -1903,7 +1933,6 @@ void ex_function(exarg_T *eap) static int func_nr = 0; // number for nameless function int paren; hashtab_T *ht; - int todo; hashitem_T *hi; linenr_T sourcing_lnum_off; linenr_T sourcing_lnum_top; @@ -1916,19 +1945,7 @@ void ex_function(exarg_T *eap) // ":function" without argument: list functions. if (ends_excmd(*eap->arg)) { if (!eap->skip) { - todo = (int)func_hashtab.ht_used; - for (hi = func_hashtab.ht_array; todo > 0 && !got_int; hi++) { - if (!HASHITEM_EMPTY(hi)) { - todo--; - fp = HI2UF(hi); - if (message_filtered((char *)fp->uf_name)) { - continue; - } - if (!func_name_refcount(fp->uf_name)) { - list_func_head(fp, false, false); - } - } - } + list_functions(NULL); } eap->nextcmd = check_nextcmd(eap->arg); return; @@ -1946,18 +1963,7 @@ void ex_function(exarg_T *eap) *p = c; if (regmatch.regprog != NULL) { regmatch.rm_ic = p_ic; - - todo = (int)func_hashtab.ht_used; - for (hi = func_hashtab.ht_array; todo > 0 && !got_int; hi++) { - if (!HASHITEM_EMPTY(hi)) { - todo--; - fp = HI2UF(hi); - if (!isdigit(*fp->uf_name) - && vim_regexec(®match, (char *)fp->uf_name, 0)) { - list_func_head(fp, false, false); - } - } - } + list_functions(®match); vim_regfree(regmatch.regprog); } } diff --git a/src/nvim/testdir/test_timers.vim b/src/nvim/testdir/test_timers.vim index 4d450e180b..771f61442d 100644 --- a/src/nvim/testdir/test_timers.vim +++ b/src/nvim/testdir/test_timers.vim @@ -3,6 +3,7 @@ source check.vim CheckFeature timers +source screendump.vim source shared.vim source term_util.vim source load.vim @@ -408,6 +409,30 @@ func Test_timer_invalid_callback() call assert_fails('call timer_start(0, "0")', 'E921') endfunc +func Test_timer_changing_function_list() + CheckRunVimInTerminal + + " Create a large number of functions. Should get the "more" prompt. + " The typing "G" triggers the timer, which changes the function table. + let lines =<< trim END + for func in map(range(1,99), "'Func' .. v:val") + exe "func " .. func .. "()" + endfunc + endfor + au CmdlineLeave : call timer_start(0, {-> 0}) + END + call writefile(lines, 'XTest_timerchange') + let buf = RunVimInTerminal('-S XTest_timerchange', #{rows: 10}) + call term_sendkeys(buf, ":fu\<CR>") + call WaitForAssert({-> assert_match('-- More --', term_getline(buf, 10))}) + call term_sendkeys(buf, "G") + call WaitForAssert({-> assert_match('E454', term_getline(buf, 9))}) + call term_sendkeys(buf, "\<Esc>") + + call StopVimInTerminal(buf) + call delete('XTest_timerchange') +endfunc + func Test_timer_using_win_execute_undo_sync() let bufnr1 = bufnr() new |