On 28-Apr-2010 Robert Webb <[email protected]> wrote:
> 
> > - left a FIXME in the code stating that duplicate tags should be removed
> >   from the list returned by the user-defined function. Not quite sure,
> >   though, whether it wouldn't be better to leave it to the user.
> 
> I don't think there's any point keeping them.  They can never be of use if
> all
> details match (name, file, search command).  Currently I have a function
> called
> UniqList() which does it.  It's very inefficient (N^2).  Would be more
> efficiently handled in C.  Maybe a uniq() function would be handy in
> general,
> to remove multiple copies in a list.
> 
> Maybe a flag to taglist() requesting that duplicate entries be removed?

What I meant is: if the user's function used in 'tagfunc' returns a list
of tags, should I verify that there are no duplicates in the list? I'd
say: no - leave it to the user to take care of what is returned.

Another issue is whether taglist() should remove duplicates. It might be
good to handle this but for now I'd rather stick to 'tagfunc' to get it
done well.

> > - maybe it would make some sense to let taglist() return a result
> >   returned by the 'tagfunc' function? taglist() could take an optional
> >   argument which would state whether recursion is allowed and an upper
> >   limit on the recursion level could be set. I can at least imagine
> >   a use case for such a feature (admittedly, awkward a bit).
> 
> I don't know that an extra option is required.  I think taglist() should
> always
> make use of 'tagfunc', unless called recursively from within the tag
> function
> itself.  I guess an extra optional arg might be useful to allow the user to
> avoid calling tagfunc if they wish.
> 
> Hmm, I suppose taglist() won't know whether the 'c' flag should be used when
> calling 'tagfunc'.  Maybe we need to be able to pass this flag to taglist()
> too so it can pass it through?

OK, I'll think about this later.

> Hmm, another small difference.  My script presumes the tag name is complete,
> whereas taglist() just searches, eg taglist("blah") will find tags for
> "blabbyblahblab".  Is this an issue?

I have no problem with that.

> Some other thoughts:
> 
> - Another thing I need is a way to do more than one search in the tag command.
[...]
> - Gets a little trickier than above too, because I want to have access to
> all
>   of vim's search capabilities in that tag command.  The format of a search
[...]
> - Vim doesn't load all tags when it does a tag search.  It looks at one file
[...]

It would be great to have all of them, but I consider them improvements
to the basic functionality offered by 'tagfunc'. Once 'tagfunc' is
implemented properly these can be considered.

> - By the way, in TagFunc2() you should use "taglist('^' . pattern . '$')"
>   rather than "taglist('\<' . pattern . '\>')".  The latter may miss some
>   non-alphabetic tags like operators.  Not that it matters for a proof of
>   concept like this, but thought I'd mention it.  Oh, I see this example is
> in
>   the docs too, so all the more reason to do it the "proper" way.  Not sure
>   it's the best example though, since not many tags will use line numbers
> (for
>   me none of them do although not sure why).

OK, I changed the example totally so this is a non-issue.

> The script shouldn't be hard to convert for use with 'tagfunc'.  Something
> like
> this:

After my small adjustments works for me almost perfectly:

function! SmartTagFunc(pattern, flags)
        if a:flags =~? 'c'
                let tags = []
                let id = GetNiceTagList(tags, 'nk')
                return tags
        else
                return taglist(a:pattern)
        endif
endfunc

I am having some problems with the code that I am working on.
Unfortunately, I cannot share it so I will try to tackle the thing
myself.

Some news about 'tagfunc':
I discovered that the 'tagfunc' implementation prevented omni completion
for C from working when 'tagfunc' was set to the SmartTagFunc() function
defined a few lines above. This was due to the fact that - as mentioned
by the documentation - in each of the dictionaries returned by
SmartTagFunc() only the entries 'name', 'filename' and 'cmd' are
considered relevant. Without thinking too much I simply discarded all
the other entries effectively filtering out the data essential to omni
completion. The latest patch fixes the problem. Find it attached.

