mirror of
				https://github.com/neovim/neovim.git
				synced 2025-11-04 01:34:25 +00:00 
			
		
		
		
	eval: implement timers. vim-patch: 7.4.1578, 7.4.1831
For the moment, timers are triggered during sleep, but not in wait-for-input modes, like press-RETURN or f_getchar()
This commit is contained in:
		@@ -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();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										106
									
								
								test/functional/eval/timer_spec.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										106
									
								
								test/functional/eval/timer_spec.lua
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,106 @@
 | 
				
			|||||||
 | 
					local helpers = require('test.functional.helpers')
 | 
				
			||||||
 | 
					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)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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>')
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user