1
0
forked from aniani/vim

patch 8.2.3871: list.c contains code for dict and blob

Problem:    List.c contains code for dict and blob.
Solution:   Refactor to put code where it belongs. (Yegappan Lakshmanan,
            closes #9386)
This commit is contained in:
Yegappan Lakshmanan
2021-12-22 18:19:26 +00:00
committed by Bram Moolenaar
parent 1aeccdb464
commit f973eeb491
13 changed files with 976 additions and 873 deletions

View File

@@ -410,24 +410,55 @@ blob_set_range(blob_T *dest, long n1, long n2, typval_T *src)
}
/*
* "remove({blob})" function
* "add(blob, item)" function
*/
void
blob_add(typval_T *argvars, typval_T *rettv)
{
blob_T *b = argvars[0].vval.v_blob;
int error = FALSE;
varnumber_T n;
if (b == NULL)
{
if (in_vim9script())
emsg(_(e_cannot_add_to_null_blob));
return;
}
if (value_check_lock(b->bv_lock, (char_u *)N_("add() argument"), TRUE))
return;
n = tv_get_number_chk(&argvars[1], &error);
if (error)
return;
ga_append(&b->bv_ga, (int)n);
copy_tv(&argvars[0], rettv);
}
/*
* "remove({blob}, {idx} [, {end}])" function
*/
void
blob_remove(typval_T *argvars, typval_T *rettv, char_u *arg_errmsg)
{
blob_T *b = argvars[0].vval.v_blob;
blob_T *newblob;
int error = FALSE;
long idx;
long end;
int len;
char_u *p;
if (b != NULL && value_check_lock(b->bv_lock, arg_errmsg, TRUE))
return;
idx = (long)tv_get_number_chk(&argvars[1], &error);
if (!error)
{
int len = blob_len(b);
char_u *p;
if (error)
return;
len = blob_len(b);
if (idx < 0)
// count from the end
@@ -444,10 +475,8 @@ blob_remove(typval_T *argvars, typval_T *rettv, char_u *arg_errmsg)
rettv->vval.v_number = (varnumber_T) *(p + idx);
mch_memmove(p + idx, p + idx + 1, (size_t)len - idx - 1);
--b->bv_ga.ga_len;
return;
}
else
{
blob_T *blob;
// Remove range of items, return blob with values.
end = (long)tv_get_number_chk(&argvars[2], &error);
@@ -461,27 +490,226 @@ blob_remove(typval_T *argvars, typval_T *rettv, char_u *arg_errmsg)
semsg(_(e_blobidx), end);
return;
}
blob = blob_alloc();
if (blob == NULL)
newblob = blob_alloc();
if (newblob == NULL)
return;
blob->bv_ga.ga_len = end - idx + 1;
if (ga_grow(&blob->bv_ga, end - idx + 1) == FAIL)
newblob->bv_ga.ga_len = end - idx + 1;
if (ga_grow(&newblob->bv_ga, end - idx + 1) == FAIL)
{
vim_free(blob);
vim_free(newblob);
return;
}
p = (char_u *)b->bv_ga.ga_data;
mch_memmove((char_u *)blob->bv_ga.ga_data, p + idx,
mch_memmove((char_u *)newblob->bv_ga.ga_data, p + idx,
(size_t)(end - idx + 1));
++blob->bv_refcount;
++newblob->bv_refcount;
rettv->v_type = VAR_BLOB;
rettv->vval.v_blob = blob;
rettv->vval.v_blob = newblob;
if (len - end - 1 > 0)
mch_memmove(p + idx, p + end + 1, (size_t)(len - end - 1));
b->bv_ga.ga_len -= end - idx + 1;
}
/*
* Implementation of map() and filter() for a Blob. Apply "expr" to every
* number in Blob "blob_arg" and return the result in "rettv".
*/
void
blob_filter_map(
blob_T *blob_arg,
filtermap_T filtermap,
typval_T *expr,
typval_T *rettv)
{
blob_T *b;
int i;
typval_T tv;
varnumber_T val;
blob_T *b_ret;
int idx = 0;
int rem;
if (filtermap == FILTERMAP_MAPNEW)
{
rettv->v_type = VAR_BLOB;
rettv->vval.v_blob = NULL;
}
if ((b = blob_arg) == NULL)
return;
b_ret = b;
if (filtermap == FILTERMAP_MAPNEW)
{
if (blob_copy(b, rettv) == FAIL)
return;
b_ret = rettv->vval.v_blob;
}
// set_vim_var_nr() doesn't set the type
set_vim_var_type(VV_KEY, VAR_NUMBER);
for (i = 0; i < b->bv_ga.ga_len; i++)
{
typval_T newtv;
tv.v_type = VAR_NUMBER;
val = blob_get(b, i);
tv.vval.v_number = val;
set_vim_var_nr(VV_KEY, idx);
if (filter_map_one(&tv, expr, filtermap, &newtv, &rem) == FAIL
|| did_emsg)
break;
if (newtv.v_type != VAR_NUMBER && newtv.v_type != VAR_BOOL)
{
clear_tv(&newtv);
emsg(_(e_invalblob));
break;
}
if (filtermap != FILTERMAP_FILTER)
{
if (newtv.vval.v_number != val)
blob_set(b_ret, i, newtv.vval.v_number);
}
else if (rem)
{
char_u *p = (char_u *)blob_arg->bv_ga.ga_data;
mch_memmove(p + i, p + i + 1,
(size_t)b->bv_ga.ga_len - i - 1);
--b->bv_ga.ga_len;
--i;
}
++idx;
}
}
/*
* "insert(blob, {item} [, {idx}])" function
*/
void
blob_insert_func(typval_T *argvars, typval_T *rettv)
{
blob_T *b = argvars[0].vval.v_blob;
long before = 0;
int error = FALSE;
int val, len;
char_u *p;
if (b == NULL)
{
if (in_vim9script())
emsg(_(e_cannot_add_to_null_blob));
return;
}
if (value_check_lock(b->bv_lock, (char_u *)N_("insert() argument"), TRUE))
return;
len = blob_len(b);
if (argvars[2].v_type != VAR_UNKNOWN)
{
before = (long)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;
}
}
val = tv_get_number_chk(&argvars[1], &error);
if (error)
return;
if (val < 0 || val > 255)
{
semsg(_(e_invarg2), tv_get_string(&argvars[1]));
return;
}
if (ga_grow(&b->bv_ga, 1) == FAIL)
return;
p = (char_u *)b->bv_ga.ga_data;
mch_memmove(p + before + 1, p + before, (size_t)len - before);
*(p + before) = val;
++b->bv_ga.ga_len;
copy_tv(&argvars[0], rettv);
}
/*
* reduce() Blob argvars[0] using the function 'funcname' with arguments in
* 'funcexe' starting with the initial value argvars[2] and return the result
* in 'rettv'.
*/
void
blob_reduce(
typval_T *argvars,
char_u *func_name,
funcexe_T *funcexe,
typval_T *rettv)
{
blob_T *b = argvars[0].vval.v_blob;
int called_emsg_start = called_emsg;
int r;
typval_T initial;
typval_T argv[3];
int i;
if (argvars[2].v_type == VAR_UNKNOWN)
{
if (b == NULL || b->bv_ga.ga_len == 0)
{
semsg(_(e_reduceempty), "Blob");
return;
}
initial.v_type = VAR_NUMBER;
initial.vval.v_number = blob_get(b, 0);
i = 1;
}
else if (argvars[2].v_type != VAR_NUMBER)
{
emsg(_(e_number_expected));
return;
}
else
{
initial = argvars[2];
i = 0;
}
copy_tv(&initial, rettv);
if (b == NULL)
return;
for ( ; i < b->bv_ga.ga_len; i++)
{
argv[0] = *rettv;
argv[1].v_type = VAR_NUMBER;
argv[1].vval.v_number = blob_get(b, i);
r = call_func(func_name, -1, rettv, 2, argv, funcexe);
clear_tv(&argv[0]);
if (r == FAIL || called_emsg != called_emsg_start)
return;
}
}
/*
* "reverse({blob})" function
*/
void
blob_reverse(blob_T *b, typval_T *rettv)
{
int i, len = blob_len(b);
for (i = 0; i < len / 2; i++)
{
int tmp = blob_get(b, i);
blob_set(b, i, blob_get(b, len - i - 1));
blob_set(b, len - i - 1, tmp);
}
rettv_blob_set(rettv, b);
}
/*

View File

@@ -896,13 +896,11 @@ eval_dict(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int literal)
int vim9script = in_vim9script();
int had_comma;
/*
* First check if it's not a curly-braces thing: {expr}.
* Must do this without evaluating, otherwise a function may be called
* twice. Unfortunately this means we need to call eval1() twice for the
* first item.
* But {} is an empty Dictionary.
*/
// First check if it's not a curly-braces thing: {expr}.
// Must do this without evaluating, otherwise a function may be called
// twice. Unfortunately this means we need to call eval1() twice for the
// first item.
// But {} is an empty Dictionary.
if (!vim9script
&& *curly_expr != '}'
&& eval1(&curly_expr, &tv, NULL) == OK
@@ -1183,6 +1181,251 @@ dict_equal(
return TRUE;
}
/*
* Count the number of times item "needle" occurs in Dict "d". Case is ignored
* if "ic" is TRUE.
*/
long
dict_count(dict_T *d, typval_T *needle, int ic)
{
int todo;
hashitem_T *hi;
long n = 0;
if (d == NULL)
return 0;
todo = (int)d->dv_hashtab.ht_used;
for (hi = d->dv_hashtab.ht_array; todo > 0; ++hi)
{
if (!HASHITEM_EMPTY(hi))
{
--todo;
if (tv_equal(&HI2DI(hi)->di_tv, needle, ic, FALSE))
++n;
}
}
return n;
}
/*
* extend() a Dict. Append Dict argvars[1] to Dict argvars[0] and return the
* resulting Dict in "rettv". "is_new" is TRUE for extendnew().
*/
void
dict_extend_func(
typval_T *argvars,
type_T *type,
char *func_name,
char_u *arg_errmsg,
int is_new,
typval_T *rettv)
{
dict_T *d1, *d2;
char_u *action;
int i;
d1 = argvars[0].vval.v_dict;
if (d1 == NULL)
{
emsg(_(e_cannot_extend_null_dict));
return;
}
d2 = argvars[1].vval.v_dict;
if ((is_new || !value_check_lock(d1->dv_lock, arg_errmsg, TRUE))
&& d2 != NULL)
{
if (is_new)
{
d1 = dict_copy(d1, FALSE, get_copyID());
if (d1 == NULL)
return;
}
// Check the third argument.
if (argvars[2].v_type != VAR_UNKNOWN)
{
static char *(av[]) = {"keep", "force", "error"};
action = tv_get_string_chk(&argvars[2]);
if (action == NULL)
return;
for (i = 0; i < 3; ++i)
if (STRCMP(action, av[i]) == 0)
break;
if (i == 3)
{
semsg(_(e_invarg2), action);
return;
}
}
else
action = (char_u *)"force";
if (type != NULL && check_typval_arg_type(type, &argvars[1],
func_name, 2) == FAIL)
return;
dict_extend(d1, d2, action, func_name);
if (is_new)
{
rettv->v_type = VAR_DICT;
rettv->vval.v_dict = d1;
rettv->v_lock = FALSE;
}
else
copy_tv(&argvars[0], rettv);
}
}
/*
* Implementation of map() and filter() for a Dict. Apply "expr" to every
* item in Dict "d" and return the result in "rettv".
*/
void
dict_filter_map(
dict_T *d,
filtermap_T filtermap,
type_T *argtype,
char *func_name,
char_u *arg_errmsg,
typval_T *expr,
typval_T *rettv)
{
int prev_lock;
dict_T *d_ret = NULL;
hashtab_T *ht;
hashitem_T *hi;
dictitem_T *di;
int todo;
int rem;
if (filtermap == FILTERMAP_MAPNEW)
{
rettv->v_type = VAR_DICT;
rettv->vval.v_dict = NULL;
}
if (d == NULL
|| (filtermap == FILTERMAP_FILTER
&& value_check_lock(d->dv_lock, arg_errmsg, TRUE)))
return;
prev_lock = d->dv_lock;
if (filtermap == FILTERMAP_MAPNEW)
{
if (rettv_dict_alloc(rettv) == FAIL)
return;
d_ret = rettv->vval.v_dict;
}
if (filtermap != FILTERMAP_FILTER && d->dv_lock == 0)
d->dv_lock = VAR_LOCKED;
ht = &d->dv_hashtab;
hash_lock(ht);
todo = (int)ht->ht_used;
for (hi = ht->ht_array; todo > 0; ++hi)
{
if (!HASHITEM_EMPTY(hi))
{
int r;
typval_T newtv;
--todo;
di = HI2DI(hi);
if (filtermap == FILTERMAP_MAP
&& (value_check_lock(di->di_tv.v_lock,
arg_errmsg, TRUE)
|| var_check_ro(di->di_flags,
arg_errmsg, TRUE)))
break;
set_vim_var_string(VV_KEY, di->di_key, -1);
newtv.v_type = VAR_UNKNOWN;
r = filter_map_one(&di->di_tv, expr, filtermap,
&newtv, &rem);
clear_tv(get_vim_var_tv(VV_KEY));
if (r == FAIL || did_emsg)
{
clear_tv(&newtv);
break;
}
if (filtermap == FILTERMAP_MAP)
{
if (argtype != NULL && check_typval_arg_type(
argtype->tt_member, &newtv,
func_name, 0) == FAIL)
{
clear_tv(&newtv);
break;
}
// map(): replace the dict item value
clear_tv(&di->di_tv);
newtv.v_lock = 0;
di->di_tv = newtv;
}
else if (filtermap == FILTERMAP_MAPNEW)
{
// mapnew(): add the item value to the new dict
r = dict_add_tv(d_ret, (char *)di->di_key, &newtv);
clear_tv(&newtv);
if (r == FAIL)
break;
}
else if (filtermap == FILTERMAP_FILTER && rem)
{
// filter(false): remove the item from the dict
if (var_check_fixed(di->di_flags, arg_errmsg, TRUE)
|| var_check_ro(di->di_flags, arg_errmsg, TRUE))
break;
dictitem_remove(d, di);
}
}
}
hash_unlock(ht);
d->dv_lock = prev_lock;
}
/*
* "remove({dict})" function
*/
void
dict_remove(typval_T *argvars, typval_T *rettv, char_u *arg_errmsg)
{
dict_T *d;
char_u *key;
dictitem_T *di;
if (argvars[2].v_type != VAR_UNKNOWN)
{
semsg(_(e_too_many_arguments_for_function_str), "remove()");
return;
}
d = argvars[0].vval.v_dict;
if (d == NULL || value_check_lock(d->dv_lock, arg_errmsg, TRUE))
return;
key = tv_get_string_chk(&argvars[1]);
if (key == NULL)
return;
di = dict_find(d, key, -1);
if (di == NULL)
{
semsg(_(e_dictkey), key);
return;
}
if (var_check_fixed(di->di_flags, arg_errmsg, TRUE)
|| var_check_ro(di->di_flags, arg_errmsg, TRUE))
return;
*rettv = di->di_tv;
init_tv(&di->di_tv);
dictitem_remove(d, di);
}
/*
* Turn a dict into a list:
* "what" == 0: list of keys
@@ -1338,36 +1581,4 @@ f_has_key(typval_T *argvars, typval_T *rettv)
tv_get_string(&argvars[1]), -1) != NULL;
}
/*
* "remove({dict})" function
*/
void
dict_remove(typval_T *argvars, typval_T *rettv, char_u *arg_errmsg)
{
dict_T *d;
char_u *key;
dictitem_T *di;
if (argvars[2].v_type != VAR_UNKNOWN)
semsg(_(e_too_many_arguments_for_function_str), "remove()");
else if ((d = argvars[0].vval.v_dict) != NULL
&& !value_check_lock(d->dv_lock, arg_errmsg, TRUE))
{
key = tv_get_string_chk(&argvars[1]);
if (key != NULL)
{
di = dict_find(d, key, -1);
if (di == NULL)
semsg(_(e_dictkey), key);
else if (!var_check_fixed(di->di_flags, arg_errmsg, TRUE)
&& !var_check_ro(di->di_flags, arg_errmsg, TRUE))
{
*rettv = di->di_tv;
init_tv(&di->di_tv);
dictitem_remove(d, di);
}
}
}
}
#endif // defined(FEAT_EVAL)

File diff suppressed because it is too large Load Diff

View File

@@ -18,7 +18,12 @@ int blob_slice_or_index(blob_T *blob, int is_range, varnumber_T n1, varnumber_T
int check_blob_index(long bloblen, varnumber_T n1, int quiet);
int check_blob_range(long bloblen, varnumber_T n1, varnumber_T n2, int quiet);
int blob_set_range(blob_T *dest, long n1, long n2, typval_T *src);
void blob_add(typval_T *argvars, typval_T *rettv);
void blob_remove(typval_T *argvars, typval_T *rettv, char_u *arg_errmsg);
void blob_filter_map(blob_T *blob_arg, filtermap_T filtermap, typval_T *expr, typval_T *rettv);
void blob_insert_func(typval_T *argvars, typval_T *rettv);
void blob_reduce(typval_T *argvars, char_u *func_name, funcexe_T *funcexe, typval_T *rettv);
void blob_reverse(blob_T *b, typval_T *rettv);
void f_blob2list(typval_T *argvars, typval_T *rettv);
void f_list2blob(typval_T *argvars, typval_T *rettv);
/* vim: set ft=c : */

View File

@@ -39,10 +39,13 @@ int eval_dict(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int literal);
void dict_extend(dict_T *d1, dict_T *d2, char_u *action, char *func_name);
dictitem_T *dict_lookup(hashitem_T *hi);
int dict_equal(dict_T *d1, dict_T *d2, int ic, int recursive);
long dict_count(dict_T *d, typval_T *needle, int ic);
void dict_extend_func(typval_T *argvars, type_T *type, char *func_name, char_u *arg_errmsg, int is_new, typval_T *rettv);
void dict_filter_map(dict_T *d, filtermap_T filtermap, type_T *argtype, char *func_name, char_u *arg_errmsg, typval_T *expr, typval_T *rettv);
void dict_remove(typval_T *argvars, typval_T *rettv, char_u *arg_errmsg);
void f_items(typval_T *argvars, typval_T *rettv);
void f_keys(typval_T *argvars, typval_T *rettv);
void f_values(typval_T *argvars, typval_T *rettv);
void dict_set_items_ro(dict_T *di);
void f_has_key(typval_T *argvars, typval_T *rettv);
void dict_remove(typval_T *argvars, typval_T *rettv, char_u *arg_errmsg);
/* vim: set ft=c : */

View File

@@ -50,6 +50,7 @@ void init_static_list(staticList10_T *sl);
void f_list2str(typval_T *argvars, typval_T *rettv);
void f_sort(typval_T *argvars, typval_T *rettv);
void f_uniq(typval_T *argvars, typval_T *rettv);
int filter_map_one(typval_T *tv, typval_T *expr, filtermap_T filtermap, typval_T *newtv, int *remp);
void f_filter(typval_T *argvars, typval_T *rettv);
void f_map(typval_T *argvars, typval_T *rettv);
void f_mapnew(typval_T *argvars, typval_T *rettv);

View File

@@ -21,6 +21,9 @@ void sort_strings(char_u **files, int count);
int has_non_ascii(char_u *s);
char_u *concat_str(char_u *str1, char_u *str2);
char_u *string_quote(char_u *str, int function);
long string_count(char_u *haystack, char_u *needle, int ic);
void string_filter_map(char_u *str, filtermap_T filtermap, typval_T *expr, typval_T *rettv);
void string_reduce(typval_T *argvars, char_u *func_name, funcexe_T *funcexe, typval_T *rettv);
void f_byteidx(typval_T *argvars, typval_T *rettv);
void f_byteidxcomp(typval_T *argvars, typval_T *rettv);
void f_charidx(typval_T *argvars, typval_T *rettv);

View File

@@ -764,7 +764,6 @@ concat_str(char_u *str1, char_u *str2)
}
#if defined(FEAT_EVAL) || defined(PROTO)
/*
* Return string "str" in ' quotes, doubling ' characters.
* If "str" is NULL an empty string is assumed.
@@ -809,6 +808,185 @@ string_quote(char_u *str, int function)
return s;
}
/*
* Count the number of times "needle" occurs in string "haystack". Case is
* ignored if "ic" is TRUE.
*/
long
string_count(char_u *haystack, char_u *needle, int ic)
{
long n = 0;
char_u *p = haystack;
char_u *next;
if (p == NULL || needle == NULL || *needle == NUL)
return 0;
if (ic)
{
size_t len = STRLEN(needle);
while (*p != NUL)
{
if (MB_STRNICMP(p, needle, len) == 0)
{
++n;
p += len;
}
else
MB_PTR_ADV(p);
}
}
else
while ((next = (char_u *)strstr((char *)p, (char *)needle)) != NULL)
{
++n;
p = next + STRLEN(needle);
}
return n;
}
/*
* Make a typval_T of the first character of "input" and store it in "output".
* Return OK or FAIL.
*/
static int
copy_first_char_to_tv(char_u *input, typval_T *output)
{
char_u buf[MB_MAXBYTES + 1];
int len;
if (input == NULL || output == NULL)
return FAIL;
len = has_mbyte ? mb_ptr2len(input) : 1;
STRNCPY(buf, input, len);
buf[len] = NUL;
output->v_type = VAR_STRING;
output->vval.v_string = vim_strsave(buf);
return output->vval.v_string == NULL ? FAIL : OK;
}
/*
* Implementation of map() and filter() for a String. Apply "expr" to every
* character in string "str" and return the result in "rettv".
*/
void
string_filter_map(
char_u *str,
filtermap_T filtermap,
typval_T *expr,
typval_T *rettv)
{
char_u *p;
typval_T tv;
garray_T ga;
int len = 0;
int idx = 0;
int rem;
rettv->v_type = VAR_STRING;
rettv->vval.v_string = NULL;
// set_vim_var_nr() doesn't set the type
set_vim_var_type(VV_KEY, VAR_NUMBER);
ga_init2(&ga, (int)sizeof(char), 80);
for (p = str; *p != NUL; p += len)
{
typval_T newtv;
if (copy_first_char_to_tv(p, &tv) == FAIL)
break;
len = (int)STRLEN(tv.vval.v_string);
set_vim_var_nr(VV_KEY, idx);
if (filter_map_one(&tv, expr, filtermap, &newtv, &rem) == FAIL
|| did_emsg)
break;
if (did_emsg)
{
clear_tv(&newtv);
clear_tv(&tv);
break;
}
else if (filtermap != FILTERMAP_FILTER)
{
if (newtv.v_type != VAR_STRING)
{
clear_tv(&newtv);
clear_tv(&tv);
emsg(_(e_stringreq));
break;
}
else
ga_concat(&ga, newtv.vval.v_string);
}
else if (!rem)
ga_concat(&ga, tv.vval.v_string);
clear_tv(&newtv);
clear_tv(&tv);
++idx;
}
ga_append(&ga, NUL);
rettv->vval.v_string = ga.ga_data;
}
/*
* reduce() String argvars[0] using the function 'funcname' with arguments in
* 'funcexe' starting with the initial value argvars[2] and return the result
* in 'rettv'.
*/
void
string_reduce(
typval_T *argvars,
char_u *func_name,
funcexe_T *funcexe,
typval_T *rettv)
{
char_u *p = tv_get_string(&argvars[0]);
int len;
typval_T argv[3];
int r;
int called_emsg_start = called_emsg;
if (argvars[2].v_type == VAR_UNKNOWN)
{
if (*p == NUL)
{
semsg(_(e_reduceempty), "String");
return;
}
if (copy_first_char_to_tv(p, rettv) == FAIL)
return;
p += STRLEN(rettv->vval.v_string);
}
else if (argvars[2].v_type != VAR_STRING)
{
semsg(_(e_string_expected_for_argument_nr), 3);
return;
}
else
copy_tv(&argvars[2], rettv);
for ( ; *p != NUL; p += len)
{
argv[0] = *rettv;
if (copy_first_char_to_tv(p, &argv[1]) == FAIL)
break;
len = (int)STRLEN(argv[1].vval.v_string);
r = call_func(func_name, -1, rettv, 2, argv, funcexe);
clear_tv(&argv[0]);
clear_tv(&argv[1]);
if (r == FAIL || called_emsg != called_emsg_start)
return;
}
}
static void
byteidx(typval_T *argvars, typval_T *rettv, int comp UNUSED)
{
@@ -1686,9 +1864,6 @@ f_trim(typval_T *argvars, typval_T *rettv)
rettv->vval.v_string = vim_strnsave(head, tail - head);
}
#endif
#if defined(FEAT_EVAL)
static char *e_printf = N_("E766: Insufficient arguments for printf()");
/*
@@ -1766,6 +1941,7 @@ tv_float(typval_T *tvs, int *idxp)
return f;
}
# endif
#endif
#ifdef FEAT_FLOAT

View File

@@ -4492,3 +4492,11 @@ typedef struct {
int sve_did_save;
hashtab_T sve_hashtab;
} save_v_event_T;
// Enum used by filter(), map() and mapnew()
typedef enum {
FILTERMAP_FILTER,
FILTERMAP_MAP,
FILTERMAP_MAPNEW
} filtermap_T;

View File

@@ -182,6 +182,7 @@ func Test_filter_map_string()
call assert_equal('', map('abc', LSTART i, x LMIDDLE '' LEND))
call assert_equal('', map('', "v:val == 'a'"))
call assert_equal('', map(test_null_string(), "v:val == 'a'"))
call assert_fails('echo map("abc", "10")', 'E928:')
END
call CheckLegacyAndVim9Success(lines)

View File

@@ -783,6 +783,32 @@ func Test_dict_lock_map()
call CheckScriptFailure(lines, 'E741:')
endfunc
" Lock one item in a list
func Test_list_item_lock_map()
let lines =<< trim END
VAR l = [99, 100, 101]
lockvar l[1]
call assert_fails("echo map(l, 'v:val + 200')", 'E741:')
call assert_equal([299, 100, 101], l)
END
" This won't work in a :def function
call CheckTransLegacySuccess(lines)
call CheckTransVim9Success(lines)
endfunc
" Lock one item in a dict
func Test_dict_item_lock_map()
let lines =<< trim END
VAR d = {'a': 99, 'b': 100, 'c': 101}
lockvar d.b
call assert_fails("echo map(d, 'v:val + 200')", 'E741:')
call assert_equal({'a': 299, 'b': 100, 'c': 101}, d)
END
" This won't work in a :def function
call CheckTransLegacySuccess(lines)
call CheckTransVim9Success(lines)
endfunc
" No extend() after lock on dict item
func Test_dict_lock_extend()
let d = {'a': 99, 'b': 100}

View File

@@ -1548,4 +1548,20 @@ func Test_sort_with_marks()
close!
endfunc
" Test for sort() using a dict function
func Test_sort_using_dict_func()
func DictSort(a, b) dict
if self.order == 'reverse'
return a:b - a:a
else
return a:a - a:b
endif
endfunc
let d = #{order: ''}
call assert_equal([1, 2, 3], sort([2, 1, 3], 'DictSort', d))
let d = #{order: 'reverse'}
call assert_equal([3, 2, 1], sort([2, 1, 3], 'DictSort', d))
delfunc DictSort
endfunc
" vim: shiftwidth=2 sts=2 expandtab

View File

@@ -749,6 +749,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
3871,
/**/
3870,
/**/