-- 
Cheers,
Lech

-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php
diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt
index e178613..98a65a4 100644
--- a/runtime/doc/options.txt
+++ b/runtime/doc/options.txt
@@ -6691,6 +6691,22 @@ A jump table for the options with a short description 
can be found at |Q_op|.
        command-line completion and ":help").
        {Vi: always uses binary search in some versions}
 
+                                               *'tagfunc'* *'tfu'*
+'tagfunc' 'tfu'                string  (default: empty)
+                       local to buffer
+                       {not in Vi}
+                       {not available when compiled without the +eval
+                       or +insert_expand feature}
+       This option specifies a function to be used to perform tag searches.
+       The function should take two parameters, the first of which is the
+       pattern to be searched, while the second is a set of flags which may
+       be used by the function to decide on its behaviour. Currently the only
+       flag that may appear here is 'c', which indicates that the context
+       around the cursor position can be used to generate more accurate
+       results.
+       See |tag-function| for an explanation of what the function should
+       return and an example of such a function.
+
                                                *'taglength'* *'tl'*
 'taglength' 'tl'       number  (default 0)
                        global
diff --git a/runtime/doc/tagsrch.txt b/runtime/doc/tagsrch.txt
index 15ebbd4..f7847c3 100644
--- a/runtime/doc/tagsrch.txt
+++ b/runtime/doc/tagsrch.txt
@@ -14,6 +14,7 @@ See section |29.1| of the user manual for an introduction.
 4. Tags details                        |tag-details|
 5. Tags file format            |tags-file-format|
 6. Include file searches       |include-search|
+7. Programmable tag search     |tag-function|
 
 ==============================================================================
 1. Jump to a tag                                       *tag-commands*
@@ -834,4 +835,58 @@ Common arguments for the commands above:
 <     For a ":djump", ":dsplit", ":dlist" and ":dsearch" command the pattern
       is used as a literal string, not as a search pattern.
 
