]> granicus.if.org Git - vim/commitdiff
patch 8.2.3665: cannot use a lambda for 'tagfunc' v8.2.3665
authorYegappan Lakshmanan <yegappan@yahoo.com>
Wed, 24 Nov 2021 16:32:55 +0000 (16:32 +0000)
committerBram Moolenaar <Bram@vim.org>
Wed, 24 Nov 2021 16:32:55 +0000 (16:32 +0000)
Problem:    Cannot use a lambda for 'tagfunc'.
Solution:   Use 'tagfunc' like 'opfunc'. (Yegappan Lakshmanan, closes #9204)

runtime/doc/options.txt
src/buffer.c
src/option.c
src/optionstr.c
src/proto/tag.pro
src/structs.h
src/tag.c
src/testdir/test_tagfunc.vim
src/version.c

index 8fa4cebb43e6067d19ff25a20a927286e3982653..f5f570ba3831355973acbc7f56fbd0adcdc72097 100644 (file)
@@ -377,9 +377,9 @@ Some options ('completefunc', 'imactivatefunc', 'imstatusfunc', 'omnifunc',
 or a function reference or a lambda function.  Examples:
 >
        set opfunc=MyOpFunc
-       set opfunc=function("MyOpFunc")
-       set opfunc=funcref("MyOpFunc")
-       set opfunc={t\ ->\ MyOpFunc(t)}
+       set opfunc=function('MyOpFunc')
+       set opfunc=funcref('MyOpFunc')
+       let &opfunc = "{t -> MyOpFunc(t)}"
 <
 
 Setting the filetype
@@ -7792,7 +7792,9 @@ A jump table for the options with a short description can be found at |Q_op|.
        This option specifies a function to be used to perform tag searches.
        The function gets the tag pattern and should return a List of matching
        tags.  See |tag-function| for an explanation of how to write the
-       function and an example.
+       function and an example.  The value can be the name of a function, a
+       |lambda| or a |Funcref|. See |option-value-function| for more
+       information.
 
                                                *'taglength'* *'tl'*
 'taglength' 'tl'       number  (default 0)
index 2983ca9fe823c3535701f92ae835073974780c81..e2732de7a8aeec4b892beb33a4c7332aee7e485e 100644 (file)
@@ -2344,6 +2344,7 @@ free_buf_options(
     clear_string_option(&buf->b_p_tc);
 #ifdef FEAT_EVAL
     clear_string_option(&buf->b_p_tfu);
+    free_callback(&buf->b_tfu_cb);
 #endif
     clear_string_option(&buf->b_p_dict);
     clear_string_option(&buf->b_p_tsr);
index b807b9b6c0a947641ed881b2a7a2fd893e5268af..4422634e2ad0403bd7fb2f000ecb1e3e2e527c78 100644 (file)
@@ -810,6 +810,7 @@ free_all_options(void)
            clear_string_option((char_u **)options[i].var);
     }
     free_operatorfunc_option();
+    free_tagfunc_option();
 }
 #endif
 
@@ -5956,6 +5957,7 @@ buf_copy_options(buf_T *buf, int flags)
 #ifdef FEAT_EVAL
            buf->b_p_tfu = vim_strsave(p_tfu);
            COPY_OPT_SCTX(buf, BV_TFU);
+           buf_set_tfu_callback(buf);
 #endif
            buf->b_p_sts = p_sts;
            COPY_OPT_SCTX(buf, BV_STS);
index 705e86089dc7a98952c37b8a4e75e47283a57d3f..8948830c80aa89085529278f422a1c2a132dece4 100644 (file)
@@ -2333,6 +2333,15 @@ ambw_end:
     }
 #endif
 
