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:
Michael Ennen
2016-12-14 00:20:48 -07:00
parent b0fc6108c9
commit bb2afeb026
7 changed files with 192 additions and 42 deletions

View File

@@ -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 */

View File

@@ -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 \

View File

@@ -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

View 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

View File

@@ -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

View File

@@ -451,7 +451,7 @@ static int included_patches[] = {
// 1992,
// 1991,
1990,
// 1989,
1989,
// 1988 NA
// 1987 NA
// 1986,