+==============================================================================
+7. Programmable tag search                                     *tag-function*
+
+It is possible to provide Vim with a script which will generate a list of tags
+used for commands like |:tag|, |:tselect| and normal mode commands like
+|CTRL-]|. The Vim script function used for generating the taglist is specified
+by setting the 'tagfunc' option. The function will be called with two
+arguments:
+   a:pattern - the tag identifier used during the tag search,
+   a:flags   - a list of flags to control the function behaviour.
+
+Currently the only flag that may be passed to the tag function is 'c' which
+indicates that the function was invoked due to a normal command being
+processed (mnemonic: the tag function may use the Context around the cursor to
+perform a better job of generating the tag list.
+
+Note that when 'tagfunc' is set, the priority of the tags described in
+|tag-priority| does not apply. Instead, the priority is exactly as the
+ordering of the elements in the list returned by the function.
+
+The function should return a list of dictionaries. Each of the dictionaries
+must at least include the following entries:
+       name            Name of the tag.
+       filename        Name of the file where the tag is
+                       defined.  It is either relative to the
+                       current directory or a full path.
+       cmd             Ex command used to locate the tag in the file. This
+                       can be either an ex search pattern, a line number or
+                       a line number followed by a byte number.
+Note that the format of the result is similar to that of |taglist()|,
+which makes it possible to use its output to generate the result.
+
+
+The following is a hypothetical example of a function used for 'tagfunc'. It
+uses the output of |taglist()| to generate the result: a list of tags in the
+inverse order of file names.
+
+>
+       function! TagFunc(pattern, flags)
+         function! CompareFilenames(item1, item2)
+           let f1 = a:item1['filename']
+           let f2 = a:item2['filename']
+           return f1 >=# f2 ?
+                       \ -1 : f1 <=# f2 ? 1 : 0
+         endfunction
+
+         let result = taglist(a:pattern)
+         call sort(result, "CompareFilenames")
+
+         return result
+       endfunc
+       set tagfunc=TagFunc
+<
+
  vim:tw=78:ts=8:ft=help:norl:
diff --git a/src/buffer.c b/src/buffer.c
index 0569f16..e86899e 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -1801,6 +1801,7 @@ free_buf_options(buf, free_p_ff)
 #ifdef FEAT_COMPL_FUNC
     clear_string_option(&buf->b_p_cfu);
     clear_string_option(&buf->b_p_ofu);
+    clear_string_option(&buf->b_p_tfu);
 #endif
 #ifdef FEAT_QUICKFIX
     clear_string_option(&buf->b_p_gp);
diff --git a/src/edit.c b/src/edit.c
index 33e580f..8cb7bc0 100644
--- a/src/edit.c
+++ b/src/edit.c
@@ -4050,6 +4050,7 @@ ins_compl_get_exp(ini)
 
            /* Find up to TAG_MANY matches.  Avoids that an enormous number
             * of matches is found when compl_pattern is empty */
+           g_tag_at_cursor = 1;
            if (find_tags(compl_pattern, &num_matches, &matches,
                    TAG_REGEXP | TAG_NAMES | TAG_NOIC |
                    TAG_INS_COMP | (ctrl_x_mode ? TAG_VERBOSE : 0),
@@ -4057,6 +4058,7 @@ ins_compl_get_exp(ini)
            {
                ins_compl_add_matches(num_matches, matches, p_ic);
            }
+           g_tag_at_cursor = 0;
            p_ic = save_p_ic;
            break;
 
diff --git a/src/eval.c b/src/eval.c
index ad127b5..f50cc94 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -6991,6 +6991,62 @@ dict_add_nr_str(d, key, nr, str)
     return OK;
 }
 
+/* Initializes a data structure used for iterating over dictionary items in
+ * dict_iterate_next().
+ */
+    void
+dict_iterate_start(argvars, iter)
+    typval_T               *argvars;
+    struct dict_iterator_S *iter;
+{
+    dict_T     *d;
+
+    if (argvars[0].v_type != VAR_DICT)
+    {
+       iter->items = 0;
+       return;
+    }
+
+    if ((d = argvars[0].vval.v_dict) == NULL)
+    {
+       iter->items = 0;
+       return;
+    }
+
+    iter->items = (int)d->dv_hashtab.ht_used;
+    iter->hi = d->dv_hashtab.ht_array;
+}
+
+/* Allows iterating over the items stored in a dictionary.
+ * Returns the pointer to the key, *tv_result is set to point to the value
+ * for that key.
+ * If there are no more items, NULL is returned.
+ * iter should be initialized with dict_iterate_start() before calling this
+ * function for the first time.
+ */
+    char_u*
+dict_iterate_next(iter, tv_result)
+    struct dict_iterator_S *iter;
+    typval_T               **tv_result;
+{
+    dictitem_T *di;
+    char_u      *result;
+
+    if (iter->items <= 0)
+       return NULL;
+
+    while (HASHITEM_EMPTY(iter->hi))
+       ++iter->hi;
+
+    di = HI2DI(iter->hi);
+    result = di->di_key;
+    *tv_result = &di->di_tv;
+
+    --iter->items;
+    ++iter->hi;
+    return result;
+}
+
 /*
  * Get the number of items in a Dictionary.
  */
diff --git a/src/ex_cmds.c b/src/ex_cmds.c
index 2296c33..b847fd5 100644
--- a/src/ex_cmds.c
+++ b/src/ex_cmds.c
@@ -5926,7 +5926,7 @@ find_help_tags(arg, num_matches, matches, keep_lang)
 
     *matches = (char_u **)"";
     *num_matches = 0;
-    flags = TAG_HELP | TAG_REGEXP | TAG_NAMES | TAG_VERBOSE;
+    flags = TAG_HELP | TAG_REGEXP | TAG_NAMES | TAG_VERBOSE | TAG_DONT_USE_TFU;
     if (keep_lang)
        flags |= TAG_KEEP_LANG;
     if (find_tags(IObuff, num_matches, matches, flags, (int)MAXCOL, NULL) == OK
diff --git a/src/globals.h b/src/globals.h
index bfe48ca..d137d73 100644
--- a/src/globals.h
+++ b/src/globals.h
@@ -1066,6 +1066,9 @@ EXTERN int        g_do_tagpreview INIT(= 0);  /* for tag 
preview commands:
                                               height of preview window */
 # endif
 #endif
+EXTERN int     g_tag_at_cursor INIT(= 0);  /* whether the tag command comes
+                                              from the command line (0) or was
+                                              invoked as a normal command (1) 
*/
 EXTERN int     replace_offset INIT(= 0);   /* offset for replace_push() */
 
 EXTERN char_u  *escape_chars INIT(= (char_u *)" \t\\\"|");
diff --git a/src/normal.c b/src/normal.c
index 8b9fea7..5eb19c2 100644
--- a/src/normal.c
+++ b/src/normal.c
@@ -5616,7 +5616,11 @@ nv_ident(cap)
        normal_search(cap, cmdchar == '*' ? '/' : '?', buf, 0);
     }
     else
+    {
+       g_tag_at_cursor = 1;
        do_cmdline_cmd(buf);
+       g_tag_at_cursor = 0;
+    }
 
     vim_free(buf);
 }
diff --git a/src/option.c b/src/option.c
index ba17c11..cfa2383 100644
--- a/src/option.c
+++ b/src/option.c
@@ -172,6 +172,9 @@
 #endif
 #define PV_SW          OPT_BUF(BV_SW)
 #define PV_SWF         OPT_BUF(BV_SWF)
+#ifdef FEAT_COMPL_FUNC
+# define PV_TFU                OPT_BUF(BV_TFU)
+#endif
 #define PV_TAGS                OPT_BOTH(OPT_BUF(BV_TAGS))
 #define PV_TS          OPT_BUF(BV_TS)
 #define PV_TW          OPT_BUF(BV_TW)
@@ -288,6 +291,7 @@ static char_u       *p_cpt;
 #ifdef FEAT_COMPL_FUNC
 static char_u  *p_cfu;
 static char_u  *p_ofu;
+static char_u  *p_tfu;
 #endif
 static int     p_eol;
 static int     p_et;
@@ -2432,6 +2436,15 @@ static struct vimoption
                            {(char_u *)TRUE, (char_u *)0L}
 #endif
                            SCRIPTID_INIT},
