aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/eval/funcs.c
diff options
context:
space:
mode:
authorJosh Rahm <joshuarahm@gmail.com>2023-11-29 21:52:58 +0000
committerJosh Rahm <joshuarahm@gmail.com>2023-11-29 21:52:58 +0000
commit931bffbda3668ddc609fc1da8f9eb576b170aa52 (patch)
treed8c1843a95da5ea0bb4acc09f7e37843d9995c86 /src/nvim/eval/funcs.c
parent142d9041391780ac15b89886a54015fdc5c73995 (diff)
parent4a8bf24ac690004aedf5540fa440e788459e5e34 (diff)
downloadrneovim-userreg.tar.gz
rneovim-userreg.tar.bz2
rneovim-userreg.zip
Merge remote-tracking branch 'upstream/master' into userreguserreg
Diffstat (limited to 'src/nvim/eval/funcs.c')
-rw-r--r--src/nvim/eval/funcs.c2257
1 files changed, 1057 insertions, 1200 deletions
diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c
index 6b580b5312..310ac29f88 100644
--- a/src/nvim/eval/funcs.c
+++ b/src/nvim/eval/funcs.c
@@ -1,6 +1,3 @@
-// This is an open source non-commercial project. Dear PVS-Studio, please check
-// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
-
#include <assert.h>
#include <fcntl.h>
#include <float.h>
@@ -11,11 +8,13 @@
#include <msgpack/pack.h>
#include <msgpack/unpack.h>
#include <signal.h>
+#include <stdarg.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
+#include <sys/types.h>
#include <time.h>
#include <uv.h>
@@ -25,14 +24,15 @@
#include "nvim/api/private/dispatch.h"
#include "nvim/api/private/helpers.h"
#include "nvim/api/vim.h"
-#include "nvim/ascii.h"
-#include "nvim/assert.h"
+#include "nvim/ascii_defs.h"
+#include "nvim/assert_defs.h"
#include "nvim/autocmd.h"
#include "nvim/buffer.h"
#include "nvim/buffer_defs.h"
#include "nvim/channel.h"
#include "nvim/charset.h"
#include "nvim/cmdexpand.h"
+#include "nvim/cmdexpand_defs.h"
#include "nvim/context.h"
#include "nvim/cursor.h"
#include "nvim/diff.h"
@@ -44,6 +44,7 @@
#include "nvim/eval/executor.h"
#include "nvim/eval/funcs.h"
#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
#include "nvim/eval/userfunc.h"
#include "nvim/eval/vars.h"
#include "nvim/eval/window.h"
@@ -57,41 +58,40 @@
#include "nvim/ex_getln.h"
#include "nvim/file_search.h"
#include "nvim/fileio.h"
+#include "nvim/func_attr.h"
#include "nvim/garray.h"
#include "nvim/getchar.h"
#include "nvim/gettext.h"
#include "nvim/globals.h"
-#include "nvim/grid_defs.h"
-#include "nvim/hashtab.h"
+#include "nvim/grid.h"
#include "nvim/highlight_defs.h"
#include "nvim/highlight_group.h"
#include "nvim/indent.h"
#include "nvim/indent_c.h"
#include "nvim/input.h"
+#include "nvim/insexpand.h"
#include "nvim/keycodes.h"
#include "nvim/lua/executor.h"
-#include "nvim/macros.h"
+#include "nvim/macros_defs.h"
#include "nvim/main.h"
#include "nvim/mark.h"
#include "nvim/math.h"
#include "nvim/mbyte.h"
-#include "nvim/memfile_defs.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
#include "nvim/menu.h"
#include "nvim/message.h"
-#include "nvim/mouse.h"
#include "nvim/move.h"
#include "nvim/msgpack_rpc/channel.h"
-#include "nvim/msgpack_rpc/channel_defs.h"
#include "nvim/msgpack_rpc/server.h"
#include "nvim/normal.h"
#include "nvim/ops.h"
#include "nvim/option.h"
+#include "nvim/option_vars.h"
#include "nvim/optionstr.h"
#include "nvim/os/dl.h"
#include "nvim/os/fileio.h"
-#include "nvim/os/fs_defs.h"
+#include "nvim/os/fs.h"
#include "nvim/os/os.h"
#include "nvim/os/pty_process.h"
#include "nvim/os/shell.h"
@@ -100,7 +100,7 @@
#include "nvim/path.h"
#include "nvim/plines.h"
#include "nvim/popupmenu.h"
-#include "nvim/pos.h"
+#include "nvim/pos_defs.h"
#include "nvim/profile.h"
#include "nvim/regexp.h"
#include "nvim/runtime.h"
@@ -113,9 +113,8 @@
#include "nvim/syntax.h"
#include "nvim/tag.h"
#include "nvim/ui.h"
-#include "nvim/undo.h"
#include "nvim/version.h"
-#include "nvim/vim.h"
+#include "nvim/vim_defs.h"
#include "nvim/window.h"
/// Describe data to return from find_some_match()
@@ -145,11 +144,17 @@ PRAGMA_DIAG_POP
PRAGMA_DIAG_POP
#endif
-static char *e_listblobarg = N_("E899: Argument of %s must be a List or Blob");
-static char *e_invalwindow = N_("E957: Invalid window number");
-static char *e_reduceempty = N_("E998: Reduce of an empty %s with no initial value");
-static char e_using_number_as_bool_nr[]
- = N_("E1023: Using a Number as a Bool: %d");
+static const char *e_listblobarg = N_("E899: Argument of %s must be a List or Blob");
+static const char *e_invalwindow = N_("E957: Invalid window number");
+static const char e_argument_of_str_must_be_list_string_or_dictionary[]
+ = N_("E706: Argument of %s must be a List, String or Dictionary");
+static const char e_invalid_submatch_number_nr[]
+ = N_("E935: Invalid submatch number: %d");
+static const char *e_reduceempty = N_("E998: Reduce of an empty %s with no initial value");
+static const char e_string_list_or_blob_required[]
+ = N_("E1098: String, List or Blob required");
+static const char e_missing_function_argument[]
+ = N_("E1132: Missing function argument");
/// Dummy va_list for passing to vim_snprintf
///
@@ -226,6 +231,31 @@ const EvalFuncDef *find_internal_func(const char *const name)
return index >= 0 ? &functions[index] : NULL;
}
+/// Check the argument count to use for internal function "fdef".
+/// @return -1 for failure, 0 if no method base accepted, 1 if method base is
+/// first argument, 2 if method base is second argument, etc.
+int check_internal_func(const EvalFuncDef *const fdef, const int argcount)
+ FUNC_ATTR_NONNULL_ALL
+{
+ int res;
+
+ if (argcount < fdef->min_argc) {
+ res = FCERR_TOOFEW;
+ } else if (argcount > fdef->max_argc) {
+ res = FCERR_TOOMANY;
+ } else {
+ return fdef->base_arg;
+ }
+
+ const char *const name = fdef->name;
+ if (res == FCERR_TOOMANY) {
+ semsg(_(e_toomanyarg), name);
+ } else {
+ semsg(_(e_toofewarg), name);
+ }
+ return -1;
+}
+
int call_internal_func(const char *const fname, const int argcount, typval_T *const argvars,
typval_T *const rettv)
FUNC_ATTR_NONNULL_ALL
@@ -261,6 +291,9 @@ int call_internal_method(const char *const fname, const int argcount, typval_T *
typval_T argv[MAX_FUNC_ARGS + 1];
const ptrdiff_t base_index = fdef->base_arg == BASE_LAST ? argcount : fdef->base_arg - 1;
+ if (argcount < base_index) {
+ return FCERR_TOOFEW;
+ }
memcpy(argv, argvars, (size_t)base_index * sizeof(typval_T));
argv[base_index] = *basetv;
memcpy(argv + base_index + 1, argvars + base_index,
@@ -319,11 +352,12 @@ static void api_wrapper(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
Object result = handler.fn(VIML_INTERNAL_CALL, args, &res_arena, &err);
if (ERROR_SET(&err)) {
- semsg_multiline((const char *)e_api_error, err.msg);
+ semsg_multiline(e_api_error, err.msg);
goto end;
}
if (!object_to_vim(result, rettv, &err)) {
+ assert(ERROR_SET(&err));
semsg(_("Error converting the call result: %s"), err.msg);
}
@@ -448,7 +482,7 @@ buf_T *tv_get_buf(typval_T *tv, int curtab_only)
int save_magic = p_magic;
p_magic = true;
char *save_cpo = p_cpo;
- p_cpo = empty_option;
+ p_cpo = empty_string_option;
buf_T *buf = buflist_findnr(buflist_findpat(name, name + strlen(name),
true, false, curtab_only));
@@ -492,7 +526,7 @@ buf_T *get_buf_arg(typval_T *arg)
/// "byte2line(byte)" function
static void f_byte2line(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- long boff = tv_get_number(&argvars[0]) - 1;
+ int boff = (int)tv_get_number(&argvars[0]) - 1;
if (boff < 0) {
rettv->vval.v_number = -1;
} else {
@@ -501,41 +535,6 @@ static void f_byte2line(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
}
}
-static void byteidx(typval_T *argvars, typval_T *rettv, int comp)
-{
- const char *const str = tv_get_string_chk(&argvars[0]);
- varnumber_T idx = tv_get_number_chk(&argvars[1], NULL);
- rettv->vval.v_number = -1;
- if (str == NULL || idx < 0) {
- return;
- }
-
- const char *t = str;
- for (; idx > 0; idx--) {
- if (*t == NUL) { // EOL reached.
- return;
- }
- if (comp) {
- t += utf_ptr2len(t);
- } else {
- t += utfc_ptr2len(t);
- }
- }
- rettv->vval.v_number = (varnumber_T)(t - str);
-}
-
-/// "byteidx()" function
-static void f_byteidx(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
-{
- byteidx(argvars, rettv, false);
-}
-
-/// "byteidxcomp()" function
-static void f_byteidxcomp(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
-{
- byteidx(argvars, rettv, true);
-}
-
/// "call(func, arglist [, dict])" function
static void f_call(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
@@ -563,14 +562,13 @@ static void f_call(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
func = (char *)tv_get_string(&argvars[0]);
}
- if (*func == NUL) {
- return; // type error or empty name
+ if (func == NULL || *func == NUL) {
+ return; // type error, empty name or null function
}
dict_T *selfdict = NULL;
if (argvars[2].v_type != VAR_UNKNOWN) {
- if (argvars[2].v_type != VAR_DICT) {
- emsg(_(e_dictreq));
+ if (tv_check_for_dict_arg(argvars, 2) == FAIL) {
if (owned) {
func_unref(func);
}
@@ -653,7 +651,7 @@ static void f_chansend(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
bool crlf = false;
#else
Channel *chan = find_channel(id);
- bool crlf = (chan != NULL && chan->term) ? true: false;
+ bool crlf = (chan != NULL && chan->term) ? true : false;
#endif
if (argvars[1].v_type == VAR_BLOB) {
@@ -761,53 +759,6 @@ static void f_charcol(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
get_col(argvars, rettv, true);
}
-/// "charidx()" function
-static void f_charidx(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
-{
- rettv->vval.v_number = -1;
-
- if (argvars[0].v_type != VAR_STRING
- || argvars[1].v_type != VAR_NUMBER
- || (argvars[2].v_type != VAR_UNKNOWN
- && argvars[2].v_type != VAR_NUMBER
- && argvars[2].v_type != VAR_BOOL)) {
- emsg(_(e_invarg));
- return;
- }
-
- const char *str = tv_get_string_chk(&argvars[0]);
- varnumber_T idx = tv_get_number_chk(&argvars[1], NULL);
- if (str == NULL || idx < 0) {
- return;
- }
- int countcc = 0;
- if (argvars[2].v_type != VAR_UNKNOWN) {
- countcc = (int)tv_get_number(&argvars[2]);
- }
- if (countcc < 0 || countcc > 1) {
- semsg(_(e_using_number_as_bool_nr), countcc);
- return;
- }
-
- int (*ptr2len)(const char *);
- if (countcc) {
- ptr2len = utf_ptr2len;
- } else {
- ptr2len = utfc_ptr2len;
- }
-
- const char *p;
- int len;
- for (p = str, len = 0; p <= str + idx; len++) {
- if (*p == NUL) {
- return;
- }
- p += ptr2len(p);
- }
-
- rettv->vval.v_number = len > 0 ? len - 1 : 0;
-}
-
/// "chdir(dir)" function
static void f_chdir(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
@@ -925,8 +876,7 @@ static void f_confirm(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
}
if (!error) {
- rettv->vval.v_number = do_dialog(type, NULL, (char *)message, (char *)buttons, def, NULL,
- false);
+ rettv->vval.v_number = do_dialog(type, NULL, message, buttons, def, NULL, false);
}
}
@@ -936,10 +886,90 @@ static void f_copy(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
var_item_copy(NULL, &argvars[0], rettv, false, 0);
}
+/// Count the number of times "needle" occurs in string "haystack".
+///
+/// @param ic ignore case
+static varnumber_T count_string(const char *haystack, const char *needle, bool ic)
+{
+ varnumber_T n = 0;
+ const char *p = haystack;
+
+ if (p == NULL || needle == NULL || *needle == NUL) {
+ return 0;
+ }
+
+ if (ic) {
+ const size_t len = strlen(needle);
+
+ while (*p != NUL) {
+ if (mb_strnicmp(p, needle, len) == 0) {
+ n++;
+ p += len;
+ } else {
+ MB_PTR_ADV(p);
+ }
+ }
+ } else {
+ const char *next;
+ while ((next = strstr(p, needle)) != NULL) {
+ n++;
+ p = next + strlen(needle);
+ }
+ }
+
+ return n;
+}
+
+/// Count the number of times item "needle" occurs in List "l" starting at index "idx".
+///
+/// @param ic ignore case
+static varnumber_T count_list(list_T *l, typval_T *needle, int64_t idx, bool ic)
+{
+ if (tv_list_len(l) == 0) {
+ return 0;
+ }
+
+ listitem_T *li = tv_list_find(l, (int)idx);
+ if (li == NULL) {
+ semsg(_(e_list_index_out_of_range_nr), idx);
+ return 0;
+ }
+
+ varnumber_T n = 0;
+
+ for (; li != NULL; li = TV_LIST_ITEM_NEXT(l, li)) {
+ if (tv_equal(TV_LIST_ITEM_TV(li), needle, ic, false)) {
+ n++;
+ }
+ }
+
+ return n;
+}
+
+/// Count the number of times item "needle" occurs in Dict "d".
+///
+/// @param ic ignore case
+static varnumber_T count_dict(dict_T *d, typval_T *needle, bool ic)
+{
+ if (d == NULL) {
+ return 0;
+ }
+
+ varnumber_T n = 0;
+
+ TV_DICT_ITER(d, di, {
+ if (tv_equal(&di->di_tv, needle, ic, false)) {
+ n++;
+ }
+ });
+
+ return n;
+}
+
/// "count()" function
static void f_count(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- long n = 0;
+ varnumber_T n = 0;
int ic = 0;
bool error = false;
@@ -947,78 +977,30 @@ static void f_count(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
ic = (int)tv_get_number_chk(&argvars[2], &error);
}
- if (argvars[0].v_type == VAR_STRING) {
- const char *expr = tv_get_string_chk(&argvars[1]);
- const char *p = argvars[0].vval.v_string;
-
- if (!error && expr != NULL && *expr != NUL && p != NULL) {
- if (ic) {
- const size_t len = strlen(expr);
-
- while (*p != NUL) {
- if (mb_strnicmp((char *)p, (char *)expr, len) == 0) {
- n++;
- p += len;
- } else {
- MB_PTR_ADV(p);
- }
- }
- } else {
- char *next;
- while ((next = strstr((char *)p, (char *)expr)) != NULL) {
- n++;
- p = next + strlen(expr);
- }
- }
+ if (!error && argvars[0].v_type == VAR_STRING) {
+ n = count_string(argvars[0].vval.v_string, tv_get_string_chk(&argvars[1]), ic);
+ } else if (!error && argvars[0].v_type == VAR_LIST) {
+ int64_t idx = 0;
+ if (argvars[2].v_type != VAR_UNKNOWN
+ && argvars[3].v_type != VAR_UNKNOWN) {
+ idx = (int64_t)tv_get_number_chk(&argvars[3], &error);
}
- } else if (argvars[0].v_type == VAR_LIST) {
- list_T *l = argvars[0].vval.v_list;
-
- if (l != NULL) {
- listitem_T *li = tv_list_first(l);
- if (argvars[2].v_type != VAR_UNKNOWN) {
- if (argvars[3].v_type != VAR_UNKNOWN) {
- long idx = tv_get_number_chk(&argvars[3], &error);
- if (!error) {
- li = tv_list_find(l, (int)idx);
- if (li == NULL) {
- semsg(_(e_listidx), (int64_t)idx);
- }
- }
- }
- if (error) {
- li = NULL;
- }
- }
-
- for (; li != NULL; li = TV_LIST_ITEM_NEXT(l, li)) {
- if (tv_equal(TV_LIST_ITEM_TV(li), &argvars[1], ic, false)) {
- n++;
- }
- }
+ if (!error) {
+ n = count_list(argvars[0].vval.v_list, &argvars[1], idx, ic);
}
- } else if (argvars[0].v_type == VAR_DICT) {
+ } else if (!error && argvars[0].v_type == VAR_DICT) {
dict_T *d = argvars[0].vval.v_dict;
if (d != NULL) {
- if (argvars[2].v_type != VAR_UNKNOWN) {
- if (argvars[3].v_type != VAR_UNKNOWN) {
- emsg(_(e_invarg));
- }
- }
-
- int todo = error ? 0 : (int)d->dv_hashtab.ht_used;
- for (hashitem_T *hi = d->dv_hashtab.ht_array; todo > 0; hi++) {
- if (!HASHITEM_EMPTY(hi)) {
- todo--;
- if (tv_equal(&TV_DICT_HI2DI(hi)->di_tv, &argvars[1], ic, false)) {
- n++;
- }
- }
+ if (argvars[2].v_type != VAR_UNKNOWN
+ && argvars[3].v_type != VAR_UNKNOWN) {
+ emsg(_(e_invarg));
+ } else {
+ n = count_dict(argvars[0].vval.v_dict, &argvars[1], ic);
}
}
- } else {
- semsg(_(e_listdictarg), "count()");
+ } else if (!error) {
+ semsg(_(e_argument_of_str_must_be_list_string_or_dictionary), "count()");
}
rettv->vval.v_number = n;
}
@@ -1042,7 +1024,7 @@ static void f_ctxget(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
Dictionary ctx_dict = ctx_to_dict(ctx);
Error err = ERROR_INIT;
- object_to_vim(DICTIONARY_OBJ(ctx_dict), rettv, &err);
+ (void)object_to_vim(DICTIONARY_OBJ(ctx_dict), rettv, &err);
api_free_dictionary(ctx_dict);
api_clear_error(&err);
}
@@ -1064,17 +1046,17 @@ static void f_ctxpush(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
TV_LIST_ITER(argvars[0].vval.v_list, li, {
typval_T *tv_li = TV_LIST_ITEM_TV(li);
if (tv_li->v_type == VAR_STRING) {
- if (strequal((char *)tv_li->vval.v_string, "regs")) {
+ if (strequal(tv_li->vval.v_string, "regs")) {
types |= kCtxRegs;
- } else if (strequal((char *)tv_li->vval.v_string, "jumps")) {
+ } else if (strequal(tv_li->vval.v_string, "jumps")) {
types |= kCtxJumps;
- } else if (strequal((char *)tv_li->vval.v_string, "bufs")) {
+ } else if (strequal(tv_li->vval.v_string, "bufs")) {
types |= kCtxBufs;
- } else if (strequal((char *)tv_li->vval.v_string, "gvars")) {
+ } else if (strequal(tv_li->vval.v_string, "gvars")) {
types |= kCtxGVars;
- } else if (strequal((char *)tv_li->vval.v_string, "sfuncs")) {
+ } else if (strequal(tv_li->vval.v_string, "sfuncs")) {
types |= kCtxSFuncs;
- } else if (strequal((char *)tv_li->vval.v_string, "funcs")) {
+ } else if (strequal(tv_li->vval.v_string, "funcs")) {
types |= kCtxFuncs;
}
}
@@ -1108,14 +1090,16 @@ static void f_ctxset(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
return;
}
- int save_did_emsg = did_emsg;
+ const int save_did_emsg = did_emsg;
did_emsg = false;
Dictionary dict = vim_to_object(&argvars[0]).data.dictionary;
Context tmp = CONTEXT_INIT;
- ctx_from_dict(dict, &tmp);
+ Error err = ERROR_INIT;
+ ctx_from_dict(dict, &tmp, &err);
- if (did_emsg) {
+ if (ERROR_SET(&err)) {
+ semsg("%s", err.msg);
ctx_free(&tmp);
} else {
ctx_free(ctx);
@@ -1123,6 +1107,7 @@ static void f_ctxset(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
}
api_free_dictionary(dict);
+ api_clear_error(&err);
did_emsg = save_did_emsg;
}
@@ -1134,12 +1119,13 @@ static void f_ctxsize(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
}
/// Set the cursor position.
-/// If 'charcol' is true, then use the column number as a character offset.
+/// If "charcol" is true, then use the column number as a character offset.
/// Otherwise use the column number as a byte offset.
static void set_cursorpos(typval_T *argvars, typval_T *rettv, bool charcol)
{
- long lnum, col;
- long coladd = 0;
+ linenr_T lnum;
+ colnr_T col;
+ colnr_T coladd = 0;
bool set_curswant = true;
rettv->vval.v_number = -1;
@@ -1167,12 +1153,12 @@ static void set_cursorpos(typval_T *argvars, typval_T *rettv, bool charcol)
} else if (lnum == 0) {
lnum = curwin->w_cursor.lnum;
}
- col = (long)tv_get_number_chk(&argvars[1], NULL);
+ col = (colnr_T)tv_get_number_chk(&argvars[1], NULL);
if (charcol) {
- col = buf_charidx_to_byteidx(curbuf, (linenr_T)lnum, (int)col) + 1;
+ col = buf_charidx_to_byteidx(curbuf, lnum, (int)col) + 1;
}
if (argvars[2].v_type != VAR_UNKNOWN) {
- coladd = (long)tv_get_number_chk(&argvars[2], NULL);
+ coladd = (colnr_T)tv_get_number_chk(&argvars[2], NULL);
}
} else {
emsg(_(e_invarg));
@@ -1182,12 +1168,12 @@ static void set_cursorpos(typval_T *argvars, typval_T *rettv, bool charcol)
return; // type error; errmsg already given
}
if (lnum > 0) {
- curwin->w_cursor.lnum = (linenr_T)lnum;
+ curwin->w_cursor.lnum = lnum;
}
if (col > 0) {
- curwin->w_cursor.col = (colnr_T)col - 1;
+ curwin->w_cursor.col = col - 1;
}
- curwin->w_cursor.coladd = (colnr_T)coladd;
+ curwin->w_cursor.coladd = coladd;
// Make sure the cursor is in a valid position.
check_cursor();
@@ -1236,18 +1222,16 @@ static void f_debugbreak(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
/// "deepcopy()" function
static void f_deepcopy(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- int noref = 0;
+ if (tv_check_for_opt_bool_arg(argvars, 1) == FAIL) {
+ return;
+ }
+ varnumber_T noref = 0;
if (argvars[1].v_type != VAR_UNKNOWN) {
- noref = (int)tv_get_bool_chk(&argvars[1], NULL);
- }
- if (noref < 0 || noref > 1) {
- semsg(_(e_using_number_as_bool_nr), noref);
- } else {
- var_item_copy(NULL, &argvars[0], rettv, true, (noref == 0
- ? get_copyID()
- : 0));
+ noref = tv_get_bool_chk(&argvars[1], NULL);
}
+
+ var_item_copy(NULL, &argvars[0], rettv, true, (noref == 0 ? get_copyID() : 0));
}
/// "delete()" function
@@ -1546,11 +1530,11 @@ static void f_eval(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
const char *s = tv_get_string_chk(&argvars[0]);
if (s != NULL) {
- s = (const char *)skipwhite(s);
+ s = skipwhite(s);
}
const char *const expr_start = s;
- if (s == NULL || eval1((char **)&s, rettv, true) == FAIL) {
+ if (s == NULL || eval1((char **)&s, rettv, &EVALARG_EVALUATE) == FAIL) {
if (expr_start != NULL && !aborting()) {
semsg(_(e_invexpr2), expr_start);
}
@@ -1722,7 +1706,7 @@ static void f_exists(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
xfree(exp);
}
} else if (*p == '&' || *p == '+') { // Option.
- n = (get_option_tv(&p, NULL, true) == OK);
+ n = (eval_option(&p, NULL, true) == OK);
if (*skipwhite(p) != NUL) {
n = false; // Trailing garbage.
}
@@ -1752,7 +1736,7 @@ static void f_expand(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
char *p_csl_save = p_csl;
// avoid using 'completeslash' here
- p_csl = empty_option;
+ p_csl = empty_string_option;
#endif
rettv->v_type = VAR_STRING;
@@ -1769,7 +1753,7 @@ static void f_expand(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
emsg_off++;
}
size_t len;
- char *errormsg = NULL;
+ const char *errormsg = NULL;
char *result = eval_vars((char *)s, s, &len, NULL, &errormsg, NULL, false);
if (p_verbose == 0) {
emsg_off--;
@@ -1779,7 +1763,7 @@ static void f_expand(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
if (rettv->v_type == VAR_LIST) {
tv_list_alloc_ret(rettv, (result != NULL));
if (result != NULL) {
- tv_list_append_string(rettv->vval.v_list, (const char *)result, -1);
+ tv_list_append_string(rettv->vval.v_list, result, -1);
}
XFREE_CLEAR(result);
} else {
@@ -1805,8 +1789,7 @@ static void f_expand(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
ExpandOne(&xpc, (char *)s, NULL, options, WILD_ALL_KEEP);
tv_list_alloc_ret(rettv, xpc.xp_numfiles);
for (int i = 0; i < xpc.xp_numfiles; i++) {
- tv_list_append_string(rettv->vval.v_list,
- (const char *)xpc.xp_files[i], -1);
+ tv_list_append_string(rettv->vval.v_list, xpc.xp_files[i], -1);
}
ExpandCleanup(&xpc);
}
@@ -1835,7 +1818,7 @@ static void f_menu_get(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
/// Expand all the special characters in a command string.
static void f_expandcmd(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- char *errormsg = NULL;
+ const char *errormsg = NULL;
bool emsgoff = true;
if (argvars[1].v_type == VAR_DICT
@@ -1870,8 +1853,8 @@ static void f_expandcmd(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
rettv->vval.v_string = cmdstr;
}
-/// "flatten(list[, {maxdepth}])" function
-static void f_flatten(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+/// "flatten()" and "flattennew()" functions
+static void flatten_common(typval_T *argvars, typval_T *rettv, bool make_copy)
{
bool error = false;
@@ -1880,11 +1863,11 @@ static void f_flatten(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
return;
}
- long maxdepth;
+ int maxdepth;
if (argvars[1].v_type == VAR_UNKNOWN) {
maxdepth = 999999;
} else {
- maxdepth = (long)tv_get_number_chk(&argvars[1], &error);
+ maxdepth = (int)tv_get_number_chk(&argvars[1], &error);
if (error) {
return;
}
@@ -1895,92 +1878,179 @@ static void f_flatten(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
}
list_T *list = argvars[0].vval.v_list;
- if (list != NULL
- && !value_check_lock(tv_list_locked(list),
- N_("flatten() argument"),
- TV_TRANSLATE)
- && tv_list_flatten(list, maxdepth) == OK) {
- tv_copy(&argvars[0], rettv);
+ rettv->v_type = VAR_LIST;
+ rettv->vval.v_list = list;
+ if (list == NULL) {
+ return;
}
+
+ if (make_copy) {
+ list = tv_list_copy(NULL, list, false, get_copyID());
+ rettv->vval.v_list = list;
+ if (list == NULL) {
+ return;
+ }
+ } else {
+ if (value_check_lock(tv_list_locked(list), N_("flatten() argument"), TV_TRANSLATE)) {
+ return;
+ }
+ tv_list_ref(list);
+ }
+
+ tv_list_flatten(list, NULL, tv_list_len(list), maxdepth);
}
-/// "extend(list, list [, idx])" function
-/// "extend(dict, dict [, action])" function
-static void f_extend(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+/// "flatten(list[, {maxdepth}])" function
+static void f_flatten(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- const char *const arg_errmsg = N_("extend() argument");
+ flatten_common(argvars, rettv, false);
+}
- if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_LIST) {
- bool error = false;
+/// "flattennew(list[, {maxdepth}])" function
+static void f_flattennew(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ flatten_common(argvars, rettv, true);
+}
- list_T *const l1 = argvars[0].vval.v_list;
- list_T *const l2 = argvars[1].vval.v_list;
- if (!value_check_lock(tv_list_locked(l1), arg_errmsg, TV_TRANSLATE)) {
- listitem_T *item;
- if (argvars[2].v_type != VAR_UNKNOWN) {
- long before = (long)tv_get_number_chk(&argvars[2], &error);
- if (error) {
- return; // Type error; errmsg already given.
- }
+/// extend() a List. Append List argvars[1] to List argvars[0] before index
+/// argvars[3] and return the resulting list in "rettv".
+///
+/// @param is_new true for extendnew()
+static void extend_list(typval_T *argvars, const char *arg_errmsg, bool is_new, typval_T *rettv)
+{
+ bool error = false;
- if (before == tv_list_len(l1)) {
- item = NULL;
- } else {
- item = tv_list_find(l1, (int)before);
- if (item == NULL) {
- semsg(_(e_listidx), (int64_t)before);
- return;
- }
- }
- } else {
+ list_T *l1 = argvars[0].vval.v_list;
+ list_T *const l2 = argvars[1].vval.v_list;
+ if (is_new || !value_check_lock(tv_list_locked(l1), arg_errmsg, TV_TRANSLATE)) {
+ if (is_new) {
+ l1 = tv_list_copy(NULL, l1, false, get_copyID());
+ if (l1 == NULL) {
+ return;
+ }
+ }
+
+ listitem_T *item;
+ if (argvars[2].v_type != VAR_UNKNOWN) {
+ int before = (int)tv_get_number_chk(&argvars[2], &error);
+ if (error) {
+ return; // Type error; errmsg already given.
+ }
+
+ if (before == tv_list_len(l1)) {
item = NULL;
+ } else {
+ item = tv_list_find(l1, before);
+ if (item == NULL) {
+ semsg(_(e_list_index_out_of_range_nr), (int64_t)before);
+ return;
+ }
}
- tv_list_extend(l1, l2, item);
+ } else {
+ item = NULL;
+ }
+ tv_list_extend(l1, l2, item);
+ if (is_new) {
+ *rettv = (typval_T){
+ .v_type = VAR_LIST,
+ .v_lock = VAR_UNLOCKED,
+ .vval.v_list = l1,
+ };
+ } else {
tv_copy(&argvars[0], rettv);
}
- } else if (argvars[0].v_type == VAR_DICT && argvars[1].v_type ==
- VAR_DICT) {
- dict_T *const d1 = argvars[0].vval.v_dict;
- dict_T *const d2 = argvars[1].vval.v_dict;
- if (d1 == NULL) {
- const bool locked = value_check_lock(VAR_FIXED, arg_errmsg, TV_TRANSLATE);
- (void)locked;
- assert(locked == true);
- } else if (d2 == NULL) {
- // Do nothing
- tv_copy(&argvars[0], rettv);
- } else if (!value_check_lock(d1->dv_lock, arg_errmsg, TV_TRANSLATE)) {
- const char *action = "force";
- // Check the third argument.
- if (argvars[2].v_type != VAR_UNKNOWN) {
- const char *const av[] = { "keep", "force", "error" };
+ }
+}
- action = tv_get_string_chk(&argvars[2]);
- if (action == NULL) {
- return; // Type error; error message already given.
- }
- size_t i;
- for (i = 0; i < ARRAY_SIZE(av); i++) {
- if (strcmp(action, av[i]) == 0) {
- break;
- }
- }
- if (i == 3) {
- semsg(_(e_invarg2), action);
- return;
+/// extend() a Dict. Append Dict argvars[1] to Dict argvars[0] and return the
+/// resulting Dict in "rettv".
+///
+/// @param is_new true for extendnew()
+static void extend_dict(typval_T *argvars, const char *arg_errmsg, bool is_new, typval_T *rettv)
+{
+ dict_T *d1 = argvars[0].vval.v_dict;
+ dict_T *const d2 = argvars[1].vval.v_dict;
+ if (d1 == NULL) {
+ const bool locked = value_check_lock(VAR_FIXED, arg_errmsg, TV_TRANSLATE);
+ (void)locked;
+ assert(locked == true);
+ } else if (d2 == NULL) {
+ // Do nothing
+ tv_copy(&argvars[0], rettv);
+ } else if (is_new || !value_check_lock(d1->dv_lock, arg_errmsg, TV_TRANSLATE)) {
+ if (is_new) {
+ d1 = tv_dict_copy(NULL, d1, false, get_copyID());
+ if (d1 == NULL) {
+ return;
+ }
+ }
+
+ const char *action = "force";
+ // Check the third argument.
+ if (argvars[2].v_type != VAR_UNKNOWN) {
+ const char *const av[] = { "keep", "force", "error" };
+
+ action = tv_get_string_chk(&argvars[2]);
+ if (action == NULL) {
+ return; // Type error; error message already given.
+ }
+ size_t i;
+ for (i = 0; i < ARRAY_SIZE(av); i++) {
+ if (strcmp(action, av[i]) == 0) {
+ break;
}
}
+ if (i == 3) {
+ semsg(_(e_invarg2), action);
+ return;
+ }
+ }
- tv_dict_extend(d1, d2, action);
+ tv_dict_extend(d1, d2, action);
+ if (is_new) {
+ *rettv = (typval_T){
+ .v_type = VAR_DICT,
+ .v_lock = VAR_UNLOCKED,
+ .vval.v_dict = d1,
+ };
+ } else {
tv_copy(&argvars[0], rettv);
}
+ }
+}
+
+/// "extend()" or "extendnew()" function.
+///
+/// @param is_new true for extendnew()
+static void extend(typval_T *argvars, typval_T *rettv, char *arg_errmsg, bool is_new)
+{
+ if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_LIST) {
+ extend_list(argvars, arg_errmsg, is_new, rettv);
+ } else if (argvars[0].v_type == VAR_DICT && argvars[1].v_type == VAR_DICT) {
+ extend_dict(argvars, arg_errmsg, is_new, rettv);
} else {
- semsg(_(e_listdictarg), "extend()");
+ semsg(_(e_listdictarg), is_new ? "extendnew()" : "extend()");
}
}
+/// "extend(list, list [, idx])" function
+/// "extend(dict, dict [, action])" function
+static void f_extend(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ char *errmsg = N_("extend() argument");
+ extend(argvars, rettv, errmsg, false);
+}
+
+/// "extendnew(list, list [, idx])" function
+/// "extendnew(dict, dict [, action])" function
+static void f_extendnew(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ char *errmsg = N_("extendnew() argument");
+ extend(argvars, rettv, errmsg, true);
+}
+
/// "feedkeys()" function
static void f_feedkeys(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
@@ -2053,6 +2123,9 @@ static void findfilendir(typval_T *argvars, typval_T *rettv, int find_what)
}
if (*fname != NUL && !error) {
+ char *file_to_find = NULL;
+ char *search_ctx = NULL;
+
do {
if (rettv->v_type == VAR_STRING || rettv->v_type == VAR_LIST) {
xfree(fresult);
@@ -2063,13 +2136,17 @@ static void findfilendir(typval_T *argvars, typval_T *rettv, int find_what)
find_what, curbuf->b_ffname,
(find_what == FINDFILE_DIR
? ""
- : curbuf->b_p_sua));
+ : curbuf->b_p_sua),
+ &file_to_find, &search_ctx);
first = false;
if (fresult != NULL && rettv->v_type == VAR_LIST) {
- tv_list_append_string(rettv->vval.v_list, (const char *)fresult, -1);
+ tv_list_append_string(rettv->vval.v_list, fresult, -1);
}
} while ((rettv->v_type == VAR_LIST || --count > 0) && fresult != NULL);
+
+ xfree(file_to_find);
+ vim_findfile_cleanup(search_ctx);
}
if (rettv->v_type == VAR_STRING) {
@@ -2077,12 +2154,6 @@ static void findfilendir(typval_T *argvars, typval_T *rettv, int find_what)
}
}
-/// "filter()" function
-static void f_filter(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
-{
- filter_map(argvars, rettv, false);
-}
-
/// "finddir({fname}[, {path}[, {count}]])" function
static void f_finddir(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
@@ -2164,7 +2235,8 @@ static void f_fnamemodify(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
/// "foreg