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.

08c308aeb5

Co-authored-by: Bram Moolenaar <Bram@vim.org>
This commit is contained in:
Jan Edmund Lazo
2025-10-06 00:08:57 -04:00
parent ed0f46af62
commit 3051e8bbb1
5 changed files with 436 additions and 435 deletions

View File

@@ -1,5 +1,7 @@
#pragma once #pragma once
#include <inttypes.h>
#include "nvim/gettext_defs.h" #include "nvim/gettext_defs.h"
#include "nvim/macros_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_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_listreq[] INIT(= N_("E714: List required"));
EXTERN const char e_listblobreq[] INIT(= N_("E897: List or Blob 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_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_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")); 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[] EXTERN const char e_menu_only_exists_in_another_mode[]
INIT(= N_("E328: 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_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_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_unsupportedoption[] INIT(= N_("E519: Option not supported"));
EXTERN const char e_fnametoolong[] INIT(= N_("E856: Filename too long")); EXTERN const char e_fnametoolong[] INIT(= N_("E856: Filename too long"));

View File

@@ -15,9 +15,6 @@
#include "eval/executor.c.generated.h" #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". /// Handle "blob1 += blob2".
/// Returns OK or FAIL. /// Returns OK or FAIL.
static int tv_op_blob(typval_T *tv1, const typval_T *tv2, const char *op) static int tv_op_blob(typval_T *tv1, const typval_T *tv2, const char *op)

View File

@@ -2,6 +2,4 @@
#include "nvim/eval/typval_defs.h" // IWYU pragma: keep #include "nvim/eval/typval_defs.h" // IWYU pragma: keep
extern char *e_list_index_out_of_range_nr;
#include "eval/executor.h.generated.h" #include "eval/executor.h.generated.h"

View File

@@ -157,10 +157,7 @@ PRAGMA_DIAG_PUSH_IGNORE_IMPLICIT_FALLTHROUGH
PRAGMA_DIAG_POP PRAGMA_DIAG_POP
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_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[] static const char e_invalid_submatch_number_nr[]
= N_("E935: Invalid submatch number: %d"); = 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_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 /// "and(expr, expr)" function
static void f_and(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 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); 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 /// "ctxget([{index}])" function
static void f_ctxget(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 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); 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 /// "feedkeys()" function
static void f_feedkeys(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 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; 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 /// "interrupt()" function
static void f_interrupt(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 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 /// "repeat()" function
static void f_repeat(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 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" /// Implementation of reduce() for list "argvars[0]", using the function "expr"
/// starting with the optional initial value argvars[2] and return the result in /// starting with the optional initial value argvars[2] and return the result in
/// "rettv". /// "rettv".

View File

@@ -1,3 +1,5 @@
// eval/list.c: List support and container (List, Dict, Blob) functions.
#include "nvim/errors.h" #include "nvim/errors.h"
#include "nvim/eval.h" #include "nvim/eval.h"
#include "nvim/eval/list.h" #include "nvim/eval/list.h"
@@ -7,6 +9,7 @@
#include "nvim/garray.h" #include "nvim/garray.h"
#include "nvim/globals.h" #include "nvim/globals.h"
#include "nvim/mbyte.h" #include "nvim/mbyte.h"
#include "nvim/strings.h"
#include "nvim/vim_defs.h" #include "nvim/vim_defs.h"
/// Enum used by filter(), map(), mapnew() and foreach() /// Enum used by filter(), map(), mapnew() and foreach()
@@ -19,6 +22,8 @@ typedef enum {
#include "eval/list.c.generated.h" #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[] 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"); = 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); 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);
}
}
}