]> granicus.if.org Git - vim/commitdiff
patch 8.0.1239: cannot use a lambda for the skip argument to searchpair() v8.0.1239
authorBram Moolenaar <Bram@vim.org>
Mon, 30 Oct 2017 20:48:41 +0000 (21:48 +0100)
committerBram Moolenaar <Bram@vim.org>
Mon, 30 Oct 2017 20:48:41 +0000 (21:48 +0100)
Problem:    Cannot use a lambda for the skip argument to searchpair().
Solution:   Evaluate a partial, funcref and lambda. (LemonBoy, closes #1454,
            closes #2265)

runtime/doc/eval.txt
src/eval.c
src/evalfunc.c
src/proto/eval.pro
src/proto/evalfunc.pro
src/search.c
src/testdir/test_search.vim
src/version.c

index 6dc05a5568818e42664405e0757bc0bad528a5bb..9c2117ca2ddf1971e9107dc7c213d76caafdde68 100644 (file)
@@ -6784,6 +6784,7 @@ searchpair({start}, {middle}, {end} [, {flags} [, {skip}
                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()|.
 
index 8cb91e783da577d9398c71e89395e7c95115520b..e8a1b0aa35425dfce1c47f558de6d14cc2c75e8a 100644 (file)
@@ -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 *expr, int map, int *remp)
 {
     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 */
index 2f294ca1ff0c20662f2033fe82101dc38cc87e0f..25536af2e0554d55a385d333789e7db61959d30b 100644 (file)
@@ -9531,13 +9531,12 @@ f_searchdecl(typval_T *argvars, typval_T *rettv)
 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 *match_pos)
     /* Optional fifth argument: skip expression */
     if (argvars[3].v_type == VAR_UNKNOWN
            || argvars[4].v_type == VAR_UNKNOWN)
-       skip = (char_u *)"";
+       skip = NULL;
     else
     {
-       skip = get_tv_string_buf_chk(&argvars[4], nbuf3);
+       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 *match_pos)
 #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)
            {
index 5fc6260029ca39fcad483a31d5e9cf29e59e2fd8..34e87a19e8b23e13d5f28138b9193761fbfa1cfa 100644 (file)
@@ -10,6 +10,7 @@ int eval_printexpr(char_u *fname, char_u *args);
 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);
index 174afb6f23519993a90052424073cbc60179d42b..279c824049b3a84bb22f3a2efd785ef261159884 100644 (file)
@@ -8,7 +8,7 @@ buf_T *buflist_find_by_name(char_u *name, int curtab_only);
 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 : */
index e56cb1b41c31b066eb3472a5986fc5feb32da9c1..25c2e6e77459535a3a3820dbec7f8ba76f64259b 100644 (file)
@@ -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, "</%.*s>\\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);
index eb781106a909c4c20bc15d34ce52b43ce54e1ddc..b863fcbba363fb1d20cf12d1f8eefc710b8a6699 100644 (file)
@@ -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
index 7d4ba569266583053b7eedd0819877e1c9d4d4a0..bb1e8b28d27a619c70509a2ba1a0b453bef39e48 100644 (file)
@@ -761,6 +761,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1239,
 /**/
     1238,
 /**/