On 29-Apr-2010 Lech Lorens <[email protected]> wrote:
> On 28-Apr-2010 Robert Webb <[email protected]> wrote:
> > Lech Lorens wrote:
> > 
> > > Here's an implementation of the 'tagfunc' option together with some
> > > documentation. Additionally, attached is a script showing how a (very
> > > unuseful) function used in 'tagfunc' can be implemented.
> 
> Here is an updated version of the 'tagfunc' patch:

Sorry, I forgot to attach the patch.
By the way, apply patch with -p1 argument.

-- 
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/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/structs.h b/src/structs.h
index 99afecf..1688202 100644
--- a/src/structs.h
+++ b/src/structs.h
@@ -1359,6 +1359,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..076f29c 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,117 @@ 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;
+       int len;
+
+       if (item->li_tv.v_type != VAR_DICT)
+           continue;
+
+       res_name = get_dict_string(item->li_tv.vval.v_dict,
+                                  (char_u *)"name", FALSE);
+       res_fname = get_dict_string(item->li_tv.vval.v_dict,
+                                  (char_u *)"filename", FALSE);
+       res_cmd = get_dict_string(item->li_tv.vval.v_dict,
+                                  (char_u *)"cmd", FALSE);
+
+       if (!res_name || !res_fname || !res_cmd)
+           continue;
+
+#ifdef FEAT_EMACS_TAGS
+       len = 3;
+#else
+       len = 2;
+#endif
+
+       len += STRLEN(res_name);
+       len += STRLEN(res_fname);
+       len += STRLEN(res_cmd);
+
+       len += 3; /* we only use 3 fields from the dictionary - we need space
+                  * for two gaps and a terminating NUL. */
+
+       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);
+
+           /* 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 +1369,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 +1453,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 +1494,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 +1564,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 +3862,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