summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2018-11-11 15:21:05 +0100
committerBram Moolenaar <Bram@vim.org>2018-11-11 15:21:05 +0100
commitf49cc60aa802862c595ff619dccc11271633a94b (patch)
treee8f01a0c236f2910f117c858f7236b8919e33b93
parent8617b401599451187fa0c0561a84944978536a90 (diff)
downloadvim-git-8.1.0519.tar.gz
patch 8.1.0519: cannot save and restore the tag stackv8.1.0519
Problem: Cannot save and restore the tag stack. Solution: Add gettagstack() and settagstack(). (Yegappan Lakshmanan, closes #3604)
-rw-r--r--runtime/doc/eval.txt62
-rw-r--r--runtime/doc/tagsrch.txt3
-rw-r--r--runtime/doc/usr_41.txt2
-rw-r--r--src/alloc.h3
-rw-r--r--src/dict.c13
-rw-r--r--src/evalfunc.c81
-rw-r--r--src/list.c13
-rw-r--r--src/misc2.c2
-rw-r--r--src/proto/dict.pro1
-rw-r--r--src/proto/list.pro1
-rw-r--r--src/proto/misc2.pro1
-rw-r--r--src/proto/tag.pro2
-rw-r--r--src/tag.c200
-rw-r--r--src/testdir/test_tagjump.vim109
-rw-r--r--src/version.c2
15 files changed, 494 insertions, 1 deletions
diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt
index b261d5822..bb80a665c 100644
--- a/runtime/doc/eval.txt
+++ b/runtime/doc/eval.txt
@@ -2206,6 +2206,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 info about each window
getwinpos([{timeout}]) List X and Y coord in pixels of the Vim window
getwinposx() Number X coord in pixels of the Vim window
@@ -2378,6 +2379,8 @@ settabvar({nr}, {varname}, {val}) none set {varname} in tab page {nr} to {val}
settabwinvar({tabnr}, {winnr}, {varname}, {val})
none 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}) none set {varname} in window {nr} to {val}
sha256({string}) String SHA256 checksum of {string}
shellescape({string} [, {special}])
@@ -4971,6 +4974,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.
+
getwininfo([{winid}]) *getwininfo()*
Returns information about windows as a List with Dictionaries.
@@ -7535,6 +7566,37 @@ 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: >
diff --git a/runtime/doc/tagsrch.txt b/runtime/doc/tagsrch.txt
index d3ceac662..75a5b114a 100644
--- a/runtime/doc/tagsrch.txt
+++ b/runtime/doc/tagsrch.txt
@@ -179,6 +179,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.
diff --git a/runtime/doc/usr_41.txt b/runtime/doc/usr_41.txt
index 1ec743a56..7c3f358aa 100644
--- a/runtime/doc/usr_41.txt
+++ b/runtime/doc/usr_41.txt
@@ -1028,6 +1028,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
mzeval() evaluate |MzScheme| expression
diff --git a/src/alloc.h b/src/alloc.h
index 60fa4bd66..36890054f 100644
--- a/src/alloc.h
+++ b/src/alloc.h
@@ -18,5 +18,8 @@ typedef enum {
aid_qf_module,
aid_qf_errmsg,
aid_qf_pattern,
+ aid_tagstack_items,
+ aid_tagstack_from,
+ aid_tagstack_details,
aid_last
} alloc_id_T;
diff --git a/src/dict.c b/src/dict.c
index c359e6f0f..da3e763e2 100644
--- a/src/dict.c
+++ b/src/dict.c
@@ -47,6 +47,19 @@ dict_alloc(void)
return d;
}
+/*
+ * dict_alloc() with an ID for alloc_fail().
+ */
+ dict_T *
+dict_alloc_id(alloc_id_T id UNUSED)
+{
+#ifdef FEAT_EVAL
+ if (alloc_fail_id == id && alloc_does_fail((long_u)sizeof(list_T)))
+ return NULL;
+#endif
+ return (dict_alloc());
+}
+
dict_T *
dict_alloc_lock(int lock)
{
diff --git a/src/evalfunc.c b/src/evalfunc.c
index 82ea05af3..afad8be6f 100644
--- a/src/evalfunc.c
+++ b/src/evalfunc.c
@@ -201,6 +201,7 @@ static void f_getregtype(typval_T *argvars, typval_T *rettv);
static void f_gettabinfo(typval_T *argvars, typval_T *rettv);
static void f_gettabvar(typval_T *argvars, typval_T *rettv);
static void f_gettabwinvar(typval_T *argvars, typval_T *rettv);
+static void f_gettagstack(typval_T *argvars, typval_T *rettv);
static void f_getwininfo(typval_T *argvars, typval_T *rettv);
static void f_getwinpos(typval_T *argvars, typval_T *rettv);
static void f_getwinposx(typval_T *argvars, typval_T *rettv);
@@ -361,6 +362,7 @@ static void f_setqflist(typval_T *argvars, typval_T *rettv);
static void f_setreg(typval_T *argvars, typval_T *rettv);
static void f_settabvar(typval_T *argvars, typval_T *rettv);
static void f_settabwinvar(typval_T *argvars, typval_T *rettv);
+static void f_settagstack(typval_T *argvars, typval_T *rettv);
static void f_setwinvar(typval_T *argvars, typval_T *rettv);
#ifdef FEAT_CRYPT
static void f_sha256(typval_T *argvars, typval_T *rettv);
@@ -666,6 +668,7 @@ static struct fst
{"gettabinfo", 0, 1, f_gettabinfo},
{"gettabvar", 2, 3, f_gettabvar},
{"gettabwinvar", 3, 4, f_gettabwinvar},
+ {"gettagstack", 0, 1, f_gettagstack},
{"getwininfo", 0, 1, f_getwininfo},
{"getwinpos", 0, 1, f_getwinpos},
{"getwinposx", 0, 0, f_getwinposx},
@@ -828,6 +831,7 @@ static struct fst
{"setreg", 2, 3, f_setreg},
{"settabvar", 3, 3, f_settabvar},
{"settabwinvar", 4, 4, f_settabwinvar},
+ {"settagstack", 2, 3, f_settagstack},
{"setwinvar", 3, 3, f_setwinvar},
#ifdef FEAT_CRYPT
{"sha256", 1, 1, f_sha256},
@@ -5657,6 +5661,27 @@ f_gettabwinvar(typval_T *argvars, typval_T *rettv)
}
/*
+ * "gettagstack()" function
+ */
+ static void
+f_gettagstack(typval_T *argvars, typval_T *rettv)
+{
+ win_T *wp = curwin; // default is current window
+
+ if (rettv_dict_alloc(rettv) != OK)
+ return;
+
+ 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 *
@@ -11119,6 +11144,62 @@ f_settabwinvar(typval_T *argvars, typval_T *rettv)
}
/*
+ * "settagstack()" function
+ */
+ static void
+f_settagstack(typval_T *argvars, typval_T *rettv)
+{
+ 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)
+ {
+ char_u *actstr;
+ actstr = get_tv_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;
+}
+
+/*
* "setwinvar()" function
*/
static void
diff --git a/src/list.c b/src/list.c
index 9a348a8a6..3cf6dff76 100644
--- a/src/list.c
+++ b/src/list.c
@@ -86,6 +86,19 @@ list_alloc(void)
}
/*
+ * list_alloc() with an ID for alloc_fail().
+ */
+ list_T *
+list_alloc_id(alloc_id_T id UNUSED)
+{
+#ifdef FEAT_EVAL
+ if (alloc_fail_id == id && alloc_does_fail((long_u)sizeof(list_T)))
+ return NULL;
+#endif
+ return (list_alloc());
+}
+
+/*
* Allocate an empty list for a return value, with reference count set.
* Returns OK or FAIL.
*/
diff --git a/src/misc2.c b/src/misc2.c
index 3287f7e11..ce695979d 100644
--- a/src/misc2.c
+++ b/src/misc2.c
@@ -835,7 +835,7 @@ vim_mem_profile_dump(void)
#endif /* MEM_PROFILE */
#ifdef FEAT_EVAL
- static int
+ int
alloc_does_fail(long_u size)
{
if (alloc_fail_countdown == 0)
diff --git a/src/proto/dict.pro b/src/proto/dict.pro
index ede4f873b..21c8d6dac 100644
--- a/src/proto/dict.pro
+++ b/src/proto/dict.pro
@@ -1,5 +1,6 @@
/* dict.c */
dict_T *dict_alloc(void);
+dict_T *dict_alloc_id(alloc_id_T id);
dict_T *dict_alloc_lock(int lock);
int rettv_dict_alloc(typval_T *rettv);
void rettv_dict_set(typval_T *rettv, dict_T *d);
diff --git a/src/proto/list.pro b/src/proto/list.pro
index c4fd195f1..2f0b404d7 100644
--- a/src/proto/list.pro
+++ b/src/proto/list.pro
@@ -3,6 +3,7 @@ void list_add_watch(list_T *l, listwatch_T *lw);
void list_rem_watch(list_T *l, listwatch_T *lwrem);
void list_fix_watch(list_T *l, listitem_T *item);
list_T *list_alloc(void);
+list_T *list_alloc_id(alloc_id_T id);
int rettv_list_alloc(typval_T *rettv);
void rettv_list_set(typval_T *rettv, list_T *l);
void list_unref(list_T *l);
diff --git a/src/proto/misc2.pro b/src/proto/misc2.pro
index 770bd33c4..b821417da 100644
--- a/src/proto/misc2.pro
+++ b/src/proto/misc2.pro
@@ -21,6 +21,7 @@ void adjust_cursor_col(void);
int leftcol_changed(void);
void vim_mem_profile_dump(void);
char_u *alloc(unsigned size);
+int alloc_does_fail(long_u size);
char_u *alloc_id(unsigned size, alloc_id_T id);
char_u *alloc_clear(unsigned size);
char_u *alloc_check(unsigned size);
diff --git a/src/proto/tag.pro b/src/proto/tag.pro
index 497a76e38..c9bcb384e 100644
--- a/src/proto/tag.pro
+++ b/src/proto/tag.pro
@@ -9,4 +9,6 @@ void tagname_free(tagname_T *tnp);
void simplify_filename(char_u *filename);
int expand_tags(int tagnames, char_u *pat, int *num_file, char_u ***file);
int get_tags(list_T *list, char_u *pat, char_u *buf_fname);
+void get_tagstack(win_T *wp, dict_T *retdict);
+int set_tagstack(win_T *wp, dict_T *d, int action);
/* vim: set ft=c : */
diff --git a/src/tag.c b/src/tag.c
index 3765fe565..5ae61f316 100644
--- a/src/tag.c
+++ b/src/tag.c
@@ -4016,4 +4016,204 @@ 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;
+
+ dict_add_string(retdict, "tagname", tag->tagname);
+ dict_add_number(retdict, "matchnr", tag->cur_match + 1);
+ dict_add_number(retdict, "bufnr", tag->cur_fnum);
+
+ if ((pos = list_alloc_id(aid_tagstack_from)) == NULL)
+ return;
+ dict_add_list(retdict, "from", pos);
+
+ fmark = &tag->fmark;
+ list_append_number(pos,
+ (varnumber_T)(fmark->fnum != -1 ? fmark->fnum : 0));
+ list_append_number(pos, (varnumber_T)fmark->mark.lnum);
+ list_append_number(pos, (varnumber_T)(fmark->mark.col == MAXCOL ?
+ MAXCOL : fmark->mark.col + 1));
+ 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;
+
+ dict_add_number(retdict, "length", wp->w_tagstacklen);
+ dict_add_number(retdict, "curidx", wp->w_tagstackidx + 1);
+ l = list_alloc_id(aid_tagstack_items);
+ if (l == NULL)
+ return;
+ dict_add_list(retdict, "items", l);
+
+ for (i = 0; i < wp->w_tagstacklen; i++)
+ {
+ if ((d = dict_alloc_id(aid_tagstack_details)) == NULL)
+ return;
+ 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)
+ vim_free(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;
+
+ vim_free(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 = dict_find(itemdict, (char_u *)"from", -1)) == NULL)
+ continue;
+ if (list2fpos(&di->di_tv, &mark, &fnum, NULL) != OK)
+ continue;
+ if ((tagname =
+ get_dict_string(itemdict, (char_u *)"tagname", TRUE)) == NULL)
+ continue;
+
+ if (mark.col > 0)
+ mark.col--;
+ tagstack_push_item(wp, tagname,
+ (int)get_dict_number(itemdict, (char_u *)"bufnr"),
+ (int)get_dict_number(itemdict, (char_u *)"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 = dict_find(d, (char_u *)"items", -1)) != NULL)
+ {
+ if (di->di_tv.v_type != VAR_LIST)
+ {
+ EMSG(_(e_listreq));
+ return FAIL;
+ }
+ l = di->di_tv.vval.v_list;
+
+ if (action == 'r')
+ tagstack_clear(wp);
+
+ tagstack_push_items(wp, l);
+ }
+
+ if ((di = dict_find(d, (char_u *)"curidx", -1)) != NULL)
+ tagstack_set_curidx(wp, (int)get_tv_number(&di->di_tv) - 1);
+
+ return OK;
+}
#endif
diff --git a/src/testdir/test_tagjump.vim b/src/testdir/test_tagjump.vim
index 9f0accc3d..ae47a69c3 100644
--- a/src/testdir/test_tagjump.vim
+++ b/src/testdir/test_tagjump.vim
@@ -257,4 +257,113 @@ 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)
+
+ " Memory allocation failures
+ call test_alloc_fail(GetAllocId('tagstack_items'), 0, 0)
+ call assert_fails('call gettagstack()', 'E342:')
+ call test_alloc_fail(GetAllocId('tagstack_from'), 0, 0)
+ call assert_fails('call gettagstack()', 'E342:')
+ call test_alloc_fail(GetAllocId('tagstack_details'), 0, 0)
+ call assert_fails('call gettagstack()', 'E342:')
+
+ call settagstack(1, {'items' : []})
+ call delete('Xfile1')
+ call delete('Xfile2')
+ call delete('Xfile3')
+ call delete('Xtags')
+ set tags&
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/version.c b/src/version.c
index e1c889e50..ad815fb17 100644
--- a/src/version.c
+++ b/src/version.c
@@ -793,6 +793,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 519,
+/**/
518,
/**/
517,