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} any variable {varname} in tab {nr} or {def}
gettabwinvar({tabnr}, {winnr}, {name} [, {def}]) gettabwinvar({tabnr}, {winnr}, {name} [, {def}])
any {name} in {winnr} in tab page {tabnr} any {name} in {winnr} in tab page {tabnr}
gettagstack([{nr}]) Dict get the tag stack of window {nr}
getwininfo([{winid}]) List list of windows getwininfo([{winid}]) List list of windows
getwinpos([{timeout}]) List X and Y coord in pixels of the Vim window getwinpos([{timeout}]) List X and Y coord in pixels of the Vim window
getwinposx() Number X coord in pixels of GUI 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} settabvar({nr}, {varname}, {val}) set {varname} in tab page {nr} to {val}
settabwinvar({tabnr}, {winnr}, {varname}, {val}) set {varname} in window settabwinvar({tabnr}, {winnr}, {varname}, {val}) set {varname} in window
{winnr} in tab page {tabnr} to {val} {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} setwinvar({nr}, {varname}, {val}) set {varname} in window {nr} to {val}
sha256({string}) String SHA256 checksum of {string} sha256({string}) String SHA256 checksum of {string}
shellescape({string} [, {special}]) shellescape({string} [, {special}])
@@ -4550,6 +4553,34 @@ gettabwinvar({tabnr}, {winnr}, {varname} [, {def}]) *gettabwinvar()*
To obtain all window-local variables use: > To obtain all window-local variables use: >
gettabwinvar({tabnr}, {winnr}, '&') 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()*
getwinposx() The result is a Number, which is the X coordinate in pixels of 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 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") :call settabwinvar(3, 2, "myvar", "foobar")
< This function is not available in the |sandbox|. < 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()* setwinvar({nr}, {varname}, {val}) *setwinvar()*
Like |settabwinvar()| for the current tab page. Like |settabwinvar()| for the current tab page.
Examples: > 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 1 1 main 1 harddisk2:text/vim/test
2 1 FuncB 59 harddisk2:text/vim/src/main.c 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* *E73*
When you try to use the tag stack while it doesn't contain anything you will When you try to use the tag stack while it doesn't contain anything you will
get an error message. get an error message.

View File

@@ -957,6 +957,8 @@ Various: *various-functions*
taglist() get list of matching tags taglist() get list of matching tags
tagfiles() get a list of tags files tagfiles() get a list of tags files
gettagstack() get the tag stack
settagstack() modify the tag stack
luaeval() evaluate Lua expression luaeval() evaluate Lua expression
py3eval() evaluate Python expression (|+python3|) 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); 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. /// Returns information about a window as a dictionary.
static dict_T *get_win_info(win_T *wp, int16_t tpnr, int16_t winnr) 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); 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 * "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 * Return FAIL when conversion is not possible, doesn't check the position for
* validity. * 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; list_T *l;
long i = 0; long i = 0;

View File

@@ -142,6 +142,7 @@ return {
gettabinfo={args={0, 1}}, gettabinfo={args={0, 1}},
gettabvar={args={2, 3}}, gettabvar={args={2, 3}},
gettabwinvar={args={3, 4}}, gettabwinvar={args={3, 4}},
gettagstack={args={0, 1}},
getwininfo={args={0, 1}}, getwininfo={args={0, 1}},
getwinposx={}, getwinposx={},
getwinposy={}, getwinposy={},
@@ -268,6 +269,7 @@ return {
setreg={args={2, 3}}, setreg={args={2, 3}},
settabvar={args=3}, settabvar={args=3},
settabwinvar={args=4}, settabwinvar={args=4},
settagstack={args={2, 3}},
setwinvar={args=3}, setwinvar={args=3},
sha256={args=1}, sha256={args=1},
shellescape={args={1, 2}}, 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 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! bwipe!
endfunc 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() func Test_tag_with_count()
call writefile([ call writefile([
\ 'test Xtest.h /^void test();$/;" p typeref:typename:void signature:()', \ 'test Xtest.h /^void test();$/;" p typeref:typename:void signature:()',