+    {"tagfunc",    "tfu",   P_STRING|P_ALLOCED|P_VI_DEF|P_SECURE,
+#ifdef FEAT_COMPL_FUNC
+                           (char_u *)&p_tfu, PV_TFU,
+                           {(char_u *)"", (char_u *)0L}
+#else
+                           (char_u *)NULL, PV_NONE,
+                           {(char_u *)0L, (char_u *)0L}
+#endif
+                           SCRIPTID_INIT},
     {"taglength",   "tl",   P_NUM|P_VI_DEF,
                            (char_u *)&p_tl, PV_NONE,
                            {(char_u *)0L, (char_u *)0L} SCRIPTID_INIT},
@@ -5174,6 +5187,7 @@ check_buf_options(buf)
 #ifdef FEAT_COMPL_FUNC
     check_string_option(&buf->b_p_cfu);
     check_string_option(&buf->b_p_ofu);
+    check_string_option(&buf->b_p_tfu);
 #endif
 #ifdef FEAT_KEYMAP
     check_string_option(&buf->b_p_keymap);
@@ -9265,6 +9279,7 @@ get_varp(p)
 #ifdef FEAT_COMPL_FUNC
        case PV_CFU:    return (char_u *)&(curbuf->b_p_cfu);
        case PV_OFU:    return (char_u *)&(curbuf->b_p_ofu);
