# HG changeset patch # User Christian Brabandt # Date 1526132705 -7200 # Node ID eadecbe4e390e5c8d6afcb697806d09096b8996d # Parent 6418f95b0eb11154f6570fac2a28dfcfbbe294f7 patch 8.0.1817: a timer may change v:count unexpectedly commit https://github.com/vim/vim/commit/b0f42ba60d9e6d101d103421ba0c351811615c15 Author: Bram Moolenaar Date: Sat May 12 15:38:26 2018 +0200 patch 8.0.1817: a timer may change v:count unexpectedly Problem: A timer may change v:count unexpectedly. Solution: Save and restore v:count and similar variables when a timer callback is invoked. (closes #2897) diff --git a/src/eval.c b/src/eval.c --- a/src/eval.c +++ b/src/eval.c @@ -6462,6 +6462,29 @@ set_vcount( } /* + * Save variables that might be changed as a side effect. Used when executing + * a timer callback. + */ + void +save_vimvars(vimvars_save_T *vvsave) +{ + vvsave->vv_prevcount = vimvars[VV_PREVCOUNT].vv_nr; + vvsave->vv_count = vimvars[VV_COUNT].vv_nr; + vvsave->vv_count1 = vimvars[VV_COUNT1].vv_nr; +} + +/* + * Restore variables saved by save_vimvars(). + */ + void +restore_vimvars(vimvars_save_T *vvsave) +{ + vimvars[VV_PREVCOUNT].vv_nr = vvsave->vv_prevcount; + vimvars[VV_COUNT].vv_nr = vvsave->vv_count; + vimvars[VV_COUNT1].vv_nr = vvsave->vv_count1; +} + +/* * Set string v: variable to a copy of "val". */ void diff --git a/src/ex_cmds2.c b/src/ex_cmds2.c --- a/src/ex_cmds2.c +++ b/src/ex_cmds2.c @@ -1336,6 +1336,8 @@ check_due_timer(void) this_due = proftime_time_left(&timer->tr_due, &now); if (this_due <= 1) { + /* Save and restore a lot of flags, because the timer fires while + * waiting for a character, which might be halfway a command. */ int save_timer_busy = timer_busy; int save_vgetc_busy = vgetc_busy; int save_did_emsg = did_emsg; @@ -1345,6 +1347,7 @@ check_due_timer(void) int save_did_throw = did_throw; int save_ex_pressedreturn = get_pressedreturn(); except_T *save_current_exception = current_exception; + vimvars_save_T vvsave; /* Create a scope for running the timer callback, ignoring most of * the current scope, such as being inside a try/catch. */ @@ -1357,6 +1360,7 @@ check_due_timer(void) trylevel = 0; did_throw = FALSE; current_exception = NULL; + save_vimvars(&vvsave); timer->tr_firing = TRUE; timer_callback(timer); @@ -1373,6 +1377,7 @@ check_due_timer(void) trylevel = save_trylevel; did_throw = save_did_throw; current_exception = save_current_exception; + restore_vimvars(&vvsave); if (must_redraw != 0) need_update_screen = TRUE; must_redraw = must_redraw > save_must_redraw diff --git a/src/proto/eval.pro b/src/proto/eval.pro --- a/src/proto/eval.pro +++ b/src/proto/eval.pro @@ -67,6 +67,8 @@ list_T *get_vim_var_list(int idx); dict_T *get_vim_var_dict(int idx); void set_vim_var_char(int c); void set_vcount(long count, long count1, int set_prevcount); +void save_vimvars(vimvars_save_T *vvsave); +void restore_vimvars(vimvars_save_T *vvsave); void set_vim_var_string(int idx, char_u *val, int len); void set_vim_var_list(int idx, list_T *val); void set_vim_var_dict(int idx, dict_T *val); diff --git a/src/structs.h b/src/structs.h --- a/src/structs.h +++ b/src/structs.h @@ -3423,3 +3423,9 @@ typedef struct { int save_opcount; tasave_T tabuf; } save_state_T; + +typedef struct { + varnumber_T vv_prevcount; + varnumber_T vv_count; + varnumber_T vv_count1; +} vimvars_save_T; diff --git a/src/testdir/test_timers.vim b/src/testdir/test_timers.vim --- a/src/testdir/test_timers.vim +++ b/src/testdir/test_timers.vim @@ -5,6 +5,7 @@ if !has('timers') endif source shared.vim +source screendump.vim func MyHandler(timer) let g:val += 1 @@ -260,4 +261,35 @@ func Test_ex_mode() call timer_stop(timer) endfunc +func Test_restore_count() + if !CanRunVimInTerminal() + return + endif + " Check that v:count is saved and restored, not changed by a timer. + call writefile([ + \ 'nnoremap L v:count ? v:count . "l" : "l"', + \ 'func Doit(id)', + \ ' normal 3j', + \ 'endfunc', + \ 'call timer_start(100, "Doit")', + \ ], 'Xtrcscript') + call writefile([ + \ '1-1234', + \ '2-1234', + \ '3-1234', + \ ], 'Xtrctext') + let buf = RunVimInTerminal('-S Xtrcscript Xtrctext', {}) + + " Wait for the timer to move the cursor to the third line. + call WaitForAssert({-> assert_equal(3, term_getcursor(buf)[0])}) + call assert_equal(1, term_getcursor(buf)[1]) + " Now check that v:count has not been set to 3 + call term_sendkeys(buf, 'L') + call WaitForAssert({-> assert_equal(2, term_getcursor(buf)[1])}) + + call StopVimInTerminal(buf) + call delete('Xtrcscript') + call delete('Xtrctext') +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -762,6 +762,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1817, +/**/ 1816, /**/ 1815,