mirror of
https://github.com/neovim/neovim.git
synced 2024-12-20 11:15:14 -07:00
Merge pull request #4624 from bfredl/timers
implement timers and process events during sleep
This commit is contained in:
commit
c74ce334f2
@ -2114,9 +2114,12 @@ tabpagewinnr({tabarg}[, {arg}])
|
|||||||
Number number of current window in tab page
|
Number number of current window in tab page
|
||||||
taglist({expr}) List list of tags matching {expr}
|
taglist({expr}) List list of tags matching {expr}
|
||||||
tagfiles() List tags files used
|
tagfiles() List tags files used
|
||||||
tempname() String name for a temporary file
|
|
||||||
tan({expr}) Float tangent of {expr}
|
tan({expr}) Float tangent of {expr}
|
||||||
tanh({expr}) Float hyperbolic tangent of {expr}
|
tanh({expr}) Float hyperbolic tangent of {expr}
|
||||||
|
tempname() String name for a temporary file
|
||||||
|
timer_start({time}, {callback} [, {options}])
|
||||||
|
Number create a timer
|
||||||
|
timer_stop({timer}) none stop a timer
|
||||||
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}
|
||||||
@ -6840,6 +6843,37 @@ tanh({expr}) *tanh()*
|
|||||||
< -0.761594
|
< -0.761594
|
||||||
|
|
||||||
|
|
||||||
|
*timer_start()*
|
||||||
|
timer_start({time}, {callback} [, {options}])
|
||||||
|
Create a timer and return the timer ID.
|
||||||
|
|
||||||
|
{time} is the waiting time in milliseconds. This is the
|
||||||
|
minimum time before invoking the callback. When the system is
|
||||||
|
busy or Vim is not waiting for input the time will be longer.
|
||||||
|
|
||||||
|
{callback} is the function to call. It can be the name of a
|
||||||
|
function or a Funcref. It is called with one argument, which
|
||||||
|
is the timer ID. The callback is only invoked when Vim is
|
||||||
|
waiting for input.
|
||||||
|
|
||||||
|
{options} is a dictionary. Supported entries:
|
||||||
|
"repeat" Number of times to repeat calling the
|
||||||
|
callback. -1 means forever.
|
||||||
|
|
||||||
|
Example: >
|
||||||
|
func MyHandler(timer)
|
||||||
|
echo 'Handler called'
|
||||||
|
endfunc
|
||||||
|
let timer = timer_start(500, 'MyHandler',
|
||||||
|
\ {'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()*
|
||||||
|
Stop a timer. {timer} is an ID returned by timer_start().
|
||||||
|
The timer callback will no longer be invoked.
|
||||||
|
|
||||||
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
|
||||||
@ -7323,6 +7357,7 @@ termresponse Compiled with support for |t_RV| and |v:termresponse|.
|
|||||||
textobjects Compiled with support for |text-objects|.
|
textobjects Compiled with support for |text-objects|.
|
||||||
tgetent Compiled with tgetent support, able to use a termcap
|
tgetent Compiled with tgetent support, able to use a termcap
|
||||||
or terminfo file.
|
or terminfo file.
|
||||||
|
timers Compiled with |timer_start()| support.
|
||||||
title Compiled with window title support |'title'|.
|
title Compiled with window title support |'title'|.
|
||||||
toolbar Compiled with support for |gui-toolbar|.
|
toolbar Compiled with support for |gui-toolbar|.
|
||||||
unix Unix version of Vim.
|
unix Unix version of Vim.
|
||||||
|
126
src/nvim/eval.c
126
src/nvim/eval.c
@ -79,6 +79,7 @@
|
|||||||
#include "nvim/event/pty_process.h"
|
#include "nvim/event/pty_process.h"
|
||||||
#include "nvim/event/rstream.h"
|
#include "nvim/event/rstream.h"
|
||||||
#include "nvim/event/wstream.h"
|
#include "nvim/event/wstream.h"
|
||||||
|
#include "nvim/event/time.h"
|
||||||
#include "nvim/os/time.h"
|
#include "nvim/os/time.h"
|
||||||
#include "nvim/msgpack_rpc/channel.h"
|
#include "nvim/msgpack_rpc/channel.h"
|
||||||
#include "nvim/msgpack_rpc/server.h"
|
#include "nvim/msgpack_rpc/server.h"
|
||||||
@ -428,6 +429,14 @@ typedef struct {
|
|||||||
int status;
|
int status;
|
||||||
} JobEvent;
|
} JobEvent;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
TimeWatcher tw;
|
||||||
|
int timer_id;
|
||||||
|
int repeat_count;
|
||||||
|
bool stopped;
|
||||||
|
ufunc_T *callback;
|
||||||
|
} timer_T;
|
||||||
|
|
||||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||||
# include "eval.c.generated.h"
|
# include "eval.c.generated.h"
|
||||||
#endif
|
#endif
|
||||||
@ -438,6 +447,9 @@ typedef struct {
|
|||||||
static uint64_t current_job_id = 1;
|
static uint64_t current_job_id = 1;
|
||||||
static PMap(uint64_t) *jobs = NULL;
|
static PMap(uint64_t) *jobs = NULL;
|
||||||
|
|
||||||
|
static uint64_t last_timer_id = 0;
|
||||||
|
static PMap(uint64_t) *timers = NULL;
|
||||||
|
|
||||||
static const char *const msgpack_type_names[] = {
|
static const char *const msgpack_type_names[] = {
|
||||||
[kMPNil] = "nil",
|
[kMPNil] = "nil",
|
||||||
[kMPBoolean] = "boolean",
|
[kMPBoolean] = "boolean",
|
||||||
@ -469,6 +481,7 @@ void eval_init(void)
|
|||||||
vimvars[VV_VERSION].vv_nr = VIM_VERSION_100;
|
vimvars[VV_VERSION].vv_nr = VIM_VERSION_100;
|
||||||
|
|
||||||
jobs = pmap_new(uint64_t)();
|
jobs = pmap_new(uint64_t)();
|
||||||
|
timers = pmap_new(uint64_t)();
|
||||||
struct vimvar *p;
|
struct vimvar *p;
|
||||||
|
|
||||||
init_var_dict(&globvardict, &globvars_var, VAR_DEF_SCOPE);
|
init_var_dict(&globvardict, &globvars_var, VAR_DEF_SCOPE);
|
||||||
@ -6930,6 +6943,8 @@ static struct fst {
|
|||||||
{ "tempname", 0, 0, f_tempname },
|
{ "tempname", 0, 0, f_tempname },
|
||||||
{ "termopen", 1, 2, f_termopen },
|
{ "termopen", 1, 2, f_termopen },
|
||||||
{ "test", 1, 1, f_test },
|
{ "test", 1, 1, f_test },
|
||||||
|
{ "timer_start", 2, 3, f_timer_start },
|
||||||
|
{ "timer_stop", 1, 1, f_timer_stop },
|
||||||
{ "tolower", 1, 1, f_tolower },
|
{ "tolower", 1, 1, f_tolower },
|
||||||
{ "toupper", 1, 1, f_toupper },
|
{ "toupper", 1, 1, f_toupper },
|
||||||
{ "tr", 3, 3, f_tr },
|
{ "tr", 3, 3, f_tr },
|
||||||
@ -10688,6 +10703,7 @@ static void f_has(typval_T *argvars, typval_T *rettv)
|
|||||||
"termguicolors",
|
"termguicolors",
|
||||||
"termresponse",
|
"termresponse",
|
||||||
"textobjects",
|
"textobjects",
|
||||||
|
"timers",
|
||||||
"title",
|
"title",
|
||||||
"user-commands", /* was accidentally included in 5.4 */
|
"user-commands", /* was accidentally included in 5.4 */
|
||||||
"user_commands",
|
"user_commands",
|
||||||
@ -16441,6 +16457,116 @@ static void f_tanh(typval_T *argvars, typval_T *rettv)
|
|||||||
float_op_wrapper(argvars, rettv, &tanh);
|
float_op_wrapper(argvars, rettv, &tanh);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// "timer_start(timeout, callback, opts)" function
|
||||||
|
static void f_timer_start(typval_T *argvars, typval_T *rettv)
|
||||||
|
{
|
||||||
|
long timeout = get_tv_number(&argvars[0]);
|
||||||
|
timer_T *timer;
|
||||||
|
int repeat = 1;
|
||||||
|
dict_T *dict;
|
||||||
|
|
||||||
|
rettv->vval.v_number = -1;
|
||||||
|
|
||||||
|
if (argvars[2].v_type != VAR_UNKNOWN) {
|
||||||
|
if (argvars[2].v_type != VAR_DICT
|
||||||
|
|| (dict = argvars[2].vval.v_dict) == NULL) {
|
||||||
|
EMSG2(_(e_invarg2), get_tv_string(&argvars[2]));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (dict_find(dict, (char_u *)"repeat", -1) != NULL) {
|
||||||
|
repeat = get_dict_number(dict, (char_u *)"repeat");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (argvars[1].v_type != VAR_FUNC && argvars[1].v_type != VAR_STRING) {
|
||||||
|
EMSG2(e_invarg2, "funcref");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ufunc_T *func = find_ufunc(argvars[1].vval.v_string);
|
||||||
|
if (!func) {
|
||||||
|
// Invalid function name. Error already reported by `find_ufunc`.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
func->uf_refcount++;
|
||||||
|
|
||||||
|
timer = xmalloc(sizeof *timer);
|
||||||
|
timer->stopped = false;
|
||||||
|
timer->repeat_count = repeat;
|
||||||
|
timer->timer_id = last_timer_id++;
|
||||||
|
timer->callback = func;
|
||||||
|
|
||||||
|
time_watcher_init(&loop, &timer->tw, timer);
|
||||||
|
timer->tw.events = queue_new_child(loop.events);
|
||||||
|
// if main loop is blocked, don't queue up multiple events
|
||||||
|
timer->tw.blockable = true;
|
||||||
|
time_watcher_start(&timer->tw, timer_due_cb, timeout,
|
||||||
|
timeout * (repeat != 1));
|
||||||
|
|
||||||
|
pmap_put(uint64_t)(timers, timer->timer_id, timer);
|
||||||
|
rettv->vval.v_number = timer->timer_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// "timer_stop(timerid)" function
|
||||||
|
static void f_timer_stop(typval_T *argvars, typval_T *rettv)
|
||||||
|
{
|
||||||
|
if (argvars[0].v_type != VAR_NUMBER) {
|
||||||
|
EMSG(_(e_number_exp));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
timer_T *timer = pmap_get(uint64_t)(timers, get_tv_number(&argvars[0]));
|
||||||
|
|
||||||
|
if (timer == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
timer_stop(timer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// invoked on the main loop
|
||||||
|
static void timer_due_cb(TimeWatcher *tw, void *data)
|
||||||
|
{
|
||||||
|
timer_T *timer = (timer_T *)data;
|
||||||
|
// if repeat was negative repeat forever
|
||||||
|
if (timer->repeat_count >= 0 && --timer->repeat_count == 0) {
|
||||||
|
timer_stop(timer);
|
||||||
|
}
|
||||||
|
|
||||||
|
typval_T argv[1];
|
||||||
|
init_tv(argv);
|
||||||
|
argv[0].v_type = VAR_NUMBER;
|
||||||
|
argv[0].vval.v_number = timer->timer_id;
|
||||||
|
typval_T rettv;
|
||||||
|
|
||||||
|
init_tv(&rettv);
|
||||||
|
call_user_func(timer->callback, ARRAY_SIZE(argv), argv, &rettv,
|
||||||
|
curwin->w_cursor.lnum, curwin->w_cursor.lnum, NULL);
|
||||||
|
clear_tv(&rettv);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void timer_stop(timer_T *timer)
|
||||||
|
{
|
||||||
|
if (timer->stopped) {
|
||||||
|
// avoid double free
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
timer->stopped = true;
|
||||||
|
time_watcher_stop(&timer->tw);
|
||||||
|
time_watcher_close(&timer->tw, timer_free_cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
// invoked on next event loop tick, so queue is empty
|
||||||
|
static void timer_free_cb(TimeWatcher *tw, void *data)
|
||||||
|
{
|
||||||
|
timer_T *timer = (timer_T *)data;
|
||||||
|
queue_free(timer->tw.events);
|
||||||
|
user_func_unref(timer->callback);
|
||||||
|
pmap_del(uint64_t)(timers, timer->timer_id);
|
||||||
|
xfree(timer);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* "tolower(string)" function
|
* "tolower(string)" function
|
||||||
*/
|
*/
|
||||||
|
@ -17,6 +17,7 @@ void time_watcher_init(Loop *loop, TimeWatcher *watcher, void *data)
|
|||||||
watcher->uv.data = watcher;
|
watcher->uv.data = watcher;
|
||||||
watcher->data = data;
|
watcher->data = data;
|
||||||
watcher->events = loop->fast_events;
|
watcher->events = loop->fast_events;
|
||||||
|
watcher->blockable = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void time_watcher_start(TimeWatcher *watcher, time_cb cb, uint64_t timeout,
|
void time_watcher_start(TimeWatcher *watcher, time_cb cb, uint64_t timeout,
|
||||||
@ -50,6 +51,10 @@ static void time_watcher_cb(uv_timer_t *handle)
|
|||||||
FUNC_ATTR_NONNULL_ALL
|
FUNC_ATTR_NONNULL_ALL
|
||||||
{
|
{
|
||||||
TimeWatcher *watcher = handle->data;
|
TimeWatcher *watcher = handle->data;
|
||||||
|
if (watcher->blockable && !queue_empty(watcher->events)) {
|
||||||
|
// the timer blocked and there already is an unprocessed event waiting
|
||||||
|
return;
|
||||||
|
}
|
||||||
CREATE_EVENT(watcher->events, time_event, 1, watcher);
|
CREATE_EVENT(watcher->events, time_event, 1, watcher);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,6 +13,7 @@ struct time_watcher {
|
|||||||
void *data;
|
void *data;
|
||||||
time_cb cb, close_cb;
|
time_cb cb, close_cb;
|
||||||
Queue *events;
|
Queue *events;
|
||||||
|
bool blockable;
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||||
|
@ -6989,10 +6989,10 @@ static void ex_sleep(exarg_T *eap)
|
|||||||
*/
|
*/
|
||||||
void do_sleep(long msec)
|
void do_sleep(long msec)
|
||||||
{
|
{
|
||||||
long done;
|
|
||||||
ui_flush(); // flush before waiting
|
ui_flush(); // flush before waiting
|
||||||
for (done = 0; !got_int && done < msec; done += 1000L) {
|
for (long left = msec; !got_int && left > 0; left -= 1000L) {
|
||||||
os_delay(msec - done > 1000L ? 1000L : msec - done, true);
|
int next = left > 1000l ? 1000 : (int)left;
|
||||||
|
LOOP_PROCESS_EVENTS_UNTIL(&loop, loop.events, (int)next, got_int);
|
||||||
os_breakcheck();
|
os_breakcheck();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -359,6 +359,7 @@ static int command_line_execute(VimState *state, int key)
|
|||||||
|
|
||||||
if (s->c == K_EVENT) {
|
if (s->c == K_EVENT) {
|
||||||
queue_process_events(loop.events);
|
queue_process_events(loop.events);
|
||||||
|
redrawcmdline();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -785,11 +785,13 @@ void wait_return(int redraw)
|
|||||||
|
|
||||||
State = HITRETURN;
|
State = HITRETURN;
|
||||||
setmouse();
|
setmouse();
|
||||||
/* Avoid the sequence that the user types ":" at the hit-return prompt
|
cmdline_row = msg_row;
|
||||||
* to start an Ex command, but the file-changed dialog gets in the
|
// Avoid the sequence that the user types ":" at the hit-return prompt
|
||||||
* way. */
|
// to start an Ex command, but the file-changed dialog gets in the
|
||||||
if (need_check_timestamps)
|
// way.
|
||||||
check_timestamps(FALSE);
|
if (need_check_timestamps) {
|
||||||
|
check_timestamps(false);
|
||||||
|
}
|
||||||
|
|
||||||
hit_return_msg();
|
hit_return_msg();
|
||||||
|
|
||||||
@ -1970,6 +1972,7 @@ static void msg_puts_printf(char *str, int maxlen)
|
|||||||
*/
|
*/
|
||||||
static int do_more_prompt(int typed_char)
|
static int do_more_prompt(int typed_char)
|
||||||
{
|
{
|
||||||
|
static bool entered = false;
|
||||||
int used_typed_char = typed_char;
|
int used_typed_char = typed_char;
|
||||||
int oldState = State;
|
int oldState = State;
|
||||||
int c;
|
int c;
|
||||||
@ -1979,6 +1982,13 @@ static int do_more_prompt(int typed_char)
|
|||||||
msgchunk_T *mp;
|
msgchunk_T *mp;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
// We get called recursively when a timer callback outputs a message. In
|
||||||
|
// that case don't show another prompt. Also when at the hit-Enter prompt.
|
||||||
|
if (entered || State == HITRETURN) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
entered = true;
|
||||||
|
|
||||||
if (typed_char == 'G') {
|
if (typed_char == 'G') {
|
||||||
/* "g<": Find first line on the last page. */
|
/* "g<": Find first line on the last page. */
|
||||||
mp_last = msg_sb_start(last_msgchunk);
|
mp_last = msg_sb_start(last_msgchunk);
|
||||||
@ -2153,9 +2163,11 @@ static int do_more_prompt(int typed_char)
|
|||||||
if (quit_more) {
|
if (quit_more) {
|
||||||
msg_row = Rows - 1;
|
msg_row = Rows - 1;
|
||||||
msg_col = 0;
|
msg_col = 0;
|
||||||
} else if (cmdmsg_rl)
|
} else if (cmdmsg_rl) {
|
||||||
msg_col = Columns - 1;
|
msg_col = Columns - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
entered = false;
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,6 +40,7 @@ NEW_TESTS = \
|
|||||||
test_cursor_func.res \
|
test_cursor_func.res \
|
||||||
test_help_tagjump.res \
|
test_help_tagjump.res \
|
||||||
test_menu.res \
|
test_menu.res \
|
||||||
|
test_timers.res \
|
||||||
test_viml.res \
|
test_viml.res \
|
||||||
test_alot.res
|
test_alot.res
|
||||||
|
|
||||||
|
32
src/nvim/testdir/test_timers.vim
Normal file
32
src/nvim/testdir/test_timers.vim
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
" Test for timers
|
||||||
|
|
||||||
|
if !has('timers')
|
||||||
|
finish
|
||||||
|
endif
|
||||||
|
|
||||||
|
func MyHandler(timer)
|
||||||
|
let s:val += 1
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func Test_oneshot()
|
||||||
|
let s:val = 0
|
||||||
|
let timer = timer_start(50, 'MyHandler')
|
||||||
|
sleep 200m
|
||||||
|
call assert_equal(1, s:val)
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func Test_repeat_three()
|
||||||
|
let s:val = 0
|
||||||
|
let timer = timer_start(50, 'MyHandler', {'repeat': 3})
|
||||||
|
sleep 500m
|
||||||
|
call assert_equal(3, s:val)
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func Test_repeat_many()
|
||||||
|
let s: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)
|
||||||
|
endfunc
|
@ -70,6 +70,7 @@ static char *features[] = {
|
|||||||
// clang-format off
|
// clang-format off
|
||||||
static int included_patches[] = {
|
static int included_patches[] = {
|
||||||
1832,
|
1832,
|
||||||
|
1831,
|
||||||
1809,
|
1809,
|
||||||
1808,
|
1808,
|
||||||
1806,
|
1806,
|
||||||
@ -82,6 +83,7 @@ static int included_patches[] = {
|
|||||||
1652,
|
1652,
|
||||||
1643,
|
1643,
|
||||||
1641,
|
1641,
|
||||||
|
// 1624 NA
|
||||||
|
|
||||||
// 1600 NA
|
// 1600 NA
|
||||||
// 1599 NA
|
// 1599 NA
|
||||||
@ -106,7 +108,7 @@ static int included_patches[] = {
|
|||||||
// 1581,
|
// 1581,
|
||||||
// 1580,
|
// 1580,
|
||||||
// 1579,
|
// 1579,
|
||||||
// 1578,
|
1578,
|
||||||
// 1577,
|
// 1577,
|
||||||
1576,
|
1576,
|
||||||
// 1575 NA
|
// 1575 NA
|
||||||
|
129
test/functional/eval/timer_spec.lua
Normal file
129
test/functional/eval/timer_spec.lua
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
local helpers = require('test.functional.helpers')
|
||||||
|
local Screen = require('test.functional.ui.screen')
|
||||||
|
local ok, feed, eq, eval = helpers.ok, helpers.feed, helpers.eq, helpers.eval
|
||||||
|
local source, nvim_async, run = helpers.source, helpers.nvim_async, helpers.run
|
||||||
|
local clear, execute, funcs = helpers.clear, helpers.execute, helpers.funcs
|
||||||
|
|
||||||
|
describe('timers', function()
|
||||||
|
before_each(function()
|
||||||
|
clear()
|
||||||
|
source([[
|
||||||
|
let g:val = 0
|
||||||
|
func MyHandler(timer)
|
||||||
|
let g:val += 1
|
||||||
|
endfunc
|
||||||
|
]])
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('works one-shot', function()
|
||||||
|
execute("call timer_start(50, 'MyHandler')")
|
||||||
|
eq(0,eval("g:val"))
|
||||||
|
run(nil, nil, nil, 200)
|
||||||
|
eq(1,eval("g:val"))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('works with repeat two', function()
|
||||||
|
execute("call timer_start(50, 'MyHandler', {'repeat': 2})")
|
||||||
|
eq(0,eval("g:val"))
|
||||||
|
run(nil, nil, nil, 300)
|
||||||
|
eq(2,eval("g:val"))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('are triggered during sleep', function()
|
||||||
|
execute("call timer_start(50, 'MyHandler', {'repeat': 2})")
|
||||||
|
nvim_async("command", "sleep 10")
|
||||||
|
eq(0,eval("g:val"))
|
||||||
|
run(nil, nil, nil, 300)
|
||||||
|
eq(2,eval("g:val"))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('can be started during sleep', function()
|
||||||
|
nvim_async("command", "sleep 10")
|
||||||
|
-- this also tests that remote requests works during sleep
|
||||||
|
eval("timer_start(50, 'MyHandler', {'repeat': 2})")
|
||||||
|
eq(0,eval("g:val"))
|
||||||
|
run(nil, nil, nil, 300)
|
||||||
|
eq(2,eval("g:val"))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('are paused when event processing is disabled', function()
|
||||||
|
-- this is not the intended behavior, but at least there will
|
||||||
|
-- not be a burst of queued up callbacks
|
||||||
|
execute("call timer_start(50, 'MyHandler', {'repeat': 2})")
|
||||||
|
run(nil, nil, nil, 100)
|
||||||
|
local count = eval("g:val")
|
||||||
|
nvim_async("command", "let g:c = getchar()")
|
||||||
|
run(nil, nil, nil, 300)
|
||||||
|
feed("c")
|
||||||
|
local diff = eval("g:val") - count
|
||||||
|
ok(0 <= diff and diff <= 2)
|
||||||
|
eq(99, eval("g:c"))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('can be stopped', function()
|
||||||
|
local t = eval("timer_start(50, 'MyHandler', {'repeat': -1})")
|
||||||
|
eq(0,eval("g:val"))
|
||||||
|
run(nil, nil, nil, 300)
|
||||||
|
funcs.timer_stop(t)
|
||||||
|
local count = eval("g:val")
|
||||||
|
run(nil, nil, nil, 300)
|
||||||
|
local count2 = eval("g:val")
|
||||||
|
ok(4 <= count and count <= 7)
|
||||||
|
-- when count is eval:ed after timer_stop this should be non-racy
|
||||||
|
eq(count, count2)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('can be stopped from the handler', function()
|
||||||
|
source([[
|
||||||
|
func! MyHandler(timer)
|
||||||
|
let g:val += 1
|
||||||
|
if g:val == 3
|
||||||
|
call timer_stop(a:timer)
|
||||||
|
" check double stop is ignored
|
||||||
|
call timer_stop(a:timer)
|
||||||
|
endif
|
||||||
|
endfunc
|
||||||
|
]])
|
||||||
|
execute("call timer_start(50, 'MyHandler', {'repeat': -1})")
|
||||||
|
eq(0,eval("g:val"))
|
||||||
|
run(nil, nil, nil, 300)
|
||||||
|
eq(3,eval("g:val"))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('can have two timers', function()
|
||||||
|
source([[
|
||||||
|
let g:val2 = 0
|
||||||
|
func! MyHandler2(timer)
|
||||||
|
let g:val2 += 1
|
||||||
|
endfunc
|
||||||
|
]])
|
||||||
|
execute("call timer_start(50, 'MyHandler', {'repeat': 3})")
|
||||||
|
execute("call timer_start(100, 'MyHandler2', {'repeat': 2})")
|
||||||
|
run(nil, nil, nil, 300)
|
||||||
|
eq(3,eval("g:val"))
|
||||||
|
eq(2,eval("g:val2"))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("doesn't mess up the cmdline", function()
|
||||||
|
local screen = Screen.new(40, 6)
|
||||||
|
screen:attach()
|
||||||
|
screen:set_default_attr_ignore({{bold=true, foreground=Screen.colors.Blue}})
|
||||||
|
source([[
|
||||||
|
func! MyHandler(timer)
|
||||||
|
echo "evil"
|
||||||
|
endfunc
|
||||||
|
]])
|
||||||
|
execute("call timer_start(100, 'MyHandler', {'repeat': 1})")
|
||||||
|
feed(":good")
|
||||||
|
screen:sleep(200)
|
||||||
|
screen:expect([[
|
||||||
|
|
|
||||||
|
~ |
|
||||||
|
~ |
|
||||||
|
~ |
|
||||||
|
~ |
|
||||||
|
:good^ |
|
||||||
|
]])
|
||||||
|
end)
|
||||||
|
|
||||||
|
end)
|
@ -1,10 +1,9 @@
|
|||||||
-- Tests for undo tree and :earlier and :later.
|
-- Tests for undo tree and :earlier and :later.
|
||||||
|
|
||||||
local helpers = require('test.functional.helpers')
|
local helpers = require('test.functional.helpers')
|
||||||
local feed, source, eq, eval, clear, execute, expect, wait, write_file =
|
local expect, feed, source = helpers.expect, helpers.feed, helpers.source
|
||||||
helpers.feed, helpers.source, helpers.eq, helpers.eval,
|
local eval, clear, execute = helpers.eval, helpers.clear, helpers.execute
|
||||||
helpers.clear, helpers.execute, helpers.expect, helpers.wait,
|
local write_file, command, eq = helpers.write_file, helpers.command, helpers.eq
|
||||||
helpers.write_file
|
|
||||||
|
|
||||||
local function expect_empty_buffer()
|
local function expect_empty_buffer()
|
||||||
-- The space will be removed by helpers.dedent but is needed because dedent
|
-- The space will be removed by helpers.dedent but is needed because dedent
|
||||||
@ -57,8 +56,7 @@ describe('undo tree:', function()
|
|||||||
-- Delete three other characters and go back in time step by step.
|
-- Delete three other characters and go back in time step by step.
|
||||||
feed('$xxx')
|
feed('$xxx')
|
||||||
expect_line('123456')
|
expect_line('123456')
|
||||||
execute('sleep 1')
|
command('sleep 1')
|
||||||
wait()
|
|
||||||
feed('g-')
|
feed('g-')
|
||||||
expect_line('1234567')
|
expect_line('1234567')
|
||||||
feed('g-')
|
feed('g-')
|
||||||
@ -79,8 +77,7 @@ describe('undo tree:', function()
|
|||||||
expect_line('123456')
|
expect_line('123456')
|
||||||
|
|
||||||
-- Delay for two seconds and go some seconds forward and backward.
|
-- Delay for two seconds and go some seconds forward and backward.
|
||||||
execute('sleep 2')
|
command('sleep 2')
|
||||||
wait()
|
|
||||||
feed('Aa<esc>')
|
feed('Aa<esc>')
|
||||||
feed('Ab<esc>')
|
feed('Ab<esc>')
|
||||||
feed('Ac<esc>')
|
feed('Ac<esc>')
|
||||||
|
@ -290,6 +290,10 @@ If everything else fails, use Screen:redraw_debug to help investigate what is
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function Screen:sleep(ms)
|
||||||
|
pcall(function() self:wait(function() return "error" end, ms) end)
|
||||||
|
end
|
||||||
|
|
||||||
function Screen:_redraw(updates)
|
function Screen:_redraw(updates)
|
||||||
for _, update in ipairs(updates) do
|
for _, update in ipairs(updates) do
|
||||||
-- print('--')
|
-- print('--')
|
||||||
@ -501,7 +505,7 @@ end
|
|||||||
|
|
||||||
function Screen:snapshot_util(attrs, ignore)
|
function Screen:snapshot_util(attrs, ignore)
|
||||||
-- util to generate screen test
|
-- util to generate screen test
|
||||||
pcall(function() self:wait(function() return "error" end, 250) end)
|
self:sleep(250)
|
||||||
self:print_snapshot(attrs, ignore)
|
self:print_snapshot(attrs, ignore)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
9
third-party/cmake/BuildLuarocks.cmake
vendored
9
third-party/cmake/BuildLuarocks.cmake
vendored
@ -114,6 +114,15 @@ add_custom_target(lpeg
|
|||||||
|
|
||||||
list(APPEND THIRD_PARTY_DEPS lpeg)
|
list(APPEND THIRD_PARTY_DEPS lpeg)
|
||||||
|
|
||||||
|
add_custom_command(OUTPUT ${HOSTDEPS_LIB_DIR}/luarocks/rocks/inspect
|
||||||
|
COMMAND ${LUAROCKS_BINARY}
|
||||||
|
ARGS build inspect ${LUAROCKS_BUILDARGS}
|
||||||
|
DEPENDS mpack)
|
||||||
|
add_custom_target(inspect
|
||||||
|
DEPENDS ${HOSTDEPS_LIB_DIR}/luarocks/rocks/inspect)
|
||||||
|
|
||||||
|
list(APPEND THIRD_PARTY_DEPS inspect)
|
||||||
|
|
||||||
if(USE_BUNDLED_BUSTED)
|
if(USE_BUNDLED_BUSTED)
|
||||||
add_custom_command(OUTPUT ${HOSTDEPS_BIN_DIR}/busted
|
add_custom_command(OUTPUT ${HOSTDEPS_BIN_DIR}/busted
|
||||||
COMMAND ${LUAROCKS_BINARY}
|
COMMAND ${LUAROCKS_BINARY}
|
||||||
|
Loading…
Reference in New Issue
Block a user