]> granicus.if.org Git - vim/commitdiff
patch 8.1.0519: cannot save and restore the tag stack v8.1.0519
authorBram Moolenaar <Bram@vim.org>
Sun, 11 Nov 2018 14:21:05 +0000 (15:21 +0100)
committerBram Moolenaar <Bram@vim.org>
Sun, 11 Nov 2018 14:21:05 +0000 (15:21 +0100)
Problem:    Cannot save and restore the tag stack.
Solution:   Add gettagstack() and settagstack(). (Yegappan Lakshmanan,
            closes #3604)

15 files changed:
runtime/doc/eval.txt
runtime/doc/tagsrch.txt
runtime/doc/usr_41.txt
src/alloc.h
src/dict.c
src/evalfunc.c
src/list.c
src/misc2.c
src/proto/dict.pro
src/proto/list.pro
src/proto/misc2.pro
src/proto/tag.pro
src/tag.c
src/testdir/test_tagjump.vim
src/version.c

index b261d582225d6f2faa71a25d5e0ea9e65c6ec10a..bb80a665c7658504a25cdf82cedf6ac7186c8d65 100644 (file)
@@ -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: >
index d3ceac662b18e6b2bc0fee5a286cc0d4b1fdd6ca..75a5b114a68783541656c2d73e6b79e5c0730e87 100644 (file)
@@ -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.
index 1ec743a56cb36b8a1ab460a4a4d1ea1d90ed34d4..7c3f358aa9f660035764bfee965c8db9a9820e5a 100644 (file)
@@ -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
index 60fa4bd66e678c4fa9e1092401c05437b8cc219a..36890054fd587f30e4c18700a718d9d12126ca0c 100644 (file)
@@ -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;
index c359e6f0f6d26023e52ebc3bc9018b953a3ef8b2..da3e763e2f8ac675c7e1b4fd0bbb7c296acf4098 100644 (file)
@@ -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)
 {
index 82ea05af3354e63411cccaffd4c865ba571f00f4..afad8be6f449fee4c2b5ceecc42d377ea4ab0e85 100644 (file)
@@ -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},
@@ -5656,6 +5660,27 @@ f_gettabwinvar(typval_T *argvars, typval_T *rettv)
     getwinvar(argvars, rettv, 1);
 }
 
+/*
+ * "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.
  */
@@ -11118,6 +11143,62 @@ f_settabwinvar(typval_T *argvars, typval_T *rettv)
     setwinvar(argvars, rettv, 1);
 }
 
+/*
+ * "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
  */
index 9a348a8a62b6b5f54e85b58a9ef28d73e1e71449..3cf6dff76ab8a031500ea659dd4b2da38b404123 100644 (file)
@@ -85,6 +85,19 @@ list_alloc(void)
     return l;
 }
 
+/*
+ * 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.
index 3287f7e1108e11c481f896492793d3208b81c728..ce695979dad64bdce99221025cb8f81b7969f1be 100644 (file)
@@ -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)
index ede4f873b87859ae4e9bfe639d8d9815eb0852e3..21c8d6dac59e3f09ec4f83967d25358f6c85301f 100644 (file)
@@ -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);
index c4fd195f1f3fc4c7b1382dd03101529e209d43bf..2f0b404d75cf15cffab18304e691e403c76957c9 100644 (file)
@@ -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);
index 770bd33c413959d990f4ba3bb183794079515fea..b821417da2b5531b1c43e45178e1853ecd029072 100644 (file)
@@ -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);
index 497a76e38be53795f89d514feff0fb17885174b0..c9bcb384eee7145e698067d46907924287656230 100644 (file)
@@ -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 : */
index 3765fe565385c13bb748931edd8ecc75b1cd4701..5ae61f316b3ed90498ed8f12d722abe2eb3cda32 100644 (file)
--- 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
index 9f0accc3d89792c14859c938a92584d57498f1ea..ae47a69c3ed9552ae17ce6afd1b39efd2ff136c7 100644 (file)
@@ -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
index e1c889e50f805734dcf8edd152133bc9dcf00393..ad815fb170db118e2f36b0e04615a0460df1997c 100644 (file)
@@ -792,6 +792,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    519,
 /**/
     518,
 /**/