summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--runtime/doc/options.txt10
-rw-r--r--src/buffer.c1
-rw-r--r--src/option.c2
-rw-r--r--src/optionstr.c9
-rw-r--r--src/proto/tag.pro3
-rw-r--r--src/structs.h3
-rw-r--r--src/tag.c51
-rw-r--r--src/testdir/test_tagfunc.vim50
-rw-r--r--src/version.c2
9 files changed, 125 insertions, 6 deletions
diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt
index 8fa4cebb4..f5f570ba3 100644
--- a/runtime/doc/options.txt
+++ b/runtime/doc/options.txt
@@ -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)
diff --git a/src/buffer.c b/src/buffer.c
index 2983ca9fe..e2732de7a 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -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);
diff --git a/src/option.c b/src/option.c
index b807b9b6c..4422634e2 100644
--- a/src/option.c
+++ b/src/option.c
@@ -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);
diff --git a/src/optionstr.c b/src/optionstr.c
index 705e86089..8948830c8 100644
--- a/src/optionstr.c
+++ b/src/optionstr.c
@@ -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
{
diff --git a/src/proto/tag.pro b/src/proto/tag.pro
index 3046406a3..e9827ea7e 100644
--- a/src/proto/tag.pro
+++ b/src/proto/tag.pro
@@ -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);
diff --git a/src/structs.h b/src/structs.h
index d9d3e30c3..bef7062df 100644
--- a/src/structs.h
+++ b/src/structs.h
@@ -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'
diff --git a/src/tag.c b/src/tag.c
index 5fd81e3dc..c0601e880 100644
--- a/src/tag.c
+++ b/src/tag.c
@@ -103,12 +103,61 @@ 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
*
* *tag != NUL: ":tag {tag}", jump to new tag, add to 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;
diff --git a/src/testdir/test_tagfunc.vim b/src/testdir/test_tagfunc.vim
index 3eb390440..a972016c9 100644
--- a/src/testdir/test_tagfunc.vim
+++ b/src/testdir/test_tagfunc.vim
@@ -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
diff --git a/src/version.c b/src/version.c
index 4356be14e..7a29e4e0d 100644
--- a/src/version.c
+++ b/src/version.c
@@ -758,6 +758,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 3665,
+/**/
3664,
/**/
3663,