vim-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().

b73598e2f0
This commit is contained in:
Jurica Bradaric 2017-03-05 16:25:40 +01:00 committed by Jurica Bradaric
parent 8924e75f34
commit 5b8ce2feed
6 changed files with 141 additions and 22 deletions

View File

@ -1947,7 +1947,7 @@ assert_exception( {error} [, {msg}]) none assert {error} is in v:exception
assert_fails( {cmd} [, {error}]) none assert {cmd} fails assert_fails( {cmd} [, {error}]) none assert {cmd} fails
assert_false({actual} [, {msg}]) none assert {actual} is false assert_false({actual} [, {msg}]) none assert {actual} is false
assert_inrange({lower}, {upper}, {actual} [, {msg}]) 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_match( {pat}, {text} [, {msg}]) none assert {pat} matches {text}
assert_notequal( {exp}, {act} [, {msg}]) none assert {exp} is not equal {act} assert_notequal( {exp}, {act} [, {msg}]) none assert {exp} is not equal {act}
assert_notmatch( {pat}, {text} [, {msg}]) none assert {pat} not matches {text} assert_notmatch( {pat}, {text} [, {msg}]) none assert {pat} not matches {text}
@ -2298,9 +2298,11 @@ tanh({expr}) Float hyperbolic tangent of {expr}
tempname() String name for a temporary file tempname() String name for a temporary file
test_garbagecollect_now() none free memory right now for testing test_garbagecollect_now() none free memory right now for testing
timer_info([{id}]) List information about timers timer_info([{id}]) List information about timers
timer_pause({id}, {pause}) none pause or unpause a timer
timer_start({time}, {callback} [, {options}]) timer_start({time}, {callback} [, {options}])
Number create a timer Number create a timer
timer_stop({timer}) none stop a timer timer_stop({timer}) none stop a timer
timer_stopall() none stop all timers
tolower({expr}) String the String {expr} switched to lowercase tolower({expr}) String the String {expr} switched to lowercase
toupper({expr}) String the String {expr} switched to uppercase toupper({expr}) String the String {expr} switched to uppercase
tr({src}, {fromstr}, {tostr}) String translate chars of {src} in {fromstr} tr({src}, {fromstr}, {tostr}) String translate chars of {src} in {fromstr}
@ -7534,6 +7536,19 @@ timer_info([{id}])
-1 means forever -1 means forever
"callback" the callback "callback" the callback
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|.
*timer_start()* *timer_start()*
timer_start({time}, {callback} [, {options}]) timer_start({time}, {callback} [, {options}])
Create a timer and return the timer ID. Create a timer and return the timer ID.
@ -7565,6 +7580,11 @@ timer_stop({timer}) *timer_stop()*
{timer} is an ID returned by timer_start(), thus it must be a {timer} is an ID returned by timer_start(), thus it must be a
Number. If {timer} does not exist there is no error. Number. If {timer} does not exist there is no error.
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.
tolower({expr}) *tolower()* tolower({expr}) *tolower()*
The result is a copy of the String given, with all uppercase The result is a copy of the String given, with all uppercase
characters turned into lowercase (just like applying |gu| to characters turned into lowercase (just like applying |gu| to

View File

@ -446,6 +446,7 @@ typedef struct {
int refcount; int refcount;
long timeout; long timeout;
bool stopped; bool stopped;
bool paused;
Callback callback; Callback callback;
} timer_T; } timer_T;
@ -17939,6 +17940,7 @@ static void add_timer_info(typval_T *rettv, timer_T *timer)
list_append_dict(list, dict); list_append_dict(list, dict);
dict_add_nr_str(dict, "id", (long)timer->timer_id, NULL); dict_add_nr_str(dict, "id", (long)timer->timer_id, NULL);
dict_add_nr_str(dict, "time", timer->timeout, NULL); dict_add_nr_str(dict, "time", timer->timeout, NULL);
dict_add_nr_str(dict, "paused", (long)timer->paused, NULL);
dict_add_nr_str(dict, "repeat", dict_add_nr_str(dict, "repeat",
(long)(timer->repeat_count < 0 ? -1 : timer->repeat_count), (long)(timer->repeat_count < 0 ? -1 : timer->repeat_count),
@ -17965,7 +17967,9 @@ static void add_timer_info_all(typval_T *rettv)
{ {
timer_T *timer; timer_T *timer;
map_foreach_value(timers, timer, { map_foreach_value(timers, timer, {
add_timer_info(rettv, timer); if (!timer->stopped) {
add_timer_info(rettv, timer);
}
}) })
} }
@ -17978,7 +17982,7 @@ static void f_timer_info(typval_T *argvars, typval_T *rettv, FunPtr fptr)
EMSG(_(e_number_exp)); EMSG(_(e_number_exp));
} else { } else {
timer_T *timer = pmap_get(uint64_t)(timers, get_tv_number(&argvars[0])); timer_T *timer = pmap_get(uint64_t)(timers, get_tv_number(&argvars[0]));
if (timer != NULL) { if (timer != NULL && !timer->stopped) {
add_timer_info(rettv, timer); add_timer_info(rettv, timer);
} }
} }
@ -17987,6 +17991,20 @@ static void f_timer_info(typval_T *argvars, typval_T *rettv, FunPtr fptr)
} }
} }
/// "timer_pause(timer, paused)" function
static void f_timer_pause(typval_T *argvars, typval_T *unused, FunPtr fptr)
{
if (argvars[0].v_type != VAR_NUMBER) {
EMSG(_(e_number_exp));
} else {
int paused = (bool)get_tv_number(&argvars[1]);
timer_T *timer = pmap_get(uint64_t)(timers, get_tv_number(&argvars[0]));
if (timer != NULL) {
timer->paused = paused;
}
}
}
/// "timer_start(timeout, callback, opts)" function /// "timer_start(timeout, callback, opts)" function
static void f_timer_start(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_timer_start(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{ {
@ -18019,6 +18037,7 @@ static void f_timer_start(typval_T *argvars, typval_T *rettv, FunPtr fptr)
timer = xmalloc(sizeof *timer); timer = xmalloc(sizeof *timer);
timer->refcount = 1; timer->refcount = 1;
timer->stopped = false; timer->stopped = false;
timer->paused = false;
timer->repeat_count = repeat; timer->repeat_count = repeat;
timer->timeout = timeout; timer->timeout = timeout;
timer->timer_id = last_timer_id++; timer->timer_id = last_timer_id++;
@ -18028,8 +18047,7 @@ static void f_timer_start(typval_T *argvars, typval_T *rettv, FunPtr fptr)
timer->tw.events = multiqueue_new_child(main_loop.events); timer->tw.events = multiqueue_new_child(main_loop.events);
// if main loop is blocked, don't queue up multiple events // if main loop is blocked, don't queue up multiple events
timer->tw.blockable = true; timer->tw.blockable = true;
time_watcher_start(&timer->tw, timer_due_cb, timeout, time_watcher_start(&timer->tw, timer_due_cb, timeout, timeout);
timeout * (repeat != 1));
pmap_put(uint64_t)(timers, timer->timer_id, timer); pmap_put(uint64_t)(timers, timer->timer_id, timer);
rettv->vval.v_number = timer->timer_id; rettv->vval.v_number = timer->timer_id;
@ -18053,13 +18071,19 @@ static void f_timer_stop(typval_T *argvars, typval_T *rettv, FunPtr fptr)
timer_stop(timer); timer_stop(timer);
} }
static void f_timer_stopall(typval_T *argvars, typval_T *unused, FunPtr fptr)
{
timer_teardown();
}
// invoked on the main loop // invoked on the main loop
static void timer_due_cb(TimeWatcher *tw, void *data) static void timer_due_cb(TimeWatcher *tw, void *data)
{ {
timer_T *timer = (timer_T *)data; timer_T *timer = (timer_T *)data;
if (timer->stopped) { if (timer->stopped || timer->paused) {
return; return;
} }
timer->refcount++; timer->refcount++;
// if repeat was negative repeat forever // if repeat was negative repeat forever
if (timer->repeat_count >= 0 && --timer->repeat_count == 0) { if (timer->repeat_count >= 0 && --timer->repeat_count == 0) {

View File

@ -307,8 +307,10 @@ return {
termopen={args={1, 2}}, termopen={args={1, 2}},
test_garbagecollect_now={}, test_garbagecollect_now={},
timer_info={args={0,1}}, timer_info={args={0,1}},
timer_pause={args=2},
timer_start={args={2,3}}, timer_start={args={2,3}},
timer_stop={args=1}, timer_stop={args=1},
timer_stopall={args=0},
tolower={args=1}, tolower={args=1},
toupper={args=1}, toupper={args=1},
tr={args=3}, tr={args=3},

View File

@ -0,0 +1,17 @@
" Functions shared by several tests.
" 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 slept
endif
catch
endtry
let slept += 10
sleep 10m
endfor
endfunc

View File

@ -4,8 +4,10 @@ if !has('timers')
finish finish
endif endif
source shared.vim
func MyHandler(timer) func MyHandler(timer)
let s:val += 1 let g:val += 1
endfunc endfunc
func MyHandlerWithLists(lists, timer) func MyHandlerWithLists(lists, timer)
@ -13,44 +15,98 @@ func MyHandlerWithLists(lists, timer)
endfunc endfunc
func Test_oneshot() func Test_oneshot()
let s:val = 0 let g:val = 0
let timer = timer_start(50, 'MyHandler') let timer = timer_start(50, 'MyHandler')
sleep 200m let slept = WaitFor('g:val == 1')
call assert_equal(1, s:val) call assert_equal(1, g:val)
call assert_inrange(30, 100, slept)
endfunc endfunc
func Test_repeat_three() func Test_repeat_three()
let s:val = 0 let g:val = 0
let timer = timer_start(50, 'MyHandler', {'repeat': 3}) let timer = timer_start(50, 'MyHandler', {'repeat': 3})
sleep 500m let slept = WaitFor('g:val == 3')
call assert_equal(3, s:val) call assert_equal(3, g:val)
call assert_inrange(100, 250, slept)
endfunc endfunc
func Test_repeat_many() func Test_repeat_many()
let s:val = 0 let g:val = 0
let timer = timer_start(50, 'MyHandler', {'repeat': -1}) let timer = timer_start(50, 'MyHandler', {'repeat': -1})
sleep 200m sleep 200m
call timer_stop(timer) call timer_stop(timer)
call assert_true(s:val > 1) call assert_inrange(2, 4, g:val)
call assert_true(s:val < 5)
endfunc endfunc
func Test_with_partial_callback() func Test_with_partial_callback()
let s:val = 0 let g:val = 0
let s:meow = {} let s:meow = {}
function s:meow.bite(...) function s:meow.bite(...)
let s:val += 1 let g:val += 1
endfunction endfunction
call timer_start(50, s:meow.bite) call timer_start(50, s:meow.bite)
sleep 200m let slept = WaitFor('g:val == 1')
call assert_equal(1, s:val) call assert_equal(1, g:val)
call assert_inrange(30, 100, slept)
endfunc endfunc
func Test_retain_partial() func Test_retain_partial()
call timer_start(100, function('MyHandlerWithLists', [['a']])) call timer_start(50, function('MyHandlerWithLists', [['a']]))
call garbagecollect() call garbagecollect()
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_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 200m sleep 200m
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 endfunc
" vim: shiftwidth=2 sts=2 expandtab " vim: shiftwidth=2 sts=2 expandtab

View File

@ -261,7 +261,7 @@ static int included_patches[] = {
2183, 2183,
// 2182 NA // 2182 NA
// 2181, // 2181,
// 2180, 2180,
// 2179, // 2179,
// 2178, // 2178,
// 2177, // 2177,