+       case PV_TFU:    return (char_u *)&(curbuf->b_p_tfu);
 #endif
        case PV_EOL:    return (char_u *)&(curbuf->b_p_eol);
        case PV_ET:     return (char_u *)&(curbuf->b_p_et);
@@ -9604,6 +9619,7 @@ buf_copy_options(buf, flags)
 #ifdef FEAT_COMPL_FUNC
            buf->b_p_cfu = vim_strsave(p_cfu);
            buf->b_p_ofu = vim_strsave(p_ofu);
+           buf->b_p_tfu = vim_strsave(p_tfu);
 #endif
            buf->b_p_sts = p_sts;
            buf->b_p_sts_nopaste = p_sts_nopaste;
diff --git a/src/option.h b/src/option.h
index cfa7692..793febc 100644
--- a/src/option.h
+++ b/src/option.h
@@ -999,6 +999,9 @@ enum
 #endif
     , BV_SW
     , BV_SWF
+#ifdef FEAT_COMPL_FUNC
+    , BV_TFU
+#endif
     , BV_TAGS
     , BV_TS
     , BV_TW
diff --git a/src/proto/eval.pro b/src/proto/eval.pro
index e817769..6474cdd 100644
--- a/src/proto/eval.pro
+++ b/src/proto/eval.pro
@@ -56,6 +56,8 @@ dictitem_T *dictitem_alloc __ARGS((char_u *key));
 void dictitem_free __ARGS((dictitem_T *item));
 int dict_add __ARGS((dict_T *d, dictitem_T *item));
 int dict_add_nr_str __ARGS((dict_T *d, char *key, long nr, char_u *str));
+void dict_iterate_start __ARGS((typval_T *argvars, struct dict_iterator_S 
*iter));
+char_u *dict_iterate_next __ARGS((struct dict_iterator_S *iter, typval_T 
**tv_result));
 char_u *get_dict_string __ARGS((dict_T *d, char_u *key, int save));
 long get_dict_number __ARGS((dict_T *d, char_u *key));
 char_u *get_function_name __ARGS((expand_T *xp, int idx));
diff --git a/src/structs.h b/src/structs.h
index 99afecf..44d552c 100644
--- a/src/structs.h
+++ b/src/structs.h
@@ -1120,6 +1120,16 @@ struct dictvar_S
     dict_T     *dv_used_prev;  /* previous dict in used dicts list */
 };
 
+/*
+ * Structure used for iterating over dictionary items.
+ * Initialize with dict_iterate_start().
+ */
+struct dict_iterator_S
+{
+    int         items;
+    hashitem_T *hi;
+};
+
 /* values for b_syn_spell: what to do with toplevel text */
 #define SYNSPL_DEFAULT 0       /* spell check if @Spell not defined */
 #define SYNSPL_TOP     1       /* spell check toplevel text */
@@ -1359,6 +1369,7 @@ struct file_buffer
 #ifdef FEAT_COMPL_FUNC
     char_u     *b_p_cfu;       /* 'completefunc' */
     char_u     *b_p_ofu;       /* 'omnifunc' */
+    char_u     *b_p_tfu;       /* 'tagfunc' */
 #endif
     int                b_p_eol;        /* 'endofline' */
     int                b_p_et;         /* 'expandtab' */
diff --git a/src/tag.c b/src/tag.c
index ba36de7..f949ff9 100644
--- a/src/tag.c
+++ b/src/tag.c
@@ -30,7 +30,8 @@ typedef struct tag_pointers
     char_u     *command;       /* first char of command */
     /* filled in by parse_match(): */
     char_u     *command_end;   /* first char after command */
-    char_u     *tag_fname;     /* file name of the tags file */
+    char_u     *tag_fname;     /* file name of the tags file. This is used
+                                * when 'tr' is set. */
 #ifdef FEAT_EMACS_TAGS
     int                is_etag;        /* TRUE for emacs tag */
 #endif
@@ -164,6 +165,7 @@ do_tag(tag, type, count, forceit, verbose)
     int                skip_msg = FALSE;
     char_u     *buf_ffname = curbuf->b_ffname;     /* name to use for
                                                       priority computation */
