# HG changeset patch # User Christian Brabandt # Date 1509397205 -3600 # Node ID 7749260f261ccb9afb112f846ce6a08a621ca518 # Parent e6b964b3856dcda9222d0b40af8452050a512c69 patch 8.0.1239: cannot use a lambda for the skip argument to searchpair() commit https://github.com/vim/vim/commit/48570488f17e397183ea7d5c7ca67d6e4ffb013d Author: Bram Moolenaar Date: Mon Oct 30 21:48:41 2017 +0100 patch 8.0.1239: cannot use a lambda for the skip argument to searchpair() Problem: Cannot use a lambda for the skip argument to searchpair(). Solution: Evaluate a partial, funcref and lambda. (LemonBoy, closes https://github.com/vim/vim/issues/1454, closes #2265) diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -6784,6 +6784,7 @@ searchpair({start}, {middle}, {end} [, { When {skip} is omitted or empty, every match is accepted. When evaluating {skip} causes an error the search is aborted and -1 returned. + {skip} can be a string, a lambda, a funcref or a partial. For {stopline} and {timeout} see |search()|. diff --git a/src/eval.c b/src/eval.c --- a/src/eval.c +++ b/src/eval.c @@ -696,6 +696,70 @@ eval_to_bool( return (int)retval; } + static int +eval_expr_typval(typval_T *expr, typval_T *argv, int argc, typval_T *rettv) +{ + char_u *s; + int dummy; + char_u buf[NUMBUFLEN]; + + if (expr->v_type == VAR_FUNC) + { + s = expr->vval.v_string; + if (s == NULL || *s == NUL) + return FAIL; + if (call_func(s, (int)STRLEN(s), rettv, argc, argv, NULL, + 0L, 0L, &dummy, TRUE, NULL, NULL) == FAIL) + return FAIL; + } + else if (expr->v_type == VAR_PARTIAL) + { + partial_T *partial = expr->vval.v_partial; + + s = partial_name(partial); + if (s == NULL || *s == NUL) + return FAIL; + if (call_func(s, (int)STRLEN(s), rettv, argc, argv, NULL, + 0L, 0L, &dummy, TRUE, partial, NULL) == FAIL) + return FAIL; + } + else + { + s = get_tv_string_buf_chk(expr, buf); + if (s == NULL) + return FAIL; + s = skipwhite(s); + if (eval1(&s, rettv, TRUE) == FAIL) + return FAIL; + if (*s != NUL) /* check for trailing chars after expr */ + { + EMSG2(_(e_invexpr2), s); + return FAIL; + } + } + return OK; +} + +/* + * Like eval_to_bool() but using a typval_T instead of a string. + * Works for string, funcref and partial. + */ + int +eval_expr_to_bool(typval_T *expr, int *error) +{ + typval_T rettv; + int res; + + if (eval_expr_typval(expr, NULL, 0, &rettv) == FAIL) + { + *error = TRUE; + return FALSE; + } + res = (get_tv_number_chk(&rettv, error) != 0); + clear_tv(&rettv); + return res; +} + /* * Top level evaluation function, returning a string. If "skip" is TRUE, * only parsing to "nextcmd" is done, without reporting errors. Return @@ -9971,44 +10035,13 @@ filter_map_one(typval_T *tv, typval_T *e { typval_T rettv; typval_T argv[3]; - char_u buf[NUMBUFLEN]; - char_u *s; int retval = FAIL; - int dummy; copy_tv(tv, &vimvars[VV_VAL].vv_tv); argv[0] = vimvars[VV_KEY].vv_tv; argv[1] = vimvars[VV_VAL].vv_tv; - if (expr->v_type == VAR_FUNC) - { - s = expr->vval.v_string; - if (call_func(s, (int)STRLEN(s), &rettv, 2, argv, NULL, - 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_name(partial); - if (call_func(s, (int)STRLEN(s), &rettv, 2, argv, NULL, - 0L, 0L, &dummy, TRUE, partial, NULL) == FAIL) - goto theend; - } - else - { - s = get_tv_string_buf_chk(expr, buf); - if (s == NULL) - goto theend; - 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 (eval_expr_typval(expr, argv, 2, &rettv) == FAIL) + goto theend; if (map) { /* map(): replace the list item value */ diff --git a/src/evalfunc.c b/src/evalfunc.c --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -9531,13 +9531,12 @@ f_searchdecl(typval_T *argvars, typval_T searchpair_cmn(typval_T *argvars, pos_T *match_pos) { char_u *spat, *mpat, *epat; - char_u *skip; + typval_T *skip; int save_p_ws = p_ws; int dir; int flags = 0; char_u nbuf1[NUMBUFLEN]; char_u nbuf2[NUMBUFLEN]; - char_u nbuf3[NUMBUFLEN]; int retval = 0; /* default: FAIL */ long lnum_stop = 0; long time_limit = 0; @@ -9571,10 +9570,16 @@ searchpair_cmn(typval_T *argvars, pos_T /* Optional fifth argument: skip expression */ if (argvars[3].v_type == VAR_UNKNOWN || argvars[4].v_type == VAR_UNKNOWN) - skip = (char_u *)""; - else - { - skip = get_tv_string_buf_chk(&argvars[4], nbuf3); + skip = NULL; + else + { + skip = &argvars[4]; + if (skip->v_type != VAR_FUNC && skip->v_type != VAR_PARTIAL + && skip->v_type != VAR_STRING) + { + /* Type error */ + goto theend; + } if (argvars[5].v_type != VAR_UNKNOWN) { lnum_stop = (long)get_tv_number_chk(&argvars[5], NULL); @@ -9590,8 +9595,6 @@ searchpair_cmn(typval_T *argvars, pos_T #endif } } - if (skip == NULL) - goto theend; /* type error */ retval = do_searchpair(spat, mpat, epat, dir, skip, flags, match_pos, lnum_stop, time_limit); @@ -9645,7 +9648,7 @@ do_searchpair( char_u *mpat, /* middle pattern */ char_u *epat, /* end pattern */ int dir, /* BACKWARD or FORWARD */ - char_u *skip, /* skip expression */ + typval_T *skip, /* skip expression */ int flags, /* SP_SETPCMARK and other SP_ values */ pos_T *match_pos, linenr_T lnum_stop, /* stop at this line if not zero */ @@ -9662,6 +9665,7 @@ do_searchpair( int n; int r; int nest = 1; + int use_skip = FALSE; int err; int options = SEARCH_KEEP; proftime_T tm; @@ -9690,6 +9694,14 @@ do_searchpair( if (flags & SP_START) options |= SEARCH_START; + if (skip != NULL) + { + /* Empty string means to not use the skip expression. */ + if (skip->v_type == VAR_STRING || skip->v_type == VAR_FUNC) + use_skip = skip->vval.v_string != NULL + && *skip->vval.v_string != NUL; + } + save_cursor = curwin->w_cursor; pos = curwin->w_cursor; CLEAR_POS(&firstpos); @@ -9721,11 +9733,12 @@ do_searchpair( options &= ~SEARCH_START; /* If the skip pattern matches, ignore this match. */ - if (*skip != NUL) + if (use_skip) { save_pos = curwin->w_cursor; curwin->w_cursor = pos; - r = eval_to_bool(skip, &err, NULL, FALSE); + err = FALSE; + r = eval_expr_to_bool(skip, &err); curwin->w_cursor = save_pos; if (err) { diff --git a/src/proto/eval.pro b/src/proto/eval.pro --- a/src/proto/eval.pro +++ b/src/proto/eval.pro @@ -10,6 +10,7 @@ int eval_printexpr(char_u *fname, char_u void eval_diff(char_u *origfile, char_u *newfile, char_u *outfile); void eval_patch(char_u *origfile, char_u *difffile, char_u *outfile); int eval_to_bool(char_u *arg, int *error, char_u **nextcmd, int skip); +int eval_expr_to_bool(typval_T *expr, int *error); char_u *eval_to_string_skip(char_u *arg, char_u **nextcmd, int skip); int skip_expr(char_u **pp); char_u *eval_to_string(char_u *arg, char_u **nextcmd, int convert); @@ -47,7 +48,7 @@ int garbage_collect(int testing); int set_ref_in_ht(hashtab_T *ht, int copyID, list_stack_T **list_stack); int set_ref_in_list(list_T *l, int copyID, ht_stack_T **ht_stack); int set_ref_in_item(typval_T *tv, int copyID, ht_stack_T **ht_stack, list_stack_T **list_stack); -char_u *echo_string_core(typval_T *tv, char_u **tofree, char_u *numbuf, int copyID, int echo_style, int restore_copyID, int dict_val); +char_u *echo_string_core(typval_T *tv, char_u **tofree, char_u *numbuf, int copyID, int echo_style, int restore_copyID, int composite_val); char_u *echo_string(typval_T *tv, char_u **tofree, char_u *numbuf, int copyID); char_u *tv2string(typval_T *tv, char_u **tofree, char_u *numbuf, int copyID); char_u *string_quote(char_u *str, int function); diff --git a/src/proto/evalfunc.pro b/src/proto/evalfunc.pro --- a/src/proto/evalfunc.pro +++ b/src/proto/evalfunc.pro @@ -8,7 +8,7 @@ buf_T *buflist_find_by_name(char_u *name void execute_redir_str(char_u *value, int value_len); void mzscheme_call_vim(char_u *name, typval_T *args, typval_T *rettv); float_T vim_round(float_T f); -long do_searchpair(char_u *spat, char_u *mpat, char_u *epat, int dir, char_u *skip, int flags, pos_T *match_pos, linenr_T lnum_stop, long time_limit); +long do_searchpair(char_u *spat, char_u *mpat, char_u *epat, int dir, typval_T *skip, int flags, pos_T *match_pos, linenr_T lnum_stop, long time_limit); char_u *get_callback(typval_T *arg, partial_T **pp); void free_callback(char_u *callback, partial_T *partial); /* vim: set ft=c : */ diff --git a/src/search.c b/src/search.c --- a/src/search.c +++ b/src/search.c @@ -4015,7 +4015,7 @@ again: { if (do_searchpair((char_u *)"<[^ \t>/!]\\+\\%(\\_s\\_[^>]\\{-}[^/]>\\|$\\|\\_s\\=>\\)", (char_u *)"", - (char_u *)"]*>", BACKWARD, (char_u *)"", 0, + (char_u *)"]*>", BACKWARD, NULL, 0, NULL, (linenr_T)0, 0L) <= 0) { curwin->w_cursor = old_pos; @@ -4049,7 +4049,7 @@ again: sprintf((char *)spat, "<%.*s\\>\\%%(\\s\\_[^>]\\{-}[^/]>\\|>\\)\\c", len, p); sprintf((char *)epat, "\\c", len, p); - r = do_searchpair(spat, (char_u *)"", epat, FORWARD, (char_u *)"", + r = do_searchpair(spat, (char_u *)"", epat, FORWARD, NULL, 0, NULL, (linenr_T)0, 0L); vim_free(spat); diff --git a/src/testdir/test_search.vim b/src/testdir/test_search.vim --- a/src/testdir/test_search.vim +++ b/src/testdir/test_search.vim @@ -296,6 +296,25 @@ func Test_searchpair() q! endfunc +func Test_searchpair_skip() + func Zero() + return 0 + endfunc + func Partial(x) + return a:x + endfunc + new + call setline(1, ['{', 'foo', 'foo', 'foo', '}']) + 3 | call assert_equal(1, searchpair('{', '', '}', 'bWn', '')) + 3 | call assert_equal(1, searchpair('{', '', '}', 'bWn', '0')) + 3 | call assert_equal(1, searchpair('{', '', '}', 'bWn', {-> 0})) + 3 | call assert_equal(1, searchpair('{', '', '}', 'bWn', function('Zero'))) + 3 | call assert_equal(1, searchpair('{', '', '}', 'bWn', function('Partial', [0]))) + " invalid argument + 3 | call assert_equal(0, searchpair('{', '', '}', 'bWn', 0)) + bw! +endfunc + func Test_searchc() " These commands used to cause memory overflow in searchc(). new diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -762,6 +762,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1239, +/**/ 1238, /**/ 1237,