mirror of
https://github.com/neovim/neovim.git
synced 2026-08-29 02:21:51 +00:00
vim-patch:7.4.1989
Problem: filter() and map() only accept a string argument.
Solution: Implement using a Funcref argument (Yasuhiro Matsumoto, Ken
Takata)
b33c7eb5b8
This commit is contained in:
@@ -5285,7 +5285,8 @@ tv_equal (
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// For VAR_FUNC and VAR_PARTIAL only compare the function name.
|
||||
// For VAR_FUNC and VAR_PARTIAL compare the function name, bound dict and
|
||||
// arguments.
|
||||
if ((tv1->v_type == VAR_FUNC
|
||||
|| (tv1->v_type == VAR_PARTIAL && tv1->vval.v_partial != NULL))
|
||||
&& (tv2->v_type == VAR_FUNC
|
||||
@@ -9306,8 +9307,7 @@ static void findfilendir(typval_T *argvars, typval_T *rettv, int find_what)
|
||||
*/
|
||||
static void filter_map(typval_T *argvars, typval_T *rettv, int map)
|
||||
{
|
||||
char_u buf[NUMBUFLEN];
|
||||
char_u *expr;
|
||||
typval_T *expr;
|
||||
listitem_T *li, *nli;
|
||||
list_T *l = NULL;
|
||||
dictitem_T *di;
|
||||
@@ -9339,16 +9339,15 @@ static void filter_map(typval_T *argvars, typval_T *rettv, int map)
|
||||
return;
|
||||
}
|
||||
|
||||
expr = get_tv_string_buf_chk(&argvars[1], buf);
|
||||
/* On type errors, the preceding call has already displayed an error
|
||||
* message. Avoid a misleading error message for an empty string that
|
||||
* was not passed as argument. */
|
||||
if (expr != NULL) {
|
||||
expr = &argvars[1];
|
||||
// On type errors, the preceding call has already displayed an error
|
||||
// message. Avoid a misleading error message for an empty string that
|
||||
// was not passed as argument.
|
||||
if (expr->v_type != VAR_UNKNOWN) {
|
||||
prepare_vimvar(VV_VAL, &save_val);
|
||||
expr = skipwhite(expr);
|
||||
|
||||
/* We reset "did_emsg" to be able to detect whether an error
|
||||
* occurred during evaluation of the expression. */
|
||||
// We reset "did_emsg" to be able to detect whether an error
|
||||
// occurred during evaluation of the expression.
|
||||
save_did_emsg = did_emsg;
|
||||
did_emsg = FALSE;
|
||||
|
||||
@@ -9412,20 +9411,41 @@ static void filter_map(typval_T *argvars, typval_T *rettv, int map)
|
||||
copy_tv(&argvars[0], rettv);
|
||||
}
|
||||
|
||||
static int filter_map_one(typval_T *tv, char_u *expr, int map, int *remp)
|
||||
static int filter_map_one(typval_T *tv, typval_T *expr, int map, int *remp)
|
||||
{
|
||||
typval_T rettv;
|
||||
typeval_T argv[3];
|
||||
char_u *s;
|
||||
int retval = FAIL;
|
||||
int dummy;
|
||||
|
||||
copy_tv(tv, &vimvars[VV_VAL].vv_tv);
|
||||
s = expr;
|
||||
if (eval1(&s, &rettv, TRUE) == FAIL)
|
||||
goto theend;
|
||||
if (*s != NUL) { /* check for trailing chars after expr */
|
||||
EMSG2(_(e_invexpr2), s);
|
||||
clear_tv(&rettv);
|
||||
goto theend;
|
||||
argv[0] = vimvars[VV_KEY].vv_tv;
|
||||
argv[1] = vimvars[VV_VAL].vv_tv;
|
||||
s = expr->vval.v_string;
|
||||
if (expr->v_type == VAR_FUNC) {
|
||||
if (call_func(s, (int)STRLEN(s), &rettv, 2, argv, 0L, 0L, &dummy,
|
||||
true, NULL, NULL) == FAIL) {
|
||||
goto theend;
|
||||
}
|
||||
} else if (expr->v_type == VAR_PARTIAL) {
|
||||
partial_T *partial = expr->vval.v_partial;
|
||||
|
||||
s = partial->pt_name;
|
||||
if (call_func(s, (int)STRLEN(s), &rettv, 2, argv, 0L, 0L, &dummy,
|
||||
true, partial, NULL) == FAIL) {
|
||||
goto theend;
|
||||
}
|
||||
} else {
|
||||
s = skipwhite(s);
|
||||
if (eval1(&s, &rettv, true) == FAIL) {
|
||||
goto theend;
|
||||
}
|
||||
|
||||
if (*s != NUL) { // check for trailing chars after expr
|
||||
EMSG2(_(e_invexpr2), s);
|
||||
goto theend;
|
||||
}
|
||||
}
|
||||
if (map) {
|
||||
/* map(): replace the list item value */
|
||||
|
||||
@@ -34,6 +34,7 @@ NEW_TESTS ?= \
|
||||
test_cscope.res \
|
||||
test_digraph.res \
|
||||
test_diffmode.res \
|
||||
test_filter_map.res \
|
||||
test_gn.res \
|
||||
test_hardcopy.res \
|
||||
test_help_tagjump.res \
|
||||
|
||||
@@ -9,6 +9,7 @@ source test_ex_undo.vim
|
||||
source test_expr.vim
|
||||
source test_expr_utf8.vim
|
||||
source test_feedkeys.vim
|
||||
source test_filter_map.vim
|
||||
source test_goto.vim
|
||||
source test_jumps.vim
|
||||
source test_match.vim
|
||||
|
||||
77
src/nvim/testdir/test_filter_map.vim
Normal file
77
src/nvim/testdir/test_filter_map.vim
Normal file
@@ -0,0 +1,77 @@
|
||||
" Test filter() and map()
|
||||
|
||||
" list with expression string
|
||||
func Test_filter_map_list_expr_string()
|
||||
" filter()
|
||||
call assert_equal([2, 3, 4], filter([1, 2, 3, 4], 'v:val > 1'))
|
||||
call assert_equal([3, 4], filter([1, 2, 3, 4], 'v:key > 1'))
|
||||
|
||||
" map()
|
||||
call assert_equal([2, 4, 6, 8], map([1, 2, 3, 4], 'v:val * 2'))
|
||||
call assert_equal([0, 2, 4, 6], map([1, 2, 3, 4], 'v:key * 2'))
|
||||
endfunc
|
||||
|
||||
" dict with expression string
|
||||
func Test_filter_map_dict_expr_string()
|
||||
let dict = {"foo": 1, "bar": 2, "baz": 3}
|
||||
|
||||
" filter()
|
||||
call assert_equal({"bar": 2, "baz": 3}, filter(copy(dict), 'v:val > 1'))
|
||||
call assert_equal({"foo": 1, "baz": 3}, filter(copy(dict), 'v:key > "bar"'))
|
||||
|
||||
" map()
|
||||
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]'))
|
||||
endfunc
|
||||
|
||||
" list with funcref
|
||||
func Test_filter_map_list_expr_funcref()
|
||||
" filter()
|
||||
func! s:filter1(index, val) abort
|
||||
return a:val > 1
|
||||
endfunc
|
||||
call assert_equal([2, 3, 4], filter([1, 2, 3, 4], function('s:filter1')))
|
||||
|
||||
func! s:filter2(index, val) abort
|
||||
return a:index > 1
|
||||
endfunc
|
||||
call assert_equal([3, 4], filter([1, 2, 3, 4], function('s:filter2')))
|
||||
|
||||
" map()
|
||||
func! s:filter3(index, val) abort
|
||||
return a:val * 2
|
||||
endfunc
|
||||
call assert_equal([2, 4, 6, 8], map([1, 2, 3, 4], function('s:filter3')))
|
||||
|
||||
func! s:filter4(index, val) abort
|
||||
return a:index * 2
|
||||
endfunc
|
||||
call assert_equal([0, 2, 4, 6], map([1, 2, 3, 4], function('s:filter4')))
|
||||
endfunc
|
||||
|
||||
" dict with funcref
|
||||
func Test_filter_map_dict_expr_funcref()
|
||||
let dict = {"foo": 1, "bar": 2, "baz": 3}
|
||||
|
||||
" filter()
|
||||
func! s:filter1(key, val) abort
|
||||
return a:val > 1
|
||||
endfunc
|
||||
call assert_equal({"bar": 2, "baz": 3}, filter(copy(dict), function('s:filter1')))
|
||||
|
||||
func! s:filter2(key, val) abort
|
||||
return a:key > "bar"
|
||||
endfunc
|
||||
call assert_equal({"foo": 1, "baz": 3}, filter(copy(dict), function('s:filter2')))
|
||||
|
||||
" map()
|
||||
func! s:filter3(key, val) abort
|
||||
return a:val * 2
|
||||
endfunc
|
||||
call assert_equal({"foo": 2, "bar": 4, "baz": 6}, map(copy(dict), function('s:filter3')))
|
||||
|
||||
func! s:filter4(key, val) abort
|
||||
return a:key[0]
|
||||
endfunc
|
||||
call assert_equal({"foo": "f", "bar": "b", "baz": "b"}, map(copy(dict), function('s:filter4')))
|
||||
endfunc
|
||||
@@ -14,6 +14,14 @@ func MySort(up, one, two)
|
||||
return a:one < a:two ? 1 : -1
|
||||
endfunc
|
||||
|
||||
func MyMap(sub, index, val)
|
||||
return a:val - a:sub
|
||||
endfunc
|
||||
|
||||
func MyFilter(threshold, index, val)
|
||||
return a:val > a:threshold
|
||||
endfunc
|
||||
|
||||
func Test_partial_args()
|
||||
let Cb = function('MyFunc', ["foo", "bar"])
|
||||
|
||||
@@ -36,6 +44,16 @@ func Test_partial_args()
|
||||
call assert_equal([1, 2, 3], sort([3, 1, 2], Sort))
|
||||
let Sort = function('MySort', [0])
|
||||
call assert_equal([3, 2, 1], sort([3, 1, 2], Sort))
|
||||
|
||||
let Map = function('MyMap', [2])
|
||||
call assert_equal([-1, 0, 1], map([1, 2, 3], Map))
|
||||
let Map = function('MyMap', [3])
|
||||
call assert_equal([-2, -1, 0], map([1, 2, 3], Map))
|
||||
|
||||
let Filter = function('MyFilter', [1])
|
||||
call assert_equal([2, 3], filter([1, 2, 3], Filter))
|
||||
let Filter = function('MyFilter', [2])
|
||||
call assert_equal([3], filter([1, 2, 3], Filter))
|
||||
endfunc
|
||||
|
||||
func MyDictFunc(arg1, arg2) dict
|
||||
@@ -59,6 +77,9 @@ func Test_partial_dict()
|
||||
call assert_equal("hello/xxx/yyy", Cb("xxx", "yyy"))
|
||||
call assert_fails('Cb("fff")', 'E492:')
|
||||
|
||||
let Cb = function('MyDictFunc', dict)
|
||||
call assert_equal({"foo": "hello/foo/1", "bar": "hello/bar/2"}, map({"foo": 1, "bar": 2}, Cb))
|
||||
|
||||
let dict = {"tr": function('tr', ['hello', 'h', 'H'])}
|
||||
call assert_equal("Hello", dict.tr())
|
||||
endfunc
|
||||
|
||||
@@ -451,7 +451,7 @@ static int included_patches[] = {
|
||||
// 1992,
|
||||
// 1991,
|
||||
1990,
|
||||
// 1989,
|
||||
1989,
|
||||
// 1988 NA
|
||||
// 1987 NA
|
||||
// 1986,
|
||||
|
||||
Reference in New Issue
Block a user