+    int         use_tfu = 1;
 
     /* remember the matches for the last used tag */
     static int         num_matches = 0;
@@ -188,6 +190,7 @@ do_tag(tag, type, count, forceit, verbose)
     {
        type = DT_TAG;
        no_regexp = TRUE;
+       use_tfu = 0;
     }
 
     prev_num_matches = num_matches;
@@ -542,6 +545,9 @@ do_tag(tag, type, count, forceit, verbose)
 #endif
            if (verbose)
                flags |= TAG_VERBOSE;
+           if (!use_tfu)
+               flags |= TAG_DONT_USE_TFU;
+
            if (find_tags(name, &new_num_matches, &new_matches, flags,
                                            max_num_matches, buf_ffname) == OK
                    && new_num_matches < max_num_matches)
@@ -1233,6 +1239,187 @@ prepare_pats(pats, has_re)
        pats->regmatch.regprog = NULL;
 }
 
+struct match_found
+{
+    int        len;                    /* nr of chars of match[] to be 
compared */
+    char_u     match[1];       /* actually longer */
+};
+
+/*
+ * find_tfu_tags() - call the user-defined function to generate a list of tags
+ *                   used by find_tags().
+ *
+ * Return OK if at least 1 tag has been successfully found, FAIL otherwise.
+ * pat         - used as the pattern supplied to the user-defined function,
+ * ga          - the tags will be placed here,
+ * match_count - here the number of tags found will be placed.
+ */
+    static int
+find_tfu_tags(char_u *pat, garray_T *ga, int *match_count)
+{
+    pos_T       pos;
+    list_T      *taglist;
+    listitem_T  *item;
+    int         ntags = 0;
+    int         result = FAIL;
+    char_u      *args[2];
+
+    args[0] = pat;
+    args[1] = (char_u *) (g_tag_at_cursor? "c": "");
+
+    if (*curbuf->b_p_tfu == NUL)
+       goto done;
+
+    pos = curwin->w_cursor;
+    taglist = call_func_retlist(curbuf->b_p_tfu, 2, args, FALSE);
+    curwin->w_cursor = pos;    /* restore the cursor position */
+
+    if (taglist == NULL)
+       goto done;
+
+    for (item = taglist->lv_first; item != NULL; item = item->li_next)
+    {
+       struct match_found     *mfp;
+       char_u                 *res_name, *res_fname, *res_cmd, *res_kind;
+       int                    len;
+       struct dict_iterator_S iter;
+       char_u                 *dict_key;
+       typval_T               *tv;
+       int                    has_extra = 0;
+
+       if (item->li_tv.v_type != VAR_DICT)
+           continue;
+
+#ifdef FEAT_EMACS_TAGS
+       len = 3;
+#else
+       len = 2;
+#endif
+
+       res_name = NULL;
+       res_fname = NULL;
+       res_cmd = NULL;
+       res_kind = NULL;
+
+       dict_iterate_start(&item->li_tv, &iter);
+       while (NULL != (dict_key = dict_iterate_next(&iter, &tv)))
+       {
+           if (tv->v_type != VAR_STRING)
+               continue;
+
+           len += STRLEN(tv->vval.v_string) + 1; /* Space for "\tVALUE". */
+           if (!STRCMP(dict_key, "name"))
+           {
+               res_name = tv->vval.v_string;
+               continue;
+           }
+           if (!STRCMP(dict_key, "filename"))
+           {
+               res_fname = tv->vval.v_string;
+               continue;
+           }
+           if (!STRCMP(dict_key, "cmd"))
+           {
+               res_cmd = tv->vval.v_string;
+               continue;
+           }
+           has_extra = 1;
+           if (!STRCMP(dict_key, "kind"))
+           {
+               res_kind = tv->vval.v_string;
+               continue;
+           }
+           len += STRLEN(dict_key) + 1; /* Other elements will be stored as 
"\tKEY:VALUE".
+                                         * Allocate space for the key and the 
colon. */
+       }
+
+       if (has_extra)
+           len += 2; /* need space for ;" */
+
+       if (!res_name || !res_fname || !res_cmd)
+           continue;
+
+       mfp = (struct match_found *)alloc(
+                       (int)sizeof(struct match_found) + len);
+       if (mfp != NULL)
+       {
+           char_u *p;
+           mfp->len = len;
+           p = mfp->match;
+           p[0] = 0;   /* mtt */
+           p[1] = NUL; /* no tag file name */
+           p = p + 2;
+#ifdef FEAT_EMACS_TAGS
+           *p = NUL;
+           ++p;
+#endif
+
+           STRCPY(p, res_name);
+           p += STRLEN(p);
+
+           *p++ = TAB;
+           STRCPY(p, res_fname);
+           p += STRLEN(p);
+
+           *p++ = TAB;
+           STRCPY(p, res_cmd);
+           p += STRLEN(p);
+
+           if (has_extra)
+           {
+               STRCPY(p, ";\"");
+               p += STRLEN(p);
+
+               if (res_kind)
+               {
+                   *p++ = TAB;
+                   STRCPY(p, res_kind);
+                   p += STRLEN(p);
+               }
+
+               dict_iterate_start(&item->li_tv, &iter);
+               while (NULL != (dict_key = dict_iterate_next(&iter, &tv)))
+               {
+                   if (tv->v_type != VAR_STRING)
+                       continue;
+
+                   if (!STRCMP(dict_key, "name"))
+                       continue;
+                   if (!STRCMP(dict_key, "filename"))
+                       continue;
+                   if (!STRCMP(dict_key, "cmd"))
+                       continue;
+                   if (!STRCMP(dict_key, "kind"))
+                       continue;
+
+                   *p++ = TAB;
+                   STRCPY(p, dict_key);
+                   p += STRLEN(p);
+                   STRCPY(p, ":");
+                   p += STRLEN(p);
+                   STRCPY(p, tv->vval.v_string);
+                   p += STRLEN(p);
+               }
+           }
+
+           /* FIXME:2010-04-24:llorens: Don't add identical matches. */
+           if (ga_grow(ga, 1) == OK)
+           {
+               ((struct match_found **)(ga->ga_data)) [ga->ga_len++] = mfp;
+               ++ntags;
+               result = OK;
+           }
+           else
+               vim_free(mfp);
+       }
+    }
+
+    list_free(taglist, TRUE);
+done:
+    *match_count = ntags;
+    return result;
+}
+
 /*
  * find_tags() - search for tags in tags files
  *
@@ -1252,11 +1439,12 @@ prepare_pats(pats, has_re)
  * Tags in an emacs-style tags file are always global.
  *
  * flags:
- * TAG_HELP      only search for help tags
- * TAG_NAMES     only return name of tag
- * TAG_REGEXP    use "pat" as a regexp
- * TAG_NOIC      don't always ignore case
- * TAG_KEEP_LANG  keep language
+ * TAG_HELP         only search for help tags
+ * TAG_NAMES        only return name of tag
+ * TAG_REGEXP       use "pat" as a regexp
+ * TAG_NOIC         don't always ignore case
+ * TAG_KEEP_LANG     keep language
+ * TAG_DONT_USE_TFU  do not invoke the 'tagfunc' command
  */
     int
 find_tags(pat, num_matches, matchesp, flags, mincount, buf_ffname)