+#ifdef FEAT_EVAL
+    // 'tagfunc'
+    else if (gvarp == &p_tfu)
+    {
+       if (set_tagfunc_option() == FAIL)
+           errmsg = e_invarg;
+    }
+#endif
+
     // Options that are a list of flags.
     else
     {
index 3046406a3f01f4b66b436e532b16b258fb42db53..e9827ea7e4a778fe01075dc357d9850c83a00b50 100644 (file)
@@ -1,4 +1,7 @@
 /* tag.c */
+int set_tagfunc_option(void);
+void free_tagfunc_option(void);
+void buf_set_tfu_callback(buf_T *buf);
 int do_tag(char_u *tag, int type, int count, int forceit, int verbose);
 void tag_freematch(void);
 void do_tags(exarg_T *eap);
index d9d3e30c378f20eeb769e2e9afe560865adba0ba..bef7062df96e5e04d175a4d486350681bc9b7618 100644 (file)
@@ -2878,7 +2878,8 @@ struct file_buffer
     char_u     *b_p_ofu;       // 'omnifunc'
 #endif
 #ifdef FEAT_EVAL
-    char_u     *b_p_tfu;       // 'tagfunc'
+    char_u     *b_p_tfu;       // 'tagfunc' option value
+    callback_T b_tfu_cb;       // 'tagfunc' callback
 #endif
     int                b_p_eol;        // 'endofline'
     int                b_p_fixeol;     // 'fixendofline'
index 5fd81e3dc302fbb455412a1384d6386871c07f22..c0601e88064a90d2232bc35b18e6f097707a48c3 100644 (file)
--- a/src/tag.c
+++ b/src/tag.c
@@ -103,11 +103,60 @@ static taggy_T ptag_entry = {NULL, {{0, 0, 0}, 0}, 0, 0, NULL};
 
 #ifdef FEAT_EVAL
 static int  tfu_in_use = FALSE;            // disallow recursive call of tagfunc
+static callback_T tfu_cb;          // 'tagfunc' callback function
 #endif
 
 // Used instead of NUL to separate tag fields in the growarrays.
 #define TAG_SEP 0x02
 
+/*
+ * Reads the 'tagfunc' option value and convert that to a callback value.
+ * Invoked when the 'tagfunc' option is set. The option value can be a name of
+ * a function (string), or function(<name>) or funcref(<name>) or a lambda.
+ */
+    int
+set_tagfunc_option()
+{
+#ifdef FEAT_EVAL
+    free_callback(&tfu_cb);
+    free_callback(&curbuf->b_tfu_cb);
+
+    if (*curbuf->b_p_tfu == NUL)
+       return OK;
+
+    if (option_set_callback_func(curbuf->b_p_tfu, &tfu_cb) == FAIL)
+       return FAIL;
+
+    copy_callback(&curbuf->b_tfu_cb, &tfu_cb);
+#endif
+
+    return OK;
+}
+
+# if defined(EXITFREE) || defined(PROTO)
+    void
+free_tagfunc_option(void)
+{
+#  ifdef FEAT_EVAL
+    free_callback(&tfu_cb);
+#  endif
+}
+# endif
+
+/*
+ * Copy the global 'tagfunc' callback function to the buffer-local 'tagfunc'
+ * callback for 'buf'.
+ */
+    void
+buf_set_tfu_callback(buf_T *buf UNUSED)
+{
+#ifdef FEAT_EVAL
+    free_callback(&buf->b_tfu_cb);
+    if (tfu_cb.cb_name != NULL && *tfu_cb.cb_name != NUL)
+       copy_callback(&buf->b_tfu_cb, &tfu_cb);
+#endif
+}
+
 /*
  * Jump to tag; handling of tag commands and tag stack
  *
@@ -1341,7 +1390,7 @@ find_tagfunc_tags(
                 flags & TAG_REGEXP   ? "r": "");
 
     save_pos = curwin->w_cursor;
-    result = call_vim_function(curbuf->b_p_tfu, 3, args, &rettv);
+    result = call_callback(&curbuf->b_tfu_cb, 0, &rettv, 3, args);
     curwin->w_cursor = save_pos;       // restore the cursor position
     --d->dv_refcount;
 
index 3eb3904406b55a62eb82cf20fc3bb122e82395da..a972016c9cf7436f8c9799b5db8840c074c594c1 100644 (file)
@@ -117,4 +117,54 @@ func Test_tagfunc_settagstack()
   delfunc Mytagfunc2
 endfunc
 
+" Test for different ways of setting the 'tagfunc' option
+func Test_tagfunc_callback()
+  " Test for using a function()
+  func MytagFunc1(pat, flags, info)
+    let g:MytagFunc1_args = [a:pat, a:flags, a:info]
+    return v:null
+  endfunc
+  let g:MytagFunc1_args = []
+  set tagfunc=function('MytagFunc1')
+  call assert_fails('tag abc', 'E433:')
+  call assert_equal(['abc', '', {}], g:MytagFunc1_args)
+
+  " Test for using a funcref()
+  new
+  func MytagFunc2(pat, flags, info)
+    let g:MytagFunc2_args = [a:pat, a:flags, a:info]
+    return v:null
+  endfunc
+  let g:MytagFunc2_args = []
+  set tagfunc=funcref('MytagFunc2')
+  call assert_fails('tag def', 'E433:')
+  call assert_equal(['def', '', {}], g:MytagFunc2_args)
+
+  " Test for using a lambda function
+  new
+  func MytagFunc3(pat, flags, info)
+    let g:MytagFunc3_args = [a:pat, a:flags, a:info]
+    return v:null
+  endfunc
+  let g:MytagFunc3_args = []
+  let &tagfunc= '{a, b, c -> MytagFunc3(a, b, c)}'
+  call assert_fails('tag ghi', 'E433:')
+  call assert_equal(['ghi', '', {}], g:MytagFunc3_args)
+
+  " Test for clearing the 'tagfunc' option
+  set tagfunc=''
+  set tagfunc&
+
+  call assert_fails("set tagfunc=function('abc')", "E700:")
+  call assert_fails("set tagfunc=funcref('abc')", "E700:")
+  let &tagfunc = "{a -> 'abc'}"
+  call assert_fails("echo taglist('a')", "E987:")
+
+  " cleanup
+  delfunc MytagFunc1
+  delfunc MytagFunc2
+  delfunc MytagFunc3
+  %bw!
+endfunc
+
 " vim: shiftwidth=2 sts=2 expandtab
index 4356be14eeb6cf2a47fcbfcac3cb1305390d1f1e..7a29e4e0d4f9a51e99e653d06298145c7d362c0d 100644 (file)
@@ -757,6 +757,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    3665,
 /**/
     3664,
 /**/