mirror of
https://github.com/neovim/neovim.git
synced 2025-09-15 07:48:18 +00:00
Make partials work with jobs, timers, and dictwatchers.
This commit is contained in:
@@ -418,7 +418,7 @@ if(NOT BUSTED_OUTPUT_TYPE)
|
|||||||
if(WIN32)
|
if(WIN32)
|
||||||
set(BUSTED_OUTPUT_TYPE "plainTerminal")
|
set(BUSTED_OUTPUT_TYPE "plainTerminal")
|
||||||
else()
|
else()
|
||||||
set(BUSTED_OUTPUT_TYPE "utfTerminal")
|
set(BUSTED_OUTPUT_TYPE "gtest")
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
491
src/nvim/eval.c
491
src/nvim/eval.c
@@ -413,6 +413,21 @@ static struct vimvar {
|
|||||||
static dictitem_T vimvars_var; // variable used for v:
|
static dictitem_T vimvars_var; // variable used for v:
|
||||||
#define vimvarht vimvardict.dv_hashtab
|
#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 {
|
typedef struct {
|
||||||
union {
|
union {
|
||||||
LibuvProcess uv;
|
LibuvProcess uv;
|
||||||
@@ -424,15 +439,14 @@ typedef struct {
|
|||||||
bool exited;
|
bool exited;
|
||||||
bool rpc;
|
bool rpc;
|
||||||
int refcount;
|
int refcount;
|
||||||
ufunc_T *on_stdout, *on_stderr, *on_exit;
|
Callback on_stdout, on_stderr, on_exit;
|
||||||
dict_T *self;
|
|
||||||
int *status_ptr;
|
int *status_ptr;
|
||||||
uint64_t id;
|
uint64_t id;
|
||||||
MultiQueue *events;
|
MultiQueue *events;
|
||||||
} TerminalJobData;
|
} TerminalJobData;
|
||||||
|
|
||||||
typedef struct dict_watcher {
|
typedef struct dict_watcher {
|
||||||
ufunc_T *callback;
|
Callback callback;
|
||||||
char *key_pattern;
|
char *key_pattern;
|
||||||
QUEUE node;
|
QUEUE node;
|
||||||
bool busy; // prevent recursion if the dict is changed in the callback
|
bool busy; // prevent recursion if the dict is changed in the callback
|
||||||
@@ -440,7 +454,7 @@ typedef struct dict_watcher {
|
|||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
TerminalJobData *data;
|
TerminalJobData *data;
|
||||||
ufunc_T *callback;
|
Callback *callback;
|
||||||
const char *type;
|
const char *type;
|
||||||
list_T *received;
|
list_T *received;
|
||||||
int status;
|
int status;
|
||||||
@@ -453,7 +467,7 @@ typedef struct {
|
|||||||
int refcount;
|
int refcount;
|
||||||
long timeout;
|
long timeout;
|
||||||
bool stopped;
|
bool stopped;
|
||||||
ufunc_T *callback;
|
Callback callback;
|
||||||
} timer_T;
|
} timer_T;
|
||||||
|
|
||||||
typedef void (*FunPtr)(void);
|
typedef void (*FunPtr)(void);
|
||||||
@@ -5909,7 +5923,17 @@ bool garbage_collect(void)
|
|||||||
{
|
{
|
||||||
TerminalJobData *data;
|
TerminalJobData *data;
|
||||||
map_foreach_value(jobs, 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;
|
*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) {
|
if (tv->v_type == VAR_PARTIAL) {
|
||||||
partial_T *pt = tv->vval.v_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
|
/// @param[out] result The address where a pointer to the wanted callback
|
||||||
/// will be left.
|
/// will be left.
|
||||||
/// @return true/false on success/failure.
|
/// @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);
|
dictitem_T *di = dict_find(d, (uint8_t *)key, -1);
|
||||||
|
|
||||||
if (di == NULL) {
|
if (di == NULL) {
|
||||||
*result = NULL;
|
result->type = kCallbackNone;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (di->di_tv.v_type != VAR_FUNC && di->di_tv.v_type != VAR_STRING) {
|
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"));
|
EMSG(_("Argument is not a function or function name"));
|
||||||
*result = NULL;
|
result->type = kCallbackNone;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((*result = find_ufunc(di->di_tv.vval.v_string)) == NULL) {
|
typval_T tv;
|
||||||
return false;
|
copy_tv(&di->di_tv, &tv);
|
||||||
}
|
set_selfdict(&tv, d);
|
||||||
|
bool res = callback_from_typval(result, &tv);
|
||||||
(*result)->uf_refcount++;
|
clear_tv(&tv);
|
||||||
return true;
|
return res;
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a string item from a dictionary.
|
/// Get a string item from a dictionary.
|
||||||
@@ -8555,16 +8565,14 @@ static void f_dictwatcheradd(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ufunc_T *func = find_ufunc(argvars[2].vval.v_string);
|
Callback callback;
|
||||||
if (!func) {
|
if (!callback_from_typval(&callback, &argvars[2])) {
|
||||||
// Invalid function name. Error already reported by `find_ufunc`.
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
func->uf_refcount++;
|
|
||||||
DictWatcher *watcher = xmalloc(sizeof(DictWatcher));
|
DictWatcher *watcher = xmalloc(sizeof(DictWatcher));
|
||||||
watcher->key_pattern = xmemdupz(key_pattern, key_len);
|
watcher->key_pattern = xmemdupz(key_pattern, key_len);
|
||||||
watcher->callback = func;
|
watcher->callback = callback;
|
||||||
watcher->busy = false;
|
watcher->busy = false;
|
||||||
QUEUE_INSERT_TAIL(&argvars[0].vval.v_dict->watchers, &watcher->node);
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ufunc_T *func = find_ufunc(argvars[2].vval.v_string);
|
Callback callback;
|
||||||
if (!func) {
|
if (!callback_from_typval(&callback, &argvars[2])) {
|
||||||
// Invalid function name. Error already reported by `find_ufunc`.
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -8612,13 +8619,15 @@ static void f_dictwatcherdel(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||||||
bool matched = false;
|
bool matched = false;
|
||||||
QUEUE_FOREACH(w, &dict->watchers) {
|
QUEUE_FOREACH(w, &dict->watchers) {
|
||||||
watcher = dictwatcher_node_data(w);
|
watcher = dictwatcher_node_data(w);
|
||||||
if (func == watcher->callback
|
if (callback_equal(&watcher->callback, &callback)
|
||||||
&& !strcmp(watcher->key_pattern, key_pattern)) {
|
&& !strcmp(watcher->key_pattern, key_pattern)) {
|
||||||
matched = true;
|
matched = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
callback_free(&callback);
|
||||||
|
|
||||||
if (!matched) {
|
if (!matched) {
|
||||||
EMSG("Couldn't find a watcher matching key and callback");
|
EMSG("Couldn't find a watcher matching key and callback");
|
||||||
return;
|
return;
|
||||||
@@ -12064,7 +12073,8 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||||||
|
|
||||||
dict_T *job_opts = NULL;
|
dict_T *job_opts = NULL;
|
||||||
bool detach = false, rpc = false, pty = false;
|
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;
|
char *cwd = NULL;
|
||||||
if (argvars[1].v_type == VAR_DICT) {
|
if (argvars[1].v_type == VAR_DICT) {
|
||||||
job_opts = argvars[1].vval.v_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,
|
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;
|
Process *proc = (Process *)&data->proc;
|
||||||
|
|
||||||
if (pty) {
|
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;
|
proc->out = NULL;
|
||||||
}
|
}
|
||||||
if (!on_stderr) {
|
if (on_stderr.type == kCallbackNone) {
|
||||||
proc->err = NULL;
|
proc->err = NULL;
|
||||||
}
|
}
|
||||||
common_job_start(data, rettv);
|
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
|
// The last item of argv must be NULL
|
||||||
argv[i] = NULL;
|
argv[i] = NULL;
|
||||||
|
|
||||||
TerminalJobData *data = common_job_init(argv, NULL, NULL, NULL,
|
TerminalJobData *data = common_job_init(argv, CALLBACK_NONE, CALLBACK_NONE,
|
||||||
NULL, false, true, false, NULL);
|
CALLBACK_NONE, false, true, false,
|
||||||
|
NULL);
|
||||||
common_job_start(data, rettv);
|
common_job_start(data, rettv);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -16904,7 +16915,8 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||||||
return;
|
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;
|
dict_T *job_opts = NULL;
|
||||||
char *cwd = ".";
|
char *cwd = ".";
|
||||||
if (argvars[1].v_type == VAR_DICT) {
|
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,
|
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.width = curwin->w_width;
|
||||||
data->proc.pty.height = curwin->w_height;
|
data->proc.pty.height = curwin->w_height;
|
||||||
data->proc.pty.term_name = xstrdup("xterm-256color");
|
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. */
|
/* 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
|
/// "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)
|
||||||
{
|
{
|
||||||
@@ -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) {
|
Callback callback;
|
||||||
EMSG2(e_invarg2, "funcref");
|
if (!callback_from_typval(&callback, &argvars[1])) {
|
||||||
return;
|
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 = xmalloc(sizeof *timer);
|
||||||
timer->refcount = 1;
|
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->repeat_count = repeat;
|
||||||
timer->timeout = timeout;
|
timer->timeout = timeout;
|
||||||
timer->timer_id = last_timer_id++;
|
timer->timer_id = last_timer_id++;
|
||||||
timer->callback = func;
|
timer->callback = callback;
|
||||||
|
|
||||||
time_watcher_init(&main_loop, &timer->tw, timer);
|
time_watcher_init(&main_loop, &timer->tw, timer);
|
||||||
timer->tw.events = multiqueue_new_child(main_loop.events);
|
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);
|
timer_stop(timer);
|
||||||
}
|
}
|
||||||
|
|
||||||
typval_T argv[1];
|
typval_T argv[2];
|
||||||
init_tv(argv);
|
init_tv(argv);
|
||||||
argv[0].v_type = VAR_NUMBER;
|
argv[0].v_type = VAR_NUMBER;
|
||||||
argv[0].vval.v_number = timer->timer_id;
|
argv[0].vval.v_number = timer->timer_id;
|
||||||
typval_T rettv;
|
typval_T rettv;
|
||||||
|
|
||||||
init_tv(&rettv);
|
init_tv(&rettv);
|
||||||
call_user_func(timer->callback, ARRAY_SIZE(argv), argv, &rettv,
|
callback_call(&timer->callback, 1, argv, &rettv);
|
||||||
curwin->w_cursor.lnum, curwin->w_cursor.lnum, NULL);
|
|
||||||
clear_tv(&rettv);
|
clear_tv(&rettv);
|
||||||
|
|
||||||
if (!timer->stopped && timer->timeout == 0) {
|
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;
|
timer_T *timer = (timer_T *)data;
|
||||||
multiqueue_free(timer->tw.events);
|
multiqueue_free(timer->tw.events);
|
||||||
user_func_unref(timer->callback);
|
callback_free(&timer->callback);
|
||||||
pmap_del(uint64_t)(timers, timer->timer_id);
|
pmap_del(uint64_t)(timers, timer->timer_id);
|
||||||
timer_decref(timer);
|
timer_decref(timer);
|
||||||
}
|
}
|
||||||
@@ -18531,72 +18655,78 @@ handle_subscript (
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Turn "dict.Func" into a partial for "Func" bound to "dict".
|
// 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
|
if (selfdict != NULL
|
||||||
&& (rettv->v_type == VAR_FUNC
|
&& (rettv->v_type == VAR_FUNC
|
||||||
|| (rettv->v_type == VAR_PARTIAL
|
|| rettv->v_type == VAR_PARTIAL)) {
|
||||||
&& (rettv->vval.v_partial->pt_auto
|
set_selfdict(rettv, selfdict);
|
||||||
|| 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);
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
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;
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dict_unref(selfdict);
|
dict_unref(selfdict);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Free the memory for a variable type-value.
|
* Free the memory for a variable type-value.
|
||||||
*/
|
*/
|
||||||
@@ -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,
|
static inline TerminalJobData *common_job_init(char **argv,
|
||||||
ufunc_T *on_stdout,
|
Callback on_stdout,
|
||||||
ufunc_T *on_stderr,
|
Callback on_stderr,
|
||||||
ufunc_T *on_exit,
|
Callback on_exit,
|
||||||
dict_T *self,
|
|
||||||
bool pty,
|
bool pty,
|
||||||
bool rpc,
|
bool rpc,
|
||||||
bool detach,
|
bool detach,
|
||||||
@@ -22458,7 +22587,6 @@ static inline TerminalJobData *common_job_init(char **argv,
|
|||||||
data->on_stdout = on_stdout;
|
data->on_stdout = on_stdout;
|
||||||
data->on_stderr = on_stderr;
|
data->on_stderr = on_stderr;
|
||||||
data->on_exit = on_exit;
|
data->on_exit = on_exit;
|
||||||
data->self = self;
|
|
||||||
data->events = multiqueue_new_child(main_loop.events);
|
data->events = multiqueue_new_child(main_loop.events);
|
||||||
data->rpc = rpc;
|
data->rpc = rpc;
|
||||||
if (pty) {
|
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
|
/// common code for getting job callbacks for jobstart, termopen and rpcstart
|
||||||
///
|
///
|
||||||
/// @return true/false on success/failure.
|
/// @return true/false on success/failure.
|
||||||
static inline bool common_job_callbacks(dict_T *vopts, ufunc_T **on_stdout,
|
static inline bool common_job_callbacks(dict_T *vopts, Callback *on_stdout,
|
||||||
ufunc_T **on_stderr, ufunc_T **on_exit)
|
Callback *on_stderr, Callback *on_exit)
|
||||||
{
|
{
|
||||||
if (get_dict_callback(vopts, "on_stdout", on_stdout)
|
if (get_dict_callback(vopts, "on_stdout", on_stdout)
|
||||||
&& get_dict_callback(vopts, "on_stderr", on_stderr)
|
&& 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++;
|
vopts->dv_refcount++;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (*on_stdout) {
|
|
||||||
user_func_unref(*on_stdout);
|
callback_free(on_stdout);
|
||||||
}
|
callback_free(on_stderr);
|
||||||
if (*on_stderr) {
|
callback_free(on_exit);
|
||||||
user_func_unref(*on_stderr);
|
|
||||||
}
|
|
||||||
if (*on_exit) {
|
|
||||||
user_func_unref(*on_exit);
|
|
||||||
}
|
|
||||||
return false;
|
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)
|
static inline void free_term_job_data_event(void **argv)
|
||||||
{
|
{
|
||||||
TerminalJobData *data = argv[0];
|
TerminalJobData *data = argv[0];
|
||||||
if (data->on_stdout) {
|
callback_free(&data->on_stdout);
|
||||||
user_func_unref(data->on_stdout);
|
callback_free(&data->on_stderr);
|
||||||
}
|
callback_free(&data->on_exit);
|
||||||
if (data->on_stderr) {
|
|
||||||
user_func_unref(data->on_stderr);
|
|
||||||
}
|
|
||||||
if (data->on_exit) {
|
|
||||||
user_func_unref(data->on_exit);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data->self) {
|
|
||||||
dict_unref(data->self);
|
|
||||||
}
|
|
||||||
multiqueue_free(data->events);
|
multiqueue_free(data->events);
|
||||||
pmap_del(uint64_t)(jobs, data->id);
|
pmap_del(uint64_t)(jobs, data->id);
|
||||||
xfree(data);
|
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
|
// vimscript job callbacks must be executed on Nvim main loop
|
||||||
static inline void process_job_event(TerminalJobData *data, ufunc_T *callback,
|
static inline void process_job_event(TerminalJobData *data, Callback *callback,
|
||||||
const char *type, char *buf, size_t count, int status)
|
const char *type, char *buf, size_t count,
|
||||||
|
int status)
|
||||||
{
|
{
|
||||||
JobEvent event_data;
|
JobEvent event_data;
|
||||||
event_data.received = NULL;
|
event_data.received = NULL;
|
||||||
@@ -22622,18 +22737,19 @@ static void on_job_stdout(Stream *stream, RBuffer *buf, size_t count,
|
|||||||
void *job, bool eof)
|
void *job, bool eof)
|
||||||
{
|
{
|
||||||
TerminalJobData *data = job;
|
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,
|
static void on_job_stderr(Stream *stream, RBuffer *buf, size_t count,
|
||||||
void *job, bool eof)
|
void *job, bool eof)
|
||||||
{
|
{
|
||||||
TerminalJobData *data = job;
|
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,
|
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) {
|
if (eof) {
|
||||||
return;
|
return;
|
||||||
@@ -22650,7 +22766,7 @@ static void on_job_output(Stream *stream, TerminalJobData *data, RBuffer *buf,
|
|||||||
terminal_receive(data->term, ptr, count);
|
terminal_receive(data->term, ptr, count);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (callback) {
|
if (callback->type != kCallbackNone) {
|
||||||
process_job_event(data, callback, type, ptr, count, 0);
|
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;
|
*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);
|
term_job_data_decref(data);
|
||||||
}
|
}
|
||||||
@@ -22733,38 +22849,30 @@ static void on_job_event(JobEvent *ev)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
typval_T argv[3];
|
typval_T argv[4];
|
||||||
int argc = ev->callback->uf_args.ga_len;
|
|
||||||
|
|
||||||
if (argc > 0) {
|
argv[0].v_type = VAR_NUMBER;
|
||||||
argv[0].v_type = VAR_NUMBER;
|
argv[0].v_lock = 0;
|
||||||
argv[0].v_lock = 0;
|
argv[0].vval.v_number = ev->data->id;
|
||||||
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 > 1) {
|
argv[2].v_type = VAR_STRING;
|
||||||
if (ev->received) {
|
argv[2].v_lock = 0;
|
||||||
argv[1].v_type = VAR_LIST;
|
argv[2].vval.v_string = (uint8_t *)ev->type;
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
typval_T rettv;
|
typval_T rettv;
|
||||||
init_tv(&rettv);
|
init_tv(&rettv);
|
||||||
call_user_func(ev->callback, argc, argv, &rettv, curwin->w_cursor.lnum,
|
callback_call(ev->callback, 3, argv, &rettv);
|
||||||
curwin->w_cursor.lnum, ev->data->self);
|
|
||||||
clear_tv(&rettv);
|
clear_tv(&rettv);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -22888,7 +22996,7 @@ static void dictwatcher_notify(dict_T *dict, const char *key, typval_T *newtv,
|
|||||||
typval_T *oldtv)
|
typval_T *oldtv)
|
||||||
FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_NONNULL_ARG(2)
|
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++) {
|
for (size_t i = 0; i < ARRAY_SIZE(argv); i++) {
|
||||||
init_tv(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)) {
|
if (!watcher->busy && dictwatcher_matches(watcher, key)) {
|
||||||
init_tv(&rettv);
|
init_tv(&rettv);
|
||||||
watcher->busy = true;
|
watcher->busy = true;
|
||||||
call_user_func(watcher->callback, ARRAY_SIZE(argv), argv, &rettv,
|
callback_call(&watcher->callback, 3, argv, &rettv);
|
||||||
curwin->w_cursor.lnum, curwin->w_cursor.lnum, NULL);
|
|
||||||
watcher->busy = false;
|
watcher->busy = false;
|
||||||
clear_tv(&rettv);
|
clear_tv(&rettv);
|
||||||
}
|
}
|
||||||
@@ -22953,7 +23060,7 @@ static bool dictwatcher_matches(DictWatcher *watcher, const char *key)
|
|||||||
static void dictwatcher_free(DictWatcher *watcher)
|
static void dictwatcher_free(DictWatcher *watcher)
|
||||||
FUNC_ATTR_NONNULL_ALL
|
FUNC_ATTR_NONNULL_ALL
|
||||||
{
|
{
|
||||||
user_func_unref(watcher->callback);
|
callback_free(&watcher->callback);
|
||||||
xfree(watcher->key_pattern);
|
xfree(watcher->key_pattern);
|
||||||
xfree(watcher);
|
xfree(watcher);
|
||||||
}
|
}
|
||||||
|
@@ -41,6 +41,7 @@ func Test_with_partial_callback()
|
|||||||
function s:meow.bite(...)
|
function s:meow.bite(...)
|
||||||
let s:val += 1
|
let s:val += 1
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
call timer_start(50, s:meow.bite)
|
call timer_start(50, s:meow.bite)
|
||||||
sleep 200m
|
sleep 200m
|
||||||
call assert_equal(1, s:val)
|
call assert_equal(1, s:val)
|
||||||
|
@@ -18,7 +18,7 @@ describe('jobs', function()
|
|||||||
channel = nvim('get_api_info')[1]
|
channel = nvim('get_api_info')[1]
|
||||||
nvim('set_var', 'channel', channel)
|
nvim('set_var', 'channel', channel)
|
||||||
source([[
|
source([[
|
||||||
function! s:OnEvent(id, data, event)
|
function! s:OnEvent(id, data, event) dict
|
||||||
let userdata = get(self, 'user')
|
let userdata = get(self, 'user')
|
||||||
call rpcnotify(g:channel, a:event, userdata, a:data)
|
call rpcnotify(g:channel, a:event, userdata, a:data)
|
||||||
endfunction
|
endfunction
|
||||||
@@ -265,9 +265,12 @@ describe('jobs', function()
|
|||||||
eq({'notification', 'exit', {45, 10}}, next_msg())
|
eq({'notification', 'exit', {45, 10}}, next_msg())
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('cannot redefine callbacks being used by a job', function()
|
it('can redefine callbacks being used by a job', function()
|
||||||
local screen = Screen.new()
|
local screen = Screen.new()
|
||||||
screen:attach()
|
screen:attach()
|
||||||
|
screen:set_default_attr_ids({
|
||||||
|
[1] = {bold=true, foreground=Screen.colors.Blue},
|
||||||
|
})
|
||||||
local script = [[
|
local script = [[
|
||||||
function! g:JobHandler(job_id, data, event)
|
function! g:JobHandler(job_id, data, event)
|
||||||
endfunction
|
endfunction
|
||||||
@@ -283,20 +286,20 @@ describe('jobs', function()
|
|||||||
feed(':function! g:JobHandler(job_id, data, event)<cr>')
|
feed(':function! g:JobHandler(job_id, data, event)<cr>')
|
||||||
feed(':endfunction<cr>')
|
feed(':endfunction<cr>')
|
||||||
screen:expect([[
|
screen:expect([[
|
||||||
~ |
|
^ |
|
||||||
~ |
|
{1:~ }|
|
||||||
~ |
|
{1:~ }|
|
||||||
~ |
|
{1:~ }|
|
||||||
~ |
|
{1:~ }|
|
||||||
~ |
|
{1:~ }|
|
||||||
~ |
|
{1:~ }|
|
||||||
~ |
|
{1:~ }|
|
||||||
~ |
|
{1:~ }|
|
||||||
:function! g:JobHandler(job_id, data, event) |
|
{1:~ }|
|
||||||
: :endfunction |
|
{1:~ }|
|
||||||
E127: Cannot redefine function JobHandler: It is in u|
|
{1:~ }|
|
||||||
se |
|
{1:~ }|
|
||||||
Press ENTER or type command to continue^ |
|
|
|
||||||
]])
|
]])
|
||||||
end)
|
end)
|
||||||
|
|
||||||
@@ -317,7 +320,7 @@ describe('jobs', function()
|
|||||||
source([[
|
source([[
|
||||||
let g:dict = {'id': 10}
|
let g:dict = {'id': 10}
|
||||||
let g:exits = 0
|
let g:exits = 0
|
||||||
function g:dict.on_exit(id, code)
|
function g:dict.on_exit(id, code, event)
|
||||||
if a:code != 5
|
if a:code != 5
|
||||||
throw 'Error!'
|
throw 'Error!'
|
||||||
endif
|
endif
|
||||||
@@ -365,7 +368,7 @@ describe('jobs', function()
|
|||||||
eq({'notification', 'wait', {{-2}}}, next_msg())
|
eq({'notification', 'wait', {{-2}}}, next_msg())
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('can be called recursively', function()
|
pending('can be called recursively', function()
|
||||||
source([[
|
source([[
|
||||||
let g:opts = {}
|
let g:opts = {}
|
||||||
let g:counter = 0
|
let g:counter = 0
|
||||||
|
@@ -2,6 +2,7 @@ local helpers = require('test.functional.helpers')(after_each)
|
|||||||
local clear, nvim, source = helpers.clear, helpers.nvim, helpers.source
|
local clear, nvim, source = helpers.clear, helpers.nvim, helpers.source
|
||||||
local eq, next_msg = helpers.eq, helpers.next_message
|
local eq, next_msg = helpers.eq, helpers.next_message
|
||||||
local exc_exec = helpers.exc_exec
|
local exc_exec = helpers.exc_exec
|
||||||
|
local command = helpers.command
|
||||||
|
|
||||||
|
|
||||||
describe('dictionary change notifications', function()
|
describe('dictionary change notifications', function()
|
||||||
@@ -229,11 +230,9 @@ describe('dictionary change notifications', function()
|
|||||||
exc_exec('call dictwatcherdel(g:, "invalid_key", "g:Watcher2")'))
|
exc_exec('call dictwatcherdel(g:, "invalid_key", "g:Watcher2")'))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it("fails to add/remove if the callback doesn't exist", function()
|
it("does not fail to add/remove if the callback doesn't exist", function()
|
||||||
eq("Vim(call):Function g:InvalidCb doesn't exist",
|
command('call dictwatcheradd(g:, "key", "g:InvalidCb")')
|
||||||
exc_exec('call dictwatcheradd(g:, "key", "g:InvalidCb")'))
|
command('call dictwatcherdel(g:, "key", "g:InvalidCb")')
|
||||||
eq("Vim(call):Function g:InvalidCb doesn't exist",
|
|
||||||
exc_exec('call dictwatcherdel(g:, "key", "g:InvalidCb")'))
|
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('fails with empty keys', function()
|
it('fails with empty keys', function()
|
||||||
@@ -243,15 +242,18 @@ describe('dictionary change notifications', function()
|
|||||||
exc_exec('call dictwatcherdel(g:, "", "g:Watcher1")'))
|
exc_exec('call dictwatcherdel(g:, "", "g:Watcher1")'))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('fails to replace a watcher function', function()
|
it('does not fail to replace a watcher function', function()
|
||||||
source([[
|
source([[
|
||||||
function! g:ReplaceWatcher2()
|
function! g:ReplaceWatcher2()
|
||||||
function! g:Watcher2()
|
function! g:Watcher2(dict, key, value)
|
||||||
|
call rpcnotify(g:channel, '2b', a:key, a:value)
|
||||||
endfunction
|
endfunction
|
||||||
endfunction
|
endfunction
|
||||||
]])
|
]])
|
||||||
eq("Vim(function):E127: Cannot redefine function Watcher2: It is in use",
|
command('call g:ReplaceWatcher2()')
|
||||||
exc_exec('call g:ReplaceWatcher2()'))
|
command('let g:key = "value"')
|
||||||
|
eq({'notification', '2b', {'key', {old = 'v2', new = 'value'}}}, next_msg())
|
||||||
|
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
Reference in New Issue
Block a user