Skip to content

Commit 4857048

Browse files
committed
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 #1454, closes #2265)
1 parent 2e51d9a commit 4857048

8 files changed

Lines changed: 115 additions & 46 deletions

File tree

runtime/doc/eval.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6784,6 +6784,7 @@ searchpair({start}, {middle}, {end} [, {flags} [, {skip}
67846784
When {skip} is omitted or empty, every match is accepted.
67856785
When evaluating {skip} causes an error the search is aborted
67866786
and -1 returned.
6787+
{skip} can be a string, a lambda, a funcref or a partial.
67876788

67886789
For {stopline} and {timeout} see |search()|.
67896790

src/eval.c

Lines changed: 66 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -696,6 +696,70 @@ eval_to_bool(
696696
return (int)retval;
697697
}
698698

699+
static int
700+
eval_expr_typval(typval_T *expr, typval_T *argv, int argc, typval_T *rettv)
701+
{
702+
char_u *s;
703+
int dummy;
704+
char_u buf[NUMBUFLEN];
705+
706+
if (expr->v_type == VAR_FUNC)
707+
{
708+
s = expr->vval.v_string;
709+
if (s == NULL || *s == NUL)
710+
return FAIL;
711+
if (call_func(s, (int)STRLEN(s), rettv, argc, argv, NULL,
712+
0L, 0L, &dummy, TRUE, NULL, NULL) == FAIL)
713+
return FAIL;
714+
}
715+
else if (expr->v_type == VAR_PARTIAL)
716+
{
717+
partial_T *partial = expr->vval.v_partial;
718+
719+
s = partial_name(partial);
720+
if (s == NULL || *s == NUL)
721+
return FAIL;
722+
if (call_func(s, (int)STRLEN(s), rettv, argc, argv, NULL,
723+
0L, 0L, &dummy, TRUE, partial, NULL) == FAIL)
724+
return FAIL;
725+
}
726+
else
727+
{
728+
s = get_tv_string_buf_chk(expr, buf);
729+
if (s == NULL)
730+
return FAIL;
731+
s = skipwhite(s);
732+
if (eval1(&s, rettv, TRUE) == FAIL)
733+
return FAIL;
734+
if (*s != NUL) /* check for trailing chars after expr */
735+
{
736+
EMSG2(_(e_invexpr2), s);
737+
return FAIL;
738+
}
739+
}
740+
return OK;
741+
}
742+
743+
/*
744+
* Like eval_to_bool() but using a typval_T instead of a string.
745+
* Works for string, funcref and partial.
746+
*/
747+
int
748+
eval_expr_to_bool(typval_T *expr, int *error)
749+
{
750+
typval_T rettv;
751+
int res;
752+
753+
if (eval_expr_typval(expr, NULL, 0, &rettv) == FAIL)
754+
{
755+
*error = TRUE;
756+
return FALSE;
757+
}
758+
res = (get_tv_number_chk(&rettv, error) != 0);
759+
clear_tv(&rettv);
760+
return res;
761+
}
762+
699763
/*
700764
* Top level evaluation function, returning a string. If "skip" is TRUE,
701765
* only parsing to "nextcmd" is done, without reporting errors. Return
@@ -9971,44 +10035,13 @@ filter_map_one(typval_T *tv, typval_T *expr, int map, int *remp)
997110035
{
997210036
typval_T rettv;
997310037
typval_T argv[3];
9974-
char_u buf[NUMBUFLEN];
9975-
char_u *s;
997610038
int retval = FAIL;
9977-
int dummy;
997810039

997910040
copy_tv(tv, &vimvars[VV_VAL].vv_tv);
998010041
argv[0] = vimvars[VV_KEY].vv_tv;
998110042
argv[1] = vimvars[VV_VAL].vv_tv;
9982-
if (expr->v_type == VAR_FUNC)
9983-
{
9984-
s = expr->vval.v_string;
9985-
if (call_func(s, (int)STRLEN(s), &rettv, 2, argv, NULL,
9986-
0L, 0L, &dummy, TRUE, NULL, NULL) == FAIL)
9987-
goto theend;
9988-
}
9989-
else if (expr->v_type == VAR_PARTIAL)
9990-
{
9991-
partial_T *partial = expr->vval.v_partial;
9992-
9993-
s = partial_name(partial);
9994-
if (call_func(s, (int)STRLEN(s), &rettv, 2, argv, NULL,
9995-
0L, 0L, &dummy, TRUE, partial, NULL) == FAIL)
9996-
goto theend;
9997-
}
9998-
else
9999-
{
10000-
s = get_tv_string_buf_chk(expr, buf);
10001-
if (s == NULL)
10002-
goto theend;
10003-
s = skipwhite(s);
10004-
if (eval1(&s, &rettv, TRUE) == FAIL)
10005-
goto theend;
10006-
if (*s != NUL) /* check for trailing chars after expr */
10007-
{
10008-
EMSG2(_(e_invexpr2), s);
10009-
goto theend;
10010-
}
10011-
}
10043+
if (eval_expr_typval(expr, argv, 2, &rettv) == FAIL)
10044+
goto theend;
1001210045
if (map)
1001310046
{
1001410047
/* map(): replace the list item value */

src/evalfunc.c

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9531,13 +9531,12 @@ f_searchdecl(typval_T *argvars, typval_T *rettv)
95319531
searchpair_cmn(typval_T *argvars, pos_T *match_pos)
95329532
{
95339533
char_u *spat, *mpat, *epat;
9534-
char_u *skip;
9534+
typval_T *skip;
95359535
int save_p_ws = p_ws;
95369536
int dir;
95379537
int flags = 0;
95389538
char_u nbuf1[NUMBUFLEN];
95399539
char_u nbuf2[NUMBUFLEN];
9540-
char_u nbuf3[NUMBUFLEN];
95419540
int retval = 0; /* default: FAIL */
95429541
long lnum_stop = 0;
95439542
long time_limit = 0;
@@ -9571,10 +9570,16 @@ searchpair_cmn(typval_T *argvars, pos_T *match_pos)
95719570
/* Optional fifth argument: skip expression */
95729571
if (argvars[3].v_type == VAR_UNKNOWN
95739572
|| argvars[4].v_type == VAR_UNKNOWN)
9574-
skip = (char_u *)"";
9573+
skip = NULL;
95759574
else
95769575
{
9577-
skip = get_tv_string_buf_chk(&argvars[4], nbuf3);
9576+
skip = &argvars[4];
9577+
if (skip->v_type != VAR_FUNC && skip->v_type != VAR_PARTIAL
9578+
&& skip->v_type != VAR_STRING)
9579+
{
9580+
/* Type error */
9581+
goto theend;
9582+
}
95789583
if (argvars[5].v_type != VAR_UNKNOWN)
95799584
{
95809585
lnum_stop = (long)get_tv_number_chk(&argvars[5], NULL);
@@ -9590,8 +9595,6 @@ searchpair_cmn(typval_T *argvars, pos_T *match_pos)
95909595
#endif
95919596
}
95929597
}
9593-
if (skip == NULL)
9594-
goto theend; /* type error */
95959598

95969599
retval = do_searchpair(spat, mpat, epat, dir, skip, flags,
95979600
match_pos, lnum_stop, time_limit);
@@ -9645,7 +9648,7 @@ do_searchpair(
96459648
char_u *mpat, /* middle pattern */
96469649
char_u *epat, /* end pattern */
96479650
int dir, /* BACKWARD or FORWARD */
9648-
char_u *skip, /* skip expression */
9651+
typval_T *skip, /* skip expression */
96499652
int flags, /* SP_SETPCMARK and other SP_ values */
96509653
pos_T *match_pos,
96519654
linenr_T lnum_stop, /* stop at this line if not zero */
@@ -9662,6 +9665,7 @@ do_searchpair(
96629665
int n;
96639666
int r;
96649667
int nest = 1;
9668+
int use_skip = FALSE;
96659669
int err;
96669670
int options = SEARCH_KEEP;
96679671
proftime_T tm;
@@ -9690,6 +9694,14 @@ do_searchpair(
96909694
if (flags & SP_START)
96919695
options |= SEARCH_START;
96929696

9697+
if (skip != NULL)
9698+
{
9699+
/* Empty string means to not use the skip expression. */
9700+
if (skip->v_type == VAR_STRING || skip->v_type == VAR_FUNC)
9701+
use_skip = skip->vval.v_string != NULL
9702+
&& *skip->vval.v_string != NUL;
9703+
}
9704+
96939705
save_cursor = curwin->w_cursor;
96949706
pos = curwin->w_cursor;
96959707
CLEAR_POS(&firstpos);
@@ -9721,11 +9733,12 @@ do_searchpair(
97219733
options &= ~SEARCH_START;
97229734

97239735
/* If the skip pattern matches, ignore this match. */
9724-
if (*skip != NUL)
9736+
if (use_skip)
97259737
{
97269738
save_pos = curwin->w_cursor;
97279739
curwin->w_cursor = pos;
9728-
r = eval_to_bool(skip, &err, NULL, FALSE);
9740+
err = FALSE;
9741+
r = eval_expr_to_bool(skip, &err);
97299742
curwin->w_cursor = save_pos;
97309743
if (err)
97319744
{

src/proto/eval.pro

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ int eval_printexpr(char_u *fname, char_u *args);
1010
void eval_diff(char_u *origfile, char_u *newfile, char_u *outfile);
1111
void eval_patch(char_u *origfile, char_u *difffile, char_u *outfile);
1212
int eval_to_bool(char_u *arg, int *error, char_u **nextcmd, int skip);
13+
int eval_expr_to_bool(typval_T *expr, int *error);
1314
char_u *eval_to_string_skip(char_u *arg, char_u **nextcmd, int skip);
1415
int skip_expr(char_u **pp);
1516
char_u *eval_to_string(char_u *arg, char_u **nextcmd, int convert);
@@ -47,7 +48,7 @@ int garbage_collect(int testing);
4748
int set_ref_in_ht(hashtab_T *ht, int copyID, list_stack_T **list_stack);
4849
int set_ref_in_list(list_T *l, int copyID, ht_stack_T **ht_stack);
4950
int set_ref_in_item(typval_T *tv, int copyID, ht_stack_T **ht_stack, list_stack_T **list_stack);
50-
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);
51+
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);
5152
char_u *echo_string(typval_T *tv, char_u **tofree, char_u *numbuf, int copyID);
5253
char_u *tv2string(typval_T *tv, char_u **tofree, char_u *numbuf, int copyID);
5354
char_u *string_quote(char_u *str, int function);

src/proto/evalfunc.pro

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ buf_T *buflist_find_by_name(char_u *name, int curtab_only);
88
void execute_redir_str(char_u *value, int value_len);
99
void mzscheme_call_vim(char_u *name, typval_T *args, typval_T *rettv);
1010
float_T vim_round(float_T f);
11-
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);
11+
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);
1212
char_u *get_callback(typval_T *arg, partial_T **pp);
1313
void free_callback(char_u *callback, partial_T *partial);
1414
/* vim: set ft=c : */

src/search.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4015,7 +4015,7 @@ current_tagblock(
40154015
{
40164016
if (do_searchpair((char_u *)"<[^ \t>/!]\\+\\%(\\_s\\_[^>]\\{-}[^/]>\\|$\\|\\_s\\=>\\)",
40174017
(char_u *)"",
4018-
(char_u *)"</[^>]*>", BACKWARD, (char_u *)"", 0,
4018+
(char_u *)"</[^>]*>", BACKWARD, NULL, 0,
40194019
NULL, (linenr_T)0, 0L) <= 0)
40204020
{
40214021
curwin->w_cursor = old_pos;
@@ -4049,7 +4049,7 @@ current_tagblock(
40494049
sprintf((char *)spat, "<%.*s\\>\\%%(\\s\\_[^>]\\{-}[^/]>\\|>\\)\\c", len, p);
40504050
sprintf((char *)epat, "</%.*s>\\c", len, p);
40514051

4052-
r = do_searchpair(spat, (char_u *)"", epat, FORWARD, (char_u *)"",
4052+
r = do_searchpair(spat, (char_u *)"", epat, FORWARD, NULL,
40534053
0, NULL, (linenr_T)0, 0L);
40544054

40554055
vim_free(spat);

src/testdir/test_search.vim

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,25 @@ func Test_searchpair()
296296
q!
297297
endfunc
298298

299+
func Test_searchpair_skip()
300+
func Zero()
301+
return 0
302+
endfunc
303+
func Partial(x)
304+
return a:x
305+
endfunc
306+
new
307+
call setline(1, ['{', 'foo', 'foo', 'foo', '}'])
308+
3 | call assert_equal(1, searchpair('{', '', '}', 'bWn', ''))
309+
3 | call assert_equal(1, searchpair('{', '', '}', 'bWn', '0'))
310+
3 | call assert_equal(1, searchpair('{', '', '}', 'bWn', {-> 0}))
311+
3 | call assert_equal(1, searchpair('{', '', '}', 'bWn', function('Zero')))
312+
3 | call assert_equal(1, searchpair('{', '', '}', 'bWn', function('Partial', [0])))
313+
" invalid argument
314+
3 | call assert_equal(0, searchpair('{', '', '}', 'bWn', 0))
315+
bw!
316+
endfunc
317+
299318
func Test_searchc()
300319
" These commands used to cause memory overflow in searchc().
301320
new

src/version.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -761,6 +761,8 @@ static char *(features[]) =
761761

762762
static int included_patches[] =
763763
{ /* Add new patch number below this line */
764+
/**/
765+
1239,
764766
/**/
765767
1238,
766768
/**/

0 commit comments

Comments
 (0)