aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/eval.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim/eval.c')
-rw-r--r--src/nvim/eval.c866
1 files changed, 669 insertions, 197 deletions
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 8c8881b398..f5cffbc3e1 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -67,6 +67,7 @@
#include "nvim/syntax.h"
#include "nvim/tag.h"
#include "nvim/ui.h"
+#include "nvim/main.h"
#include "nvim/mouse.h"
#include "nvim/terminal.h"
#include "nvim/undo.h"
@@ -76,9 +77,10 @@
#include "nvim/eval/decode.h"
#include "nvim/os/os.h"
#include "nvim/event/libuv_process.h"
-#include "nvim/event/pty_process.h"
+#include "nvim/os/pty_process.h"
#include "nvim/event/rstream.h"
#include "nvim/event/wstream.h"
+#include "nvim/event/time.h"
#include "nvim/os/time.h"
#include "nvim/msgpack_rpc/channel.h"
#include "nvim/msgpack_rpc/server.h"
@@ -89,6 +91,7 @@
#include "nvim/os/input.h"
#include "nvim/event/loop.h"
#include "nvim/lib/queue.h"
+#include "nvim/eval/typval_encode.h"
#define DICT_MAXNEST 100 /* maximum nesting of lists and dicts */
@@ -428,6 +431,14 @@ typedef struct {
int status;
} JobEvent;
+typedef struct {
+ TimeWatcher tw;
+ int timer_id;
+ int repeat_count;
+ bool stopped;
+ ufunc_T *callback;
+} timer_T;
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "eval.c.generated.h"
#endif
@@ -438,6 +449,9 @@ typedef struct {
static uint64_t current_job_id = 1;
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[] = {
[kMPNil] = "nil",
[kMPBoolean] = "boolean",
@@ -469,6 +483,7 @@ void eval_init(void)
vimvars[VV_VERSION].vv_nr = VIM_VERSION_100;
jobs = pmap_new(uint64_t)();
+ timers = pmap_new(uint64_t)();
struct vimvar *p;
init_var_dict(&globvardict, &globvars_var, VAR_DEF_SCOPE);
@@ -494,6 +509,7 @@ void eval_init(void)
/* add to compat scope dict */
hash_add(&compat_hashtab, p->vv_di.di_key);
}
+ vimvars[VV_VERSION].vv_nr = VIM_VERSION_100;
dict_T *const msgpack_types_dict = dict_alloc();
for (size_t i = 0; i < ARRAY_SIZE(msgpack_type_names); i++) {
@@ -1876,7 +1892,7 @@ ex_let_one (
}
}
if (p != NULL) {
- write_reg_contents(*arg == '@' ? '"' : *arg, p, -1, FALSE);
+ write_reg_contents(*arg == '@' ? '"' : *arg, p, STRLEN(p), false);
arg_end = arg + 1;
}
xfree(ptofree);
@@ -2964,11 +2980,16 @@ int do_unlet(char_u *name, int forceit)
} else if (current_funccal != NULL
&& ht == &current_funccal->l_vars.dv_hashtab) {
d = &current_funccal->l_vars;
+ } else if (ht == &compat_hashtab) {
+ d = &vimvardict;
} else {
di = find_var_in_ht(ht, *name, (char_u *)"", false);
d = di->di_tv.vval.v_dict;
}
-
+ if (d == NULL) {
+ EMSG2(_(e_intern2), "do_unlet()");
+ return FAIL;
+ }
hi = hash_find(ht, varname);
if (!HASHITEM_EMPTY(hi)) {
di = HI2DI(hi);
@@ -2977,6 +2998,11 @@ int do_unlet(char_u *name, int forceit)
|| tv_check_lock(d->dv_lock, name, false)) {
return FAIL;
}
+
+ if (d == NULL || tv_check_lock(d->dv_lock, name, false)) {
+ return FAIL;
+ }
+
typval_T oldtv;
bool watched = is_watched(dict);
@@ -6659,6 +6685,7 @@ static struct fst {
{ "acos", 1, 1, f_acos }, // WJMc
{ "add", 2, 2, f_add },
{ "and", 2, 2, f_and },
+ { "api_info", 0, 0, f_api_info },
{ "append", 2, 2, f_append },
{ "argc", 0, 0, f_argc },
{ "argidx", 0, 0, f_argidx },
@@ -6667,6 +6694,7 @@ static struct fst {
{ "asin", 1, 1, f_asin }, // WJMc
{ "assert_equal", 2, 3, f_assert_equal },
{ "assert_exception", 1, 2, f_assert_exception },
+ { "assert_fails", 1, 2, f_assert_fails },
{ "assert_false", 1, 2, f_assert_false },
{ "assert_true", 1, 2, f_assert_true },
{ "atan", 1, 1, f_atan },
@@ -6686,6 +6714,7 @@ static struct fst {
{ "byteidx", 2, 2, f_byteidx },
{ "byteidxcomp", 2, 2, f_byteidxcomp },
{ "call", 2, 3, f_call },
+ { "capture", 1, 1, f_capture },
{ "ceil", 1, 1, f_ceil },
{ "changenr", 0, 0, f_changenr },
{ "char2nr", 1, 2, f_char2nr },
@@ -6877,6 +6906,7 @@ static struct fst {
{ "setbufvar", 3, 3, f_setbufvar },
{ "setcharsearch", 1, 1, f_setcharsearch },
{ "setcmdpos", 1, 1, f_setcmdpos },
+ { "setfperm", 2, 2, f_setfperm },
{ "setline", 2, 2, f_setline },
{ "setloclist", 2, 4, f_setloclist },
{ "setmatches", 1, 1, f_setmatches },
@@ -6929,6 +6959,8 @@ static struct fst {
{ "tempname", 0, 0, f_tempname },
{ "termopen", 1, 2, f_termopen },
{ "test", 1, 1, f_test },
+ { "timer_start", 2, 3, f_timer_start },
+ { "timer_stop", 1, 1, f_timer_stop },
{ "tolower", 1, 1, f_tolower },
{ "toupper", 1, 1, f_toupper },
{ "tr", 3, 3, f_tr },
@@ -7437,6 +7469,15 @@ static void f_and(typval_T *argvars, typval_T *rettv)
& get_tv_number_chk(&argvars[1], NULL);
}
+
+/// "api_info()" function
+static void f_api_info(typval_T *argvars, typval_T *rettv)
+{
+ Dictionary metadata = api_metadata();
+ (void)object_to_vim(DICTIONARY_OBJ(metadata), rettv, NULL);
+ api_free_dictionary(metadata);
+}
+
/*
* "append(lnum, string/list)" function
*/
@@ -7512,25 +7553,9 @@ static void f_argidx(typval_T *argvars, typval_T *rettv)
static void f_arglistid(typval_T *argvars, typval_T *rettv)
{
rettv->vval.v_number = -1;
- if (argvars[0].v_type != VAR_UNKNOWN) {
- tabpage_T *tp = NULL;
- if (argvars[1].v_type != VAR_UNKNOWN) {
- long n = get_tv_number(&argvars[1]);
- if (n >= 0) {
- tp = find_tabpage(n);
- }
- } else {
- tp = curtab;
- }
-
- if (tp != NULL) {
- win_T *wp = find_win_by_nr(&argvars[0], tp);
- if (wp != NULL) {
- rettv->vval.v_number = wp->w_alist->id;
- }
- }
- } else {
- rettv->vval.v_number = curwin->w_alist->id;
+ win_T *wp = find_tabwin(&argvars[0], &argvars[1]);
+ if (wp != NULL) {
+ rettv->vval.v_number = wp->w_alist->id;
}
}
@@ -7651,6 +7676,43 @@ static void f_assert_exception(typval_T *argvars, typval_T *rettv)
}
}
+/// "assert_fails(cmd [, error])" function
+static void f_assert_fails(typval_T *argvars, typval_T *rettv)
+{
+ char_u *cmd = get_tv_string_chk(&argvars[0]);
+ garray_T ga;
+
+ called_emsg = false;
+ suppress_errthrow = true;
+ emsg_silent = true;
+ do_cmdline_cmd((char *)cmd);
+ if (!called_emsg) {
+ prepare_assert_error(&ga);
+ ga_concat(&ga, (char_u *)"command did not fail: ");
+ ga_concat(&ga, cmd);
+ assert_error(&ga);
+ ga_clear(&ga);
+ } else if (argvars[1].v_type != VAR_UNKNOWN) {
+ char_u buf[NUMBUFLEN];
+ char *error = (char *)get_tv_string_buf_chk(&argvars[1], buf);
+
+ if (error == NULL
+ || strstr((char *)vimvars[VV_ERRMSG].vv_str, error) == NULL) {
+ prepare_assert_error(&ga);
+ fill_assert_error(&ga, &argvars[2], NULL, &argvars[1],
+ &vimvars[VV_ERRMSG].vv_tv);
+ assert_error(&ga);
+ ga_clear(&ga);
+ }
+ }
+
+ called_emsg = false;
+ suppress_errthrow = false;
+ emsg_silent = false;
+ emsg_on_display = false;
+ set_vim_var_string(VV_ERRMSG, NULL, 0);
+}
+
// Common for assert_true() and assert_false().
static void assert_bool(typval_T *argvars, bool is_true)
{
@@ -8023,6 +8085,38 @@ static void f_call(typval_T *argvars, typval_T *rettv)
(void)func_call(func, &argvars[1], selfdict, rettv);
}
+// "capture(command)" function
+static void f_capture(typval_T *argvars, typval_T *rettv)
+{
+ int save_msg_silent = msg_silent;
+ garray_T *save_capture_ga = capture_ga;
+
+ if (check_secure()) {
+ return;
+ }
+
+ garray_T capture_local;
+ capture_ga = &capture_local;
+ ga_init(capture_ga, (int)sizeof(char), 80);
+
+ msg_silent++;
+ if (argvars[0].v_type != VAR_LIST) {
+ do_cmdline_cmd((char *)get_tv_string(&argvars[0]));
+ } else if (argvars[0].vval.v_list != NULL) {
+ for (listitem_T *li = argvars[0].vval.v_list->lv_first;
+ li != NULL; li = li->li_next) {
+ do_cmdline_cmd((char *)get_tv_string(&li->li_tv));
+ }
+ }
+ msg_silent = save_msg_silent;
+
+ ga_append(capture_ga, NUL);
+ rettv->v_type = VAR_STRING;
+ rettv->vval.v_string = capture_ga->ga_data;
+
+ capture_ga = save_capture_ga;
+}
+
/*
* "ceil({float})" function
*/
@@ -9833,7 +9927,7 @@ static void f_getcmdwintype(typval_T *argvars, typval_T *rettv)
static void f_getcwd(typval_T *argvars, typval_T *rettv)
{
// Possible scope of working directory to return.
- CdScope scope = MIN_CD_SCOPE;
+ CdScope scope = kCdScopeInvalid;
// Numbers of the scope objects (window, tab) we want the working directory
// of. A `-1` means to skip this scope, a `0` means the current object.
@@ -9862,26 +9956,27 @@ static void f_getcwd(typval_T *argvars, typval_T *rettv)
return;
}
scope_number[i] = argvars[i].vval.v_number;
- // The scope is the current iteration step.
- scope = i;
// It is an error for the scope number to be less than `-1`.
if (scope_number[i] < -1) {
EMSG(_(e_invarg));
return;
}
+ // Use the narrowest scope the user requested
+ if (scope_number[i] >= 0 && scope == kCdScopeInvalid) {
+ // The scope is the current iteration step.
+ scope = i;
+ } else if (scope_number[i] < 0) {
+ scope = i + 1;
+ }
}
- // Normalize scope, the number of the new scope will be 0.
- if (scope_number[scope] < 0) {
- // Arguments to `getcwd` always end at second-highest scope, so scope will
- // always be <= `MAX_CD_SCOPE`.
- scope++;
+ // If the user didn't specify anything, default to window scope
+ if (scope == kCdScopeInvalid) {
+ scope = MIN_CD_SCOPE;
}
// Find the tabpage by number
- if (scope_number[kCdScopeTab] == -1) {
- tp = NULL;
- } else if (scope_number[kCdScopeTab] > 0) {
+ if (scope_number[kCdScopeTab] > 0) {
tp = find_tabpage(scope_number[kCdScopeTab]);
if (!tp) {
EMSG(_("E5000: Cannot find tab number."));
@@ -9890,16 +9985,14 @@ static void f_getcwd(typval_T *argvars, typval_T *rettv)
}
// Find the window in `tp` by number, `NULL` if none.
- if (scope_number[kCdScopeWindow] == -1) {
- win = NULL;
- } else if (scope_number[kCdScopeWindow] >= 0) {
- if (!tp) {
+ if (scope_number[kCdScopeWindow] >= 0) {
+ if (scope_number[kCdScopeTab] < 0) {
EMSG(_("E5001: Higher scope cannot be -1 if lower scope is >= 0."));
return;
}
if (scope_number[kCdScopeWindow] > 0) {
- win = find_win_by_nr(&argvars[0], curtab);
+ win = find_win_by_nr(&argvars[0], tp);
if (!win) {
EMSG(_("E5002: Cannot find window number."));
return;
@@ -9934,6 +10027,9 @@ static void f_getcwd(typval_T *argvars, typval_T *rettv)
}
}
break;
+ case kCdScopeInvalid:
+ // We should never get here
+ assert(false);
}
if (from) {
@@ -10405,9 +10501,33 @@ find_win_by_nr (
return NULL;
}
-/*
- * "getwinvar()" function
- */
+/// Find window specified by "wvp" in tabpage "tvp".
+static win_T *find_tabwin(typval_T *wvp, typval_T *tvp)
+{
+ win_T *wp = NULL;
+ tabpage_T *tp = NULL;
+
+ if (wvp->v_type != VAR_UNKNOWN) {
+ if (tvp->v_type != VAR_UNKNOWN) {
+ long n = get_tv_number(tvp);
+ if (n >= 0) {
+ tp = find_tabpage(n);
+ }
+ } else {
+ tp = curtab;
+ }
+
+ if (tp != NULL) {
+ wp = find_win_by_nr(wvp, tp);
+ }
+ } else {
+ wp = curwin;
+ }
+
+ return wp;
+}
+
+/// "getwinvar()" function
static void f_getwinvar(typval_T *argvars, typval_T *rettv)
{
getwinvar(argvars, rettv, 0);
@@ -10687,6 +10807,7 @@ static void f_has(typval_T *argvars, typval_T *rettv)
"termguicolors",
"termresponse",
"textobjects",
+ "timers",
"title",
"user-commands", /* was accidentally included in 5.4 */
"user_commands",
@@ -10780,7 +10901,7 @@ static void f_has_key(typval_T *argvars, typval_T *rettv)
static void f_haslocaldir(typval_T *argvars, typval_T *rettv)
{
// Possible scope of working directory to return.
- CdScope scope = MIN_CD_SCOPE;
+ CdScope scope = kCdScopeInvalid;
// Numbers of the scope objects (window, tab) we want the working directory
// of. A `-1` means to skip this scope, a `0` means the current object.
@@ -10805,25 +10926,26 @@ static void f_haslocaldir(typval_T *argvars, typval_T *rettv)
return;
}
scope_number[i] = argvars[i].vval.v_number;
- // The scope is the current iteration step.
- scope = i;
if (scope_number[i] < -1) {
EMSG(_(e_invarg));
return;
}
+ // Use the narrowest scope the user requested
+ if (scope_number[i] >= 0 && scope == kCdScopeInvalid) {
+ // The scope is the current iteration step.
+ scope = i;
+ } else if (scope_number[i] < 0) {
+ scope = i + 1;
+ }
}
- // Normalize scope, the number of the new scope will be 0.
- if (scope_number[scope] < 0) {
- // Arguments to `haslocaldir` always end at second-highest scope, so scope
- // will always be <= `MAX_CD_SCOPE`.
- scope++;
+ // If the user didn't specify anything, default to window scope
+ if (scope == kCdScopeInvalid) {
+ scope = MIN_CD_SCOPE;
}
// Find the tabpage by number
- if (scope_number[kCdScopeTab] == -1) {
- tp = NULL;
- } else if (scope_number[kCdScopeTab] > 0) {
+ if (scope_number[kCdScopeTab] > 0) {
tp = find_tabpage(scope_number[kCdScopeTab]);
if (!tp) {
EMSG(_("5000: Cannot find tab number."));
@@ -10832,16 +10954,14 @@ static void f_haslocaldir(typval_T *argvars, typval_T *rettv)
}
// Find the window in `tp` by number, `NULL` if none.
- if (scope_number[kCdScopeWindow] == -1) {
- win = NULL;
- } else if (scope_number[kCdScopeWindow] >= 0) {
- if (!tp) {
+ if (scope_number[kCdScopeWindow] >= 0) {
+ if (scope_number[kCdScopeTab] < 0) {
EMSG(_("E5001: Higher scope cannot be -1 if lower scope is >= 0."));
return;
}
if (scope_number[kCdScopeWindow] > 0) {
- win = find_win_by_nr(&argvars[0], curtab);
+ win = find_win_by_nr(&argvars[0], tp);
if (!win) {
EMSG(_("E5002: Cannot find window number."));
return;
@@ -10862,6 +10982,9 @@ static void f_haslocaldir(typval_T *argvars, typval_T *rettv)
// The global scope never has a local directory
rettv->vval.v_number = 0;
break;
+ case kCdScopeInvalid:
+ // We should never get here
+ assert(false);
}
}
@@ -11671,8 +11794,21 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv)
dict_T *job_opts = NULL;
ufunc_T *on_stdout = NULL, *on_stderr = NULL, *on_exit = NULL;
+ char *cwd = NULL;
if (argvars[1].v_type == VAR_DICT) {
job_opts = argvars[1].vval.v_dict;
+
+ char *new_cwd = (char *)get_dict_string(job_opts, (char_u *)"cwd", false);
+ if (new_cwd && strlen(new_cwd) > 0) {
+ cwd = new_cwd;
+ // The new cwd must be a directory.
+ if (!os_isdir((char_u *)cwd)) {
+ EMSG2(_(e_invarg2), "expected valid directory");
+ shell_free_argv(argv);
+ return;
+ }
+ }
+
if (!common_job_callbacks(job_opts, &on_stdout, &on_stderr, &on_exit)) {
shell_free_argv(argv);
return;
@@ -11682,7 +11818,7 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv)
bool pty = job_opts && get_dict_number(job_opts, (uint8_t *)"pty") != 0;
bool detach = job_opts && get_dict_number(job_opts, (uint8_t *)"detach") != 0;
TerminalJobData *data = common_job_init(argv, on_stdout, on_stderr, on_exit,
- job_opts, pty, detach);
+ job_opts, pty, detach, cwd);
Process *proc = (Process *)&data->proc;
if (pty) {
@@ -11757,7 +11893,7 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv)
list_T *rv = list_alloc();
ui_busy_start();
- Queue *waiting_jobs = queue_new_parent(loop_on_put, &loop);
+ Queue *waiting_jobs = queue_new_parent(loop_on_put, &main_loop);
// For each item in the input list append an integer to the output list. -3
// is used to represent an invalid job id, -2 is for a interrupted job and
// -1 for jobs that were skipped or timed out.
@@ -11835,7 +11971,7 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv)
}
// restore the parent queue for the job
queue_process_events(data->events);
- queue_replace_parent(data->events, loop.events);
+ queue_replace_parent(data->events, main_loop.events);
}
queue_free(waiting_jobs);
@@ -14446,6 +14582,38 @@ static void f_setcmdpos(typval_T *argvars, typval_T *rettv)
rettv->vval.v_number = set_cmdline_pos(pos);
}
+
+/// "setfperm({fname}, {mode})" function
+static void f_setfperm(typval_T *argvars, typval_T *rettv)
+{
+ rettv->vval.v_number = 0;
+
+ char_u *fname = get_tv_string_chk(&argvars[0]);
+ if (fname == NULL) {
+ return;
+ }
+
+ char_u modebuf[NUMBUFLEN];
+ char_u *mode_str = get_tv_string_buf_chk(&argvars[1], modebuf);
+ if (mode_str == NULL) {
+ return;
+ }
+ if (STRLEN(mode_str) != 9) {
+ EMSG2(_(e_invarg2), mode_str);
+ return;
+ }
+
+ int mask = 1;
+ int mode = 0;
+ for (int i = 8; i >= 0; i--) {
+ if (mode_str[i] != '-') {
+ mode |= mask;
+ }
+ mask = mask << 1;
+ }
+ rettv->vval.v_number = os_setperm(fname, mode) == OK;
+}
+
/*
* "setline()" function
*/
@@ -14804,7 +14972,8 @@ static void f_setreg(typval_T *argvars, typval_T *rettv)
}
*curval++ = NULL;
- write_reg_contents_lst(regname, lstval, -1, append, yank_type, block_len);
+ write_reg_contents_lst(regname, lstval, STRLEN(lstval),
+ append, yank_type, block_len);
free_lstval:
while (curallocval > allocval)
@@ -14815,7 +14984,8 @@ free_lstval:
if (strval == NULL) {
return;
}
- write_reg_contents_ex(regname, strval, -1, append, yank_type, block_len);
+ write_reg_contents_ex(regname, strval, STRLEN(strval),
+ append, yank_type, block_len);
}
rettv->vval.v_number = 0;
}
@@ -14991,13 +15161,18 @@ typedef struct {
int idx;
} sortItem_T;
-static int item_compare_ic;
-static bool item_compare_numeric;
-static bool item_compare_numbers;
-static bool item_compare_float;
-static char_u *item_compare_func;
-static dict_T *item_compare_selfdict;
-static int item_compare_func_err;
+/// struct storing information about current sort
+typedef struct {
+ int item_compare_ic;
+ bool item_compare_numeric;
+ bool item_compare_numbers;
+ bool item_compare_float;
+ char_u *item_compare_func;
+ dict_T *item_compare_selfdict;
+ int item_compare_func_err;
+} sortinfo_T;
+static sortinfo_T *sortinfo = NULL;
+
#define ITEM_COMPARE_FAIL 999
/*
@@ -15017,14 +15192,14 @@ static int item_compare(const void *s1, const void *s2, bool keep_zero)
typval_T *tv1 = &si1->item->li_tv;
typval_T *tv2 = &si2->item->li_tv;
- if (item_compare_numbers) {
+ if (sortinfo->item_compare_numbers) {
long v1 = get_tv_number(tv1);
long v2 = get_tv_number(tv2);
return v1 == v2 ? 0 : v1 > v2 ? 1 : -1;
}
- if (item_compare_float) {
+ if (sortinfo->item_compare_float) {
float_T v1 = get_tv_float(tv1);
float_T v2 = get_tv_float(tv2);
@@ -15035,7 +15210,7 @@ static int item_compare(const void *s1, const void *s2, bool keep_zero)
// do that for string variables. Use a single quote when comparing with
// a non-string to do what the docs promise.
if (tv1->v_type == VAR_STRING) {
- if (tv2->v_type != VAR_STRING || item_compare_numeric) {
+ if (tv2->v_type != VAR_STRING || sortinfo->item_compare_numeric) {
p1 = (char_u *)"'";
} else {
p1 = tv1->vval.v_string;
@@ -15044,7 +15219,7 @@ static int item_compare(const void *s1, const void *s2, bool keep_zero)
tofree1 = p1 = (char_u *) encode_tv2string(tv1, NULL);
}
if (tv2->v_type == VAR_STRING) {
- if (tv1->v_type != VAR_STRING || item_compare_numeric) {
+ if (tv1->v_type != VAR_STRING || sortinfo->item_compare_numeric) {
p2 = (char_u *)"'";
} else {
p2 = tv2->vval.v_string;
@@ -15052,12 +15227,14 @@ static int item_compare(const void *s1, const void *s2, bool keep_zero)
} else {
tofree2 = p2 = (char_u *) encode_tv2string(tv2, NULL);
}
- if (p1 == NULL)
+ if (p1 == NULL) {
p1 = (char_u *)"";
- if (p2 == NULL)
+ }
+ if (p2 == NULL) {
p2 = (char_u *)"";
- if (!item_compare_numeric) {
- if (item_compare_ic) {
+ }
+ if (!sortinfo->item_compare_numeric) {
+ if (sortinfo->item_compare_ic) {
res = STRICMP(p1, p2);
} else {
res = STRCMP(p1, p2);
@@ -15098,9 +15275,10 @@ static int item_compare2(const void *s1, const void *s2, bool keep_zero)
typval_T argv[3];
int dummy;
- /* shortcut after failure in previous call; compare all items equal */
- if (item_compare_func_err)
+ // shortcut after failure in previous call; compare all items equal
+ if (sortinfo->item_compare_func_err) {
return 0;
+ }
si1 = (sortItem_T *)s1;
si2 = (sortItem_T *)s2;
@@ -15110,19 +15288,22 @@ static int item_compare2(const void *s1, const void *s2, bool keep_zero)
copy_tv(&si1->item->li_tv, &argv[0]);
copy_tv(&si2->item->li_tv, &argv[1]);
- rettv.v_type = VAR_UNKNOWN; /* clear_tv() uses this */
- res = call_func(item_compare_func, (int)STRLEN(item_compare_func),
- &rettv, 2, argv, 0L, 0L, &dummy, TRUE,
- item_compare_selfdict);
+ rettv.v_type = VAR_UNKNOWN; // clear_tv() uses this
+ res = call_func(sortinfo->item_compare_func,
+ (int)STRLEN(sortinfo->item_compare_func),
+ &rettv, 2, argv, 0L, 0L, &dummy, true,
+ sortinfo->item_compare_selfdict);
clear_tv(&argv[0]);
clear_tv(&argv[1]);
- if (res == FAIL)
+ if (res == FAIL) {
res = ITEM_COMPARE_FAIL;
- else
- res = get_tv_number_chk(&rettv, &item_compare_func_err);
- if (item_compare_func_err)
- res = ITEM_COMPARE_FAIL; /* return value has wrong type */
+ } else {
+ res = get_tv_number_chk(&rettv, &sortinfo->item_compare_func_err);
+ }
+ if (sortinfo->item_compare_func_err) {
+ res = ITEM_COMPARE_FAIL; // return value has wrong type
+ }
clear_tv(&rettv);
// When the result would be zero, compare the pointers themselves. Makes
@@ -15155,6 +15336,12 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort)
long len;
long i;
+ // Pointer to current info struct used in compare function. Save and restore
+ // the current one for nested calls.
+ sortinfo_T info;
+ sortinfo_T *old_sortinfo = sortinfo;
+ sortinfo = &info;
+
if (argvars[0].v_type != VAR_LIST) {
EMSG2(_(e_listarg), sort ? "sort()" : "uniq()");
} else {
@@ -15165,61 +15352,70 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort)
? N_("sort() argument")
: N_("uniq() argument")),
true)) {
- return;
+ goto theend;
}
rettv->vval.v_list = l;
rettv->v_type = VAR_LIST;
++l->lv_refcount;
len = list_len(l);
- if (len <= 1)
- return; /* short list sorts pretty quickly */
+ if (len <= 1) {
+ goto theend; // short list sorts pretty quickly
+ }
- item_compare_ic = FALSE;
- item_compare_numeric = false;
- item_compare_numbers = false;
- item_compare_float = false;
- item_compare_func = NULL;
- item_compare_selfdict = NULL;
+ info.item_compare_ic = false;
+ info.item_compare_numeric = false;
+ info.item_compare_numbers = false;
+ info.item_compare_float = false;
+ info.item_compare_func = NULL;
+ info.item_compare_selfdict = NULL;
if (argvars[1].v_type != VAR_UNKNOWN) {
/* optional second argument: {func} */
if (argvars[1].v_type == VAR_FUNC) {
- item_compare_func = argvars[1].vval.v_string;
+ info.item_compare_func = argvars[1].vval.v_string;
} else {
int error = FALSE;
i = get_tv_number_chk(&argvars[1], &error);
- if (error)
- return; /* type error; errmsg already given */
- if (i == 1)
- item_compare_ic = TRUE;
- else
- item_compare_func = get_tv_string(&argvars[1]);
- if (item_compare_func != NULL) {
- if (STRCMP(item_compare_func, "n") == 0) {
- item_compare_func = NULL;
- item_compare_numeric = true;
- } else if (STRCMP(item_compare_func, "N") == 0) {
- item_compare_func = NULL;
- item_compare_numbers = true;
- } else if (STRCMP(item_compare_func, "f") == 0) {
- item_compare_func = NULL;
- item_compare_float = true;
- } else if (STRCMP(item_compare_func, "i") == 0) {
- item_compare_func = NULL;
- item_compare_ic = TRUE;
+ if (error) {
+ goto theend; // type error; errmsg already given
+ }
+ if (i == 1) {
+ info.item_compare_ic = true;
+ } else if (argvars[1].v_type != VAR_NUMBER) {
+ info.item_compare_func = get_tv_string(&argvars[1]);
+ } else if (i != 0) {
+ EMSG(_(e_invarg));
+ goto theend;
+ }
+ if (info.item_compare_func != NULL) {
+ if (*info.item_compare_func == NUL) {
+ // empty string means default sort
+ info.item_compare_func = NULL;
+ } else if (STRCMP(info.item_compare_func, "n") == 0) {
+ info.item_compare_func = NULL;
+ info.item_compare_numeric = true;
+ } else if (STRCMP(info.item_compare_func, "N") == 0) {
+ info.item_compare_func = NULL;
+ info.item_compare_numbers = true;
+ } else if (STRCMP(info.item_compare_func, "f") == 0) {
+ info.item_compare_func = NULL;
+ info.item_compare_float = true;
+ } else if (STRCMP(info.item_compare_func, "i") == 0) {
+ info.item_compare_func = NULL;
+ info.item_compare_ic = true;
}
}
}
if (argvars[2].v_type != VAR_UNKNOWN) {
- /* optional third argument: {dict} */
+ // optional third argument: {dict}
if (argvars[2].v_type != VAR_DICT) {
EMSG(_(e_dictreq));
- return;
+ goto theend;
}
- item_compare_selfdict = argvars[2].vval.v_dict;
+ info.item_compare_selfdict = argvars[2].vval.v_dict;
}
}
@@ -15235,19 +15431,20 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort)
i++;
}
- item_compare_func_err = FALSE;
+ info.item_compare_func_err = false;
// Test the compare function.
- if (item_compare_func != NULL
+ if (info.item_compare_func != NULL
&& item_compare2_not_keeping_zero(&ptrs[0], &ptrs[1])
== ITEM_COMPARE_FAIL) {
EMSG(_("E702: Sort compare function failed"));
} else {
// Sort the array with item pointers.
qsort(ptrs, (size_t)len, sizeof (sortItem_T),
- item_compare_func == NULL ? item_compare_not_keeping_zero :
- item_compare2_not_keeping_zero);
+ (info.item_compare_func == NULL ?
+ item_compare_not_keeping_zero :
+ item_compare2_not_keeping_zero));
- if (!item_compare_func_err) {
+ if (!info.item_compare_func_err) {
// Clear the list and append the items in the sorted order.
l->lv_first = NULL;
l->lv_last = NULL;
@@ -15263,21 +15460,24 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort)
int (*item_compare_func_ptr)(const void *, const void *);
// f_uniq(): ptrs will be a stack of items to remove.
- item_compare_func_err = FALSE;
- item_compare_func_ptr = item_compare_func ? item_compare2_keeping_zero :
- item_compare_keeping_zero;
+ info.item_compare_func_err = false;
+ if (info.item_compare_func != NULL) {
+ item_compare_func_ptr = item_compare2_keeping_zero;
+ } else {
+ item_compare_func_ptr = item_compare_keeping_zero;
+ }
for (li = l->lv_first; li != NULL && li->li_next != NULL; li = li->li_next) {
if (item_compare_func_ptr(&li, &li->li_next) == 0) {
ptrs[i++].item = li;
}
- if (item_compare_func_err) {
+ if (info.item_compare_func_err) {
EMSG(_("E882: Uniq compare function failed"));
break;
}
}
- if (!item_compare_func_err) {
+ if (!info.item_compare_func_err) {
while (--i >= 0) {
assert(ptrs[i].item->li_next);
li = ptrs[i].item->li_next;
@@ -15296,6 +15496,9 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort)
xfree(ptrs);
}
+
+theend:
+ sortinfo = old_sortinfo;
}
/// "sort"({list})" function
@@ -16332,8 +16535,21 @@ static void f_termopen(typval_T *argvars, typval_T *rettv)
ufunc_T *on_stdout = NULL, *on_stderr = NULL, *on_exit = NULL;
dict_T *job_opts = NULL;
+ char *cwd = ".";
if (argvars[1].v_type == VAR_DICT) {
job_opts = argvars[1].vval.v_dict;
+
+ char *new_cwd = (char *)get_dict_string(job_opts, (char_u *)"cwd", false);
+ if (new_cwd && strlen(new_cwd) > 0) {
+ cwd = new_cwd;
+ // The new cwd must be a directory.
+ if (!os_isdir((char_u *)cwd)) {
+ EMSG2(_(e_invarg2), "expected valid directory");
+ shell_free_argv(argv);
+ return;
+ }
+ }
+
if (!common_job_callbacks(job_opts, &on_stdout, &on_stderr, &on_exit)) {
shell_free_argv(argv);
return;
@@ -16341,7 +16557,7 @@ static void f_termopen(typval_T *argvars, typval_T *rettv)
}
TerminalJobData *data = common_job_init(argv, on_stdout, on_stderr, on_exit,
- job_opts, true, false);
+ job_opts, true, false, cwd);
data->proc.pty.width = curwin->w_width;
data->proc.pty.height = curwin->w_height;
data->proc.pty.term_name = xstrdup("xterm-256color");
@@ -16356,11 +16572,6 @@ static void f_termopen(typval_T *argvars, typval_T *rettv)
topts.resize_cb = term_resize;
topts.close_cb = term_close;
- char *cwd = ".";
- if (argvars[1].v_type == VAR_STRING
- && os_isdir(argvars[1].vval.v_string)) {
- cwd = (char *)argvars[1].vval.v_string;
- }
int pid = data->proc.pty.process.pid;
char buf[1024];
@@ -16408,6 +16619,127 @@ static void f_tanh(typval_T *argvars, typval_T *rettv)
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(&main_loop, &timer->tw, timer);
+ timer->tw.events = queue_new_child(main_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 (timer->stopped) {
+ return;
+ }
+ // 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);
+}
+
+void timer_teardown(void)
+{
+ timer_T *timer;
+ map_foreach_value(timers, timer, {
+ timer_stop(timer);
+ })
+}
+
/*
* "tolower(string)" function
*/
@@ -17507,7 +17839,8 @@ void set_vim_var_special(const VimVarIndex idx, const SpecialVarValue val)
void set_vim_var_string(const VimVarIndex idx, const char *const val,
const ptrdiff_t len)
{
- xfree(vimvars[idx].vv_str);
+ clear_tv(&vimvars[idx].vv_di.di_tv);
+ vimvars[idx].vv_type = VAR_STRING;
if (val == NULL) {
vimvars[idx].vv_str = NULL;
} else if (len == -1) {
@@ -17523,7 +17856,8 @@ void set_vim_var_string(const VimVarIndex idx, const char *const val,
/// @param[in,out] val Value to set to. Reference count will be incremented.
void set_vim_var_list(const VimVarIndex idx, list_T *const val)
{
- list_unref(vimvars[idx].vv_list);
+ clear_tv(&vimvars[idx].vv_di.di_tv);
+ vimvars[idx].vv_type = VAR_LIST;
vimvars[idx].vv_list = val;
if (val != NULL) {
val->lv_refcount++;
@@ -17537,7 +17871,8 @@ void set_vim_var_list(const VimVarIndex idx, list_T *const val)
/// Also keys of the dictionary will be made read-only.
void set_vim_var_dict(const VimVarIndex idx, dict_T *const val)
{
- dict_unref(vimvars[idx].vv_dict);
+ clear_tv(&vimvars[idx].vv_di.di_tv);
+ vimvars[idx].vv_type = VAR_DICT;
vimvars[idx].vv_dict = val;
if (val != NULL) {
@@ -17814,45 +18149,147 @@ void free_tv(typval_T *varp)
}
}
-/*
- * Free the memory for a variable value and set the value to NULL or 0.
- */
+#define TYPVAL_ENCODE_ALLOW_SPECIALS false
+
+#define TYPVAL_ENCODE_CONV_NIL() \
+ do { \
+ tv->vval.v_special = kSpecialVarFalse; \
+ tv->v_lock = VAR_UNLOCKED; \
+ } while (0)
+
+#define TYPVAL_ENCODE_CONV_BOOL(ignored) \
+ TYPVAL_ENCODE_CONV_NIL()
+
+#define TYPVAL_ENCODE_CONV_NUMBER(ignored) \
+ do { \
+ (void)ignored; \
+ tv->vval.v_number = 0; \
+ tv->v_lock = VAR_UNLOCKED; \
+ } while (0)
+
+#define TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER(ignored) \
+ assert(false)
+
+#define TYPVAL_ENCODE_CONV_FLOAT(ignored) \
+ do { \
+ tv->vval.v_float = 0; \
+ tv->v_lock = VAR_UNLOCKED; \
+ } while (0)
+
+#define TYPVAL_ENCODE_CONV_STRING(str, ignored) \
+ do { \
+ xfree(str); \
+ tv->vval.v_string = NULL; \
+ tv->v_lock = VAR_UNLOCKED; \
+ } while (0)
+
+#define TYPVAL_ENCODE_CONV_STR_STRING(ignored1, ignored2)
+
+#define TYPVAL_ENCODE_CONV_EXT_STRING(ignored1, ignored2, ignored3)
+
+#define TYPVAL_ENCODE_CONV_FUNC(fun) \
+ do { \
+ func_unref(fun); \
+ if (fun != empty_string) { \
+ xfree(fun); \
+ } \
+ tv->vval.v_string = NULL; \
+ tv->v_lock = VAR_UNLOCKED; \
+ } while (0)
+
+#define TYPVAL_ENCODE_CONV_EMPTY_LIST() \
+ do { \
+ list_unref(tv->vval.v_list); \
+ tv->vval.v_list = NULL; \
+ tv->v_lock = VAR_UNLOCKED; \
+ } while (0)
+
+#define TYPVAL_ENCODE_CONV_EMPTY_DICT() \
+ do { \
+ dict_unref(tv->vval.v_dict); \
+ tv->vval.v_dict = NULL; \
+ tv->v_lock = VAR_UNLOCKED; \
+ } while (0)
+
+#define TYPVAL_ENCODE_CONV_LIST_START(ignored) \
+ do { \
+ if (tv->vval.v_list->lv_refcount > 1) { \
+ tv->vval.v_list->lv_refcount--; \
+ tv->vval.v_list = NULL; \
+ tv->v_lock = VAR_UNLOCKED; \
+ return OK; \
+ } \
+ } while (0)
+
+#define TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS()
+
+#define TYPVAL_ENCODE_CONV_LIST_END() \
+ do { \
+ typval_T *const cur_tv = cur_mpsv->tv; \
+ assert(cur_tv->v_type == VAR_LIST); \
+ list_unref(cur_tv->vval.v_list); \
+ cur_tv->vval.v_list = NULL; \
+ cur_tv->v_lock = VAR_UNLOCKED; \
+ } while (0)
+
+#define TYPVAL_ENCODE_CONV_DICT_START(ignored) \
+ do { \
+ if (tv->vval.v_dict->dv_refcount > 1) { \
+ tv->vval.v_dict->dv_refcount--; \
+ tv->vval.v_dict = NULL; \
+ tv->v_lock = VAR_UNLOCKED; \
+ return OK; \
+ } \
+ } while (0)
+
+#define TYPVAL_ENCODE_CONV_SPECIAL_DICT_KEY_CHECK(ignored1, ignored2)
+
+#define TYPVAL_ENCODE_CONV_DICT_AFTER_KEY()
+
+#define TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS()
+
+#define TYPVAL_ENCODE_CONV_DICT_END() \
+ do { \
+ typval_T *const cur_tv = cur_mpsv->tv; \
+ assert(cur_tv->v_type == VAR_DICT); \
+ dict_unref(cur_tv->vval.v_dict); \
+ cur_tv->vval.v_dict = NULL; \
+ cur_tv->v_lock = VAR_UNLOCKED; \
+ } while (0)
+
+#define TYPVAL_ENCODE_CONV_RECURSE(ignored1, ignored2)
+
+TYPVAL_ENCODE_DEFINE_CONV_FUNCTIONS(static, nothing, void *, ignored)
+
+#undef TYPVAL_ENCODE_ALLOW_SPECIALS
+#undef TYPVAL_ENCODE_CONV_NIL
+#undef TYPVAL_ENCODE_CONV_BOOL
+#undef TYPVAL_ENCODE_CONV_NUMBER
+#undef TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER
+#undef TYPVAL_ENCODE_CONV_FLOAT
+#undef TYPVAL_ENCODE_CONV_STRING
+#undef TYPVAL_ENCODE_CONV_STR_STRING
+#undef TYPVAL_ENCODE_CONV_EXT_STRING
+#undef TYPVAL_ENCODE_CONV_FUNC
+#undef TYPVAL_ENCODE_CONV_EMPTY_LIST
+#undef TYPVAL_ENCODE_CONV_EMPTY_DICT
+#undef TYPVAL_ENCODE_CONV_LIST_START
+#undef TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS
+#undef TYPVAL_ENCODE_CONV_LIST_END
+#undef TYPVAL_ENCODE_CONV_DICT_START
+#undef TYPVAL_ENCODE_CONV_SPECIAL_DICT_KEY_CHECK
+#undef TYPVAL_ENCODE_CONV_DICT_AFTER_KEY
+#undef TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS
+#undef TYPVAL_ENCODE_CONV_DICT_END
+#undef TYPVAL_ENCODE_CONV_RECURSE
+
+/// Free memory for a variable value and set the value to NULL or 0
+///
+/// @param[in,out] varp Value to free.
void clear_tv(typval_T *varp)
{
- if (varp != NULL) {
- switch (varp->v_type) {
- case VAR_FUNC:
- func_unref(varp->vval.v_string);
- if (varp->vval.v_string != empty_string) {
- xfree(varp->vval.v_string);
- }
- varp->vval.v_string = NULL;
- break;
- case VAR_STRING:
- xfree(varp->vval.v_string);
- varp->vval.v_string = NULL;
- break;
- case VAR_LIST:
- list_unref(varp->vval.v_list);
- varp->vval.v_list = NULL;
- break;
- case VAR_DICT:
- dict_unref(varp->vval.v_dict);
- varp->vval.v_dict = NULL;
- break;
- case VAR_NUMBER:
- varp->vval.v_number = 0;
- break;
- case VAR_FLOAT:
- varp->vval.v_float = 0.0;
- break;
- case VAR_SPECIAL:
- varp->vval.v_special = kSpecialVarFalse;
- break;
- case VAR_UNKNOWN:
- break;
- }
- varp->v_lock = 0;
+ if (varp != NULL && varp->v_type != VAR_UNKNOWN) {
+ encode_vim_to_nothing(varp, varp, "clear_tv argument");
}
}
@@ -18123,6 +18560,25 @@ static dictitem_T *find_var_in_ht(hashtab_T *ht, int htname, char_u *varname, in
return HI2DI(hi);
}
+// Get function call environment based on backtrace debug level
+static funccall_T *get_funccal(void)
+{
+ funccall_T *funccal = current_funccal;
+ if (debug_backtrace_level > 0) {
+ for (int i = 0; i < debug_backtrace_level; i++) {
+ funccall_T *temp_funccal = funccal->caller;
+ if (temp_funccal) {
+ funccal = temp_funccal;
+ } else {
+ // backtrace level overflow. reset to max
+ debug_backtrace_level = i;
+ }
+ }
+ }
+
+ return funccal;
+}
+
// Find the dict and hashtable used for a variable name. Set "varname" to the
// start of name without ':'.
static hashtab_T *find_var_ht_dict(char_u *name, uint8_t **varname, dict_T **d)
@@ -18147,7 +18603,11 @@ static hashtab_T *find_var_ht_dict(char_u *name, uint8_t **varname, dict_T **d)
return &compat_hashtab;
}
- *d = current_funccal ? &current_funccal->l_vars : &globvardict;
+ if (current_funccal == NULL) {
+ *d = &globvardict;
+ } else {
+ *d = &get_funccal()->l_vars; // l: variable
+ }
goto end;
}
@@ -18169,9 +18629,9 @@ static hashtab_T *find_var_ht_dict(char_u *name, uint8_t **varname, dict_T **d)
} else if (*name == 'v') { // v: variable
*d = &vimvardict;
} else if (*name == 'a' && current_funccal != NULL) { // function argument
- *d = &current_funccal->l_avars;
+ *d = &get_funccal()->l_avars;
} else if (*name == 'l' && current_funccal != NULL) { // local variable
- *d = &current_funccal->l_vars;
+ *d = &get_funccal()->l_vars;
} else if (*name == 's' // script variable
&& current_SID > 0 && current_SID <= ga_scripts.ga_len) {
*d = &SCRIPT_SV(current_SID)->sv_dict;
@@ -21481,7 +21941,8 @@ static inline TerminalJobData *common_job_init(char **argv,
ufunc_T *on_exit,
dict_T *self,
bool pty,
- bool detach)
+ bool detach,
+ char *cwd)
{
TerminalJobData *data = xcalloc(1, sizeof(TerminalJobData));
data->stopped = false;
@@ -21489,11 +21950,11 @@ static inline TerminalJobData *common_job_init(char **argv,
data->on_stderr = on_stderr;
data->on_exit = on_exit;
data->self = self;
- data->events = queue_new_child(loop.events);
+ data->events = queue_new_child(main_loop.events);
if (pty) {
- data->proc.pty = pty_process_init(&loop, data);
+ data->proc.pty = pty_process_init(&main_loop, data);
} else {
- data->proc.uv = libuv_process_init(&loop, data);
+ data->proc.uv = libuv_process_init(&main_loop, data);
}
Process *proc = (Process *)&data->proc;
proc->argv = argv;
@@ -21505,6 +21966,7 @@ static inline TerminalJobData *common_job_init(char **argv,
proc->cb = on_process_exit;
proc->events = data->events;
proc->detach = detach;
+ proc->cwd = cwd;
return data;
}
@@ -21591,7 +22053,7 @@ static inline void free_term_job_data(TerminalJobData *data)
{
// data->queue may still be used after this function returns(process_wait), so
// only free in the next event loop iteration
- queue_put(loop.fast_events, free_term_job_data_event, 1, data);
+ queue_put(main_loop.fast_events, free_term_job_data_event, 1, data);
}
// vimscript job callbacks must be executed on Nvim main loop
@@ -21701,6 +22163,18 @@ static void term_resize(uint16_t width, uint16_t height, void *d)
pty_process_resize(&data->proc.pty, width, height);
}
+static inline void term_delayed_free(void **argv)
+{
+ TerminalJobData *j = argv[0];
+ if (j->in.pending_reqs || j->out.pending_reqs || j->err.pending_reqs) {
+ queue_put(j->events, term_delayed_free, 1, j);
+ return;
+ }
+
+ terminal_destroy(j->term);
+ term_job_data_decref(j);
+}
+
static void term_close(void *d)
{
TerminalJobData *data = d;
@@ -21708,8 +22182,7 @@ static void term_close(void *d)
data->exited = true;
process_stop((Process *)&data->proc);
}
- terminal_destroy(data->term);
- term_job_data_decref(d);
+ queue_put(data->events, term_delayed_free, 1, data);
}
static void term_job_data_decref(TerminalJobData *data)
@@ -21784,6 +22257,7 @@ static void script_host_eval(char *name, typval_T *argvars, typval_T *rettv)
if (argvars[0].v_type != VAR_STRING) {
EMSG(_(e_invarg));
+ return;
}
list_T *args = list_alloc();
@@ -21841,14 +22315,13 @@ typval_T eval_call_provider(char *provider, char *method, list_T *arguments)
bool eval_has_provider(char *name)
{
-
-#define check_provider(name) \
- if (has_##name == -1) { \
- has_##name = !!find_func((uint8_t *)"provider#" #name "#Call"); \
- if (!has_##name) { \
- script_autoload((uint8_t *)"provider#" #name "#Call", false); \
- has_##name = !!find_func((uint8_t *)"provider#" #name "#Call"); \
- } \
+#define check_provider(name) \
+ if (has_##name == -1) { \
+ has_##name = !!find_func((uint8_t *)"provider#" #name "#Call"); \
+ if (!has_##name) { \
+ script_autoload((uint8_t *)"provider#" #name "#Call", false); \
+ has_##name = !!find_func((uint8_t *)"provider#" #name "#Call"); \
+ } \
}
static int has_clipboard = -1, has_python = -1, has_python3 = -1;
@@ -21867,9 +22340,8 @@ bool eval_has_provider(char *name)
return false;
}
-// Compute the `DictWatcher` address from a QUEUE node. This only exists because
-// ASAN doesn't handle `QUEUE_DATA` pointer arithmetic, and we blacklist this
-// function on .asan-blacklist.
+// Compute the `DictWatcher` address from a QUEUE node. This only exists for
+// .asan-blacklist (ASAN doesn't handle QUEUE_DATA pointer arithmetic).
static DictWatcher *dictwatcher_node_data(QUEUE *q)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET
{