aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/eval
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim/eval')
-rw-r--r--src/nvim/eval/funcs.c519
-rw-r--r--src/nvim/eval/typval.c39
-rw-r--r--src/nvim/eval/typval.h1
-rw-r--r--src/nvim/eval/userfunc.c13
4 files changed, 273 insertions, 299 deletions
diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c
index 01d23654de..911b3c2c2f 100644
--- a/src/nvim/eval/funcs.c
+++ b/src/nvim/eval/funcs.c
@@ -391,28 +391,16 @@ static void f_argv(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
+// "assert_beeps(cmd [, error])" function
static void f_assert_beeps(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- const char *const cmd = tv_get_string_chk(&argvars[0]);
- garray_T ga;
- int ret = 0;
-
- called_vim_beep = false;
- suppress_errthrow = true;
- emsg_silent = false;
- do_cmdline_cmd(cmd);
- if (!called_vim_beep) {
- prepare_assert_error(&ga);
- ga_concat(&ga, (const char_u *)"command did not beep: ");
- ga_concat(&ga, (const char_u *)cmd);
- assert_error(&ga);
- ga_clear(&ga);
- ret = 1;
- }
+ rettv->vval.v_number = assert_beeps(argvars, false);
+}
- suppress_errthrow = false;
- emsg_on_display = false;
- rettv->vval.v_number = ret;
+// "assert_nobeep(cmd [, error])" function
+static void f_assert_nobeep(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ rettv->vval.v_number = assert_beeps(argvars, true);
}
// "assert_equal(expected, actual[, msg])" function
@@ -614,12 +602,7 @@ static void f_bufname(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (argvars[0].v_type == VAR_UNKNOWN) {
buf = curbuf;
} else {
- if (!tv_check_str_or_nr(&argvars[0])) {
- return;
- }
- emsg_off++;
- buf = tv_get_buf(&argvars[0], false);
- emsg_off--;
+ buf = tv_get_buf_from_arg(&argvars[0]);
}
if (buf != NULL && buf->b_fname != NULL) {
rettv->vval.v_string = (char_u *)xstrdup((char *)buf->b_fname);
@@ -639,6 +622,9 @@ static void f_bufnr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (argvars[0].v_type == VAR_UNKNOWN) {
buf = curbuf;
} else {
+ // Don't use tv_get_buf_from_arg(); we continue if the buffer wasn't found
+ // and the second argument isn't zero, but we want to return early if the
+ // first argument isn't a string or number so only one error is shown.
if (!tv_check_str_or_nr(&argvars[0])) {
return;
}
@@ -665,18 +651,12 @@ static void f_bufnr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
static void buf_win_common(typval_T *argvars, typval_T *rettv, bool get_nr)
{
- if (!tv_check_str_or_nr(&argvars[0])) {
+ const buf_T *const buf = tv_get_buf_from_arg(&argvars[0]);
+ if (buf == NULL) { // no need to search if invalid arg or buffer not found
rettv->vval.v_number = -1;
return;
}
- emsg_off++;
- buf_T *buf = tv_get_buf(&argvars[0], true);
- if (buf == NULL) { // no need to search if buffer was not found
- rettv->vval.v_number = -1;
- goto end;
- }
-
int winnr = 0;
int winid;
bool found_buf = false;
@@ -689,8 +669,6 @@ static void buf_win_common(typval_T *argvars, typval_T *rettv, bool get_nr)
}
}
rettv->vval.v_number = (found_buf ? (get_nr ? winnr : winid) : -1);
-end:
- emsg_off--;
}
/// "bufwinid(nr)" function
@@ -743,6 +721,18 @@ buf_T *tv_get_buf(typval_T *tv, int curtab_only)
return buf;
}
+/// Like tv_get_buf() but give an error message if the type is wrong.
+buf_T *tv_get_buf_from_arg(typval_T *const tv) FUNC_ATTR_NONNULL_ALL
+{
+ if (!tv_check_str_or_nr(tv)) {
+ return NULL;
+ }
+ emsg_off++;
+ buf_T *const buf = tv_get_buf(tv, false);
+ emsg_off--;
+ return buf;
+}
+
/// Get the buffer from "arg" and give an error and return NULL if it is not
/// valid.
buf_T * get_buf_arg(typval_T *arg)
@@ -822,6 +812,7 @@ static void f_call(typval_T *argvars, typval_T *rettv, FunPtr fptr)
return;
}
+ bool owned = false;
char_u *func;
partial_T *partial = NULL;
dict_T *selfdict = NULL;
@@ -832,6 +823,7 @@ static void f_call(typval_T *argvars, typval_T *rettv, FunPtr fptr)
func = partial_name(partial);
} else if (nlua_is_table_from_lua(&argvars[0])) {
func = nlua_register_table_as_callable(&argvars[0]);
+ owned = true;
} else {
func = (char_u *)tv_get_string(&argvars[0]);
}
@@ -849,6 +841,9 @@ static void f_call(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
func_call(func, &argvars[1], partial, selfdict, rettv);
+ if (owned) {
+ func_unref(func);
+ }
}
/*
@@ -923,7 +918,7 @@ static void f_chansend(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
uint64_t id = argvars[0].vval.v_number;
const char *error = NULL;
- rettv->vval.v_number = channel_send(id, input, input_len, &error);
+ rettv->vval.v_number = channel_send(id, input, input_len, true, &error);
if (error) {
EMSG(error);
}
@@ -1105,7 +1100,7 @@ static void f_complete(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_complete_add(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- rettv->vval.v_number = ins_compl_add_tv(&argvars[0], 0);
+ rettv->vval.v_number = ins_compl_add_tv(&argvars[0], 0, false);
}
/*
@@ -1958,8 +1953,8 @@ static char_u *get_list_line(int c, void *cookie, int indent, bool do_concat)
return (char_u *)(s == NULL ? NULL : xstrdup(s));
}
-// "execute(command)" function
-static void f_execute(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void execute_common(typval_T *argvars, typval_T *rettv, FunPtr fptr,
+ int arg_off)
{
const int save_msg_silent = msg_silent;
const int save_emsg_silent = emsg_silent;
@@ -1973,9 +1968,9 @@ static void f_execute(typval_T *argvars, typval_T *rettv, FunPtr fptr)
return;
}
- if (argvars[1].v_type != VAR_UNKNOWN) {
+ if (argvars[arg_off + 1].v_type != VAR_UNKNOWN) {
char buf[NUMBUFLEN];
- const char *const s = tv_get_string_buf_chk(&argvars[1], buf);
+ const char *const s = tv_get_string_buf_chk(&argvars[arg_off + 1], buf);
if (s == NULL) {
return;
@@ -2002,10 +1997,10 @@ static void f_execute(typval_T *argvars, typval_T *rettv, FunPtr fptr)
msg_col = 0; // prevent leading spaces
}
- if (argvars[0].v_type != VAR_LIST) {
- do_cmdline_cmd(tv_get_string(&argvars[0]));
- } else if (argvars[0].vval.v_list != NULL) {
- list_T *const list = argvars[0].vval.v_list;
+ if (argvars[arg_off].v_type != VAR_LIST) {
+ do_cmdline_cmd(tv_get_string(&argvars[arg_off]));
+ } else if (argvars[arg_off].vval.v_list != NULL) {
+ list_T *const list = argvars[arg_off].vval.v_list;
tv_list_ref(list);
GetListLineCookie cookie = {
.l = list,
@@ -2037,6 +2032,39 @@ static void f_execute(typval_T *argvars, typval_T *rettv, FunPtr fptr)
capture_ga = save_capture_ga;
}
+// "execute(command)" function
+static void f_execute(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ execute_common(argvars, rettv, fptr, 0);
+}
+
+// "win_execute(win_id, command)" function
+static void f_win_execute(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ tabpage_T *tp;
+ win_T *wp = win_id2wp_tp(argvars, &tp);
+ win_T *save_curwin;
+ tabpage_T *save_curtab;
+ // Return an empty string if something fails.
+ rettv->v_type = VAR_STRING;
+ rettv->vval.v_string = NULL;
+
+ if (wp != NULL && tp != NULL) {
+ pos_T curpos = wp->w_cursor;
+ if (switch_win_noblock(&save_curwin, &save_curtab, wp, tp, true) ==
+ OK) {
+ check_cursor();
+ execute_common(argvars, rettv, fptr, 1);
+ }
+ restore_win_noblock(save_curwin, save_curtab, true);
+
+ // Update the status line if the cursor moved.
+ if (win_valid(wp) && !equalpos(curpos, wp->w_cursor)) {
+ wp->w_redr_status = true;
+ }
+ }
+}
+
/// "exepath()" function
static void f_exepath(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
@@ -2058,7 +2086,6 @@ static void f_exepath(typval_T *argvars, typval_T *rettv, FunPtr fptr)
static void f_exists(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
int n = false;
- int len = 0;
const char *p = tv_get_string(&argvars[0]);
if (*p == '$') { // Environment variable.
@@ -2089,29 +2116,7 @@ static void f_exists(typval_T *argvars, typval_T *rettv, FunPtr fptr)
n = au_exists(p + 1);
}
} else { // Internal variable.
- typval_T tv;
-
- // get_name_len() takes care of expanding curly braces
- const char *name = p;
- char *tofree;
- len = get_name_len((const char **)&p, &tofree, true, false);
- if (len > 0) {
- if (tofree != NULL) {
- name = tofree;
- }
- n = (get_var_tv(name, len, &tv, NULL, false, true) == OK);
- if (n) {
- // Handle d.key, l[idx], f(expr).
- n = (handle_subscript(&p, &tv, true, false) == OK);
- if (n) {
- tv_clear(&tv);
- }
- }
- }
- if (*p != NUL)
- n = FALSE;
-
- xfree(tofree);
+ n = var_exists(p);
}
rettv->vval.v_number = n;
@@ -2829,13 +2834,9 @@ static void f_getbufinfo(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
} else if (argvars[0].v_type != VAR_UNKNOWN) {
// Information about one buffer. Argument specifies the buffer
- if (tv_check_num(&argvars[0])) { // issue errmsg if type error
- emsg_off++;
- argbuf = tv_get_buf(&argvars[0], false);
- emsg_off--;
- if (argbuf == NULL) {
- return;
- }
+ argbuf = tv_get_buf_from_arg(&argvars[0]);
+ if (argbuf == NULL) {
+ return;
}
}
@@ -2905,13 +2906,7 @@ static void get_buffer_lines(buf_T *buf,
*/
static void f_getbufline(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- buf_T *buf = NULL;
-
- if (tv_check_str_or_nr(&argvars[0])) {
- emsg_off++;
- buf = tv_get_buf(&argvars[0], false);
- emsg_off--;
- }
+ buf_T *const buf = tv_get_buf_from_arg(&argvars[0]);
const linenr_T lnum = tv_get_lnum_buf(&argvars[1], buf);
const linenr_T end = (argvars[2].v_type == VAR_UNKNOWN
@@ -3054,7 +3049,10 @@ static void f_getchar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
n = safe_vgetc();
}
- if (n == K_IGNORE || n == K_VER_SCROLLBAR || n == K_HOR_SCROLLBAR) {
+ if (n == K_IGNORE
+ || n == K_MOUSEMOVE
+ || n == K_VER_SCROLLBAR
+ || n == K_HOR_SCROLLBAR) {
continue;
}
break;
@@ -4312,6 +4310,7 @@ static void f_has(typval_T *argvars, typval_T *rettv, FunPtr fptr)
"title",
"user-commands", // was accidentally included in 5.4
"user_commands",
+ "vartabs",
"vertsplit",
"virtualedit",
"visual",
@@ -6528,6 +6527,26 @@ static void f_prompt_setinterrupt(typval_T *argvars,
buf->b_prompt_interrupt= interrupt_callback;
}
+/// "prompt_getprompt({buffer})" function
+void f_prompt_getprompt(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+ FUNC_ATTR_NONNULL_ALL
+{
+ // return an empty string by default, e.g. it's not a prompt buffer
+ rettv->v_type = VAR_STRING;
+ rettv->vval.v_string = NULL;
+
+ buf_T *const buf = tv_get_buf_from_arg(&argvars[0]);
+ if (buf == NULL) {
+ return;
+ }
+
+ if (!bt_prompt(buf)) {
+ return;
+ }
+
+ rettv->vval.v_string = vim_strsave(buf_prompt_text(buf));
+}
+
// "prompt_setprompt({buffer}, {text})" function
static void f_prompt_setprompt(typval_T *argvars,
typval_T *rettv, FunPtr fptr)
@@ -6642,37 +6661,6 @@ static void f_range(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
-// Evaluate "expr" for readdir().
-static varnumber_T readdir_checkitem(typval_T *expr, const char *name)
-{
- typval_T save_val;
- typval_T rettv;
- typval_T argv[2];
- varnumber_T retval = 0;
- bool error = false;
-
- prepare_vimvar(VV_VAL, &save_val);
- set_vim_var_string(VV_VAL, name, -1);
- argv[0].v_type = VAR_STRING;
- argv[0].vval.v_string = (char_u *)name;
-
- if (eval_expr_typval(expr, argv, 1, &rettv) == FAIL) {
- goto theend;
- }
-
- retval = tv_get_number_chk(&rettv, &error);
- if (error) {
- retval = -1;
- }
-
- tv_clear(&rettv);
-
-theend:
- set_vim_var_string(VV_VAL, NULL, 0);
- restore_vimvar(VV_VAL, &save_val);
- return retval;
-}
-
// "readdir()" function
static void f_readdir(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
@@ -6684,43 +6672,14 @@ static void f_readdir(typval_T *argvars, typval_T *rettv, FunPtr fptr)
tv_list_alloc_ret(rettv, kListLenUnknown);
path = tv_get_string(&argvars[0]);
expr = &argvars[1];
- ga_init(&ga, (int)sizeof(char *), 20);
if (!os_scandir(&dir, path)) {
smsg(_(e_notopen), path);
} else {
- for (;;) {
- bool ignore;
-
- path = os_scandir_next(&dir);
- if (path == NULL) {
- break;
- }
-
- ignore = (path[0] == '.'
- && (path[1] == NUL || (path[1] == '.' && path[2] == NUL)));
- if (!ignore && expr->v_type != VAR_UNKNOWN) {
- varnumber_T r = readdir_checkitem(expr, path);
-
- if (r < 0) {
- break;
- }
- if (r == 0) {
- ignore = true;
- }
- }
-
- if (!ignore) {
- ga_grow(&ga, 1);
- ((char **)ga.ga_data)[ga.ga_len++] = xstrdup(path);
- }
- }
-
- os_closedir(&dir);
+ readdir_core(&ga, &dir, expr, true);
}
if (rettv->vval.v_list != NULL && ga.ga_len > 0) {
- sort_strings((char_u **)ga.ga_data, ga.ga_len);
for (int i = 0; i < ga.ga_len; i++) {
path = ((const char **)ga.ga_data)[i];
tv_list_append_string(rettv->vval.v_list, path, -1);
@@ -8848,6 +8807,18 @@ static void f_shellescape(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_shiftwidth(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
+ rettv->vval.v_number = 0;
+
+ if (argvars[0].v_type != VAR_UNKNOWN) {
+ long col;
+
+ col = (long)tv_get_number_chk(argvars, NULL);
+ if (col < 0) {
+ return; // type error; errmsg already given
+ }
+ rettv->vval.v_number = get_sw_value_col(curbuf, col);
+ return;
+ }
rettv->vval.v_number = get_sw_value(curbuf);
}
@@ -8855,56 +8826,30 @@ static void f_shiftwidth(typval_T *argvars, typval_T *rettv, FunPtr fptr)
static void f_sign_define(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
const char *name;
- dict_T *dict;
- char *icon = NULL;
- char *linehl = NULL;
- char *text = NULL;
- char *texthl = NULL;
- char *numhl = NULL;
- rettv->vval.v_number = -1;
+ if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_UNKNOWN) {
+ // Define multiple signs
+ tv_list_alloc_ret(rettv, kListLenMayKnow);
- name = tv_get_string_chk(&argvars[0]);
- if (name == NULL) {
+ sign_define_multiple(argvars[0].vval.v_list, rettv->vval.v_list);
return;
}
- if (argvars[1].v_type != VAR_UNKNOWN) {
- if (argvars[1].v_type != VAR_DICT) {
- EMSG(_(e_dictreq));
- return;
- }
+ // Define a single sign
+ rettv->vval.v_number = -1;
- // sign attributes
- dict = argvars[1].vval.v_dict;
- if (tv_dict_find(dict, "icon", -1) != NULL) {
- icon = tv_dict_get_string(dict, "icon", true);
- }
- if (tv_dict_find(dict, "linehl", -1) != NULL) {
- linehl = tv_dict_get_string(dict, "linehl", true);
- }
- if (tv_dict_find(dict, "text", -1) != NULL) {
- text = tv_dict_get_string(dict, "text", true);
- }
- if (tv_dict_find(dict, "texthl", -1) != NULL) {
- texthl = tv_dict_get_string(dict, "texthl", true);
- }
- if (tv_dict_find(dict, "numhl", -1) != NULL) {
- numhl = tv_dict_get_string(dict, "numhl", true);
- }
+ name = tv_get_string_chk(&argvars[0]);
+ if (name == NULL) {
+ return;
}
- if (sign_define_by_name((char_u *)name, (char_u *)icon, (char_u *)linehl,
- (char_u *)text, (char_u *)texthl, (char_u *)numhl)
- == OK) {
- rettv->vval.v_number = 0;
+ if (argvars[1].v_type != VAR_UNKNOWN && argvars[1].v_type != VAR_DICT) {
+ EMSG(_(e_dictreq));
+ return;
}
- xfree(icon);
- xfree(linehl);
- xfree(text);
- xfree(texthl);
- xfree(numhl);
+ rettv->vval.v_number = sign_define_from_dict(
+ name, argvars[1].v_type == VAR_DICT ? argvars[1].vval.v_dict : NULL);
}
/// "sign_getdefined()" function
@@ -8989,7 +8934,7 @@ static void f_sign_jump(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->vval.v_number = -1;
- // Sign identifer
+ // Sign identifier
sign_id = (int)tv_get_number_chk(&argvars[0], &notanum);
if (notanum) {
return;
@@ -9025,83 +8970,44 @@ cleanup:
/// "sign_place()" function
static void f_sign_place(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- int sign_id;
- char_u *group = NULL;
- const char *sign_name;
- buf_T *buf;
- dict_T *dict;
- dictitem_T *di;
- linenr_T lnum = 0;
- int prio = SIGN_DEF_PRIO;
- bool notanum = false;
+ dict_T *dict = NULL;
rettv->vval.v_number = -1;
- // Sign identifer
- sign_id = (int)tv_get_number_chk(&argvars[0], &notanum);
- if (notanum) {
- return;
- }
- if (sign_id < 0) {
- EMSG(_(e_invarg));
+ if (argvars[4].v_type != VAR_UNKNOWN
+ && (argvars[4].v_type != VAR_DICT
+ || ((dict = argvars[4].vval.v_dict) == NULL))) {
+ EMSG(_(e_dictreq));
return;
}
- // Sign group
- const char *group_chk = tv_get_string_chk(&argvars[1]);
- if (group_chk == NULL) {
- return;
- }
- if (group_chk[0] == '\0') {
- group = NULL; // global sign group
- } else {
- group = vim_strsave((const char_u *)group_chk);
- }
+ rettv->vval.v_number = sign_place_from_dict(
+ &argvars[0], &argvars[1], &argvars[2], &argvars[3], dict);
+}
- // Sign name
- sign_name = tv_get_string_chk(&argvars[2]);
- if (sign_name == NULL) {
- goto cleanup;
- }
+/// "sign_placelist()" function. Place multiple signs.
+static void f_sign_placelist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ int sign_id;
- // Buffer to place the sign
- buf = get_buf_arg(&argvars[3]);
- if (buf == NULL) {
- goto cleanup;
+ tv_list_alloc_ret(rettv, kListLenMayKnow);
+
+ if (argvars[0].v_type != VAR_LIST) {
+ EMSG(_(e_listreq));
+ return;
}
- if (argvars[4].v_type != VAR_UNKNOWN) {
- if (argvars[4].v_type != VAR_DICT
- || ((dict = argvars[4].vval.v_dict) == NULL)) {
+ // Process the List of sign attributes
+ TV_LIST_ITER_CONST(argvars[0].vval.v_list, li, {
+ sign_id = -1;
+ if (TV_LIST_ITEM_TV(li)->v_type == VAR_DICT) {
+ sign_id = sign_place_from_dict(
+ NULL, NULL, NULL, NULL, TV_LIST_ITEM_TV(li)->vval.v_dict);
+ } else {
EMSG(_(e_dictreq));
- goto cleanup;
}
-
- // Line number where the sign is to be placed
- if ((di = tv_dict_find(dict, "lnum", -1)) != NULL) {
- lnum = (linenr_T)tv_get_number_chk(&di->di_tv, &notanum);
- if (notanum) {
- goto cleanup;
- }
- (void)lnum;
- lnum = tv_get_lnum(&di->di_tv);
- }
- if ((di = tv_dict_find(dict, "priority", -1)) != NULL) {
- // Sign priority
- prio = (int)tv_get_number_chk(&di->di_tv, &notanum);
- if (notanum) {
- goto cleanup;
- }
- }
- }
-
- if (sign_place(&sign_id, group, (const char_u *)sign_name, buf, lnum, prio)
- == OK) {
- rettv->vval.v_number = sign_id;
- }
-
-cleanup:
- xfree(group);
+ tv_list_append_number(rettv->vval.v_list, sign_id);
+ });
}
/// "sign_undefine()" function
@@ -9109,6 +9015,14 @@ static void f_sign_undefine(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
const char *name;
+ if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_UNKNOWN) {
+ // Undefine multiple signs
+ tv_list_alloc_ret(rettv, kListLenMayKnow);
+
+ sign_undefine_multiple(argvars[0].vval.v_list, rettv->vval.v_list);
+ return;
+ }
+
rettv->vval.v_number = -1;
if (argvars[0].v_type == VAR_UNKNOWN) {
@@ -9131,11 +9045,7 @@ static void f_sign_undefine(typval_T *argvars, typval_T *rettv, FunPtr fptr)
/// "sign_unplace()" function
static void f_sign_unplace(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- dict_T *dict;
- dictitem_T *di;
- int sign_id = 0;
- buf_T *buf = NULL;
- char_u *group = NULL;
+ dict_T *dict = NULL;
rettv->vval.v_number = -1;
@@ -9144,46 +9054,38 @@ static void f_sign_unplace(typval_T *argvars, typval_T *rettv, FunPtr fptr)
return;
}
- const char *group_chk = tv_get_string(&argvars[0]);
- if (group_chk[0] == '\0') {
- group = NULL; // global sign group
- } else {
- group = vim_strsave((const char_u *)group_chk);
- }
-
if (argvars[1].v_type != VAR_UNKNOWN) {
if (argvars[1].v_type != VAR_DICT) {
EMSG(_(e_dictreq));
- goto cleanup;
+ return;
}
dict = argvars[1].vval.v_dict;
-
- if ((di = tv_dict_find(dict, "buffer", -1)) != NULL) {
- buf = get_buf_arg(&di->di_tv);
- if (buf == NULL) {
- goto cleanup;
- }
- }
- if (tv_dict_find(dict, "id", -1) != NULL) {
- sign_id = tv_dict_get_number(dict, "id");
- }
}
- if (buf == NULL) {
- // Delete the sign in all the buffers
- FOR_ALL_BUFFERS(cbuf) {
- if (sign_unplace(sign_id, group, cbuf, 0) == OK) {
- rettv->vval.v_number = 0;
- }
- }
- } else {
- if (sign_unplace(sign_id, group, buf, 0) == OK) {
- rettv->vval.v_number = 0;
- }
+ rettv->vval.v_number = sign_unplace_from_dict(&argvars[0], dict);
+}
+
+/// "sign_unplacelist()" function
+static void f_sign_unplacelist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ int retval;
+
+ tv_list_alloc_ret(rettv, kListLenMayKnow);
+
+ if (argvars[0].v_type != VAR_LIST) {
+ EMSG(_(e_listreq));
+ return;
}
-cleanup:
- xfree(group);
+ TV_LIST_ITER_CONST(argvars[0].vval.v_list, li, {
+ retval = -1;
+ if (TV_LIST_ITEM_TV(li)->v_type == VAR_DICT) {
+ retval = sign_unplace_from_dict(NULL, TV_LIST_ITEM_TV(li)->vval.v_dict);
+ } else {
+ EMSG(_(e_dictreq));
+ }
+ tv_list_append_number(rettv->vval.v_list, retval);
+ });
}
/*
@@ -9252,6 +9154,7 @@ static void f_sockconnect(typval_T *argvars, typval_T *rettv, FunPtr fptr)
/// struct storing information about current sort
typedef struct {
int item_compare_ic;
+ bool item_compare_lc;
bool item_compare_numeric;
bool item_compare_numbers;
bool item_compare_float;
@@ -9326,10 +9229,10 @@ static int item_compare(const void *s1, const void *s2, bool keep_zero)
p2 = "";
}
if (!sortinfo->item_compare_numeric) {
- if (sortinfo->item_compare_ic) {
- res = STRICMP(p1, p2);
+ if (sortinfo->item_compare_lc) {
+ res = strcoll(p1, p2);
} else {
- res = STRCMP(p1, p2);
+ res = sortinfo->item_compare_ic ? STRICMP(p1, p2): STRCMP(p1, p2);
}
} else {
double n1, n2;
@@ -9464,6 +9367,7 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort)
}
info.item_compare_ic = false;
+ info.item_compare_lc = false;
info.item_compare_numeric = false;
info.item_compare_numbers = false;
info.item_compare_float = false;
@@ -9508,6 +9412,9 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort)
} else if (strcmp(info.item_compare_func, "i") == 0) {
info.item_compare_func = NULL;
info.item_compare_ic = true;
+ } else if (strcmp(info.item_compare_func, "l") == 0) {
+ info.item_compare_func = NULL;
+ info.item_compare_lc = true;
}
}
}
@@ -10189,6 +10096,38 @@ static void f_strpart(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->vval.v_string = (char_u *)xmemdupz(p + n, (size_t)len);
}
+// "strptime({format}, {timestring})" function
+static void f_strptime(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ char fmt_buf[NUMBUFLEN];
+ char str_buf[NUMBUFLEN];
+
+ struct tm tmval = {
+ .tm_isdst = -1,
+ };
+ char *fmt = (char *)tv_get_string_buf(&argvars[0], fmt_buf);
+ char *str = (char *)tv_get_string_buf(&argvars[1], str_buf);
+
+ vimconv_T conv = {
+ .vc_type = CONV_NONE,
+ };
+ char_u *enc = enc_locale();
+ convert_setup(&conv, p_enc, enc);
+ if (conv.vc_type != CONV_NONE) {
+ fmt = (char *)string_convert(&conv, (char_u *)fmt, NULL);
+ }
+ if (fmt == NULL
+ || os_strptime(str, fmt, &tmval) == NULL
+ || (rettv->vval.v_number = mktime(&tmval)) == -1) {
+ rettv->vval.v_number = 0;
+ }
+ if (conv.vc_type != CONV_NONE) {
+ xfree(fmt);
+ }
+ convert_setup(&conv, NULL, NULL);
+ xfree(enc);
+}
+
/*
* "strridx()" function
*/
diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c
index 9be487f4fd..71e4edc667 100644
--- a/src/nvim/eval/typval.c
+++ b/src/nvim/eval/typval.c
@@ -219,6 +219,7 @@ list_T *tv_list_alloc(const ptrdiff_t len)
list->lv_used_next = gc_first_list;
gc_first_list = list;
list_log(list, NULL, (void *)(uintptr_t)len, "alloc");
+ list->lua_table_ref = LUA_NOREF;
return list;
}
@@ -302,7 +303,7 @@ void tv_list_free_list(list_T *const l)
}
list_log(l, NULL, NULL, "freelist");
- nlua_free_typval_list(l);
+ NLUA_CLEAR_REF(l->lua_table_ref);
xfree(l);
}
@@ -1109,6 +1110,7 @@ void tv_dict_watcher_add(dict_T *const dict, const char *const key_pattern,
watcher->key_pattern_len = key_pattern_len;
watcher->callback = callback;
watcher->busy = false;
+ watcher->needs_free = false;
QUEUE_INSERT_TAIL(&dict->watchers, &watcher->node);
}
@@ -1182,22 +1184,30 @@ bool tv_dict_watcher_remove(dict_T *const dict, const char *const key_pattern,
QUEUE *w = NULL;
DictWatcher *watcher = NULL;
bool matched = false;
- QUEUE_FOREACH(w, &dict->watchers) {
+ bool queue_is_busy = false;
+ QUEUE_FOREACH(w, &dict->watchers, {
watcher = tv_dict_watcher_node_data(w);
+ if (watcher->busy) {
+ queue_is_busy = true;
+ }
if (tv_callback_equal(&watcher->callback, &callback)
&& watcher->key_pattern_len == key_pattern_len
&& memcmp(watcher->key_pattern, key_pattern, key_pattern_len) == 0) {
matched = true;
break;
}
- }
+ })
if (!matched) {
return false;
}
- QUEUE_REMOVE(w);
- tv_dict_watcher_free(watcher);
+ if (queue_is_busy) {
+ watcher->needs_free = true;
+ } else {
+ QUEUE_REMOVE(w);
+ tv_dict_watcher_free(watcher);
+ }
return true;
}
@@ -1258,9 +1268,10 @@ void tv_dict_watcher_notify(dict_T *const dict, const char *const key,
typval_T rettv;
+ bool any_needs_free = false;
dict->dv_refcount++;
QUEUE *w;
- QUEUE_FOREACH(w, &dict->watchers) {
+ QUEUE_FOREACH(w, &dict->watchers, {
DictWatcher *watcher = tv_dict_watcher_node_data(w);
if (!watcher->busy && tv_dict_watcher_matches(watcher, key)) {
rettv = TV_INITIAL_VALUE;
@@ -1268,7 +1279,19 @@ void tv_dict_watcher_notify(dict_T *const dict, const char *const key,
callback_call(&watcher->callback, 3, argv, &rettv);
watcher->busy = false;
tv_clear(&rettv);
+ if (watcher->needs_free) {
+ any_needs_free = true;
+ }
}
+ })
+ if (any_needs_free) {
+ QUEUE_FOREACH(w, &dict->watchers, {
+ DictWatcher *watcher = tv_dict_watcher_node_data(w);
+ if (watcher->needs_free) {
+ QUEUE_REMOVE(w);
+ tv_dict_watcher_free(watcher);
+ }
+ })
}
tv_dict_unref(dict);
@@ -1382,6 +1405,8 @@ dict_T *tv_dict_alloc(void)
d->dv_copyID = 0;
QUEUE_INIT(&d->watchers);
+ d->lua_table_ref = LUA_NOREF;
+
return d;
}
@@ -1432,7 +1457,7 @@ void tv_dict_free_dict(dict_T *const d)
d->dv_used_next->dv_used_prev = d->dv_used_prev;
}
- nlua_free_typval_dict(d);
+ NLUA_CLEAR_REF(d->lua_table_ref);
xfree(d);
}
diff --git a/src/nvim/eval/typval.h b/src/nvim/eval/typval.h
index 531b17cb59..2b4612016b 100644
--- a/src/nvim/eval/typval.h
+++ b/src/nvim/eval/typval.h
@@ -89,6 +89,7 @@ typedef struct dict_watcher {
size_t key_pattern_len;
QUEUE node;
bool busy; // prevent recursion if the dict is changed in the callback
+ bool needs_free;
} DictWatcher;
/// Bool variable values
diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c
index 689d05e079..dc7027980e 100644
--- a/src/nvim/eval/userfunc.c
+++ b/src/nvim/eval/userfunc.c
@@ -537,7 +537,7 @@ static char_u *fname_trans_sid(const char_u *const name,
if (current_sctx.sc_sid <= 0) {
*error = ERROR_SCRIPT;
} else {
- snprintf((char *)fname_buf + 3, FLEN_FIXED + 1, "%" PRId64 "_",
+ snprintf((char *)fname_buf + i, FLEN_FIXED + 1 - i, "%" PRId64 "_",
(int64_t)current_sctx.sc_sid);
i = (int)STRLEN(fname_buf);
}
@@ -833,6 +833,8 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars,
bool islambda = false;
char_u numbuf[NUMBUFLEN];
char_u *name;
+ typval_T *tv_to_free[MAX_FUNC_ARGS];
+ int tv_to_free_len = 0;
proftime_T wait_start;
proftime_T call_start;
int started_profiling = false;
@@ -985,6 +987,11 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars,
v->di_tv = isdefault ? def_rettv : argvars[i];
v->di_tv.v_lock = VAR_FIXED;
+ if (isdefault) {
+ // Need to free this later, no matter where it's stored.
+ tv_to_free[tv_to_free_len++] = &v->di_tv;
+ }
+
if (addlocal) {
// Named arguments can be accessed without the "a:" prefix in lambda
// expressions. Add to the l: dict.
@@ -1209,7 +1216,9 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars,
did_emsg |= save_did_emsg;
depth--;
-
+ for (int i = 0; i < tv_to_free_len; i++) {
+ tv_clear(tv_to_free[i]);
+ }
cleanup_function_call(fc);
if (--fp->uf_calls <= 0 && fp->uf_refcount <= 0) {