diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/nvim/eval.c | 481 | ||||
-rw-r--r-- | src/nvim/testdir/test_timers.vim | 1 |
2 files changed, 295 insertions, 187 deletions
diff --git a/src/nvim/eval.c b/src/nvim/eval.c index e764117f1a..043e799b4e 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -413,6 +413,21 @@ static struct vimvar { static dictitem_T vimvars_var; // variable used for v: #define vimvarht vimvardict.dv_hashtab +typedef enum { + kCallbackNone, + kCallbackFuncref, + kCallbackPartial, +} CallbackType; + +typedef struct { + union { + char_u *funcref; + partial_T *partial; + } data; + CallbackType type; +} Callback; +#define CALLBACK_NONE ((Callback){ .type = kCallbackNone }) + typedef struct { union { LibuvProcess uv; @@ -424,15 +439,14 @@ typedef struct { bool exited; bool rpc; int refcount; - ufunc_T *on_stdout, *on_stderr, *on_exit; - dict_T *self; + Callback on_stdout, on_stderr, on_exit; int *status_ptr; uint64_t id; MultiQueue *events; } TerminalJobData; typedef struct dict_watcher { - ufunc_T *callback; + Callback callback; char *key_pattern; QUEUE node; bool busy; // prevent recursion if the dict is changed in the callback @@ -440,7 +454,7 @@ typedef struct dict_watcher { typedef struct { TerminalJobData *data; - ufunc_T *callback; + Callback *callback; const char *type; list_T *received; int status; @@ -453,7 +467,7 @@ typedef struct { int refcount; long timeout; bool stopped; - ufunc_T *callback; + Callback callback; } timer_T; typedef void (*FunPtr)(void); @@ -5909,7 +5923,17 @@ bool garbage_collect(void) { TerminalJobData *data; map_foreach_value(jobs, data, { - ABORTING(set_ref_dict)(data->self, copyID); + set_ref_in_callback(&data->on_stdout, copyID, NULL, NULL); + set_ref_in_callback(&data->on_stderr, copyID, NULL, NULL); + set_ref_in_callback(&data->on_exit, copyID, NULL, NULL); + }) + } + + // Timers + { + timer_T *timer; + map_foreach_value(timers, timer, { + set_ref_in_callback(&timer->callback, copyID, NULL, NULL); }) } @@ -6159,6 +6183,13 @@ bool set_ref_in_item(typval_T *tv, int copyID, ht_stack_T **ht_stack, *ht_stack = newitem; } } + + QUEUE *w = NULL; + DictWatcher *watcher = NULL; + QUEUE_FOREACH(w, &dd->watchers) { + watcher = dictwatcher_node_data(w); + set_ref_in_callback(&watcher->callback, copyID, ht_stack, list_stack); + } } if (tv->v_type == VAR_PARTIAL) { partial_T *pt = tv->vval.v_partial; @@ -6639,49 +6670,28 @@ dictitem_T *dict_find(dict_T *d, char_u *key, int len) /// @param[out] result The address where a pointer to the wanted callback /// will be left. /// @return true/false on success/failure. -static bool get_dict_callback(dict_T *d, char *key, ufunc_T **result) +static bool get_dict_callback(dict_T *d, char *key, Callback *result) { dictitem_T *di = dict_find(d, (uint8_t *)key, -1); if (di == NULL) { - *result = NULL; + result->type = kCallbackNone; return true; } - - if (di->di_tv.v_type != VAR_FUNC && di->di_tv.v_type != VAR_STRING) { - EMSG(_("Argument is not a function or function name")); - *result = NULL; - return false; - } - if ((*result = find_ufunc(di->di_tv.vval.v_string)) == NULL) { + if (di->di_tv.v_type != VAR_FUNC && di->di_tv.v_type != VAR_STRING + && di->di_tv.v_type != VAR_PARTIAL) { + EMSG(_("Argument is not a function or function name")); + result->type = kCallbackNone; return false; } - (*result)->uf_refcount++; - return true; -} - -static ufunc_T *find_ufunc(uint8_t *name) -{ - uint8_t *n = name; - ufunc_T *rv = NULL; - if (*n > '9' || *n < '0') { - if ((n = trans_function_name(&n, false, TFN_INT|TFN_QUIET, NULL, NULL))) { - rv = find_func(n); - xfree(n); - } - } else { - // dict function, name is already translated - rv = find_func(n); - } - - if (!rv) { - EMSG2(_("Function %s doesn't exist"), name); - return NULL; - } - - return rv; + typval_T tv; + copy_tv(&di->di_tv, &tv); + set_selfdict(&tv, d); + bool res = callback_from_typval(result, &tv); + clear_tv(&tv); + return res; } /// Get a string item from a dictionary. @@ -8555,16 +8565,14 @@ static void f_dictwatcheradd(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - ufunc_T *func = find_ufunc(argvars[2].vval.v_string); - if (!func) { - // Invalid function name. Error already reported by `find_ufunc`. + Callback callback; + if (!callback_from_typval(&callback, &argvars[2])) { return; } - func->uf_refcount++; DictWatcher *watcher = xmalloc(sizeof(DictWatcher)); watcher->key_pattern = xmemdupz(key_pattern, key_len); - watcher->callback = func; + watcher->callback = callback; watcher->busy = false; QUEUE_INSERT_TAIL(&argvars[0].vval.v_dict->watchers, &watcher->node); } @@ -8600,9 +8608,8 @@ static void f_dictwatcherdel(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - ufunc_T *func = find_ufunc(argvars[2].vval.v_string); - if (!func) { - // Invalid function name. Error already reported by `find_ufunc`. + Callback callback; + if (!callback_from_typval(&callback, &argvars[2])) { return; } @@ -8612,13 +8619,15 @@ static void f_dictwatcherdel(typval_T *argvars, typval_T *rettv, FunPtr fptr) bool matched = false; QUEUE_FOREACH(w, &dict->watchers) { watcher = dictwatcher_node_data(w); - if (func == watcher->callback + if (callback_equal(&watcher->callback, &callback) && !strcmp(watcher->key_pattern, key_pattern)) { matched = true; break; } } + callback_free(&callback); + if (!matched) { EMSG("Couldn't find a watcher matching key and callback"); return; @@ -12064,7 +12073,8 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr) dict_T *job_opts = NULL; bool detach = false, rpc = false, pty = false; - ufunc_T *on_stdout = NULL, *on_stderr = NULL, *on_exit = NULL; + Callback on_stdout = CALLBACK_NONE, on_stderr = CALLBACK_NONE, + on_exit = CALLBACK_NONE; char *cwd = NULL; if (argvars[1].v_type == VAR_DICT) { job_opts = argvars[1].vval.v_dict; @@ -12096,7 +12106,7 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr) } TerminalJobData *data = common_job_init(argv, on_stdout, on_stderr, on_exit, - job_opts, pty, rpc, detach, cwd); + pty, rpc, detach, cwd); Process *proc = (Process *)&data->proc; if (pty) { @@ -12114,10 +12124,10 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } - if (!rpc && !on_stdout) { + if (!rpc && on_stdout.type == kCallbackNone) { proc->out = NULL; } - if (!on_stderr) { + if (on_stderr.type == kCallbackNone) { proc->err = NULL; } common_job_start(data, rettv); @@ -14325,8 +14335,9 @@ static void f_rpcstart(typval_T *argvars, typval_T *rettv, FunPtr fptr) // The last item of argv must be NULL argv[i] = NULL; - TerminalJobData *data = common_job_init(argv, NULL, NULL, NULL, - NULL, false, true, false, NULL); + TerminalJobData *data = common_job_init(argv, CALLBACK_NONE, CALLBACK_NONE, + CALLBACK_NONE, false, true, false, + NULL); common_job_start(data, rettv); } @@ -16904,7 +16915,8 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - ufunc_T *on_stdout = NULL, *on_stderr = NULL, *on_exit = NULL; + Callback on_stdout = CALLBACK_NONE, on_stderr = CALLBACK_NONE, + on_exit = CALLBACK_NONE; dict_T *job_opts = NULL; char *cwd = "."; if (argvars[1].v_type == VAR_DICT) { @@ -16928,7 +16940,7 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr) } TerminalJobData *data = common_job_init(argv, on_stdout, on_stderr, on_exit, - job_opts, true, false, false, cwd); + true, false, false, cwd); data->proc.pty.width = curwin->w_width; data->proc.pty.height = curwin->w_height; data->proc.pty.term_name = xstrdup("xterm-256color"); @@ -16974,6 +16986,125 @@ static void f_test(typval_T *argvars, typval_T *rettv, FunPtr fptr) /* Used for unit testing. Change the code below to your liking. */ } +static bool callback_from_typval(Callback *callback, typval_T *arg) +{ + if (arg->v_type == VAR_PARTIAL && arg->vval.v_partial != NULL) { + callback->data.partial = arg->vval.v_partial; + callback->data.partial->pt_refcount++; + callback->type = kCallbackPartial; + } else if (arg->v_type == VAR_FUNC || arg->v_type == VAR_STRING) { + char_u *name = arg->vval.v_string; + func_ref(name); + callback->data.funcref = vim_strsave(name); + callback->type = kCallbackFuncref; + } else if (arg->v_type == VAR_NUMBER && arg->vval.v_number == 0) { + callback->type = kCallbackNone; + } else { + EMSG(_("E921: Invalid callback argument")); + return false; + } + return true; +} + + +/// Unref/free callback +static void callback_free(Callback *callback) +{ + switch (callback->type) { + case kCallbackFuncref: + func_unref(callback->data.funcref); + xfree(callback->data.funcref); + break; + + case kCallbackPartial: + partial_unref(callback->data.partial); + break; + + case kCallbackNone: + break; + + default: + abort(); + } + callback->type = kCallbackNone; +} + +static bool callback_equal(Callback *cb1, Callback *cb2) +{ + if (cb1->type != cb2->type) { + return false; + } + switch (cb1->type) { + case kCallbackFuncref: + return STRCMP(cb1->data.funcref, cb2->data.funcref) == 0; + + case kCallbackPartial: + // FIXME: this is inconsistent with tv_equal but is needed for precision + // maybe change dictwatcheradd to return a watcher id instead? + return cb1->data.partial == cb2->data.partial; + + case kCallbackNone: + return true; + + default: + abort(); + } +} + +static bool callback_call(Callback *callback, int argcount_in, + typval_T *argvars_in, typval_T *rettv) +{ + partial_T *partial; + char_u *name; + switch (callback->type) { + case kCallbackFuncref: + name = callback->data.funcref; + partial = NULL; + break; + + case kCallbackPartial: + partial = callback->data.partial; + name = partial->pt_name; + break; + + case kCallbackNone: + return false; + break; + + default: + abort(); + } + + int dummy; + return call_func(name, (int)STRLEN(name), rettv, argcount_in, argvars_in, + curwin->w_cursor.lnum, curwin->w_cursor.lnum, &dummy, + true, partial, NULL); +} + +static bool set_ref_in_callback(Callback *callback, int copyID, + ht_stack_T **ht_stack, + list_stack_T **list_stack) +{ + typval_T tv; + switch (callback->type) { + case kCallbackFuncref: + case kCallbackNone: + break; + + case kCallbackPartial: + tv.v_type = VAR_PARTIAL; + tv.vval.v_partial = callback->data.partial; + return set_ref_in_item(&tv, copyID, ht_stack, list_stack); + break; + + + default: + abort(); + } + return false; +} + + /// "timer_start(timeout, callback, opts)" function static void f_timer_start(typval_T *argvars, typval_T *rettv, FunPtr fptr) { @@ -16998,16 +17129,10 @@ static void f_timer_start(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } - 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`. + Callback callback; + if (!callback_from_typval(&callback, &argvars[1])) { return; } - func->uf_refcount++; timer = xmalloc(sizeof *timer); timer->refcount = 1; @@ -17015,7 +17140,7 @@ static void f_timer_start(typval_T *argvars, typval_T *rettv, FunPtr fptr) timer->repeat_count = repeat; timer->timeout = timeout; timer->timer_id = last_timer_id++; - timer->callback = func; + timer->callback = callback; time_watcher_init(&main_loop, &timer->tw, timer); timer->tw.events = multiqueue_new_child(main_loop.events); @@ -17059,15 +17184,14 @@ static void timer_due_cb(TimeWatcher *tw, void *data) timer_stop(timer); } - typval_T argv[1]; + typval_T argv[2]; 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); + callback_call(&timer->callback, 1, argv, &rettv); clear_tv(&rettv); if (!timer->stopped && timer->timeout == 0) { @@ -17096,7 +17220,7 @@ static void timer_close_cb(TimeWatcher *tw, void *data) { timer_T *timer = (timer_T *)data; multiqueue_free(timer->tw.events); - user_func_unref(timer->callback); + callback_free(&timer->callback); pmap_del(uint64_t)(timers, timer->timer_id); timer_decref(timer); } @@ -18531,70 +18655,76 @@ handle_subscript ( } // Turn "dict.Func" into a partial for "Func" bound to "dict". - // Don't do this when "Func" is already a partial that was bound - // explicitly (pt_auto is false). if (selfdict != NULL && (rettv->v_type == VAR_FUNC - || (rettv->v_type == VAR_PARTIAL - && (rettv->vval.v_partial->pt_auto - || rettv->vval.v_partial->pt_dict == NULL)))) { - char_u *fname = rettv->v_type == VAR_FUNC ? rettv->vval.v_string - : rettv->vval.v_partial->pt_name; - char_u *tofree = NULL; - ufunc_T *fp; - char_u fname_buf[FLEN_FIXED + 1]; - int error; - - // Translate "s:func" to the stored function name. - fname = fname_trans_sid(fname, fname_buf, &tofree, &error); - - fp = find_func(fname); - xfree(tofree); + || rettv->v_type == VAR_PARTIAL)) { + set_selfdict(rettv, selfdict); + } - // Turn "dict.Func" into a partial for "Func" with "dict". - if (fp != NULL && (fp->uf_flags & FC_DICT)) { - partial_T *pt = (partial_T *)xcalloc(1, sizeof(partial_T)); + dict_unref(selfdict); + return ret; +} - if (pt != NULL) { - pt->pt_refcount = 1; - pt->pt_dict = selfdict; - pt->pt_auto = true; - selfdict = NULL; - if (rettv->v_type == VAR_FUNC) { - // Just a function: Take over the function name and use selfdict. - pt->pt_name = rettv->vval.v_string; - } else { - partial_T *ret_pt = rettv->vval.v_partial; - int i; +static void set_selfdict(typval_T *rettv, dict_T *selfdict) { + // Don't do this when "dict.Func" is already a partial that was bound + // explicitly (pt_auto is false). + if (rettv->v_type == VAR_PARTIAL && !rettv->vval.v_partial->pt_auto + && rettv->vval.v_partial->pt_dict != NULL) { + return; + } + char_u *fname = rettv->v_type == VAR_FUNC ? rettv->vval.v_string + : rettv->vval.v_partial->pt_name; + char_u *tofree = NULL; + ufunc_T *fp; + char_u fname_buf[FLEN_FIXED + 1]; + int error; - // Partial: copy the function name, use selfdict and copy - // args. Can't take over name or args, the partial might - // be referenced elsewhere. - pt->pt_name = vim_strsave(ret_pt->pt_name); - func_ref(pt->pt_name); - if (ret_pt->pt_argc > 0) { - size_t arg_size = sizeof(typval_T) * ret_pt->pt_argc; - pt->pt_argv = (typval_T *)xmalloc(arg_size); - if (pt->pt_argv == NULL) { - // out of memory: drop the arguments - pt->pt_argc = 0; - } else { - pt->pt_argc = ret_pt->pt_argc; - for (i = 0; i < pt->pt_argc; i++) { - copy_tv(&ret_pt->pt_argv[i], &pt->pt_argv[i]); - } + // Translate "s:func" to the stored function name. + fname = fname_trans_sid(fname, fname_buf, &tofree, &error); + + fp = find_func(fname); + xfree(tofree); + + // Turn "dict.Func" into a partial for "Func" with "dict". + if (fp != NULL && (fp->uf_flags & FC_DICT)) { + partial_T *pt = (partial_T *)xcalloc(1, sizeof(partial_T)); + + if (pt != NULL) { + pt->pt_refcount = 1; + pt->pt_dict = selfdict; + (selfdict->dv_refcount)++; + pt->pt_auto = true; + if (rettv->v_type == VAR_FUNC) { + // Just a function: Take over the function name and use selfdict. + pt->pt_name = rettv->vval.v_string; + } else { + partial_T *ret_pt = rettv->vval.v_partial; + int i; + + // Partial: copy the function name, use selfdict and copy + // args. Can't take over name or args, the partial might + // be referenced elsewhere. + pt->pt_name = vim_strsave(ret_pt->pt_name); + func_ref(pt->pt_name); + if (ret_pt->pt_argc > 0) { + size_t arg_size = sizeof(typval_T) * ret_pt->pt_argc; + pt->pt_argv = (typval_T *)xmalloc(arg_size); + if (pt->pt_argv == NULL) { + // out of memory: drop the arguments + pt->pt_argc = 0; + } else { + pt->pt_argc = ret_pt->pt_argc; + for (i = 0; i < pt->pt_argc; i++) { + copy_tv(&ret_pt->pt_argv[i], &pt->pt_argv[i]); } } - partial_unref(ret_pt); } - rettv->v_type = VAR_PARTIAL; - rettv->vval.v_partial = pt; + partial_unref(ret_pt); } + rettv->v_type = VAR_PARTIAL; + rettv->vval.v_partial = pt; } } - - dict_unref(selfdict); - return ret; } /* @@ -22444,10 +22574,9 @@ char_u *do_string_sub(char_u *str, char_u *pat, char_u *sub, char_u *flags) } static inline TerminalJobData *common_job_init(char **argv, - ufunc_T *on_stdout, - ufunc_T *on_stderr, - ufunc_T *on_exit, - dict_T *self, + Callback on_stdout, + Callback on_stderr, + Callback on_exit, bool pty, bool rpc, bool detach, @@ -22458,7 +22587,6 @@ static inline TerminalJobData *common_job_init(char **argv, data->on_stdout = on_stdout; data->on_stderr = on_stderr; data->on_exit = on_exit; - data->self = self; data->events = multiqueue_new_child(main_loop.events); data->rpc = rpc; if (pty) { @@ -22483,8 +22611,8 @@ static inline TerminalJobData *common_job_init(char **argv, /// common code for getting job callbacks for jobstart, termopen and rpcstart /// /// @return true/false on success/failure. -static inline bool common_job_callbacks(dict_T *vopts, ufunc_T **on_stdout, - ufunc_T **on_stderr, ufunc_T **on_exit) +static inline bool common_job_callbacks(dict_T *vopts, Callback *on_stdout, + Callback *on_stderr, Callback *on_exit) { if (get_dict_callback(vopts, "on_stdout", on_stdout) && get_dict_callback(vopts, "on_stderr", on_stderr) @@ -22492,15 +22620,10 @@ static inline bool common_job_callbacks(dict_T *vopts, ufunc_T **on_stdout, vopts->dv_refcount++; return true; } - if (*on_stdout) { - user_func_unref(*on_stdout); - } - if (*on_stderr) { - user_func_unref(*on_stderr); - } - if (*on_exit) { - user_func_unref(*on_exit); - } + + callback_free(on_stdout); + callback_free(on_stderr); + callback_free(on_exit); return false; } @@ -22555,19 +22678,10 @@ static inline bool common_job_start(TerminalJobData *data, typval_T *rettv) static inline void free_term_job_data_event(void **argv) { TerminalJobData *data = argv[0]; - if (data->on_stdout) { - user_func_unref(data->on_stdout); - } - if (data->on_stderr) { - user_func_unref(data->on_stderr); - } - if (data->on_exit) { - user_func_unref(data->on_exit); - } + callback_free(&data->on_stdout); + callback_free(&data->on_stderr); + callback_free(&data->on_exit); - if (data->self) { - dict_unref(data->self); - } multiqueue_free(data->events); pmap_del(uint64_t)(jobs, data->id); xfree(data); @@ -22581,8 +22695,9 @@ static inline void free_term_job_data(TerminalJobData *data) } // vimscript job callbacks must be executed on Nvim main loop -static inline void process_job_event(TerminalJobData *data, ufunc_T *callback, - const char *type, char *buf, size_t count, int status) +static inline void process_job_event(TerminalJobData *data, Callback *callback, + const char *type, char *buf, size_t count, + int status) { JobEvent event_data; event_data.received = NULL; @@ -22622,18 +22737,19 @@ static void on_job_stdout(Stream *stream, RBuffer *buf, size_t count, void *job, bool eof) { TerminalJobData *data = job; - on_job_output(stream, job, buf, count, eof, data->on_stdout, "stdout"); + on_job_output(stream, job, buf, count, eof, &data->on_stdout, "stdout"); } static void on_job_stderr(Stream *stream, RBuffer *buf, size_t count, void *job, bool eof) { TerminalJobData *data = job; - on_job_output(stream, job, buf, count, eof, data->on_stderr, "stderr"); + on_job_output(stream, job, buf, count, eof, &data->on_stderr, "stderr"); } static void on_job_output(Stream *stream, TerminalJobData *data, RBuffer *buf, - size_t count, bool eof, ufunc_T *callback, const char *type) + size_t count, bool eof, Callback *callback, + const char *type) { if (eof) { return; @@ -22650,7 +22766,7 @@ static void on_job_output(Stream *stream, TerminalJobData *data, RBuffer *buf, terminal_receive(data->term, ptr, count); } - if (callback) { + if (callback->type != kCallbackNone) { process_job_event(data, callback, type, ptr, count, 0); } @@ -22674,7 +22790,7 @@ static void eval_job_process_exit_cb(Process *proc, int status, void *d) *data->status_ptr = status; } - process_job_event(data, data->on_exit, "exit", NULL, 0, status); + process_job_event(data, &data->on_exit, "exit", NULL, 0, status); term_job_data_decref(data); } @@ -22733,38 +22849,30 @@ static void on_job_event(JobEvent *ev) return; } - typval_T argv[3]; - int argc = ev->callback->uf_args.ga_len; + typval_T argv[4]; - if (argc > 0) { - argv[0].v_type = VAR_NUMBER; - argv[0].v_lock = 0; - argv[0].vval.v_number = ev->data->id; - } - - if (argc > 1) { - if (ev->received) { - argv[1].v_type = VAR_LIST; - argv[1].v_lock = 0; - argv[1].vval.v_list = ev->received; - argv[1].vval.v_list->lv_refcount++; - } else { - argv[1].v_type = VAR_NUMBER; - argv[1].v_lock = 0; - argv[1].vval.v_number = ev->status; - } + argv[0].v_type = VAR_NUMBER; + argv[0].v_lock = 0; + argv[0].vval.v_number = ev->data->id; + + if (ev->received) { + argv[1].v_type = VAR_LIST; + argv[1].v_lock = 0; + argv[1].vval.v_list = ev->received; + argv[1].vval.v_list->lv_refcount++; + } else { + argv[1].v_type = VAR_NUMBER; + argv[1].v_lock = 0; + argv[1].vval.v_number = ev->status; } - if (argc > 2) { - argv[2].v_type = VAR_STRING; - argv[2].v_lock = 0; - argv[2].vval.v_string = (uint8_t *)ev->type; - } + argv[2].v_type = VAR_STRING; + argv[2].v_lock = 0; + argv[2].vval.v_string = (uint8_t *)ev->type; typval_T rettv; init_tv(&rettv); - call_user_func(ev->callback, argc, argv, &rettv, curwin->w_cursor.lnum, - curwin->w_cursor.lnum, ev->data->self); + callback_call(ev->callback, 3, argv, &rettv); clear_tv(&rettv); } @@ -22888,7 +22996,7 @@ static void dictwatcher_notify(dict_T *dict, const char *key, typval_T *newtv, typval_T *oldtv) FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_NONNULL_ARG(2) { - typval_T argv[3]; + typval_T argv[4]; for (size_t i = 0; i < ARRAY_SIZE(argv); i++) { init_tv(argv + i); } @@ -22921,8 +23029,7 @@ static void dictwatcher_notify(dict_T *dict, const char *key, typval_T *newtv, if (!watcher->busy && dictwatcher_matches(watcher, key)) { init_tv(&rettv); watcher->busy = true; - call_user_func(watcher->callback, ARRAY_SIZE(argv), argv, &rettv, - curwin->w_cursor.lnum, curwin->w_cursor.lnum, NULL); + callback_call(&watcher->callback, 3, argv, &rettv); watcher->busy = false; clear_tv(&rettv); } @@ -22953,7 +23060,7 @@ static bool dictwatcher_matches(DictWatcher *watcher, const char *key) static void dictwatcher_free(DictWatcher *watcher) FUNC_ATTR_NONNULL_ALL { - user_func_unref(watcher->callback); + callback_free(&watcher->callback); xfree(watcher->key_pattern); xfree(watcher); } diff --git a/src/nvim/testdir/test_timers.vim b/src/nvim/testdir/test_timers.vim index 136f32a80e..d92cbe6897 100644 --- a/src/nvim/testdir/test_timers.vim +++ b/src/nvim/testdir/test_timers.vim @@ -41,6 +41,7 @@ func Test_with_partial_callback() function s:meow.bite(...) let s:val += 1 endfunction + call timer_start(50, s:meow.bite) sleep 200m call assert_equal(1, s:val) |