# HG changeset patch # User Christian Brabandt # Date 1470587405 -7200 # Node ID 6a28d0c6f92929c21307a78adb2cd4219f4b5325 # Parent f08483937ffda7ef79febd15c1334dd11fbaa8dd commit https://github.com/vim/vim/commit/b73598e2f022a22fec512ea681c70d2775e8fd87 Author: Bram Moolenaar Date: Sun Aug 7 18:22:53 2016 +0200 patch 7.4.2180 Problem: There is no easy way to stop all timers. There is no way to temporary pause a timer. Solution: Add timer_stopall() and timer_pause(). diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -1,4 +1,4 @@ -*eval.txt* For Vim version 7.4. Last change: 2016 Aug 06 +*eval.txt* For Vim version 7.4. Last change: 2016 Aug 07 VIM REFERENCE MANUAL by Bram Moolenaar @@ -1969,7 +1969,7 @@ assert_exception({error} [, {msg}]) assert_fails({cmd} [, {error}]) none assert {cmd} fails assert_false({actual} [, {msg}]) none assert {actual} is false assert_inrange({lower}, {upper}, {actual} [, {msg}]) - none assert {actual} is inside the range + none assert {actual} is inside the range assert_match({pat}, {text} [, {msg}]) none assert {pat} matches {text} assert_notequal({exp}, {act} [, {msg}]) none assert {exp} is not equal {act} assert_notmatch({pat}, {text} [, {msg}]) none assert {pat} not matches {text} @@ -2340,9 +2340,11 @@ test_null_partial() Funcref null value test_null_string() String null value for testing test_settime({expr}) none set current time for testing timer_info([{id}]) List information about timers +timer_pause({id}, {pause}) none pause or unpause a timer timer_start({time}, {callback} [, {options}]) Number create a timer timer_stop({timer}) none stop a timer +timer_stopall() none stop all timers tolower({expr}) String the String {expr} switched to lowercase toupper({expr}) String the String {expr} switched to uppercase tr({src}, {fromstr}, {tostr}) String translate chars of {src} in {fromstr} @@ -7555,8 +7557,26 @@ timer_info([{id}]) "time" time the timer was started with "remaining" time until the timer fires "repeat" number of times the timer will still fire; - -1 means forever + -1 means forever "callback" the callback + "paused" 1 if the timer is paused, 0 otherwise + + {only available when compiled with the |+timers| feature} + +timer_pause({timer}, {paused}) *timer_pause()* + Pause or unpause a timer. A paused timer does not invoke its + callback, while the time it would is not changed. Unpausing a + timer may cause the callback to be invoked almost immediately + if enough time has passed. + + Pausing a timer is useful to avoid the callback to be called + for a short time. + + If {paused} evaluates to a non-zero Number or a non-empty + String, then the timer is paused, otherwise it is unpaused. + See |non-zero-arg|. + + {only available when compiled with the |+timers| feature} *timer_start()* timer_start({time}, {callback} [, {options}]) @@ -7583,6 +7603,7 @@ timer_start({time}, {callback} [, {optio \ {'repeat': 3}) < This will invoke MyHandler() three times at 500 msec intervals. + {only available when compiled with the |+timers| feature} timer_stop({timer}) *timer_stop()* @@ -7590,6 +7611,15 @@ timer_stop({timer}) *timer_stop()* {timer} is an ID returned by timer_start(), thus it must be a Number. If {timer} does not exist there is no error. + {only available when compiled with the |+timers| feature} + +timer_stopall() *timer_stopall()* + Stop all timers. The timer callbacks will no longer be + invoked. Useful if some timers is misbehaving. If there are + no timers there is no error. + + {only available when compiled with the |+timers| feature} + tolower({expr}) *tolower()* The result is a copy of the String given, with all uppercase characters turned into lowercase (just like applying |gu| to diff --git a/src/evalfunc.c b/src/evalfunc.c --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -397,8 +397,10 @@ static void f_tanh(typval_T *argvars, ty #endif #ifdef FEAT_TIMERS static void f_timer_info(typval_T *argvars, typval_T *rettv); +static void f_timer_pause(typval_T *argvars, typval_T *rettv); static void f_timer_start(typval_T *argvars, typval_T *rettv); static void f_timer_stop(typval_T *argvars, typval_T *rettv); +static void f_timer_stopall(typval_T *argvars, typval_T *rettv); #endif static void f_tolower(typval_T *argvars, typval_T *rettv); static void f_toupper(typval_T *argvars, typval_T *rettv); @@ -817,8 +819,10 @@ static struct fst {"test_settime", 1, 1, f_test_settime}, #ifdef FEAT_TIMERS {"timer_info", 0, 1, f_timer_info}, + {"timer_pause", 2, 2, f_timer_pause}, {"timer_start", 2, 3, f_timer_start}, {"timer_stop", 1, 1, f_timer_stop}, + {"timer_stopall", 0, 0, f_timer_stopall}, #endif {"tolower", 1, 1, f_tolower}, {"toupper", 1, 1, f_toupper}, @@ -11988,6 +11992,25 @@ f_timer_info(typval_T *argvars, typval_T } /* + * "timer_pause(timer, paused)" function + */ + static void +f_timer_pause(typval_T *argvars, typval_T *rettv UNUSED) +{ + timer_T *timer = NULL; + int paused = (int)get_tv_number(&argvars[1]); + + if (argvars[0].v_type != VAR_NUMBER) + EMSG(_(e_number_exp)); + else + { + timer = find_timer((int)get_tv_number(&argvars[0])); + if (timer != NULL) + timer->tr_paused = paused; + } +} + +/* * "timer_start(time, callback [, options])" function */ static void @@ -12048,6 +12071,15 @@ f_timer_stop(typval_T *argvars, typval_T if (timer != NULL) stop_timer(timer); } + +/* + * "timer_stopall()" function + */ + static void +f_timer_stopall(typval_T *argvars UNUSED, typval_T *rettv UNUSED) +{ + stop_all_timers(); +} #endif /* diff --git a/src/ex_cmds2.c b/src/ex_cmds2.c --- a/src/ex_cmds2.c +++ b/src/ex_cmds2.c @@ -1189,6 +1189,8 @@ check_due_timer(void) next_due = -1; for (timer = first_timer; timer != NULL; timer = timer->tr_next) { + if (timer->tr_paused) + continue; # ifdef WIN3264 this_due = (long)(((double)(timer->tr_due.QuadPart - now.QuadPart) / (double)fr.QuadPart) * 1000); @@ -1252,6 +1254,15 @@ stop_timer(timer_T *timer) } void +stop_all_timers(void) +{ + timer_T *timer; + + while (first_timer != NULL) + stop_timer(first_timer); +} + + void add_timer_info(typval_T *rettv, timer_T *timer) { list_T *list = rettv->vval.v_list; @@ -1283,6 +1294,7 @@ add_timer_info(typval_T *rettv, timer_T dict_add_nr_str(dict, "repeat", (long)(timer->tr_repeat < 0 ? -1 : timer->tr_repeat + 1), NULL); + dict_add_nr_str(dict, "paused", (long)(timer->tr_paused), NULL); di = dictitem_alloc((char_u *)"callback"); if (di != NULL) diff --git a/src/proto/ex_cmds2.pro b/src/proto/ex_cmds2.pro --- a/src/proto/ex_cmds2.pro +++ b/src/proto/ex_cmds2.pro @@ -22,6 +22,7 @@ timer_T *create_timer(long msec, int rep long check_due_timer(void); timer_T *find_timer(int id); void stop_timer(timer_T *timer); +void stop_all_timers(void); void add_timer_info(typval_T *rettv, timer_T *timer); void add_timer_info_all(typval_T *rettv); int set_ref_in_timer(int copyID); diff --git a/src/structs.h b/src/structs.h --- a/src/structs.h +++ b/src/structs.h @@ -3159,6 +3159,7 @@ struct timer_S timer_T *tr_next; timer_T *tr_prev; proftime_T tr_due; /* when the callback is to be invoked */ + int tr_paused; /* when TRUE callback is not invoked */ int tr_repeat; /* number of times to repeat, -1 forever */ long tr_interval; /* msec */ char_u *tr_callback; /* allocated */ diff --git a/src/testdir/shared.vim b/src/testdir/shared.vim --- a/src/testdir/shared.vim +++ b/src/testdir/shared.vim @@ -109,14 +109,17 @@ func s:kill_server(cmd) endfunc " Wait for up to a second for "expr" to become true. +" Return time slept in milliseconds. func WaitFor(expr) + let slept = 0 for i in range(100) try if eval(a:expr) - return + return slept endif catch endtry + let slept += 10 sleep 10m endfor endfunc 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 @@ -1,11 +1,13 @@ " Test for timers +source shared.vim + if !has('timers') finish endif func MyHandler(timer) - let s:val += 1 + let g:val += 1 endfunc func MyHandlerWithLists(lists, timer) @@ -13,43 +15,101 @@ func MyHandlerWithLists(lists, timer) endfunc func Test_oneshot() - let s:val = 0 + let g:val = 0 let timer = timer_start(50, 'MyHandler') - sleep 200m - call assert_equal(1, s:val) + let slept = WaitFor('g:val == 1') + call assert_equal(1, g:val) + call assert_inrange(30, 100, slept) endfunc func Test_repeat_three() - let s:val = 0 + let g:val = 0 let timer = timer_start(50, 'MyHandler', {'repeat': 3}) - sleep 500m - call assert_equal(3, s:val) + let slept = WaitFor('g:val == 3') + call assert_equal(3, g:val) + call assert_inrange(100, 250, slept) endfunc func Test_repeat_many() - let s:val = 0 + let g:val = 0 let timer = timer_start(50, 'MyHandler', {'repeat': -1}) sleep 200m call timer_stop(timer) - call assert_true(s:val > 1) - call assert_true(s:val < 5) + call assert_inrange(2, 4, g:val) endfunc func Test_with_partial_callback() - let s:val = 0 + let g:val = 0 let s:meow = {} function s:meow.bite(...) - let s:val += 1 + let g:val += 1 endfunction call timer_start(50, s:meow.bite) - sleep 200m - call assert_equal(1, s:val) + let slept = WaitFor('g:val == 1') + call assert_equal(1, g:val) + call assert_inrange(30, 100, slept) endfunc func Test_retain_partial() - call timer_start(100, function('MyHandlerWithLists', [['a']])) + call timer_start(50, function('MyHandlerWithLists', [['a']])) call test_garbagecollect_now() - sleep 200m + sleep 100m +endfunc + +func Test_info() + let id = timer_start(1000, 'MyHandler') + let info = timer_info(id) + call assert_equal(id, info[0]['id']) + call assert_equal(1000, info[0]['time']) + call assert_true(info[0]['remaining'] > 500) + call assert_true(info[0]['remaining'] <= 1000) + call assert_equal(1, info[0]['repeat']) + call assert_equal("function('MyHandler')", string(info[0]['callback'])) + + let found = 0 + for info in timer_info() + if info['id'] == id + let found += 1 + endif + endfor + call assert_equal(1, found) + + call timer_stop(id) + call assert_equal([], timer_info(id)) endfunc + +func Test_stopall() + let id1 = timer_start(1000, 'MyHandler') + let id2 = timer_start(2000, 'MyHandler') + let info = timer_info() + call assert_equal(2, len(info)) + + call timer_stopall() + let info = timer_info() + call assert_equal(0, len(info)) +endfunc + +func Test_paused() + let g:val = 0 + + let id = timer_start(50, 'MyHandler') + let info = timer_info(id) + call assert_equal(0, info[0]['paused']) + + call timer_pause(id, 1) + let info = timer_info(id) + call assert_equal(1, info[0]['paused']) + sleep 100m + call assert_equal(0, g:val) + + call timer_pause(id, 0) + let info = timer_info(id) + call assert_equal(0, info[0]['paused']) + + let slept = WaitFor('g:val == 1') + call assert_equal(1, g:val) + call assert_inrange(0, 10, slept) +endfunc + " vim: ts=2 sw=0 et diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -764,6 +764,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 2180, +/**/ 2179, /**/ 2178,