Skip to content

Commit

Permalink
patch 8.0.1817: a timer may change v:count unexpectedly
Browse files Browse the repository at this point in the history
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)
  • Loading branch information
brammool committed May 12, 2018
1 parent ff3be4f commit b0f42ba
Show file tree
Hide file tree
Showing 6 changed files with 70 additions and 0 deletions.
23 changes: 23 additions & 0 deletions src/eval.c
Original file line number Diff line number Diff line change
Expand Up @@ -6461,6 +6461,29 @@ set_vcount(
vimvars[VV_COUNT1].vv_nr = count1;
}

/*
* 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".
*/
Expand Down
5 changes: 5 additions & 0 deletions src/ex_cmds2.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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. */
Expand All @@ -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);
Expand All @@ -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
Expand Down
2 changes: 2 additions & 0 deletions src/proto/eval.pro
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
6 changes: 6 additions & 0 deletions src/structs.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
32 changes: 32 additions & 0 deletions src/testdir/test_timers.vim
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ if !has('timers')
endif

source shared.vim
source screendump.vim

func MyHandler(timer)
let g:val += 1
Expand Down Expand Up @@ -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 <expr><silent> 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
2 changes: 2 additions & 0 deletions src/version.c
Original file line number Diff line number Diff line change
Expand Up @@ -761,6 +761,8 @@ static char *(features[]) =

static int included_patches[] =
{ /* Add new patch number below this line */
/**/
1817,
/**/
1816,
/**/
Expand Down

0 comments on commit b0f42ba

Please sign in to comment.