@@ -1335,11 +1523,7 @@ find_tags(pat, num_matches, matchesp, flags, mincount, 
buf_ffname)
     int                is_etag;                /* current file is emaces style 
*/
 #endif
 
-    struct match_found
-    {
-       int     len;            /* nr of chars of match[] to be compared */
-       char_u  match[1];       /* actually longer */
-    } *mfp, *mfp2;
+    struct match_found *mfp, *mfp2;
     garray_T   ga_match[MT_COUNT];
     int                match_count = 0;                /* number of matches 
found */
     char_u     **matches;
@@ -1380,6 +1564,8 @@ find_tags(pat, num_matches, matchesp, flags, mincount, 
buf_ffname)
     int                use_cscope = (flags & TAG_CSCOPE);
 #endif
     int                verbose = (flags & TAG_VERBOSE);
+    int         use_tfu = ((flags & TAG_DONT_USE_TFU) == 0);
+    static int  tfu_call_level = 0;
 
     help_save = curbuf->b_help;
     orgpat.pat = pat;
@@ -1448,6 +1634,14 @@ find_tags(pat, num_matches, matchesp, flags, mincount, 
buf_ffname)
     vim_memset(&search_info, 0, (size_t)1);
 #endif
 
+    if (*curbuf->b_p_tfu != NUL && use_tfu && tfu_call_level == 0)
+    {
+       ++tfu_call_level;
+       retval = find_tfu_tags(pat, &ga_match[0], &match_count);
+       --tfu_call_level;
+       goto findtag_end;
+    }
+
     /*
      * When finding a specified number of matches, first try with matching
      * case, so binary search can be used, and try ignore-case matches in a
@@ -3738,11 +3932,11 @@ expand_tags(tagnames, pat, num_file, file)
        tagnmflag = 0;
     if (pat[0] == '/')
        ret = find_tags(pat + 1, num_file, file,
-               TAG_REGEXP | tagnmflag | TAG_VERBOSE,
+               TAG_REGEXP | tagnmflag | TAG_VERBOSE | TAG_DONT_USE_TFU,
                TAG_MANY, curbuf->b_ffname);
     else
        ret = find_tags(pat, num_file, file,
-               TAG_REGEXP | tagnmflag | TAG_VERBOSE | TAG_NOIC,
+               TAG_REGEXP | tagnmflag | TAG_VERBOSE | TAG_DONT_USE_TFU | 
TAG_NOIC,
                TAG_MANY, curbuf->b_ffname);
     if (ret == OK && !tagnames)
     {
diff --git a/src/vim.h b/src/vim.h
index 9a36b9b..6882e91 100644
--- a/src/vim.h
+++ b/src/vim.h
@@ -1045,19 +1045,20 @@ extern char *(*dyn_libintl_textdomain)(const char 
*domainname);
 /*
  * flags for find_tags().
  */
