commit 3051e8bbb11f1446729fc964083a62f475938207
parent ed0f46af62808849f586949c59393655151abeb7
Author: Jan Edmund Lazo <jan.lazo@mail.utoronto.ca>
Date: Mon, 6 Oct 2025 00:08:57 -0400
vim-patch:8.1.1981: the evalfunc.c file is too big
Problem: The evalfunc.c file is too big.
Solution: Move undo functions to undo.c. Move cmdline functions to
ex_getln.c. Move some container functions to list.c.
https://github.com/vim/vim/commit/08c308aeb5e7dfa18fa61f261b0bff79517a4883
Co-authored-by: Bram Moolenaar <Bram@vim.org>
Diffstat:
5 files changed, 436 insertions(+), 435 deletions(-)
diff --git a/src/nvim/errors.h b/src/nvim/errors.h
@@ -1,5 +1,7 @@
#pragma once
+#include <inttypes.h>
+
#include "nvim/gettext_defs.h"
#include "nvim/macros_defs.h"
@@ -101,6 +103,7 @@ EXTERN const char e_dictkey[] INIT(= N_("E716: Key not present in Dictionary: \"
EXTERN const char e_dictkey_len[] INIT(= N_("E716: Key not present in Dictionary: \"%.*s\""));
EXTERN const char e_listreq[] INIT(= N_("E714: List required"));
EXTERN const char e_listblobreq[] INIT(= N_("E897: List or Blob required"));
+EXTERN const char e_listblobarg[] INIT(= N_("E899: Argument of %s must be a List or Blob"));
EXTERN const char e_listdictarg[] INIT(= N_("E712: Argument of %s must be a List or Dictionary"));
EXTERN const char e_listdictblobarg[] INIT(= N_("E896: Argument of %s must be a List, Dictionary or Blob"));
EXTERN const char e_readerrf[] INIT(= N_("E47: Error while reading errorfile"));
@@ -141,6 +144,7 @@ EXTERN const char e_au_recursive[] INIT(= N_("E952: Autocommand caused recursive
EXTERN const char e_menu_only_exists_in_another_mode[]
INIT(= N_("E328: Menu only exists in another mode"));
EXTERN const char e_autocmd_close[] INIT(= N_("E813: Cannot close autocmd window"));
+EXTERN const char e_list_index_out_of_range_nr[] INIT(= N_("E684: List index out of range: %" PRId64));
EXTERN const char e_listarg[] INIT(= N_("E686: Argument of %s must be a List"));
EXTERN const char e_unsupportedoption[] INIT(= N_("E519: Option not supported"));
EXTERN const char e_fnametoolong[] INIT(= N_("E856: Filename too long"));
diff --git a/src/nvim/eval/executor.c b/src/nvim/eval/executor.c
@@ -15,9 +15,6 @@
#include "eval/executor.c.generated.h"
-char *e_list_index_out_of_range_nr
- = N_("E684: List index out of range: %" PRId64);
-
/// Handle "blob1 += blob2".
/// Returns OK or FAIL.
static int tv_op_blob(typval_T *tv1, const typval_T *tv2, const char *op)
diff --git a/src/nvim/eval/executor.h b/src/nvim/eval/executor.h
@@ -2,6 +2,4 @@
#include "nvim/eval/typval_defs.h" // IWYU pragma: keep
-extern char *e_list_index_out_of_range_nr;
-
#include "eval/executor.h.generated.h"
diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c
@@ -157,10 +157,7 @@ PRAGMA_DIAG_PUSH_IGNORE_IMPLICIT_FALLTHROUGH
PRAGMA_DIAG_POP
PRAGMA_DIAG_POP
-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");
@@ -398,34 +395,6 @@ static void f_abs(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
}
}
-/// "add(list, item)" function
-static void f_add(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
-{
- rettv->vval.v_number = 1; // Default: failed.
- if (argvars[0].v_type == VAR_LIST) {
- list_T *const l = argvars[0].vval.v_list;
- if (!value_check_lock(tv_list_locked(l), N_("add() argument"),
- TV_TRANSLATE)) {
- tv_list_append_tv(l, &argvars[1]);
- tv_copy(&argvars[0], rettv);
- }
- } else if (argvars[0].v_type == VAR_BLOB) {
- blob_T *const b = argvars[0].vval.v_blob;
- if (b != NULL
- && !value_check_lock(b->bv_lock, N_("add() argument"), TV_TRANSLATE)) {
- bool error = false;
- const varnumber_T n = tv_get_number_chk(&argvars[1], &error);
-
- if (!error) {
- ga_append(&b->bv_ga, (uint8_t)n);
- tv_copy(&argvars[0], rettv);
- }
- }
- } else {
- emsg(_(e_listblobreq));
- }
-}
-
/// "and(expr, expr)" function
static void f_and(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
@@ -853,125 +822,6 @@ 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)) {
- 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)) {
- n++;
- }
- });
-
- return n;
-}
-
-/// "count()" function
-static void f_count(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
-{
- varnumber_T n = 0;
- int ic = 0;
- bool error = false;
-
- if (argvars[2].v_type != VAR_UNKNOWN) {
- ic = (int)tv_get_number_chk(&argvars[2], &error);
- }
-
- 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);
- }
- if (!error) {
- n = count_list(argvars[0].vval.v_list, &argvars[1], idx, ic);
- }
- } 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
- && argvars[3].v_type != VAR_UNKNOWN) {
- emsg(_(e_invarg));
- } else {
- n = count_dict(argvars[0].vval.v_dict, &argvars[1], ic);
- }
- }
- } else if (!error) {
- semsg(_(e_argument_of_str_must_be_list_string_or_dictionary), "count()");
- }
- rettv->vval.v_number = n;
-}
-
/// "ctxget([{index}])" function
static void f_ctxget(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
@@ -1757,161 +1607,6 @@ static void f_flattennew(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
flatten_common(argvars, rettv, true);
}
-/// 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;
-
- 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)) {
- return;
- }
-
- 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;
- }
- }
- } 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);
- }
-}
-
-/// 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;
- if (d1 == NULL) {
- const bool locked = value_check_lock(VAR_FIXED, arg_errmsg, TV_TRANSLATE);
- (void)locked;
- assert(locked == true);
- return;
- }
- dict_T *const d2 = argvars[1].vval.v_dict;
- if (d2 == NULL) {
- // Do nothing
- tv_copy(&argvars[0], rettv);
- return;
- }
-
- if (!is_new && value_check_lock(d1->dv_lock, arg_errmsg, TV_TRANSLATE)) {
- return;
- }
-
- 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) {
- if (is_new) {
- tv_dict_unref(d1);
- }
- 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) {
- if (is_new) {
- tv_dict_unref(d1);
- }
- semsg(_(e_invarg2), action);
- return;
- }
- }
-
- 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), 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)
{
@@ -3535,81 +3230,6 @@ static void f_inputsecret(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
inputsecret_flag = false;
}
-/// "insert()" function
-static void f_insert(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
-{
- bool error = false;
-
- if (argvars[0].v_type == VAR_BLOB) {
- blob_T *const b = argvars[0].vval.v_blob;
-
- if (b == NULL
- || value_check_lock(b->bv_lock, N_("insert() argument"),
- TV_TRANSLATE)) {
- return;
- }
-
- int before = 0;
- const int len = tv_blob_len(b);
-
- if (argvars[2].v_type != VAR_UNKNOWN) {
- before = (int)tv_get_number_chk(&argvars[2], &error);
- if (error) {
- return; // type error; errmsg already given
- }
- if (before < 0 || before > len) {
- semsg(_(e_invarg2), tv_get_string(&argvars[2]));
- return;
- }
- }
- const int val = (int)tv_get_number_chk(&argvars[1], &error);
- if (error) {
- return;
- }
- if (val < 0 || val > 255) {
- semsg(_(e_invarg2), tv_get_string(&argvars[1]));
- return;
- }
-
- ga_grow(&b->bv_ga, 1);
- uint8_t *const p = (uint8_t *)b->bv_ga.ga_data;
- memmove(p + before + 1, p + before, (size_t)(len - before));
- *(p + before) = (uint8_t)val;
- b->bv_ga.ga_len++;
-
- tv_copy(&argvars[0], rettv);
- } else if (argvars[0].v_type != VAR_LIST) {
- semsg(_(e_listblobarg), "insert()");
- } else {
- list_T *l = argvars[0].vval.v_list;
- if (value_check_lock(tv_list_locked(l), N_("insert() argument"), TV_TRANSLATE)) {
- return;
- }
-
- int64_t before = 0;
- if (argvars[2].v_type != VAR_UNKNOWN) {
- before = tv_get_number_chk(&argvars[2], &error);
- }
- if (error) {
- // type error; errmsg already given
- return;
- }
-
- listitem_T *item = NULL;
- if (before != tv_list_len(l)) {
- item = tv_list_find(l, (int)before);
- if (item == NULL) {
- semsg(_(e_list_index_out_of_range_nr), before);
- l = NULL;
- }
- }
- if (l != NULL) {
- tv_list_insert_tv(l, &argvars[1], item);
- tv_copy(&argvars[0], rettv);
- }
- }
-}
-
/// "interrupt()" function
static void f_interrupt(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
@@ -5656,22 +5276,6 @@ static void f_reltimestr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
}
}
-/// "remove()" function
-static void f_remove(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
-{
- const char *const arg_errmsg = N_("remove() argument");
-
- if (argvars[0].v_type == VAR_DICT) {
- tv_dict_remove(argvars, rettv, arg_errmsg);
- } else if (argvars[0].v_type == VAR_BLOB) {
- tv_blob_remove(argvars, rettv, arg_errmsg);
- } else if (argvars[0].v_type == VAR_LIST) {
- tv_list_remove(argvars, rettv, arg_errmsg);
- } else {
- semsg(_(e_listdictblobarg), "remove()");
- }
-}
-
/// "repeat()" function
static void f_repeat(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
@@ -5740,40 +5344,6 @@ static void f_repeat(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
}
}
-/// "reverse({list})" function
-static void f_reverse(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
-{
- if (tv_check_for_string_or_list_or_blob_arg(argvars, 0) == FAIL) {
- return;
- }
-
- if (argvars[0].v_type == VAR_BLOB) {
- blob_T *const b = argvars[0].vval.v_blob;
- const int len = tv_blob_len(b);
-
- for (int i = 0; i < len / 2; i++) {
- const uint8_t tmp = tv_blob_get(b, i);
- tv_blob_set(b, i, tv_blob_get(b, len - i - 1));
- tv_blob_set(b, len - i - 1, tmp);
- }
- tv_blob_set_ret(rettv, b);
- } else if (argvars[0].v_type == VAR_STRING) {
- rettv->v_type = VAR_STRING;
- if (argvars[0].vval.v_string != NULL) {
- rettv->vval.v_string = reverse_text(argvars[0].vval.v_string);
- } else {
- rettv->vval.v_string = NULL;
- }
- } else if (argvars[0].v_type == VAR_LIST) {
- list_T *const l = argvars[0].vval.v_list;
- if (!value_check_lock(tv_list_locked(l), N_("reverse() argument"),
- TV_TRANSLATE)) {
- tv_list_reverse(l);
- tv_list_set_ret(rettv, l);
- }
- }
-}
-
/// Implementation of reduce() for list "argvars[0]", using the function "expr"
/// starting with the optional initial value argvars[2] and return the result in
/// "rettv".
diff --git a/src/nvim/eval/list.c b/src/nvim/eval/list.c
@@ -1,3 +1,5 @@
+// eval/list.c: List support and container (List, Dict, Blob) functions.
+
#include "nvim/errors.h"
#include "nvim/eval.h"
#include "nvim/eval/list.h"
@@ -7,6 +9,7 @@
#include "nvim/garray.h"
#include "nvim/globals.h"
#include "nvim/mbyte.h"
+#include "nvim/strings.h"
#include "nvim/vim_defs.h"
/// Enum used by filter(), map(), mapnew() and foreach()
@@ -19,6 +22,8 @@ typedef enum {
#include "eval/list.c.generated.h"
+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_argument_of_str_must_be_list_string_dictionary_or_blob[]
= N_("E1250: Argument of %s must be a List, String, Dictionary or Blob");
@@ -417,3 +422,430 @@ void f_foreach(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
filter_map(argvars, rettv, FILTERMAP_FOREACH);
}
+
+/// "add(list, item)" function
+void f_add(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ rettv->vval.v_number = 1; // Default: failed.
+ if (argvars[0].v_type == VAR_LIST) {
+ list_T *const l = argvars[0].vval.v_list;
+ if (!value_check_lock(tv_list_locked(l), N_("add() argument"),
+ TV_TRANSLATE)) {
+ tv_list_append_tv(l, &argvars[1]);
+ tv_copy(&argvars[0], rettv);
+ }
+ } else if (argvars[0].v_type == VAR_BLOB) {
+ blob_T *const b = argvars[0].vval.v_blob;
+ if (b != NULL
+ && !value_check_lock(b->bv_lock, N_("add() argument"), TV_TRANSLATE)) {
+ bool error = false;
+ const varnumber_T n = tv_get_number_chk(&argvars[1], &error);
+
+ if (!error) {
+ ga_append(&b->bv_ga, (uint8_t)n);
+ tv_copy(&argvars[0], rettv);
+ }
+ }
+ } else {
+ emsg(_(e_listblobreq));
+ }
+}
+
+/// 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)) {
+ 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)) {
+ n++;
+ }
+ });
+
+ return n;
+}
+
+/// "count()" function
+void f_count(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ varnumber_T n = 0;
+ int ic = 0;
+ bool error = false;
+
+ if (argvars[2].v_type != VAR_UNKNOWN) {
+ ic = (int)tv_get_number_chk(&argvars[2], &error);
+ }
+
+ 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);
+ }
+ if (!error) {
+ n = count_list(argvars[0].vval.v_list, &argvars[1], idx, ic);
+ }
+ } 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
+ && argvars[3].v_type != VAR_UNKNOWN) {
+ emsg(_(e_invarg));
+ } else {
+ n = count_dict(argvars[0].vval.v_dict, &argvars[1], ic);
+ }
+ }
+ } else if (!error) {
+ semsg(_(e_argument_of_str_must_be_list_string_or_dictionary), "count()");
+ }
+ rettv->vval.v_number = n;
+}
+
+/// 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;
+ if (d1 == NULL) {
+ const bool locked = value_check_lock(VAR_FIXED, arg_errmsg, TV_TRANSLATE);
+ (void)locked;
+ assert(locked == true);
+ return;
+ }
+ dict_T *const d2 = argvars[1].vval.v_dict;
+ if (d2 == NULL) {
+ // Do nothing
+ tv_copy(&argvars[0], rettv);
+ return;
+ }
+
+ if (!is_new && value_check_lock(d1->dv_lock, arg_errmsg, TV_TRANSLATE)) {
+ return;
+ }
+
+ 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) {
+ if (is_new) {
+ tv_dict_unref(d1);
+ }
+ 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) {
+ if (is_new) {
+ tv_dict_unref(d1);
+ }
+ semsg(_(e_invarg2), action);
+ return;
+ }
+ }
+
+ 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() 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;
+
+ 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)) {
+ return;
+ }
+
+ 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;
+ }
+ }
+ } 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);
+ }
+}
+
+/// "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), is_new ? "extendnew()" : "extend()");
+ }
+}
+
+/// "extend(list, list [, idx])" function
+/// "extend(dict, dict [, action])" function
+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
+void f_extendnew(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ char *errmsg = N_("extendnew() argument");
+ extend(argvars, rettv, errmsg, true);
+}
+
+/// "insert()" function
+void f_insert(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ bool error = false;
+
+ if (argvars[0].v_type == VAR_BLOB) {
+ blob_T *const b = argvars[0].vval.v_blob;
+
+ if (b == NULL
+ || value_check_lock(b->bv_lock, N_("insert() argument"),
+ TV_TRANSLATE)) {
+ return;
+ }
+
+ int before = 0;
+ const int len = tv_blob_len(b);
+
+ if (argvars[2].v_type != VAR_UNKNOWN) {
+ before = (int)tv_get_number_chk(&argvars[2], &error);
+ if (error) {
+ return; // type error; errmsg already given
+ }
+ if (before < 0 || before > len) {
+ semsg(_(e_invarg2), tv_get_string(&argvars[2]));
+ return;
+ }
+ }
+ const int val = (int)tv_get_number_chk(&argvars[1], &error);
+ if (error) {
+ return;
+ }
+ if (val < 0 || val > 255) {
+ semsg(_(e_invarg2), tv_get_string(&argvars[1]));
+ return;
+ }
+
+ ga_grow(&b->bv_ga, 1);
+ uint8_t *const p = (uint8_t *)b->bv_ga.ga_data;
+ memmove(p + before + 1, p + before, (size_t)(len - before));
+ *(p + before) = (uint8_t)val;
+ b->bv_ga.ga_len++;
+
+ tv_copy(&argvars[0], rettv);
+ } else if (argvars[0].v_type != VAR_LIST) {
+ semsg(_(e_listblobarg), "insert()");
+ } else {
+ list_T *l = argvars[0].vval.v_list;
+ if (value_check_lock(tv_list_locked(l), N_("insert() argument"), TV_TRANSLATE)) {
+ return;
+ }
+
+ int64_t before = 0;
+ if (argvars[2].v_type != VAR_UNKNOWN) {
+ before = tv_get_number_chk(&argvars[2], &error);
+ }
+ if (error) {
+ // type error; errmsg already given
+ return;
+ }
+
+ listitem_T *item = NULL;
+ if (before != tv_list_len(l)) {
+ item = tv_list_find(l, (int)before);
+ if (item == NULL) {
+ semsg(_(e_list_index_out_of_range_nr), before);
+ l = NULL;
+ }
+ }
+ if (l != NULL) {
+ tv_list_insert_tv(l, &argvars[1], item);
+ tv_copy(&argvars[0], rettv);
+ }
+ }
+}
+
+/// "remove()" function
+void f_remove(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ const char *const arg_errmsg = N_("remove() argument");
+
+ if (argvars[0].v_type == VAR_DICT) {
+ tv_dict_remove(argvars, rettv, arg_errmsg);
+ } else if (argvars[0].v_type == VAR_BLOB) {
+ tv_blob_remove(argvars, rettv, arg_errmsg);
+ } else if (argvars[0].v_type == VAR_LIST) {
+ tv_list_remove(argvars, rettv, arg_errmsg);
+ } else {
+ semsg(_(e_listdictblobarg), "remove()");
+ }
+}
+
+/// "reverse({list})" function
+void f_reverse(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ if (tv_check_for_string_or_list_or_blob_arg(argvars, 0) == FAIL) {
+ return;
+ }
+
+ if (argvars[0].v_type == VAR_BLOB) {
+ blob_T *const b = argvars[0].vval.v_blob;
+ const int len = tv_blob_len(b);
+
+ for (int i = 0; i < len / 2; i++) {
+ const uint8_t tmp = tv_blob_get(b, i);
+ tv_blob_set(b, i, tv_blob_get(b, len - i - 1));
+ tv_blob_set(b, len - i - 1, tmp);
+ }
+ tv_blob_set_ret(rettv, b);
+ } else if (argvars[0].v_type == VAR_STRING) {
+ rettv->v_type = VAR_STRING;
+ if (argvars[0].vval.v_string != NULL) {
+ rettv->vval.v_string = reverse_text(argvars[0].vval.v_string);
+ } else {
+ rettv->vval.v_string = NULL;
+ }
+ } else if (argvars[0].v_type == VAR_LIST) {
+ list_T *const l = argvars[0].vval.v_list;
+ if (!value_check_lock(tv_list_locked(l), N_("reverse() argument"),
+ TV_TRANSLATE)) {
+ tv_list_reverse(l);
+ tv_list_set_ret(rettv, l);
+ }
+ }
+}