mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
Merge #11199 from bobrippling/vim-8.1.1228
vim-patch:8.1.{1099,1228,1962} add 'tagfunc'
This commit is contained in:
commit
9e4db2ef50
@ -6159,6 +6159,14 @@ A jump table for the options with a short description can be found at |Q_op|.
|
||||
match Match case
|
||||
smart Ignore case unless an upper case letter is used
|
||||
|
||||
*'tagfunc'* *'tfu'*
|
||||
'tagfunc' 'tfu' string (default: empty)
|
||||
local to buffer
|
||||
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.
|
||||
|
||||
*'taglength'* *'tl'*
|
||||
'taglength' 'tl' number (default 0)
|
||||
global
|
||||
|
@ -838,4 +838,70 @@ 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. Using 'tagfunc' *tag-function*
|
||||
|
||||
It is possible to provide Vim with a function which will generate a list of
|
||||
tags used for commands like |:tag|, |:tselect| and Normal mode tag commands
|
||||
like |CTRL-]|.
|
||||
|
||||
The function used for generating the taglist is specified by setting the
|
||||
'tagfunc' option. The function will be called with three arguments:
|
||||
a:pattern The tag identifier used during the tag search.
|
||||
a:flags List of flags to control the function behavior.
|
||||
a:info Dict containing the following entries:
|
||||
buf_ffname Full filename which can be used for priority.
|
||||
user_data Custom data String, if stored in the tag
|
||||
stack previously by tagfunc.
|
||||
|
||||
Currently two flags may be passed to the tag function:
|
||||
'c' The function was invoked by 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.)
|
||||
'i' In Insert mode, the user was completing a tag (with
|
||||
|i_CTRL-X_CTRL-]|).
|
||||
|
||||
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.
|
||||
*E987*
|
||||
The function should return a List of Dict entries. Each Dict must at least
|
||||
include the following entries and each value must be a string:
|
||||
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 or a line number.
|
||||
Note that the format is similar to that of |taglist()|, which makes it possible
|
||||
to use its output to generate the result.
|
||||
The following fields are optional:
|
||||
kind Type of the tag.
|
||||
user_data String of custom data stored in the tag stack which
|
||||
can be used to disambiguate tags between operations.
|
||||
|
||||
If the function returns |v:null| instead of a List, a standard tag lookup will
|
||||
be performed instead.
|
||||
|
||||
It is not allowed to change the tagstack from inside 'tagfunc'. *E986*
|
||||
|
||||
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, info)
|
||||
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:noet:ft=help:norl:
|
||||
|
@ -300,6 +300,11 @@ call append("$", "tagstack\ta :tag command will use the tagstack")
|
||||
call <SID>BinOptionG("tgst", &tgst)
|
||||
call append("$", "showfulltag\twhen completing tags in Insert mode show more info")
|
||||
call <SID>BinOptionG("sft", &sft)
|
||||
if has("eval")
|
||||
call append("$", "tagfunc\ta function to be used to perform tag searches")
|
||||
call append("$", "\t(local to buffer)")
|
||||
call <SID>OptionL("tfu")
|
||||
endif
|
||||
if has("cscope")
|
||||
call append("$", "cscopeprg\tcommand for executing cscope")
|
||||
call <SID>OptionG("csprg", &csprg)
|
||||
|
@ -1947,6 +1947,7 @@ void free_buf_options(buf_T *buf, int free_p_ff)
|
||||
clear_string_option(&buf->b_p_path);
|
||||
clear_string_option(&buf->b_p_tags);
|
||||
clear_string_option(&buf->b_p_tc);
|
||||
clear_string_option(&buf->b_p_tfu);
|
||||
clear_string_option(&buf->b_p_dict);
|
||||
clear_string_option(&buf->b_p_tsr);
|
||||
clear_string_option(&buf->b_p_qe);
|
||||
|
@ -119,10 +119,11 @@ typedef uint16_t disptick_T; // display tick type
|
||||
* The taggy struct is used to store the information about a :tag command.
|
||||
*/
|
||||
typedef struct taggy {
|
||||
char_u *tagname; /* tag name */
|
||||
fmark_T fmark; /* cursor position BEFORE ":tag" */
|
||||
int cur_match; /* match number */
|
||||
int cur_fnum; /* buffer number used for cur_match */
|
||||
char_u *tagname; // tag name
|
||||
fmark_T fmark; // cursor position BEFORE ":tag"
|
||||
int cur_match; // match number
|
||||
int cur_fnum; // buffer number used for cur_match
|
||||
char_u *user_data; // used with tagfunc
|
||||
} taggy_T;
|
||||
|
||||
typedef struct buffblock buffblock_T;
|
||||
@ -647,6 +648,7 @@ struct file_buffer {
|
||||
char_u *b_p_cpt; ///< 'complete'
|
||||
char_u *b_p_cfu; ///< 'completefunc'
|
||||
char_u *b_p_ofu; ///< 'omnifunc'
|
||||
char_u *b_p_tfu; ///< 'tagfunc'
|
||||
int b_p_eol; ///< 'endofline'
|
||||
int b_p_fixeol; ///< 'fixendofline'
|
||||
int b_p_et; ///< 'expandtab'
|
||||
|
@ -4047,12 +4047,14 @@ static int ins_compl_get_exp(pos_T *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 = true;
|
||||
if (find_tags(compl_pattern, &num_matches, &matches,
|
||||
TAG_REGEXP | TAG_NAMES | TAG_NOIC | TAG_INS_COMP
|
||||
| (l_ctrl_x_mode != CTRL_X_NORMAL ? TAG_VERBOSE : 0),
|
||||
TAG_MANY, curbuf->b_ffname) == OK && num_matches > 0) {
|
||||
ins_compl_add_matches(num_matches, matches, p_ic);
|
||||
}
|
||||
g_tag_at_cursor = false;
|
||||
p_ic = save_p_ic;
|
||||
break;
|
||||
|
||||
|
@ -4905,7 +4905,7 @@ int find_help_tags(const char_u *arg, int *num_matches, char_u ***matches,
|
||||
|
||||
*matches = NULL;
|
||||
*num_matches = 0;
|
||||
int flags = TAG_HELP | TAG_REGEXP | TAG_NAMES | TAG_VERBOSE;
|
||||
int flags = TAG_HELP | TAG_REGEXP | TAG_NAMES | TAG_VERBOSE | TAG_NO_TAGFUNC;
|
||||
if (keep_lang) {
|
||||
flags |= TAG_KEEP_LANG;
|
||||
}
|
||||
|
@ -787,7 +787,11 @@ EXTERN int postponed_split_flags INIT(= 0); /* args for win_split() */
|
||||
EXTERN int postponed_split_tab INIT(= 0); /* cmdmod.tab */
|
||||
EXTERN int g_do_tagpreview INIT(= 0); /* for tag preview commands:
|
||||
height of preview window */
|
||||
EXTERN int replace_offset INIT(= 0); /* offset for replace_push() */
|
||||
EXTERN int g_tag_at_cursor INIT(= false); // 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\\\"|");
|
||||
/* need backslash in cmd line */
|
||||
|
@ -81,10 +81,10 @@ typedef struct hashtable_S {
|
||||
size_t hi##todo_ = hi##ht_->ht_used; \
|
||||
for (hashitem_T *hi = hi##ht_->ht_array; hi##todo_; hi++) { \
|
||||
if (!HASHITEM_EMPTY(hi)) { \
|
||||
hi##todo_--; \
|
||||
{ \
|
||||
code \
|
||||
} \
|
||||
hi##todo_--; \
|
||||
} \
|
||||
} \
|
||||
} while (0)
|
||||
|
@ -4938,7 +4938,9 @@ static void nv_ident(cmdarg_T *cap)
|
||||
add_to_history(HIST_SEARCH, (char_u *)buf, true, NUL);
|
||||
(void)normal_search(cap, cmdchar == '*' ? '/' : '?', (char_u *)buf, 0);
|
||||
} else {
|
||||
g_tag_at_cursor = true;
|
||||
do_cmdline_cmd(buf);
|
||||
g_tag_at_cursor = false;
|
||||
}
|
||||
|
||||
xfree(buf);
|
||||
|
@ -133,6 +133,7 @@ static char_u *p_cms;
|
||||
static char_u *p_cpt;
|
||||
static char_u *p_cfu;
|
||||
static char_u *p_ofu;
|
||||
static char_u *p_tfu;
|
||||
static int p_eol;
|
||||
static int p_fixeol;
|
||||
static int p_et;
|
||||
@ -2273,6 +2274,7 @@ void check_buf_options(buf_T *buf)
|
||||
check_string_option(&buf->b_p_ep);
|
||||
check_string_option(&buf->b_p_path);
|
||||
check_string_option(&buf->b_p_tags);
|
||||
check_string_option(&buf->b_p_tfu);
|
||||
check_string_option(&buf->b_p_tc);
|
||||
check_string_option(&buf->b_p_dict);
|
||||
check_string_option(&buf->b_p_tsr);
|
||||
@ -5590,6 +5592,7 @@ static char_u *get_varp_scope(vimoption_T *p, int opt_flags)
|
||||
case PV_INC: return (char_u *)&(curbuf->b_p_inc);
|
||||
case PV_DICT: return (char_u *)&(curbuf->b_p_dict);
|
||||
case PV_TSR: return (char_u *)&(curbuf->b_p_tsr);
|
||||
case PV_TFU: return (char_u *)&(curbuf->b_p_tfu);
|
||||
case PV_STL: return (char_u *)&(curwin->w_p_stl);
|
||||
case PV_UL: return (char_u *)&(curbuf->b_p_ul);
|
||||
case PV_LW: return (char_u *)&(curbuf->b_p_lw);
|
||||
@ -5742,6 +5745,7 @@ static char_u *get_varp(vimoption_T *p)
|
||||
case PV_SPF: return (char_u *)&(curwin->w_s->b_p_spf);
|
||||
case PV_SPL: return (char_u *)&(curwin->w_s->b_p_spl);
|
||||
case PV_SW: return (char_u *)&(curbuf->b_p_sw);
|
||||
case PV_TFU: return (char_u *)&(curbuf->b_p_tfu);
|
||||
case PV_TS: return (char_u *)&(curbuf->b_p_ts);
|
||||
case PV_TW: return (char_u *)&(curbuf->b_p_tw);
|
||||
case PV_UDF: return (char_u *)&(curbuf->b_p_udf);
|
||||
@ -6004,6 +6008,7 @@ void buf_copy_options(buf_T *buf, int flags)
|
||||
buf->b_p_cpt = vim_strsave(p_cpt);
|
||||
buf->b_p_cfu = vim_strsave(p_cfu);
|
||||
buf->b_p_ofu = vim_strsave(p_ofu);
|
||||
buf->b_p_tfu = vim_strsave(p_tfu);
|
||||
buf->b_p_sts = p_sts;
|
||||
buf->b_p_sts_nopaste = p_sts_nopaste;
|
||||
buf->b_p_com = vim_strsave(p_com);
|
||||
|
@ -780,6 +780,7 @@ enum {
|
||||
, BV_SUA
|
||||
, BV_SW
|
||||
, BV_SWF
|
||||
, BV_TFU
|
||||
, BV_TAGS
|
||||
, BV_TC
|
||||
, BV_TS
|
||||
|
@ -2388,6 +2388,14 @@ return {
|
||||
varname='p_syn',
|
||||
defaults={if_true={vi=""}}
|
||||
},
|
||||
{
|
||||
full_name='tagfunc', abbreviation='tfu',
|
||||
type='string', scope={'buffer'},
|
||||
vim=true,
|
||||
vi_def=true,
|
||||
varname='p_tfu',
|
||||
defaults={if_true={vi=""}}
|
||||
},
|
||||
{
|
||||
full_name='tabline', abbreviation='tal',
|
||||
type='string', scope={'global'},
|
||||
|
1186
src/nvim/tag.c
1186
src/nvim/tag.c
File diff suppressed because it is too large
Load Diff
@ -20,20 +20,21 @@
|
||||
#define DT_LTAG 11 /* tag using location list */
|
||||
#define DT_FREE 99 /* free cached matches */
|
||||
|
||||
/*
|
||||
* 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_CSCOPE 16 /* cscope tag */
|
||||
#define TAG_VERBOSE 32 /* message verbosity */
|
||||
#define TAG_INS_COMP 64 /* Currently doing insert completion */
|
||||
#define TAG_KEEP_LANG 128 /* keep current language */
|
||||
//
|
||||
// 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_CSCOPE 16 // cscope tag
|
||||
#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_NO_TAGFUNC 256 // do not use 'tagfunc'
|
||||
|
||||
#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
|
||||
|
||||
/*
|
||||
* Structure used for get_tagfname().
|
||||
|
@ -44,6 +44,7 @@ source test_syn_attr.vim
|
||||
source test_tabline.vim
|
||||
source test_tabpage.vim
|
||||
source test_tagcase.vim
|
||||
source test_tagfunc.vim
|
||||
source test_tagjump.vim
|
||||
source test_taglist.vim
|
||||
source test_true_false.vim
|
||||
|
84
src/nvim/testdir/test_tagfunc.vim
Normal file
84
src/nvim/testdir/test_tagfunc.vim
Normal file
@ -0,0 +1,84 @@
|
||||
" Test 'tagfunc'
|
||||
|
||||
func TagFunc(pat, flag, info)
|
||||
let g:tagfunc_args = [a:pat, a:flag, a:info]
|
||||
let tags = []
|
||||
for num in range(1,10)
|
||||
let tags += [{
|
||||
\ 'cmd': '2', 'name': 'nothing'.num, 'kind': 'm',
|
||||
\ 'filename': 'Xfile1', 'user_data': 'somedata'.num,
|
||||
\}]
|
||||
endfor
|
||||
return tags
|
||||
endfunc
|
||||
|
||||
func Test_tagfunc()
|
||||
set tagfunc=TagFunc
|
||||
new Xfile1
|
||||
call setline(1, ['empty', 'one()', 'empty'])
|
||||
write
|
||||
|
||||
call assert_equal({'cmd': '2', 'static': 0,
|
||||
\ 'name': 'nothing2', 'user_data': 'somedata2',
|
||||
\ 'kind': 'm', 'filename': 'Xfile1'}, taglist('.')[1])
|
||||
|
||||
call settagstack(win_getid(), {'items': []})
|
||||
|
||||
tag arbitrary
|
||||
call assert_equal('arbitrary', g:tagfunc_args[0])
|
||||
call assert_equal('', g:tagfunc_args[1])
|
||||
call assert_equal('somedata1', gettagstack().items[0].user_data)
|
||||
5tag arbitrary
|
||||
call assert_equal('arbitrary', g:tagfunc_args[0])
|
||||
call assert_equal('', g:tagfunc_args[1])
|
||||
call assert_equal('somedata5', gettagstack().items[1].user_data)
|
||||
pop
|
||||
tag
|
||||
call assert_equal('arbitrary', g:tagfunc_args[0])
|
||||
call assert_equal('', g:tagfunc_args[1])
|
||||
call assert_equal('somedata5', gettagstack().items[1].user_data)
|
||||
|
||||
let g:tagfunc_args=[]
|
||||
execute "normal! \<c-]>"
|
||||
call assert_equal('one', g:tagfunc_args[0])
|
||||
call assert_equal('c', g:tagfunc_args[1])
|
||||
|
||||
set cpt=t
|
||||
let g:tagfunc_args=[]
|
||||
execute "normal! i\<c-n>\<c-y>"
|
||||
call assert_equal('ci', g:tagfunc_args[1])
|
||||
call assert_equal('nothing1', getline('.')[0:7])
|
||||
|
||||
func BadTagFunc1(...)
|
||||
return 0
|
||||
endfunc
|
||||
func BadTagFunc2(...)
|
||||
return [1]
|
||||
endfunc
|
||||
func BadTagFunc3(...)
|
||||
return [{'name': 'foo'}]
|
||||
endfunc
|
||||
|
||||
for &tagfunc in ['BadTagFunc1', 'BadTagFunc2', 'BadTagFunc3']
|
||||
try
|
||||
tag nothing
|
||||
call assert_false(1, 'tag command should have failed')
|
||||
catch
|
||||
call assert_exception('E987:')
|
||||
endtry
|
||||
exe 'delf' &tagfunc
|
||||
endfor
|
||||
|
||||
func NullTagFunc(...)
|
||||
return v:null
|
||||
endfunc
|
||||
set tags= tfu=NullTagFunc
|
||||
call assert_fails('tag nothing', 'E426')
|
||||
delf NullTagFunc
|
||||
|
||||
bwipe!
|
||||
set tags& tfu& cpt&
|
||||
call delete('Xfile1')
|
||||
endfunc
|
||||
|
||||
" vim: shiftwidth=2 sts=2 expandtab
|
@ -70,8 +70,8 @@ static char *m_onlyone = N_("Already only one window");
|
||||
/*
|
||||
* all CTRL-W window commands are handled here, called from normal_cmd().
|
||||
*/
|
||||
void
|
||||
do_window (
|
||||
void
|
||||
do_window(
|
||||
int nchar,
|
||||
long Prenum,
|
||||
int xchar /* extra char from ":wincmd gx" or NUL */
|
||||
@ -1537,10 +1537,14 @@ static void win_init(win_T *newp, win_T *oldp, int flags)
|
||||
|
||||
/* copy tagstack and folds */
|
||||
for (i = 0; i < oldp->w_tagstacklen; i++) {
|
||||
newp->w_tagstack[i] = oldp->w_tagstack[i];
|
||||
if (newp->w_tagstack[i].tagname != NULL)
|
||||
newp->w_tagstack[i].tagname =
|
||||
vim_strsave(newp->w_tagstack[i].tagname);
|
||||
taggy_T *tag = &newp->w_tagstack[i];
|
||||
*tag = oldp->w_tagstack[i];
|
||||
if (tag->tagname != NULL) {
|
||||
tag->tagname = vim_strsave(tag->tagname);
|
||||
}
|
||||
if (tag->user_data != NULL) {
|
||||
tag->user_data = vim_strsave(tag->user_data);
|
||||
}
|
||||
}
|
||||
newp->w_tagstackidx = oldp->w_tagstackidx;
|
||||
newp->w_tagstacklen = oldp->w_tagstacklen;
|
||||
@ -4638,8 +4642,10 @@ win_free (
|
||||
|
||||
xfree(wp->w_lines);
|
||||
|
||||
for (i = 0; i < wp->w_tagstacklen; ++i)
|
||||
for (i = 0; i < wp->w_tagstacklen; i++) {
|
||||
xfree(wp->w_tagstack[i].tagname);
|
||||
xfree(wp->w_tagstack[i].user_data);
|
||||
}
|
||||
|
||||
xfree(wp->w_localdir);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user