vim-patch:8.1.0519: cannot save and restore the tag stack

Problem:    Cannot save and restore the tag stack.
Solution:   Add gettagstack() and settagstack(). (Yegappan Lakshmanan,
            closes vim/vim#3604)
f49cc60aa8
This commit is contained in:
rolag 2019-04-28 10:59:19 +01:00
parent 8072f085d2
commit 924dd6f14a
7 changed files with 449 additions and 1 deletions

View File

@ -2112,6 +2112,7 @@ gettabvar({nr}, {varname} [, {def}])
any variable {varname} in tab {nr} or {def}
gettabwinvar({tabnr}, {winnr}, {name} [, {def}])
any {name} in {winnr} in tab page {tabnr}
gettagstack([{nr}]) Dict get the tag stack of window {nr}
getwininfo([{winid}]) List list of windows
getwinpos([{timeout}]) List X and Y coord in pixels of the Vim window
getwinposx() Number X coord in pixels of GUI Vim window
@ -2277,6 +2278,8 @@ setreg({n}, {v}[, {opt}]) Number set register to value and type
settabvar({nr}, {varname}, {val}) set {varname} in tab page {nr} to {val}
settabwinvar({tabnr}, {winnr}, {varname}, {val}) set {varname} in window
{winnr} in tab page {tabnr} to {val}
settagstack({nr}, {dict} [, {action}])
Number modify tag stack using {dict}
setwinvar({nr}, {varname}, {val}) set {varname} in window {nr} to {val}
sha256({string}) String SHA256 checksum of {string}
shellescape({string} [, {special}])
@ -4550,6 +4553,34 @@ gettabwinvar({tabnr}, {winnr}, {varname} [, {def}]) *gettabwinvar()*
To obtain all window-local variables use: >
gettabwinvar({tabnr}, {winnr}, '&')
gettagstack([{nr}]) *gettagstack()*
The result is a Dict, which is the tag stack of window {nr}.
{nr} can be the window number or the |window-ID|.
When {nr} is not specified, the current window is used.
When window {nr} doesn't exist, an empty Dict is returned.
The returned dictionary contains the following entries:
curidx Current index in the stack. When at
top of the stack, set to (length + 1).
Index of bottom of the stack is 1.
items List of items in the stack. Each item
is a dictionary containing the
entries described below.
length Number of entries in the stack.
Each item in the stack is a dictionary with the following
entries:
bufnr buffer number of the current jump
from cursor position before the tag jump.
See |getpos()| for the format of the
returned list.
matchnr current matching tag number. Used when
multiple matching tags are found for a
name.
tagname name of the tag
See |tagstack| for more information about the tag stack.
*getwinposx()*
getwinposx() The result is a Number, which is the X coordinate in pixels of
the left hand side of the GUI Vim window. The result will be
@ -7181,6 +7212,38 @@ settabwinvar({tabnr}, {winnr}, {varname}, {val}) *settabwinvar()*
:call settabwinvar(3, 2, "myvar", "foobar")
< This function is not available in the |sandbox|.
settagstack({nr}, {dict} [, {action}]) *settagstack()*
Modify the tag stack of the window {nr} using {dict}.
{nr} can be the window number or the |window-ID|.
For a list of supported items in {dict}, refer to
|gettagstack()|
*E962*
If {action} is not present or is set to 'r', then the tag
stack is replaced. If {action} is set to 'a', then new entries
from {dict} are pushed onto the tag stack.
Returns zero for success, -1 for failure.
Examples:
Set current index of the tag stack to 4: >
call settagstack(1005, {'curidx' : 4})
< Empty the tag stack of window 3: >
call settagstack(3, {'items' : []})
< Push a new item onto the tag stack: >
let pos = [bufnr('myfile.txt'), 10, 1, 0]
let newtag = [{'tagname' : 'mytag', 'from' : pos}]
call settagstack(2, {'items' : newtag}, 'a')
< Save and restore the tag stack: >
let stack = gettagstack(1003)
" do something else
call settagstack(1003, stack)
unlet stack
<
setwinvar({nr}, {varname}, {val}) *setwinvar()*
Like |settabwinvar()| for the current tab page.
Examples: >

View File

@ -173,6 +173,9 @@ commands explained above the tag stack will look like this:
1 1 main 1 harddisk2:text/vim/test
2 1 FuncB 59 harddisk2:text/vim/src/main.c
The gettagstack() function returns the tag stack of a specified window. The
settagstack() function modifies the tag stack of a window.
*E73*
When you try to use the tag stack while it doesn't contain anything you will
get an error message.

View File

@ -957,6 +957,8 @@ Various: *various-functions*
taglist() get list of matching tags
tagfiles() get a list of tags files
gettagstack() get the tag stack
settagstack() modify the tag stack
luaeval() evaluate Lua expression
py3eval() evaluate Python expression (|+python3|)

View File

@ -10404,6 +10404,26 @@ static void f_gettabwinvar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
getwinvar(argvars, rettv, 1);
}
/*
* "gettagstack()" function
*/
static void
f_gettagstack(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
win_T *wp = curwin; // default is current window
tv_dict_alloc_ret(rettv);
if (argvars[0].v_type != VAR_UNKNOWN)
{
wp = find_win_by_nr_or_id(&argvars[0]);
if (wp == NULL)
return;
}
get_tagstack(wp, rettv->vval.v_dict);
}
/// Returns information about a window as a dictionary.
static dict_T *get_win_info(win_T *wp, int16_t tpnr, int16_t winnr)
{
@ -15196,6 +15216,64 @@ static void f_settabwinvar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
setwinvar(argvars, rettv, 1);
}
/*
* "settagstack()" function
*/
static void
f_settagstack(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
static char *e_invact2 = N_("E962: Invalid action: '%s'");
win_T *wp;
dict_T *d;
int action = 'r';
rettv->vval.v_number = -1;
// first argument: window number or id
wp = find_win_by_nr_or_id(&argvars[0]);
if (wp == NULL)
return;
// second argument: dict with items to set in the tag stack
if (argvars[1].v_type != VAR_DICT)
{
EMSG(_(e_dictreq));
return;
}
d = argvars[1].vval.v_dict;
if (d == NULL)
return;
// third argument: action - 'a' for append and 'r' for replace.
// default is to replace the stack.
if (argvars[2].v_type == VAR_UNKNOWN)
action = 'r';
else if (argvars[2].v_type == VAR_STRING)
{
const char *actstr;
actstr = tv_get_string_chk(&argvars[2]);
if (actstr == NULL)
return;
if ((*actstr == 'r' || *actstr == 'a') && actstr[1] == NUL)
action = *actstr;
else
{
EMSG2(_(e_invact2), actstr);
return;
}
}
else
{
EMSG(_(e_stringreq));
return;
}
if (set_tagstack(wp, d, action) == OK)
rettv->vval.v_number = 0;
else
EMSG(_(e_listreq));
}
/*
* "setwinvar()" function
*/
@ -18197,7 +18275,7 @@ pos_T *var2fpos(const typval_T *const tv, const int dollar_lnum,
* Return FAIL when conversion is not possible, doesn't check the position for
* validity.
*/
static int list2fpos(typval_T *arg, pos_T *posp, int *fnump, colnr_T *curswantp)
int list2fpos(typval_T *arg, pos_T *posp, int *fnump, colnr_T *curswantp)
{
list_T *l;
long i = 0;

View File

@ -142,6 +142,7 @@ return {
gettabinfo={args={0, 1}},
gettabvar={args={2, 3}},
gettabwinvar={args={3, 4}},
gettagstack={args={0, 1}},
getwininfo={args={0, 1}},
getwinposx={},
getwinposy={},
@ -268,6 +269,7 @@ return {
setreg={args={2, 3}},
settabvar={args=3},
settabwinvar={args=4},
settagstack={args={2, 3}},
setwinvar={args=3},
sha256={args=1},
shellescape={args={1, 2}},

View File

@ -2863,3 +2863,202 @@ int get_tags(list_T *list, char_u *pat, char_u *buf_fname)
}
return ret;
}
/*
* Return information about 'tag' in dict 'retdict'.
*/
static void
get_tag_details(taggy_T *tag, dict_T *retdict)
{
list_T *pos;
fmark_T *fmark;
tv_dict_add_str(retdict, S_LEN("tagname"), (const char *)tag->tagname);
tv_dict_add_nr(retdict, S_LEN("matchnr"), tag->cur_match + 1);
tv_dict_add_nr(retdict, S_LEN("bufnr"), tag->cur_fnum);
if ((pos = tv_list_alloc(4)) == NULL)
return;
tv_dict_add_list(retdict, S_LEN("from"), pos);
fmark = &tag->fmark;
tv_list_append_number(pos,
(varnumber_T)(fmark->fnum != -1 ? fmark->fnum : 0));
tv_list_append_number(pos, (varnumber_T)fmark->mark.lnum);
tv_list_append_number(pos, (varnumber_T)(fmark->mark.col == MAXCOL ?
MAXCOL : fmark->mark.col + 1));
tv_list_append_number(pos, (varnumber_T)fmark->mark.coladd);
}
/*
* Return the tag stack entries of the specified window 'wp' in dictionary
* 'retdict'.
*/
void
get_tagstack(win_T *wp, dict_T *retdict)
{
list_T *l;
int i;
dict_T *d;
tv_dict_add_nr(retdict, S_LEN("length"), wp->w_tagstacklen);
tv_dict_add_nr(retdict, S_LEN("curidx"), wp->w_tagstackidx + 1);
l = tv_list_alloc(2);
if (l == NULL)
return;
tv_dict_add_list(retdict, S_LEN("items"), l);
for (i = 0; i < wp->w_tagstacklen; i++)
{
if ((d = tv_dict_alloc()) == NULL)
return;
tv_list_append_dict(l, d);
get_tag_details(&wp->w_tagstack[i], d);
}
}
/*
* Free all the entries in the tag stack of the specified window
*/
static void
tagstack_clear(win_T *wp)
{
int i;
// Free the current tag stack
for (i = 0; i < wp->w_tagstacklen; ++i)
xfree(wp->w_tagstack[i].tagname);
wp->w_tagstacklen = 0;
wp->w_tagstackidx = 0;
}
/*
* Remove the oldest entry from the tag stack and shift the rest of
* the entires to free up the top of the stack.
*/
static void
tagstack_shift(win_T *wp)
{
taggy_T *tagstack = wp->w_tagstack;
int i;
xfree(tagstack[0].tagname);
for (i = 1; i < wp->w_tagstacklen; ++i)
tagstack[i - 1] = tagstack[i];
wp->w_tagstacklen--;
}
/*
* Push a new item to the tag stack
*/
static void
tagstack_push_item(
win_T *wp,
char_u *tagname,
int cur_fnum,
int cur_match,
pos_T mark,
int fnum)
{
taggy_T *tagstack = wp->w_tagstack;
int idx = wp->w_tagstacklen; // top of the stack
// if the tagstack is full: remove the oldest entry
if (idx >= TAGSTACKSIZE)
{
tagstack_shift(wp);
idx = TAGSTACKSIZE - 1;
}
wp->w_tagstacklen++;
tagstack[idx].tagname = tagname;
tagstack[idx].cur_fnum = cur_fnum;
tagstack[idx].cur_match = cur_match;
if (tagstack[idx].cur_match < 0)
tagstack[idx].cur_match = 0;
tagstack[idx].fmark.mark = mark;
tagstack[idx].fmark.fnum = fnum;
}
/*
* Add a list of items to the tag stack in the specified window
*/
static void
tagstack_push_items(win_T *wp, list_T *l)
{
listitem_T *li;
dictitem_T *di;
dict_T *itemdict;
char_u *tagname;
pos_T mark;
int fnum;
// Add one entry at a time to the tag stack
for (li = l->lv_first; li != NULL; li = li->li_next)
{
if (li->li_tv.v_type != VAR_DICT || li->li_tv.vval.v_dict == NULL)
continue; // Skip non-dict items
itemdict = li->li_tv.vval.v_dict;
// parse 'from' for the cursor position before the tag jump
if ((di = tv_dict_find(itemdict, "from", -1)) == NULL)
continue;
if (list2fpos(&di->di_tv, &mark, &fnum, NULL) != OK)
continue;
if ((tagname = (char_u *)
tv_dict_get_string(itemdict, "tagname", TRUE)) == NULL)
continue;
if (mark.col > 0)
mark.col--;
tagstack_push_item(wp, tagname,
(int)tv_dict_get_number(itemdict, "bufnr"),
(int)tv_dict_get_number(itemdict, "matchnr") - 1,
mark, fnum);
}
}
/*
* Set the current index in the tag stack. Valid values are between 0
* and the stack length (inclusive).
*/
static void
tagstack_set_curidx(win_T *wp, int curidx)
{
wp->w_tagstackidx = curidx;
if (wp->w_tagstackidx < 0) // sanity check
wp->w_tagstackidx = 0;
if (wp->w_tagstackidx > wp->w_tagstacklen)
wp->w_tagstackidx = wp->w_tagstacklen;
}
/*
* Set the tag stack entries of the specified window.
* 'action' is set to either 'a' for append or 'r' for replace.
*/
int
set_tagstack(win_T *wp, dict_T *d, int action)
{
dictitem_T *di;
list_T *l;
if ((di = tv_dict_find(d, "items", -1)) != NULL)
{
if (di->di_tv.v_type != VAR_LIST)
{
return FAIL;
}
l = di->di_tv.vval.v_list;
if (action == 'r')
tagstack_clear(wp);
tagstack_push_items(wp, l);
}
if ((di = tv_dict_find(d, "curidx", -1)) != NULL)
tagstack_set_curidx(wp, (int)tv_get_number(&di->di_tv) - 1);
return OK;
}

View File

@ -258,6 +258,107 @@ func Test_tagjump_etags()
bwipe!
endfunc
" Test for getting and modifying the tag stack
func Test_getsettagstack()
call writefile(['line1', 'line2', 'line3'], 'Xfile1')
call writefile(['line1', 'line2', 'line3'], 'Xfile2')
call writefile(['line1', 'line2', 'line3'], 'Xfile3')
enew | only
call settagstack(1, {'items' : []})
call assert_equal(0, gettagstack(1).length)
call assert_equal([], gettagstack(1).items)
" Error cases
call assert_equal({}, gettagstack(100))
call assert_equal(-1, settagstack(100, {'items' : []}))
call assert_fails('call settagstack(1, [1, 10])', 'E715')
call assert_fails("call settagstack(1, {'items' : 10})", 'E714')
call assert_fails("call settagstack(1, {'items' : []}, 10)", 'E928')
call assert_fails("call settagstack(1, {'items' : []}, 'b')", 'E962')
set tags=Xtags
call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//",
\ "one\tXfile1\t1",
\ "three\tXfile3\t3",
\ "two\tXfile2\t2"],
\ 'Xtags')
let stk = []
call add(stk, {'bufnr' : bufnr('%'), 'tagname' : 'one',
\ 'from' : [bufnr('%'), line('.'), col('.'), 0], 'matchnr' : 1})
tag one
call add(stk, {'bufnr' : bufnr('%'), 'tagname' : 'two',
\ 'from' : [bufnr('%'), line('.'), col('.'), 0], 'matchnr' : 1})
tag two
call add(stk, {'bufnr' : bufnr('%'), 'tagname' : 'three',
\ 'from' : [bufnr('%'), line('.'), col('.'), 0], 'matchnr' : 1})
tag three
call assert_equal(3, gettagstack(1).length)
call assert_equal(stk, gettagstack(1).items)
" Check for default - current window
call assert_equal(3, gettagstack().length)
call assert_equal(stk, gettagstack().items)
" Try to set current index to invalid values
call settagstack(1, {'curidx' : -1})
call assert_equal(1, gettagstack().curidx)
call settagstack(1, {'curidx' : 50})
call assert_equal(4, gettagstack().curidx)
" Try pushing invalid items onto the stack
call settagstack(1, {'items' : []})
call settagstack(1, {'items' : ["plate"]}, 'a')
call assert_equal(0, gettagstack().length)
call assert_equal([], gettagstack().items)
call settagstack(1, {'items' : [{"tagname" : "abc"}]}, 'a')
call assert_equal(0, gettagstack().length)
call assert_equal([], gettagstack().items)
call settagstack(1, {'items' : [{"from" : 100}]}, 'a')
call assert_equal(0, gettagstack().length)
call assert_equal([], gettagstack().items)
call settagstack(1, {'items' : [{"from" : [2, 1, 0, 0]}]}, 'a')
call assert_equal(0, gettagstack().length)
call assert_equal([], gettagstack().items)
" Push one item at a time to the stack
call settagstack(1, {'items' : []})
call settagstack(1, {'items' : [stk[0]]}, 'a')
call settagstack(1, {'items' : [stk[1]]}, 'a')
call settagstack(1, {'items' : [stk[2]]}, 'a')
call settagstack(1, {'curidx' : 4})
call assert_equal({'length' : 3, 'curidx' : 4, 'items' : stk},
\ gettagstack(1))
" Try pushing items onto a full stack
for i in range(7)
call settagstack(1, {'items' : stk}, 'a')
endfor
call assert_equal(20, gettagstack().length)
call settagstack(1,
\ {'items' : [{'tagname' : 'abc', 'from' : [1, 10, 1, 0]}]}, 'a')
call assert_equal('abc', gettagstack().items[19].tagname)
" Tag with multiple matches
call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//",
\ "two\tXfile1\t1",
\ "two\tXfile2\t3",
\ "two\tXfile3\t2"],
\ 'Xtags')
call settagstack(1, {'items' : []})
tag two
tnext
tnext
call assert_equal(1, gettagstack().length)
call assert_equal(3, gettagstack().items[0].matchnr)
call settagstack(1, {'items' : []})
call delete('Xfile1')
call delete('Xfile2')
call delete('Xfile3')
call delete('Xtags')
set tags&
endfunc
func Test_tag_with_count()
call writefile([
\ 'test Xtest.h /^void test();$/;" p typeref:typename:void signature:()',