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:
committed by
Bram Moolenaar
parent
1aeccdb464
commit
f973eeb491
260
src/blob.c
260
src/blob.c
@@ -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
|
void
|
||||||
blob_remove(typval_T *argvars, typval_T *rettv, char_u *arg_errmsg)
|
blob_remove(typval_T *argvars, typval_T *rettv, char_u *arg_errmsg)
|
||||||
{
|
{
|
||||||
blob_T *b = argvars[0].vval.v_blob;
|
blob_T *b = argvars[0].vval.v_blob;
|
||||||
|
blob_T *newblob;
|
||||||
int error = FALSE;
|
int error = FALSE;
|
||||||
long idx;
|
long idx;
|
||||||
long end;
|
long end;
|
||||||
|
int len;
|
||||||
|
char_u *p;
|
||||||
|
|
||||||
if (b != NULL && value_check_lock(b->bv_lock, arg_errmsg, TRUE))
|
if (b != NULL && value_check_lock(b->bv_lock, arg_errmsg, TRUE))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
idx = (long)tv_get_number_chk(&argvars[1], &error);
|
idx = (long)tv_get_number_chk(&argvars[1], &error);
|
||||||
if (!error)
|
if (error)
|
||||||
{
|
return;
|
||||||
int len = blob_len(b);
|
|
||||||
char_u *p;
|
len = blob_len(b);
|
||||||
|
|
||||||
if (idx < 0)
|
if (idx < 0)
|
||||||
// count from the end
|
// 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);
|
rettv->vval.v_number = (varnumber_T) *(p + idx);
|
||||||
mch_memmove(p + idx, p + idx + 1, (size_t)len - idx - 1);
|
mch_memmove(p + idx, p + idx + 1, (size_t)len - idx - 1);
|
||||||
--b->bv_ga.ga_len;
|
--b->bv_ga.ga_len;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
blob_T *blob;
|
|
||||||
|
|
||||||
// Remove range of items, return blob with values.
|
// Remove range of items, return blob with values.
|
||||||
end = (long)tv_get_number_chk(&argvars[2], &error);
|
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);
|
semsg(_(e_blobidx), end);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
blob = blob_alloc();
|
newblob = blob_alloc();
|
||||||
if (blob == NULL)
|
if (newblob == NULL)
|
||||||
return;
|
return;
|
||||||
blob->bv_ga.ga_len = end - idx + 1;
|
newblob->bv_ga.ga_len = end - idx + 1;
|
||||||
if (ga_grow(&blob->bv_ga, end - idx + 1) == FAIL)
|
if (ga_grow(&newblob->bv_ga, end - idx + 1) == FAIL)
|
||||||
{
|
{
|
||||||
vim_free(blob);
|
vim_free(newblob);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
p = (char_u *)b->bv_ga.ga_data;
|
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));
|
(size_t)(end - idx + 1));
|
||||||
++blob->bv_refcount;
|
++newblob->bv_refcount;
|
||||||
rettv->v_type = VAR_BLOB;
|
rettv->v_type = VAR_BLOB;
|
||||||
rettv->vval.v_blob = blob;
|
rettv->vval.v_blob = newblob;
|
||||||
|
|
||||||
if (len - end - 1 > 0)
|
if (len - end - 1 > 0)
|
||||||
mch_memmove(p + idx, p + end + 1, (size_t)(len - end - 1));
|
mch_memmove(p + idx, p + end + 1, (size_t)(len - end - 1));
|
||||||
b->bv_ga.ga_len -= end - idx + 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
289
src/dict.c
289
src/dict.c
@@ -896,13 +896,11 @@ eval_dict(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int literal)
|
|||||||
int vim9script = in_vim9script();
|
int vim9script = in_vim9script();
|
||||||
int had_comma;
|
int had_comma;
|
||||||
|
|
||||||
/*
|
// First check if it's not a curly-braces thing: {expr}.
|
||||||
* First check if it's not a curly-braces thing: {expr}.
|
// Must do this without evaluating, otherwise a function may be called
|
||||||
* Must do this without evaluating, otherwise a function may be called
|
// twice. Unfortunately this means we need to call eval1() twice for the
|
||||||
* twice. Unfortunately this means we need to call eval1() twice for the
|
// first item.
|
||||||
* first item.
|
// But {} is an empty Dictionary.
|
||||||
* But {} is an empty Dictionary.
|
|
||||||
*/
|
|
||||||
if (!vim9script
|
if (!vim9script
|
||||||
&& *curly_expr != '}'
|
&& *curly_expr != '}'
|
||||||
&& eval1(&curly_expr, &tv, NULL) == OK
|
&& eval1(&curly_expr, &tv, NULL) == OK
|
||||||
@@ -1183,6 +1181,251 @@ dict_equal(
|
|||||||
return TRUE;
|
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:
|
* Turn a dict into a list:
|
||||||
* "what" == 0: list of keys
|
* "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;
|
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)
|
#endif // defined(FEAT_EVAL)
|
||||||
|
813
src/list.c
813
src/list.c
File diff suppressed because it is too large
Load Diff
@@ -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_index(long bloblen, varnumber_T n1, int quiet);
|
||||||
int check_blob_range(long bloblen, varnumber_T n1, varnumber_T n2, 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);
|
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_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_blob2list(typval_T *argvars, typval_T *rettv);
|
||||||
void f_list2blob(typval_T *argvars, typval_T *rettv);
|
void f_list2blob(typval_T *argvars, typval_T *rettv);
|
||||||
/* vim: set ft=c : */
|
/* vim: set ft=c : */
|
||||||
|
@@ -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);
|
void dict_extend(dict_T *d1, dict_T *d2, char_u *action, char *func_name);
|
||||||
dictitem_T *dict_lookup(hashitem_T *hi);
|
dictitem_T *dict_lookup(hashitem_T *hi);
|
||||||
int dict_equal(dict_T *d1, dict_T *d2, int ic, int recursive);
|
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_items(typval_T *argvars, typval_T *rettv);
|
||||||
void f_keys(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 f_values(typval_T *argvars, typval_T *rettv);
|
||||||
void dict_set_items_ro(dict_T *di);
|
void dict_set_items_ro(dict_T *di);
|
||||||
void f_has_key(typval_T *argvars, typval_T *rettv);
|
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 : */
|
/* vim: set ft=c : */
|
||||||
|
@@ -50,6 +50,7 @@ void init_static_list(staticList10_T *sl);
|
|||||||
void f_list2str(typval_T *argvars, typval_T *rettv);
|
void f_list2str(typval_T *argvars, typval_T *rettv);
|
||||||
void f_sort(typval_T *argvars, typval_T *rettv);
|
void f_sort(typval_T *argvars, typval_T *rettv);
|
||||||
void f_uniq(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_filter(typval_T *argvars, typval_T *rettv);
|
||||||
void f_map(typval_T *argvars, typval_T *rettv);
|
void f_map(typval_T *argvars, typval_T *rettv);
|
||||||
void f_mapnew(typval_T *argvars, typval_T *rettv);
|
void f_mapnew(typval_T *argvars, typval_T *rettv);
|
||||||
|
@@ -21,6 +21,9 @@ void sort_strings(char_u **files, int count);
|
|||||||
int has_non_ascii(char_u *s);
|
int has_non_ascii(char_u *s);
|
||||||
char_u *concat_str(char_u *str1, char_u *str2);
|
char_u *concat_str(char_u *str1, char_u *str2);
|
||||||
char_u *string_quote(char_u *str, int function);
|
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_byteidx(typval_T *argvars, typval_T *rettv);
|
||||||
void f_byteidxcomp(typval_T *argvars, typval_T *rettv);
|
void f_byteidxcomp(typval_T *argvars, typval_T *rettv);
|
||||||
void f_charidx(typval_T *argvars, typval_T *rettv);
|
void f_charidx(typval_T *argvars, typval_T *rettv);
|
||||||
|
184
src/strings.c
184
src/strings.c
@@ -764,7 +764,6 @@ concat_str(char_u *str1, char_u *str2)
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if defined(FEAT_EVAL) || defined(PROTO)
|
#if defined(FEAT_EVAL) || defined(PROTO)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Return string "str" in ' quotes, doubling ' characters.
|
* Return string "str" in ' quotes, doubling ' characters.
|
||||||
* If "str" is NULL an empty string is assumed.
|
* If "str" is NULL an empty string is assumed.
|
||||||
@@ -809,6 +808,185 @@ string_quote(char_u *str, int function)
|
|||||||
return s;
|
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
|
static void
|
||||||
byteidx(typval_T *argvars, typval_T *rettv, int comp UNUSED)
|
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);
|
rettv->vval.v_string = vim_strnsave(head, tail - head);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(FEAT_EVAL)
|
|
||||||
static char *e_printf = N_("E766: Insufficient arguments for printf()");
|
static char *e_printf = N_("E766: Insufficient arguments for printf()");
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -1766,6 +1941,7 @@ tv_float(typval_T *tvs, int *idxp)
|
|||||||
return f;
|
return f;
|
||||||
}
|
}
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef FEAT_FLOAT
|
#ifdef FEAT_FLOAT
|
||||||
|
@@ -4492,3 +4492,11 @@ typedef struct {
|
|||||||
int sve_did_save;
|
int sve_did_save;
|
||||||
hashtab_T sve_hashtab;
|
hashtab_T sve_hashtab;
|
||||||
} save_v_event_T;
|
} save_v_event_T;
|
||||||
|
|
||||||
|
// Enum used by filter(), map() and mapnew()
|
||||||
|
typedef enum {
|
||||||
|
FILTERMAP_FILTER,
|
||||||
|
FILTERMAP_MAP,
|
||||||
|
FILTERMAP_MAPNEW
|
||||||
|
} filtermap_T;
|
||||||
|
|
||||||
|
@@ -182,6 +182,7 @@ func Test_filter_map_string()
|
|||||||
call assert_equal('', map('abc', LSTART i, x LMIDDLE '' LEND))
|
call assert_equal('', map('abc', LSTART i, x LMIDDLE '' LEND))
|
||||||
call assert_equal('', map('', "v:val == 'a'"))
|
call assert_equal('', map('', "v:val == 'a'"))
|
||||||
call assert_equal('', map(test_null_string(), "v:val == 'a'"))
|
call assert_equal('', map(test_null_string(), "v:val == 'a'"))
|
||||||
|
call assert_fails('echo map("abc", "10")', 'E928:')
|
||||||
END
|
END
|
||||||
call CheckLegacyAndVim9Success(lines)
|
call CheckLegacyAndVim9Success(lines)
|
||||||
|
|
||||||
|
@@ -783,6 +783,32 @@ func Test_dict_lock_map()
|
|||||||
call CheckScriptFailure(lines, 'E741:')
|
call CheckScriptFailure(lines, 'E741:')
|
||||||
endfunc
|
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
|
" No extend() after lock on dict item
|
||||||
func Test_dict_lock_extend()
|
func Test_dict_lock_extend()
|
||||||
let d = {'a': 99, 'b': 100}
|
let d = {'a': 99, 'b': 100}
|
||||||
|
@@ -1548,4 +1548,20 @@ func Test_sort_with_marks()
|
|||||||
close!
|
close!
|
||||||
endfunc
|
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
|
" vim: shiftwidth=2 sts=2 expandtab
|
||||||
|
@@ -749,6 +749,8 @@ static char *(features[]) =
|
|||||||
|
|
||||||
static int included_patches[] =
|
static int included_patches[] =
|
||||||
{ /* Add new patch number below this line */
|
{ /* Add new patch number below this line */
|
||||||
|
/**/
|
||||||
|
3871,
|
||||||
/**/
|
/**/
|
||||||
3870,
|
3870,
|
||||||
/**/
|
/**/
|
||||||
|
Reference in New Issue
Block a user