mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
vim-patch:partial:9.1.0027: Vim is missing a foreach() func (#27037)
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: vim/vim#12166e79e207760
Partial port as this doesn't handle non-materialized range() lists. vim-patch:c92b8bed1fa6 runtime(help): delete duplicate help tag E741 (vim/vim#13861)c92b8bed1f
Co-authored-by: Ernie Rael <errael@raelity.com>
This commit is contained in:
parent
267e90f31d
commit
46a7c1b319
35
runtime/doc/builtin.txt
generated
35
runtime/doc/builtin.txt
generated
@ -1866,6 +1866,41 @@ foldtextresult({lnum}) *foldtextresult()*
|
|||||||
line, "'m" mark m, etc.
|
line, "'m" mark m, etc.
|
||||||
Useful when exporting folded text, e.g., to HTML.
|
Useful when exporting folded text, e.g., to HTML.
|
||||||
|
|
||||||
|
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, 'let used[v:val] = v: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 lambda you don't get an error if it only accepts one
|
||||||
|
argument.
|
||||||
|
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.
|
||||||
|
|
||||||
|
|
||||||
fullcommand({name}) *fullcommand()*
|
fullcommand({name}) *fullcommand()*
|
||||||
Get the full command name from a short abbreviated command
|
Get the full command name from a short abbreviated command
|
||||||
|
@ -665,6 +665,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
|
||||||
@ -696,6 +697,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
|
||||||
|
39
runtime/lua/vim/_meta/vimfn.lua
generated
39
runtime/lua/vim/_meta/vimfn.lua
generated
@ -2308,6 +2308,45 @@ function vim.fn.foldtext() end
|
|||||||
--- @return string
|
--- @return string
|
||||||
function vim.fn.foldtextresult(lnum) end
|
function vim.fn.foldtextresult(lnum) end
|
||||||
|
|
||||||
|
--- {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, 'let used[v:val] = v: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 lambda you don't get an error if it only accepts one
|
||||||
|
--- argument.
|
||||||
|
--- 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.
|
||||||
|
---
|
||||||
|
--- @param expr1 any
|
||||||
|
--- @param expr2 any
|
||||||
|
--- @return any
|
||||||
|
function vim.fn.foreach(expr1, expr2) end
|
||||||
|
|
||||||
--- Get the full command name from a short abbreviated command
|
--- Get the full command name from a short abbreviated command
|
||||||
--- name; see |20.2| for details on command abbreviations.
|
--- name; see |20.2| for details on command abbreviations.
|
||||||
---
|
---
|
||||||
|
@ -308,11 +308,12 @@ static partial_T *vvlua_partial;
|
|||||||
/// v: hashtab
|
/// v: hashtab
|
||||||
#define vimvarht vimvardict.dv_hashtab
|
#define vimvarht vimvardict.dv_hashtab
|
||||||
|
|
||||||
/// 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;
|
||||||
|
|
||||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||||
@ -5098,7 +5099,8 @@ void assert_error(garray_T *gap)
|
|||||||
tv_list_append_string(vimvars[VV_ERRORS].vv_list, gap->ga_data, (ptrdiff_t)gap->ga_len);
|
tv_list_append_string(vimvars[VV_ERRORS].vv_list, gap->ga_data, (ptrdiff_t)gap->ga_len);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Implementation of map() and filter() for a Dict.
|
/// Implementation of map(), filter(), foreach() for a Dict. Apply "expr" to
|
||||||
|
/// every item in Dict "d" and return the result in "rettv".
|
||||||
static void filter_map_dict(dict_T *d, filtermap_T filtermap, const char *func_name,
|
static void filter_map_dict(dict_T *d, filtermap_T filtermap, const char *func_name,
|
||||||
const char *arg_errmsg, typval_T *expr, typval_T *rettv)
|
const char *arg_errmsg, typval_T *expr, typval_T *rettv)
|
||||||
{
|
{
|
||||||
@ -5166,7 +5168,7 @@ static void filter_map_dict(dict_T *d, filtermap_T filtermap, const char *func_n
|
|||||||
d->dv_lock = prev_lock;
|
d->dv_lock = prev_lock;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Implementation of map() and filter() for a Blob.
|
/// Implementation of map(), filter(), foreach() for a Blob.
|
||||||
static void filter_map_blob(blob_T *blob_arg, filtermap_T filtermap, typval_T *expr,
|
static void filter_map_blob(blob_T *blob_arg, filtermap_T filtermap, typval_T *expr,
|
||||||
const char *arg_errmsg, typval_T *rettv)
|
const char *arg_errmsg, typval_T *rettv)
|
||||||
{
|
{
|
||||||
@ -5209,6 +5211,7 @@ static void filter_map_blob(blob_T *blob_arg, filtermap_T filtermap, typval_T *e
|
|||||||
|| did_emsg) {
|
|| did_emsg) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if (filtermap != FILTERMAP_FOREACH) {
|
||||||
if (newtv.v_type != VAR_NUMBER && newtv.v_type != VAR_BOOL) {
|
if (newtv.v_type != VAR_NUMBER && newtv.v_type != VAR_BOOL) {
|
||||||
tv_clear(&newtv);
|
tv_clear(&newtv);
|
||||||
emsg(_(e_invalblob));
|
emsg(_(e_invalblob));
|
||||||
@ -5224,13 +5227,14 @@ static void filter_map_blob(blob_T *blob_arg, filtermap_T filtermap, typval_T *e
|
|||||||
b->bv_ga.ga_len--;
|
b->bv_ga.ga_len--;
|
||||||
i--;
|
i--;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
idx++;
|
idx++;
|
||||||
}
|
}
|
||||||
|
|
||||||
b->bv_lock = prev_lock;
|
b->bv_lock = prev_lock;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Implementation of map() and filter() for a String.
|
/// Implementation of map(), filter(), foreach() for a String.
|
||||||
static void filter_map_string(const char *str, filtermap_T filtermap, typval_T *expr,
|
static void filter_map_string(const char *str, filtermap_T filtermap, typval_T *expr,
|
||||||
typval_T *rettv)
|
typval_T *rettv)
|
||||||
{
|
{
|
||||||
@ -5259,7 +5263,8 @@ static void filter_map_string(const char *str, filtermap_T filtermap, typval_T *
|
|||||||
tv_clear(&newtv);
|
tv_clear(&newtv);
|
||||||
tv_clear(&tv);
|
tv_clear(&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) {
|
||||||
tv_clear(&newtv);
|
tv_clear(&newtv);
|
||||||
tv_clear(&tv);
|
tv_clear(&tv);
|
||||||
@ -5268,7 +5273,7 @@ static void filter_map_string(const char *str, filtermap_T filtermap, typval_T *
|
|||||||
} 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5281,7 +5286,8 @@ static void filter_map_string(const char *str, filtermap_T filtermap, typval_T *
|
|||||||
rettv->vval.v_string = ga.ga_data;
|
rettv->vval.v_string = ga.ga_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Implementation of map() and filter() for a List.
|
/// Implementation of map(), filter(), foreach() for a List. Apply "expr" to
|
||||||
|
/// every item in List "l" and return the result in "rettv".
|
||||||
static void filter_map_list(list_T *l, filtermap_T filtermap, const char *func_name,
|
static void filter_map_list(list_T *l, filtermap_T filtermap, const char *func_name,
|
||||||
const char *arg_errmsg, typval_T *expr, typval_T *rettv)
|
const char *arg_errmsg, typval_T *expr, typval_T *rettv)
|
||||||
{
|
{
|
||||||
@ -5345,21 +5351,25 @@ static void filter_map_list(list_T *l, filtermap_T filtermap, const char *func_n
|
|||||||
tv_list_set_lock(l, prev_lock);
|
tv_list_set_lock(l, prev_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Implementation of map() and filter().
|
/// Implementation of map(), filter() and foreach().
|
||||||
static void filter_map(typval_T *argvars, typval_T *rettv, filtermap_T filtermap)
|
static void filter_map(typval_T *argvars, typval_T *rettv, filtermap_T filtermap)
|
||||||
{
|
{
|
||||||
const char *const func_name = (filtermap == FILTERMAP_MAP
|
const char *const func_name = (filtermap == FILTERMAP_MAP
|
||||||
? "map()"
|
? "map()"
|
||||||
: (filtermap == FILTERMAP_MAPNEW
|
: (filtermap == FILTERMAP_MAPNEW
|
||||||
? "mapnew()"
|
? "mapnew()"
|
||||||
: "filter()"));
|
: (filtermap == FILTERMAP_FILTER
|
||||||
|
? "filter()"
|
||||||
|
: "foreach()")));
|
||||||
const char *const arg_errmsg = (filtermap == FILTERMAP_MAP
|
const char *const arg_errmsg = (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"))));
|
||||||
|
|
||||||
// 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) {
|
||||||
tv_copy(&argvars[0], rettv);
|
tv_copy(&argvars[0], rettv);
|
||||||
}
|
}
|
||||||
@ -5407,7 +5417,7 @@ static void filter_map(typval_T *argvars, typval_T *rettv, filtermap_T filtermap
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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.
|
||||||
///
|
///
|
||||||
/// @param tv original value
|
/// @param tv original value
|
||||||
@ -5422,6 +5432,17 @@ static int filter_map_one(typval_T *tv, typval_T *expr, const filtermap_T filter
|
|||||||
int retval = FAIL;
|
int retval = FAIL;
|
||||||
|
|
||||||
tv_copy(tv, &vimvars[VV_VAL].vv_tv);
|
tv_copy(tv, &vimvars[VV_VAL].vv_tv);
|
||||||
|
|
||||||
|
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] = vimvars[VV_KEY].vv_tv;
|
argv[0] = vimvars[VV_KEY].vv_tv;
|
||||||
argv[1] = vimvars[VV_VAL].vv_tv;
|
argv[1] = vimvars[VV_VAL].vv_tv;
|
||||||
if (eval_expr_typval(expr, false, argv, 2, newtv) == FAIL) {
|
if (eval_expr_typval(expr, false, argv, 2, newtv) == FAIL) {
|
||||||
@ -5438,6 +5459,8 @@ static int filter_map_one(typval_T *tv, typval_T *expr, const filtermap_T filter
|
|||||||
if (error) {
|
if (error) {
|
||||||
goto theend;
|
goto theend;
|
||||||
}
|
}
|
||||||
|
} else if (filtermap == FILTERMAP_FOREACH) {
|
||||||
|
tv_clear(newtv);
|
||||||
}
|
}
|
||||||
retval = OK;
|
retval = OK;
|
||||||
theend:
|
theend:
|
||||||
@ -5463,6 +5486,12 @@ void f_mapnew(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
|||||||
filter_map(argvars, rettv, FILTERMAP_MAPNEW);
|
filter_map(argvars, rettv, FILTERMAP_MAPNEW);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// "foreach()" function
|
||||||
|
void f_foreach(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
||||||
|
{
|
||||||
|
filter_map(argvars, rettv, FILTERMAP_FOREACH);
|
||||||
|
}
|
||||||
|
|
||||||
/// "function()" function
|
/// "function()" function
|
||||||
/// "funcref()" function
|
/// "funcref()" function
|
||||||
void common_function(typval_T *argvars, typval_T *rettv, bool is_funcref)
|
void common_function(typval_T *argvars, typval_T *rettv, bool is_funcref)
|
||||||
|
@ -2923,6 +2923,48 @@ M.funcs = {
|
|||||||
returns = 'string',
|
returns = 'string',
|
||||||
signature = 'foldtextresult({lnum})',
|
signature = 'foldtextresult({lnum})',
|
||||||
},
|
},
|
||||||
|
foreach = {
|
||||||
|
args = 2,
|
||||||
|
base = 1,
|
||||||
|
desc = [=[
|
||||||
|
{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, 'let used[v:val] = v: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 lambda you don't get an error if it only accepts one
|
||||||
|
argument.
|
||||||
|
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.
|
||||||
|
]=],
|
||||||
|
name = 'foreach',
|
||||||
|
params = { { 'expr1', 'any' }, { 'expr2', 'any' } },
|
||||||
|
signature = 'foreach({expr1}, {expr2})',
|
||||||
|
},
|
||||||
foreground = {
|
foreground = {
|
||||||
args = 0,
|
args = 0,
|
||||||
params = {},
|
params = {},
|
||||||
|
@ -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:')
|
||||||
@ -108,6 +173,8 @@ func Test_map_filter_fails()
|
|||||||
" Nvim doesn't have null partials
|
" Nvim doesn't have null partials
|
||||||
" 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()
|
||||||
@ -125,7 +192,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}
|
||||||
@ -135,6 +202,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)})
|
||||||
@ -162,6 +241,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 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 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()
|
||||||
@ -221,6 +330,37 @@ func Test_filter_map_string()
|
|||||||
END
|
END
|
||||||
call CheckLegacyAndVim9Success(lines)
|
call 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 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 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 .. "]"'))
|
||||||
|
@ -2,6 +2,10 @@
|
|||||||
" Use a different file name for each run.
|
" Use a different file name for each run.
|
||||||
let s:sequence = 1
|
let s:sequence = 1
|
||||||
|
|
||||||
|
func CheckDefSuccess(lines)
|
||||||
|
return
|
||||||
|
endfunc
|
||||||
|
|
||||||
func CheckDefFailure(lines, error, lnum = -3)
|
func CheckDefFailure(lines, error, lnum = -3)
|
||||||
return
|
return
|
||||||
endfunc
|
endfunc
|
||||||
|
Loading…
Reference in New Issue
Block a user