mirror of
https://github.com/vim/vim.git
synced 2025-09-25 03:54:15 -04:00
patch 9.1.0027: Vim is missing a foreach() func
Problem: Vim is missing a foreach() func Solution: Implement foreach({expr1}, {expr2}) function, which applies {expr2} for each item in {expr1} without changing it (Ernie Rael) closes: #12166 Signed-off-by: Ernie Rael <errael@raelity.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
This commit is contained in:
committed by
Christian Brabandt
parent
d8cb1ddab7
commit
e79e207760
@@ -1,4 +1,4 @@
|
|||||||
*builtin.txt* For Vim version 9.1. Last change: 2024 Jan 05
|
*builtin.txt* For Vim version 9.1. Last change: 2024 Jan 13
|
||||||
|
|
||||||
|
|
||||||
VIM REFERENCE MANUAL by Bram Moolenaar
|
VIM REFERENCE MANUAL by Bram Moolenaar
|
||||||
@@ -198,6 +198,8 @@ foldclosedend({lnum}) Number last line of fold at {lnum} if closed
|
|||||||
foldlevel({lnum}) Number fold level at {lnum}
|
foldlevel({lnum}) Number fold level at {lnum}
|
||||||
foldtext() String line displayed for closed fold
|
foldtext() String line displayed for closed fold
|
||||||
foldtextresult({lnum}) String text for closed fold at {lnum}
|
foldtextresult({lnum}) String text for closed fold at {lnum}
|
||||||
|
foreach({expr1}, {expr2}) List/Dict/Blob/String
|
||||||
|
for each item in {expr1} call {expr2}
|
||||||
foreground() Number bring the Vim window to the foreground
|
foreground() Number bring the Vim window to the foreground
|
||||||
fullcommand({name} [, {vim9}]) String get full command from {name}
|
fullcommand({name} [, {vim9}]) String get full command from {name}
|
||||||
funcref({name} [, {arglist}] [, {dict}])
|
funcref({name} [, {arglist}] [, {dict}])
|
||||||
@@ -2995,6 +2997,45 @@ foldtextresult({lnum}) *foldtextresult()*
|
|||||||
|
|
||||||
Can also be used as a |method|: >
|
Can also be used as a |method|: >
|
||||||
GetLnum()->foldtextresult()
|
GetLnum()->foldtextresult()
|
||||||
|
|
||||||
|
foreach({expr1}, {expr2}) *foreach()*
|
||||||
|
{expr1} must be a |List|, |String|, |Blob| or |Dictionary|.
|
||||||
|
For each item in {expr1} execute {expr2}. {expr1} is not
|
||||||
|
modified; its values may be, as with |:lockvar| 1. *E741*
|
||||||
|
See |map()| and |filter()| to modify {expr1}.
|
||||||
|
|
||||||
|
{expr2} must be a |string| or |Funcref|.
|
||||||
|
|
||||||
|
If {expr2} is a |string|, inside {expr2} |v:val| has the value
|
||||||
|
of the current item. For a |Dictionary| |v:key| has the key
|
||||||
|
of the current item and for a |List| |v:key| has the index of
|
||||||
|
the current item. For a |Blob| |v:key| has the index of the
|
||||||
|
current byte. For a |String| |v:key| has the index of the
|
||||||
|
current character.
|
||||||
|
Examples: >
|
||||||
|
call foreach(mylist, 'used[v:val] = true')
|
||||||
|
< This records the items that are in the {expr1} list.
|
||||||
|
|
||||||
|
Note that {expr2} is the result of expression and is then used
|
||||||
|
as a command. Often it is good to use a |literal-string| to
|
||||||
|
avoid having to double backslashes.
|
||||||
|
|
||||||
|
If {expr2} is a |Funcref| it must take two arguments:
|
||||||
|
1. the key or the index of the current item.
|
||||||
|
2. the value of the current item.
|
||||||
|
With a legacy script lambda you don't get an error if it only
|
||||||
|
accepts one argument, but with a Vim9 lambda you get "E1106:
|
||||||
|
One argument too many", the number of arguments must match.
|
||||||
|
If the function returns a value, it is ignored.
|
||||||
|
|
||||||
|
Returns {expr1} in all cases.
|
||||||
|
When an error is encountered while executing {expr2} no
|
||||||
|
further items in {expr1} are processed.
|
||||||
|
When {expr2} is a Funcref errors inside a function are ignored,
|
||||||
|
unless it was defined with the "abort" flag.
|
||||||
|
|
||||||
|
Can also be used as a |method|: >
|
||||||
|
mylist->foreach(expr2)
|
||||||
<
|
<
|
||||||
*foreground()*
|
*foreground()*
|
||||||
foreground() Move the Vim window to the foreground. Useful when sent from
|
foreground() Move the Vim window to the foreground. Useful when sent from
|
||||||
|
@@ -5167,6 +5167,7 @@ E738 eval.txt /*E738*
|
|||||||
E739 builtin.txt /*E739*
|
E739 builtin.txt /*E739*
|
||||||
E74 message.txt /*E74*
|
E74 message.txt /*E74*
|
||||||
E740 userfunc.txt /*E740*
|
E740 userfunc.txt /*E740*
|
||||||
|
E741 builtin.txt /*E741*
|
||||||
E741 eval.txt /*E741*
|
E741 eval.txt /*E741*
|
||||||
E742 userfunc.txt /*E742*
|
E742 userfunc.txt /*E742*
|
||||||
E743 eval.txt /*E743*
|
E743 eval.txt /*E743*
|
||||||
@@ -7148,6 +7149,7 @@ foldtextresult() builtin.txt /*foldtextresult()*
|
|||||||
font-sizes gui_x11.txt /*font-sizes*
|
font-sizes gui_x11.txt /*font-sizes*
|
||||||
fontset mbyte.txt /*fontset*
|
fontset mbyte.txt /*fontset*
|
||||||
forced-motion motion.txt /*forced-motion*
|
forced-motion motion.txt /*forced-motion*
|
||||||
|
foreach() builtin.txt /*foreach()*
|
||||||
foreground() builtin.txt /*foreground()*
|
foreground() builtin.txt /*foreground()*
|
||||||
fork os_unix.txt /*fork*
|
fork os_unix.txt /*fork*
|
||||||
form.vim syntax.txt /*form.vim*
|
form.vim syntax.txt /*form.vim*
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
*usr_41.txt* For Vim version 9.1. Last change: 2023 May 06
|
*usr_41.txt* For Vim version 9.1. Last change: 2024 Jan 13
|
||||||
|
|
||||||
VIM USER MANUAL - by Bram Moolenaar
|
VIM USER MANUAL - by Bram Moolenaar
|
||||||
|
|
||||||
@@ -798,6 +798,7 @@ List manipulation: *list-functions*
|
|||||||
filter() remove selected items from a List
|
filter() remove selected items from a List
|
||||||
map() change each List item
|
map() change each List item
|
||||||
mapnew() make a new List with changed items
|
mapnew() make a new List with changed items
|
||||||
|
foreach() apply function to List items
|
||||||
reduce() reduce a List to a value
|
reduce() reduce a List to a value
|
||||||
slice() take a slice of a List
|
slice() take a slice of a List
|
||||||
sort() sort a List
|
sort() sort a List
|
||||||
@@ -829,6 +830,7 @@ Dictionary manipulation: *dict-functions*
|
|||||||
filter() remove selected entries from a Dictionary
|
filter() remove selected entries from a Dictionary
|
||||||
map() change each Dictionary entry
|
map() change each Dictionary entry
|
||||||
mapnew() make a new Dictionary with changed items
|
mapnew() make a new Dictionary with changed items
|
||||||
|
foreach() apply function to Dictionary items
|
||||||
keys() get List of Dictionary keys
|
keys() get List of Dictionary keys
|
||||||
values() get List of Dictionary values
|
values() get List of Dictionary values
|
||||||
items() get List of Dictionary key-value pairs
|
items() get List of Dictionary key-value pairs
|
||||||
|
37
src/blob.c
37
src/blob.c
@@ -641,25 +641,28 @@ blob_filter_map(
|
|||||||
if (filter_map_one(&tv, expr, filtermap, fc, &newtv, &rem) == FAIL
|
if (filter_map_one(&tv, expr, filtermap, fc, &newtv, &rem) == FAIL
|
||||||
|| did_emsg)
|
|| did_emsg)
|
||||||
break;
|
break;
|
||||||
if (newtv.v_type != VAR_NUMBER && newtv.v_type != VAR_BOOL)
|
if (filtermap != FILTERMAP_FOREACH)
|
||||||
{
|
{
|
||||||
clear_tv(&newtv);
|
if (newtv.v_type != VAR_NUMBER && newtv.v_type != VAR_BOOL)
|
||||||
emsg(_(e_invalid_operation_for_blob));
|
{
|
||||||
break;
|
clear_tv(&newtv);
|
||||||
}
|
emsg(_(e_invalid_operation_for_blob));
|
||||||
if (filtermap != FILTERMAP_FILTER)
|
break;
|
||||||
{
|
}
|
||||||
if (newtv.vval.v_number != val)
|
if (filtermap != FILTERMAP_FILTER)
|
||||||
blob_set(b_ret, i, newtv.vval.v_number);
|
{
|
||||||
}
|
if (newtv.vval.v_number != val)
|
||||||
else if (rem)
|
blob_set(b_ret, i, newtv.vval.v_number);
|
||||||
{
|
}
|
||||||
char_u *p = (char_u *)blob_arg->bv_ga.ga_data;
|
else if (rem)
|
||||||
|
{
|
||||||
|
char_u *p = (char_u *)blob_arg->bv_ga.ga_data;
|
||||||
|
|
||||||
mch_memmove(p + i, p + i + 1,
|
mch_memmove(p + i, p + i + 1,
|
||||||
(size_t)b->bv_ga.ga_len - i - 1);
|
(size_t)b->bv_ga.ga_len - i - 1);
|
||||||
--b->bv_ga.ga_len;
|
--b->bv_ga.ga_len;
|
||||||
--i;
|
--i;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
++idx;
|
++idx;
|
||||||
}
|
}
|
||||||
|
@@ -1329,8 +1329,8 @@ dict_extend_func(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Implementation of map() and filter() for a Dict. Apply "expr" to every
|
* Implementation of map(), filter(), foreach() for a Dict. Apply "expr" to
|
||||||
* item in Dict "d" and return the result in "rettv".
|
* every item in Dict "d" and return the result in "rettv".
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
dict_filter_map(
|
dict_filter_map(
|
||||||
@@ -1392,7 +1392,6 @@ dict_filter_map(
|
|||||||
arg_errmsg, TRUE)))
|
arg_errmsg, TRUE)))
|
||||||
break;
|
break;
|
||||||
set_vim_var_string(VV_KEY, di->di_key, -1);
|
set_vim_var_string(VV_KEY, di->di_key, -1);
|
||||||
newtv.v_type = VAR_UNKNOWN;
|
|
||||||
r = filter_map_one(&di->di_tv, expr, filtermap, fc, &newtv, &rem);
|
r = filter_map_one(&di->di_tv, expr, filtermap, fc, &newtv, &rem);
|
||||||
clear_tv(get_vim_var_tv(VV_KEY));
|
clear_tv(get_vim_var_tv(VV_KEY));
|
||||||
if (r == FAIL || did_emsg)
|
if (r == FAIL || did_emsg)
|
||||||
|
@@ -607,10 +607,11 @@ arg_list_or_dict_or_blob_or_string_mod(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check second argument of map() or filter().
|
* Check second argument of map(), filter(), foreach().
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
check_map_filter_arg2(type_T *type, argcontext_T *context, int is_map)
|
check_map_filter_arg2(type_T *type, argcontext_T *context,
|
||||||
|
filtermap_T filtermap)
|
||||||
{
|
{
|
||||||
type_T *expected_member = NULL;
|
type_T *expected_member = NULL;
|
||||||
type_T *(args[2]);
|
type_T *(args[2]);
|
||||||
@@ -663,12 +664,14 @@ check_map_filter_arg2(type_T *type, argcontext_T *context, int is_map)
|
|||||||
{
|
{
|
||||||
where_T where = WHERE_INIT;
|
where_T where = WHERE_INIT;
|
||||||
|
|
||||||
if (is_map)
|
if (filtermap == FILTERMAP_MAP)
|
||||||
t_func_exp.tt_member = expected_member == NULL
|
t_func_exp.tt_member = expected_member == NULL
|
||||||
|| type_any_or_unknown(type->tt_member)
|
|| type_any_or_unknown(type->tt_member)
|
||||||
? &t_any : expected_member;
|
? &t_any : expected_member;
|
||||||
else
|
else if (filtermap == FILTERMAP_FILTER)
|
||||||
t_func_exp.tt_member = &t_bool;
|
t_func_exp.tt_member = &t_bool;
|
||||||
|
else // filtermap == FILTERMAP_FOREACH
|
||||||
|
t_func_exp.tt_member = &t_unknown;
|
||||||
if (args[0] == NULL)
|
if (args[0] == NULL)
|
||||||
args[0] = &t_unknown;
|
args[0] = &t_unknown;
|
||||||
if (type->tt_argcount == -1)
|
if (type->tt_argcount == -1)
|
||||||
@@ -693,7 +696,7 @@ arg_filter_func(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
|
|||||||
return OK;
|
return OK;
|
||||||
|
|
||||||
if (type->tt_type == VAR_FUNC)
|
if (type->tt_type == VAR_FUNC)
|
||||||
return check_map_filter_arg2(type, context, FALSE);
|
return check_map_filter_arg2(type, context, FILTERMAP_FILTER);
|
||||||
semsg(_(e_string_or_function_required_for_argument_nr), 2);
|
semsg(_(e_string_or_function_required_for_argument_nr), 2);
|
||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
}
|
||||||
@@ -710,7 +713,24 @@ arg_map_func(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
|
|||||||
return OK;
|
return OK;
|
||||||
|
|
||||||
if (type->tt_type == VAR_FUNC)
|
if (type->tt_type == VAR_FUNC)
|
||||||
return check_map_filter_arg2(type, context, TRUE);
|
return check_map_filter_arg2(type, context, FILTERMAP_MAP);
|
||||||
|
semsg(_(e_string_or_function_required_for_argument_nr), 2);
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check second argument of foreach(), the function.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
arg_foreach_func(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
|
||||||
|
{
|
||||||
|
if (type->tt_type == VAR_STRING
|
||||||
|
|| type->tt_type == VAR_PARTIAL
|
||||||
|
|| type_any_or_unknown(type))
|
||||||
|
return OK;
|
||||||
|
|
||||||
|
if (type->tt_type == VAR_FUNC)
|
||||||
|
return check_map_filter_arg2(type, context, FILTERMAP_FOREACH);
|
||||||
semsg(_(e_string_or_function_required_for_argument_nr), 2);
|
semsg(_(e_string_or_function_required_for_argument_nr), 2);
|
||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
}
|
||||||
@@ -1173,6 +1193,7 @@ static argcheck_T arg1_len[] = {arg_len1};
|
|||||||
static argcheck_T arg3_libcall[] = {arg_string, arg_string, arg_string_or_nr};
|
static argcheck_T arg3_libcall[] = {arg_string, arg_string, arg_string_or_nr};
|
||||||
static argcheck_T arg14_maparg[] = {arg_string, arg_string, arg_bool, arg_bool};
|
static argcheck_T arg14_maparg[] = {arg_string, arg_string, arg_bool, arg_bool};
|
||||||
static argcheck_T arg2_filter[] = {arg_list_or_dict_or_blob_or_string_mod, arg_filter_func};
|
static argcheck_T arg2_filter[] = {arg_list_or_dict_or_blob_or_string_mod, arg_filter_func};
|
||||||
|
static argcheck_T arg2_foreach[] = {arg_list_or_dict_or_blob_or_string, arg_foreach_func};
|
||||||
static argcheck_T arg2_instanceof[] = {arg_object, varargs_class, NULL };
|
static argcheck_T arg2_instanceof[] = {arg_object, varargs_class, NULL };
|
||||||
static argcheck_T arg2_map[] = {arg_list_or_dict_or_blob_or_string_mod, arg_map_func};
|
static argcheck_T arg2_map[] = {arg_list_or_dict_or_blob_or_string_mod, arg_map_func};
|
||||||
static argcheck_T arg2_mapnew[] = {arg_list_or_dict_or_blob_or_string, arg_any};
|
static argcheck_T arg2_mapnew[] = {arg_list_or_dict_or_blob_or_string, arg_any};
|
||||||
@@ -2013,6 +2034,8 @@ static funcentry_T global_functions[] =
|
|||||||
ret_string, f_foldtext},
|
ret_string, f_foldtext},
|
||||||
{"foldtextresult", 1, 1, FEARG_1, arg1_lnum,
|
{"foldtextresult", 1, 1, FEARG_1, arg1_lnum,
|
||||||
ret_string, f_foldtextresult},
|
ret_string, f_foldtextresult},
|
||||||
|
{"foreach", 2, 2, FEARG_1, arg2_foreach,
|
||||||
|
ret_first_arg, f_foreach},
|
||||||
{"foreground", 0, 0, 0, NULL,
|
{"foreground", 0, 0, 0, NULL,
|
||||||
ret_void, f_foreground},
|
ret_void, f_foreground},
|
||||||
{"fullcommand", 1, 2, FEARG_1, arg2_string_bool,
|
{"fullcommand", 1, 2, FEARG_1, arg2_string_bool,
|
||||||
|
83
src/list.c
83
src/list.c
@@ -2325,7 +2325,7 @@ f_uniq(typval_T *argvars, typval_T *rettv)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Handle one item for map() and filter().
|
* Handle one item for map(), filter(), foreach().
|
||||||
* Sets v:val to "tv". Caller must set v:key.
|
* Sets v:val to "tv". Caller must set v:key.
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
@@ -2341,6 +2341,17 @@ filter_map_one(
|
|||||||
int retval = FAIL;
|
int retval = FAIL;
|
||||||
|
|
||||||
copy_tv(tv, get_vim_var_tv(VV_VAL));
|
copy_tv(tv, get_vim_var_tv(VV_VAL));
|
||||||
|
|
||||||
|
newtv->v_type = VAR_UNKNOWN;
|
||||||
|
if (filtermap == FILTERMAP_FOREACH && expr->v_type == VAR_STRING)
|
||||||
|
{
|
||||||
|
// foreach() is not limited to an expression
|
||||||
|
do_cmdline_cmd(expr->vval.v_string);
|
||||||
|
if (!did_emsg)
|
||||||
|
retval = OK;
|
||||||
|
goto theend;
|
||||||
|
}
|
||||||
|
|
||||||
argv[0] = *get_vim_var_tv(VV_KEY);
|
argv[0] = *get_vim_var_tv(VV_KEY);
|
||||||
argv[1] = *get_vim_var_tv(VV_VAL);
|
argv[1] = *get_vim_var_tv(VV_VAL);
|
||||||
if (eval_expr_typval(expr, FALSE, argv, 2, fc, newtv) == FAIL)
|
if (eval_expr_typval(expr, FALSE, argv, 2, fc, newtv) == FAIL)
|
||||||
@@ -2360,6 +2371,8 @@ filter_map_one(
|
|||||||
if (error)
|
if (error)
|
||||||
goto theend;
|
goto theend;
|
||||||
}
|
}
|
||||||
|
else if (filtermap == FILTERMAP_FOREACH)
|
||||||
|
clear_tv(newtv);
|
||||||
retval = OK;
|
retval = OK;
|
||||||
theend:
|
theend:
|
||||||
clear_tv(get_vim_var_tv(VV_VAL));
|
clear_tv(get_vim_var_tv(VV_VAL));
|
||||||
@@ -2367,8 +2380,8 @@ theend:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Implementation of map() and filter() for a List. Apply "expr" to every item
|
* Implementation of map(), filter(), foreach() for a List. Apply "expr" to
|
||||||
* in List "l" and return the result in "rettv".
|
* every item in List "l" and return the result in "rettv".
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
list_filter_map(
|
list_filter_map(
|
||||||
@@ -2421,7 +2434,8 @@ list_filter_map(
|
|||||||
int stride = l->lv_u.nonmat.lv_stride;
|
int stride = l->lv_u.nonmat.lv_stride;
|
||||||
|
|
||||||
// List from range(): loop over the numbers
|
// List from range(): loop over the numbers
|
||||||
if (filtermap != FILTERMAP_MAPNEW)
|
// NOTE: foreach() returns the range_list_item
|
||||||
|
if (filtermap != FILTERMAP_MAPNEW && filtermap != FILTERMAP_FOREACH)
|
||||||
{
|
{
|
||||||
l->lv_first = NULL;
|
l->lv_first = NULL;
|
||||||
l->lv_u.mat.lv_last = NULL;
|
l->lv_u.mat.lv_last = NULL;
|
||||||
@@ -2444,27 +2458,30 @@ list_filter_map(
|
|||||||
clear_tv(&newtv);
|
clear_tv(&newtv);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (filtermap != FILTERMAP_FILTER)
|
if (filtermap != FILTERMAP_FOREACH)
|
||||||
{
|
{
|
||||||
if (filtermap == FILTERMAP_MAP && argtype != NULL
|
if (filtermap != FILTERMAP_FILTER)
|
||||||
&& check_typval_arg_type(
|
|
||||||
argtype->tt_member, &newtv,
|
|
||||||
func_name, 0) == FAIL)
|
|
||||||
{
|
{
|
||||||
clear_tv(&newtv);
|
if (filtermap == FILTERMAP_MAP && argtype != NULL
|
||||||
break;
|
&& check_typval_arg_type(
|
||||||
|
argtype->tt_member, &newtv,
|
||||||
|
func_name, 0) == FAIL)
|
||||||
|
{
|
||||||
|
clear_tv(&newtv);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// map(), mapnew(): always append the new value to the
|
||||||
|
// list
|
||||||
|
if (list_append_tv_move(filtermap == FILTERMAP_MAP
|
||||||
|
? l : l_ret, &newtv) == FAIL)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (!rem)
|
||||||
|
{
|
||||||
|
// filter(): append the list item value when not rem
|
||||||
|
if (list_append_tv_move(l, &tv) == FAIL)
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
// map(), mapnew(): always append the new value to the
|
|
||||||
// list
|
|
||||||
if (list_append_tv_move(filtermap == FILTERMAP_MAP
|
|
||||||
? l : l_ret, &newtv) == FAIL)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else if (!rem)
|
|
||||||
{
|
|
||||||
// filter(): append the list item value when not rem
|
|
||||||
if (list_append_tv_move(l, &tv) == FAIL)
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val += stride;
|
val += stride;
|
||||||
@@ -2508,7 +2525,7 @@ list_filter_map(
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else if (filtermap == FILTERMAP_FILTER && rem)
|
else if (filtermap == FILTERMAP_FILTER && rem)
|
||||||
listitem_remove(l, li);
|
listitem_remove(l, li);
|
||||||
++idx;
|
++idx;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2519,7 +2536,7 @@ list_filter_map(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Implementation of map() and filter().
|
* Implementation of map(), filter() and foreach().
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
filter_map(typval_T *argvars, typval_T *rettv, filtermap_T filtermap)
|
filter_map(typval_T *argvars, typval_T *rettv, filtermap_T filtermap)
|
||||||
@@ -2527,16 +2544,19 @@ filter_map(typval_T *argvars, typval_T *rettv, filtermap_T filtermap)
|
|||||||
typval_T *expr;
|
typval_T *expr;
|
||||||
char *func_name = filtermap == FILTERMAP_MAP ? "map()"
|
char *func_name = filtermap == FILTERMAP_MAP ? "map()"
|
||||||
: filtermap == FILTERMAP_MAPNEW ? "mapnew()"
|
: filtermap == FILTERMAP_MAPNEW ? "mapnew()"
|
||||||
: "filter()";
|
: filtermap == FILTERMAP_FILTER ? "filter()"
|
||||||
|
: "foreach()";
|
||||||
char_u *arg_errmsg = (char_u *)(filtermap == FILTERMAP_MAP
|
char_u *arg_errmsg = (char_u *)(filtermap == FILTERMAP_MAP
|
||||||
? N_("map() argument")
|
? N_("map() argument")
|
||||||
: filtermap == FILTERMAP_MAPNEW
|
: filtermap == FILTERMAP_MAPNEW
|
||||||
? N_("mapnew() argument")
|
? N_("mapnew() argument")
|
||||||
: N_("filter() argument"));
|
: filtermap == FILTERMAP_FILTER
|
||||||
|
? N_("filter() argument")
|
||||||
|
: N_("foreach() argument"));
|
||||||
int save_did_emsg;
|
int save_did_emsg;
|
||||||
type_T *type = NULL;
|
type_T *type = NULL;
|
||||||
|
|
||||||
// map() and filter() return the first argument, also on failure.
|
// map(), filter(), foreach() return the first argument, also on failure.
|
||||||
if (filtermap != FILTERMAP_MAPNEW && argvars[0].v_type != VAR_STRING)
|
if (filtermap != FILTERMAP_MAPNEW && argvars[0].v_type != VAR_STRING)
|
||||||
copy_tv(&argvars[0], rettv);
|
copy_tv(&argvars[0], rettv);
|
||||||
|
|
||||||
@@ -2629,6 +2649,15 @@ f_mapnew(typval_T *argvars, typval_T *rettv)
|
|||||||
filter_map(argvars, rettv, FILTERMAP_MAPNEW);
|
filter_map(argvars, rettv, FILTERMAP_MAPNEW);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* "foreach()" function
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
f_foreach(typval_T *argvars, typval_T *rettv)
|
||||||
|
{
|
||||||
|
filter_map(argvars, rettv, FILTERMAP_FOREACH);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* "add(list, item)" function
|
* "add(list, item)" function
|
||||||
*/
|
*/
|
||||||
|
@@ -56,6 +56,7 @@ int filter_map_one(typval_T *tv, typval_T *expr, filtermap_T filtermap, funccall
|
|||||||
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);
|
||||||
|
void f_foreach(typval_T *argvars, typval_T *rettv);
|
||||||
void f_add(typval_T *argvars, typval_T *rettv);
|
void f_add(typval_T *argvars, typval_T *rettv);
|
||||||
void f_count(typval_T *argvars, typval_T *rettv);
|
void f_count(typval_T *argvars, typval_T *rettv);
|
||||||
void f_extend(typval_T *argvars, typval_T *rettv);
|
void f_extend(typval_T *argvars, typval_T *rettv);
|
||||||
|
@@ -942,7 +942,6 @@ string_filter_map(
|
|||||||
break;
|
break;
|
||||||
len = (int)STRLEN(tv.vval.v_string);
|
len = (int)STRLEN(tv.vval.v_string);
|
||||||
|
|
||||||
newtv.v_type = VAR_UNKNOWN;
|
|
||||||
set_vim_var_nr(VV_KEY, idx);
|
set_vim_var_nr(VV_KEY, idx);
|
||||||
if (filter_map_one(&tv, expr, filtermap, fc, &newtv, &rem) == FAIL
|
if (filter_map_one(&tv, expr, filtermap, fc, &newtv, &rem) == FAIL
|
||||||
|| did_emsg)
|
|| did_emsg)
|
||||||
@@ -951,7 +950,7 @@ string_filter_map(
|
|||||||
clear_tv(&tv);
|
clear_tv(&tv);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else if (filtermap != FILTERMAP_FILTER)
|
if (filtermap == FILTERMAP_MAP || filtermap == FILTERMAP_MAPNEW)
|
||||||
{
|
{
|
||||||
if (newtv.v_type != VAR_STRING)
|
if (newtv.v_type != VAR_STRING)
|
||||||
{
|
{
|
||||||
@@ -963,7 +962,7 @@ string_filter_map(
|
|||||||
else
|
else
|
||||||
ga_concat(&ga, newtv.vval.v_string);
|
ga_concat(&ga, newtv.vval.v_string);
|
||||||
}
|
}
|
||||||
else if (!rem)
|
else if (filtermap == FILTERMAP_FOREACH || !rem)
|
||||||
ga_concat(&ga, tv.vval.v_string);
|
ga_concat(&ga, tv.vval.v_string);
|
||||||
|
|
||||||
clear_tv(&newtv);
|
clear_tv(&newtv);
|
||||||
|
@@ -4879,11 +4879,12 @@ typedef struct {
|
|||||||
hashtab_T sve_hashtab;
|
hashtab_T sve_hashtab;
|
||||||
} save_v_event_T;
|
} save_v_event_T;
|
||||||
|
|
||||||
// Enum used by filter(), map() and mapnew()
|
// Enum used by filter(), map(), mapnew() and foreach()
|
||||||
typedef enum {
|
typedef enum {
|
||||||
FILTERMAP_FILTER,
|
FILTERMAP_FILTER,
|
||||||
FILTERMAP_MAP,
|
FILTERMAP_MAP,
|
||||||
FILTERMAP_MAPNEW
|
FILTERMAP_MAPNEW,
|
||||||
|
FILTERMAP_FOREACH
|
||||||
} filtermap_T;
|
} filtermap_T;
|
||||||
|
|
||||||
// Structure used by switch_win() to pass values to restore_win()
|
// Structure used by switch_win() to pass values to restore_win()
|
||||||
|
@@ -14,6 +14,18 @@ func Test_filter_map_list_expr_string()
|
|||||||
call assert_equal([0, 2, 4, 6], map([1, 2, 3, 4], 'v:key * 2'))
|
call assert_equal([0, 2, 4, 6], map([1, 2, 3, 4], 'v:key * 2'))
|
||||||
call assert_equal([9, 9, 9, 9], map([1, 2, 3, 4], 9))
|
call assert_equal([9, 9, 9, 9], map([1, 2, 3, 4], 9))
|
||||||
call assert_equal([7, 7, 7], map([1, 2, 3], ' 7 '))
|
call assert_equal([7, 7, 7], map([1, 2, 3], ' 7 '))
|
||||||
|
|
||||||
|
" foreach()
|
||||||
|
let list01 = [1, 2, 3, 4]
|
||||||
|
let list02 = []
|
||||||
|
call assert_equal([1, 2, 3, 4], foreach(list01, 'call add(list02, v:val * 2)'))
|
||||||
|
call assert_equal([2, 4, 6, 8], list02)
|
||||||
|
let list02 = []
|
||||||
|
call assert_equal([1, 2, 3, 4], foreach(list01, 'call add(list02, v:key * 2)'))
|
||||||
|
call assert_equal([0, 2, 4, 6], list02)
|
||||||
|
let list02 = []
|
||||||
|
call assert_equal([1, 2, 3, 4], foreach(list01, 'call add(list02, 9)'))
|
||||||
|
call assert_equal([9, 9, 9, 9], list02)
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
" dict with expression string
|
" dict with expression string
|
||||||
@@ -29,6 +41,14 @@ func Test_filter_map_dict_expr_string()
|
|||||||
call assert_equal({"foo": 2, "bar": 4, "baz": 6}, map(copy(dict), 'v:val * 2'))
|
call assert_equal({"foo": 2, "bar": 4, "baz": 6}, map(copy(dict), 'v:val * 2'))
|
||||||
call assert_equal({"foo": "f", "bar": "b", "baz": "b"}, map(copy(dict), 'v:key[0]'))
|
call assert_equal({"foo": "f", "bar": "b", "baz": "b"}, map(copy(dict), 'v:key[0]'))
|
||||||
call assert_equal({"foo": 9, "bar": 9, "baz": 9}, map(copy(dict), 9))
|
call assert_equal({"foo": 9, "bar": 9, "baz": 9}, map(copy(dict), 9))
|
||||||
|
|
||||||
|
" foreach()
|
||||||
|
let dict01 = {}
|
||||||
|
call assert_equal(dict, foreach(copy(dict), 'let dict01[v:key] = v:val * 2'))
|
||||||
|
call assert_equal({"foo": 2, "bar": 4, "baz": 6}, dict01)
|
||||||
|
let dict01 = {}
|
||||||
|
call assert_equal(dict, foreach(copy(dict), 'let dict01[v:key] = v:key[0]'))
|
||||||
|
call assert_equal({"foo": "f", "bar": "b", "baz": "b"}, dict01)
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
" list with funcref
|
" list with funcref
|
||||||
@@ -54,6 +74,16 @@ func Test_filter_map_list_expr_funcref()
|
|||||||
return a:index * 2
|
return a:index * 2
|
||||||
endfunc
|
endfunc
|
||||||
call assert_equal([0, 2, 4, 6], map([1, 2, 3, 4], function('s:filter4')))
|
call assert_equal([0, 2, 4, 6], map([1, 2, 3, 4], function('s:filter4')))
|
||||||
|
|
||||||
|
" foreach()
|
||||||
|
func! s:foreach1(index, val) abort
|
||||||
|
call add(g:test_variable, a:val + 1)
|
||||||
|
return [ 11, 12, 13, 14 ]
|
||||||
|
endfunc
|
||||||
|
let g:test_variable = []
|
||||||
|
call assert_equal([0, 1, 2, 3, 4], foreach(range(5), function('s:foreach1')))
|
||||||
|
call assert_equal([1, 2, 3, 4, 5], g:test_variable)
|
||||||
|
call remove(g:, 'test_variable')
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
func Test_filter_map_nested()
|
func Test_filter_map_nested()
|
||||||
@@ -90,11 +120,46 @@ func Test_filter_map_dict_expr_funcref()
|
|||||||
return a:key[0]
|
return a:key[0]
|
||||||
endfunc
|
endfunc
|
||||||
call assert_equal({"foo": "f", "bar": "b", "baz": "b"}, map(copy(dict), function('s:filter4')))
|
call assert_equal({"foo": "f", "bar": "b", "baz": "b"}, map(copy(dict), function('s:filter4')))
|
||||||
|
|
||||||
|
" foreach()
|
||||||
|
func! s:foreach1(key, val) abort
|
||||||
|
call extend(g:test_variable, {a:key: a:val * 2})
|
||||||
|
return [ 11, 12, 13, 14 ]
|
||||||
|
endfunc
|
||||||
|
let g:test_variable = {}
|
||||||
|
call assert_equal(dict, foreach(copy(dict), function('s:foreach1')))
|
||||||
|
call assert_equal({"foo": 2, "bar": 4, "baz": 6}, g:test_variable)
|
||||||
|
call remove(g:, 'test_variable')
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func Test_map_filter_locked()
|
||||||
|
let list01 = [1, 2, 3, 4]
|
||||||
|
lockvar 1 list01
|
||||||
|
call assert_fails('call filter(list01, "v:val > 1")', 'E741:')
|
||||||
|
call assert_equal([2, 4, 6, 8], map(list01, 'v:val * 2'))
|
||||||
|
call assert_equal([1, 2, 3, 4], map(list01, 'v:val / 2'))
|
||||||
|
call assert_equal([2, 4, 6, 8], mapnew(list01, 'v:val * 2'))
|
||||||
|
let g:test_variable = []
|
||||||
|
call assert_equal([1, 2, 3, 4], foreach(list01, 'call add(g:test_variable, v:val * 2)'))
|
||||||
|
call remove(g:, 'test_variable')
|
||||||
|
call assert_fails('call filter(list01, "v:val > 1")', 'E741:')
|
||||||
|
unlockvar 1 list01
|
||||||
|
lockvar! list01
|
||||||
|
call assert_fails('call filter(list01, "v:val > 1")', 'E741:')
|
||||||
|
call assert_fails('call map(list01, "v:val * 2")', 'E741:')
|
||||||
|
call assert_equal([2, 4, 6, 8], mapnew(list01, 'v:val * 2'))
|
||||||
|
let g:test_variable = []
|
||||||
|
call assert_equal([1, 2, 3, 4], foreach(list01, 'call add(g:test_variable, v:val * 2)'))
|
||||||
|
call assert_fails('call foreach(list01, "let list01[0] = -1")', 'E741:')
|
||||||
|
call assert_fails('call filter(list01, "v:val > 1")', 'E741:')
|
||||||
|
call remove(g:, 'test_variable')
|
||||||
|
unlockvar! list01
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
func Test_map_filter_fails()
|
func Test_map_filter_fails()
|
||||||
call assert_fails('call map([1], "42 +")', 'E15:')
|
call assert_fails('call map([1], "42 +")', 'E15:')
|
||||||
call assert_fails('call filter([1], "42 +")', 'E15:')
|
call assert_fails('call filter([1], "42 +")', 'E15:')
|
||||||
|
call assert_fails('call foreach([1], "let a = }")', 'E15:')
|
||||||
call assert_fails("let l = filter([1, 2, 3], '{}')", 'E728:')
|
call assert_fails("let l = filter([1, 2, 3], '{}')", 'E728:')
|
||||||
call assert_fails("let l = filter({'k' : 10}, '{}')", 'E728:')
|
call assert_fails("let l = filter({'k' : 10}, '{}')", 'E728:')
|
||||||
call assert_fails("let l = filter([1, 2], {})", 'E731:')
|
call assert_fails("let l = filter([1, 2], {})", 'E731:')
|
||||||
@@ -106,6 +171,8 @@ func Test_map_filter_fails()
|
|||||||
call assert_fails("let l = filter([1, 2], function('min'))", 'E118:')
|
call assert_fails("let l = filter([1, 2], function('min'))", 'E118:')
|
||||||
call assert_equal([1, 2, 3], filter([1, 2, 3], test_null_partial()))
|
call assert_equal([1, 2, 3], filter([1, 2, 3], test_null_partial()))
|
||||||
call assert_fails("let l = filter([1, 2], {a, b, c -> 1})", 'E119:')
|
call assert_fails("let l = filter([1, 2], {a, b, c -> 1})", 'E119:')
|
||||||
|
call assert_fails('call foreach([1], "xyzzy")', 'E492:')
|
||||||
|
call assert_fails('call foreach([1], "let a = foo")', 'E121:')
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
func Test_map_and_modify()
|
func Test_map_and_modify()
|
||||||
@@ -123,7 +190,7 @@ endfunc
|
|||||||
|
|
||||||
func Test_filter_and_modify()
|
func Test_filter_and_modify()
|
||||||
let l = [0]
|
let l = [0]
|
||||||
" cannot change the list halfway a map()
|
" cannot change the list halfway thru filter()
|
||||||
call assert_fails('call filter(l, "remove(l, 0)")', 'E741:')
|
call assert_fails('call filter(l, "remove(l, 0)")', 'E741:')
|
||||||
|
|
||||||
let d = #{a: 0, b: 0, c: 0}
|
let d = #{a: 0, b: 0, c: 0}
|
||||||
@@ -133,6 +200,18 @@ func Test_filter_and_modify()
|
|||||||
call assert_fails('call filter(b, "remove(b, 0)")', 'E741:')
|
call assert_fails('call filter(b, "remove(b, 0)")', 'E741:')
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
|
func Test_foreach_and_modify()
|
||||||
|
let l = [0]
|
||||||
|
" cannot change the list halfway thru foreach()
|
||||||
|
call assert_fails('call foreach(l, "let a = remove(l, 0)")', 'E741:')
|
||||||
|
|
||||||
|
let d = #{a: 0, b: 0, c: 0}
|
||||||
|
call assert_fails('call foreach(d, "let a = remove(d, v:key)")', 'E741:')
|
||||||
|
|
||||||
|
let b = 0z1234
|
||||||
|
call assert_fails('call foreach(b, "let a = remove(b, 0)")', 'E741:')
|
||||||
|
endfunc
|
||||||
|
|
||||||
func Test_mapnew_dict()
|
func Test_mapnew_dict()
|
||||||
let din = #{one: 1, two: 2}
|
let din = #{one: 1, two: 2}
|
||||||
let dout = mapnew(din, {k, v -> string(v)})
|
let dout = mapnew(din, {k, v -> string(v)})
|
||||||
@@ -160,6 +239,36 @@ func Test_mapnew_blob()
|
|||||||
call assert_equal(0z129956, bout)
|
call assert_equal(0z129956, bout)
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
|
func Test_foreach_blob()
|
||||||
|
let lines =<< trim END
|
||||||
|
LET g:test_variable = []
|
||||||
|
call assert_equal(0z0001020304, foreach(0z0001020304, 'call add(g:test_variable, v:val)'))
|
||||||
|
call assert_equal([0, 1, 2, 3, 4], g:test_variable)
|
||||||
|
END
|
||||||
|
call v9.CheckLegacyAndVim9Success(lines)
|
||||||
|
|
||||||
|
func! s:foreach1(index, val) abort
|
||||||
|
call add(g:test_variable, a:val)
|
||||||
|
return [ 11, 12, 13, 14 ]
|
||||||
|
endfunc
|
||||||
|
let g:test_variable = []
|
||||||
|
call assert_equal(0z0001020304, foreach(0z0001020304, function('s:foreach1')))
|
||||||
|
call assert_equal([0, 1, 2, 3, 4], g:test_variable)
|
||||||
|
|
||||||
|
let lines =<< trim END
|
||||||
|
def Foreach1(_, val: any): list<number>
|
||||||
|
add(g:test_variable, val)
|
||||||
|
return [ 11, 12, 13, 14 ]
|
||||||
|
enddef
|
||||||
|
g:test_variable = []
|
||||||
|
assert_equal(0z0001020304, foreach(0z0001020304, Foreach1))
|
||||||
|
assert_equal([0, 1, 2, 3, 4], g:test_variable)
|
||||||
|
END
|
||||||
|
call v9.CheckDefSuccess(lines)
|
||||||
|
|
||||||
|
call remove(g:, 'test_variable')
|
||||||
|
endfunc
|
||||||
|
|
||||||
" Test for using map(), filter() and mapnew() with a string
|
" Test for using map(), filter() and mapnew() with a string
|
||||||
func Test_filter_map_string()
|
func Test_filter_map_string()
|
||||||
" filter()
|
" filter()
|
||||||
@@ -219,6 +328,37 @@ func Test_filter_map_string()
|
|||||||
END
|
END
|
||||||
call v9.CheckLegacyAndVim9Success(lines)
|
call v9.CheckLegacyAndVim9Success(lines)
|
||||||
|
|
||||||
|
" foreach()
|
||||||
|
let lines =<< trim END
|
||||||
|
VAR s = "abc"
|
||||||
|
LET g:test_variable = []
|
||||||
|
call assert_equal(s, foreach(s, 'call add(g:test_variable, v:val)'))
|
||||||
|
call assert_equal(['a', 'b', 'c'], g:test_variable)
|
||||||
|
LET g:test_variable = []
|
||||||
|
LET s = 'あiうえお'
|
||||||
|
call assert_equal(s, foreach(s, 'call add(g:test_variable, v:val)'))
|
||||||
|
call assert_equal(['あ', 'i', 'う', 'え', 'お'], g:test_variable)
|
||||||
|
END
|
||||||
|
call v9.CheckLegacyAndVim9Success(lines)
|
||||||
|
func! s:foreach1(index, val) abort
|
||||||
|
call add(g:test_variable, a:val)
|
||||||
|
return [ 11, 12, 13, 14 ]
|
||||||
|
endfunc
|
||||||
|
let g:test_variable = []
|
||||||
|
call assert_equal('abcd', foreach('abcd', function('s:foreach1')))
|
||||||
|
call assert_equal(['a', 'b', 'c', 'd'], g:test_variable)
|
||||||
|
let lines =<< trim END
|
||||||
|
def Foreach1(_, val: string): list<number>
|
||||||
|
add(g:test_variable, val)
|
||||||
|
return [ 11, 12, 13, 14 ]
|
||||||
|
enddef
|
||||||
|
g:test_variable = []
|
||||||
|
assert_equal('abcd', foreach('abcd', Foreach1))
|
||||||
|
assert_equal(['a', 'b', 'c', 'd'], g:test_variable)
|
||||||
|
END
|
||||||
|
call v9.CheckDefSuccess(lines)
|
||||||
|
call remove(g:, 'test_variable')
|
||||||
|
|
||||||
let lines =<< trim END
|
let lines =<< trim END
|
||||||
#" map() and filter()
|
#" map() and filter()
|
||||||
call assert_equal('[あ][⁈][a][😊][⁉][💕][💕][b][💕]', map(filter('あx⁈ax😊x⁉💕💕b💕x', '"x" != v:val'), '"[" .. v:val .. "]"'))
|
call assert_equal('[あ][⁈][a][😊][⁉][💕][💕][b][💕]', map(filter('あx⁈ax😊x⁉💕💕b💕x', '"x" != v:val'), '"[" .. v:val .. "]"'))
|
||||||
|
@@ -704,6 +704,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 */
|
||||||
|
/**/
|
||||||
|
27,
|
||||||
/**/
|
/**/
|
||||||
26,
|
26,
|
||||||
/**/
|
/**/
|
||||||
|
Reference in New Issue
Block a user