-#define TAG_HELP       1       /* only search for help tags */
-#define TAG_NAMES      2       /* only return name of tag */
-#define        TAG_REGEXP      4       /* use tag pattern as regexp */
-#define        TAG_NOIC        8       /* don't always ignore case */
+#define TAG_HELP               1       /* only search for help tags */
+#define TAG_NAMES              2       /* only return name of tag */
+#define        TAG_REGEXP              4       /* use tag pattern as regexp */
+#define        TAG_NOIC                8       /* don't always ignore case */
 #ifdef FEAT_CSCOPE
-# define TAG_CSCOPE    16      /* cscope tag */
+# define TAG_CSCOPE            16      /* cscope tag */
 #endif
-#define TAG_VERBOSE    32      /* message verbosity */
-#define TAG_INS_COMP   64      /* Currently doing insert completion */
-#define TAG_KEEP_LANG  128     /* keep current language */
+#define TAG_VERBOSE            32      /* message verbosity */
+#define TAG_INS_COMP           64      /* Currently doing insert completion */
+#define TAG_KEEP_LANG          128     /* keep current language */
+#define TAG_DONT_USE_TFU       256     /* whether 'tagfunc' should be 
evaluated */
 
-#define TAG_MANY       300     /* When finding many tags (for completion),
-                                  find up to this many tags */
+#define TAG_MANY               300     /* When finding many tags (for 
completion),
+                                          find up to this many tags */
 
 /*
  * Types of dialogs passed to do_vim_dialog().

Raspunde prin e-mail lui