Patch 8.1.0519
Problem:    Cannot save and restore the tag stack.
Solution:   Add gettagstack() and settagstack(). (Yegappan Lakshmanan,
            closes #3604)
Files:      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


*** ../vim-8.1.0518/runtime/doc/eval.txt        2018-11-10 17:33:23.091518784 
+0100
--- runtime/doc/eval.txt        2018-11-11 15:16:01.878631709 +0100
***************
*** 2198,2203 ****
--- 2206,2212 ----
                                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
***************
*** 2371,2376 ****
--- 2379,2386 ----
  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}])
***************
*** 4959,4964 ****
--- 4971,5007 ----
                        :let list_is_on = gettabwinvar(1, 2, '&list')
                        :echo "myvar = " . gettabwinvar(3, 1, 'myvar')
  <
+               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.
  
***************
*** 7522,7527 ****
--- 7566,7602 ----
                        :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: >
*** ../vim-8.1.0518/runtime/doc/tagsrch.txt     2018-05-17 13:42:03.000000000 
+0200
--- runtime/doc/tagsrch.txt     2018-11-10 19:23:26.025972138 +0100
***************
*** 179,184 ****
--- 179,187 ----
     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.
*** ../vim-8.1.0518/runtime/doc/usr_41.txt      2018-05-22 20:35:13.550009275 
+0200
--- runtime/doc/usr_41.txt      2018-11-10 19:23:26.025972138 +0100
***************
*** 809,814 ****
--- 809,815 ----
        getwininfo()            get a list with window information
        getchangelist()         get a list of change list entries
        getjumplist()           get a list of jump list entries
+       swapinfo()              information about a swap file
  
  Command line:                                 *command-line-functions*
        getcmdline()            get the current command line
***************
*** 1027,1032 ****
--- 1028,1035 ----
  
        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
*** ../vim-8.1.0518/src/alloc.h 2018-05-01 14:44:41.000000000 +0200
--- src/alloc.h 2018-11-10 19:23:26.025972138 +0100
***************
*** 18,22 ****
--- 18,25 ----
        aid_qf_module,
        aid_qf_errmsg,
        aid_qf_pattern,
+       aid_tagstack_items,
+       aid_tagstack_from,
+       aid_tagstack_details,
        aid_last
  } alloc_id_T;
*** ../vim-8.1.0518/src/dict.c  2018-07-08 17:18:58.416462371 +0200
--- src/dict.c  2018-11-10 19:23:26.025972138 +0100
***************
*** 47,52 ****
--- 47,65 ----
      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)
  {
*** ../vim-8.1.0518/src/evalfunc.c      2018-10-25 13:11:13.111143297 +0200
--- src/evalfunc.c      2018-11-10 19:23:26.029972104 +0100
***************
*** 201,206 ****
--- 201,207 ----
  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,366 ****
--- 362,368 ----
  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,671 ****
--- 668,674 ----
      {"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,833 ****
--- 831,837 ----
      {"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,5662 ****
--- 5661,5687 ----
  }
  
  /*
+  * "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,11124 ****
--- 11144,11205 ----
  }
  
  /*
+  * "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
*** ../vim-8.1.0518/src/list.c  2018-07-25 19:49:41.895812670 +0200
--- src/list.c  2018-11-10 19:23:26.029972104 +0100
***************
*** 86,91 ****
--- 86,104 ----
  }
  
  /*
+  * 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.
   */
*** ../vim-8.1.0518/src/misc2.c 2018-10-20 20:53:58.147284793 +0200
--- src/misc2.c 2018-11-10 19:23:26.029972104 +0100
***************
*** 835,841 ****
  #endif /* MEM_PROFILE */
  
  #ifdef FEAT_EVAL
!     static int
  alloc_does_fail(long_u size)
  {
      if (alloc_fail_countdown == 0)
--- 835,841 ----
  #endif /* MEM_PROFILE */
  
  #ifdef FEAT_EVAL
!     int
  alloc_does_fail(long_u size)
  {
      if (alloc_fail_countdown == 0)
*** ../vim-8.1.0518/src/proto/dict.pro  2018-07-08 16:50:33.107216836 +0200
--- src/proto/dict.pro  2018-11-10 19:23:26.029972104 +0100
***************
*** 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);
*** ../vim-8.1.0518/src/proto/list.pro  2018-05-17 13:52:42.000000000 +0200
--- src/proto/list.pro  2018-11-10 19:23:26.029972104 +0100
***************
*** 3,8 ****
--- 3,9 ----
  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);
*** ../vim-8.1.0518/src/proto/misc2.pro 2018-05-17 13:52:46.000000000 +0200
--- src/proto/misc2.pro 2018-11-10 19:23:26.029972104 +0100
***************
*** 21,26 ****
--- 21,27 ----
  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);
*** ../vim-8.1.0518/src/proto/tag.pro   2018-05-17 13:52:52.000000000 +0200
--- src/proto/tag.pro   2018-11-10 19:23:26.029972104 +0100
***************
*** 9,12 ****
--- 9,14 ----
  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 : */
*** ../vim-8.1.0518/src/tag.c   2018-09-30 21:43:17.207693209 +0200
--- src/tag.c   2018-11-11 15:03:52.375828252 +0100
***************
*** 4016,4019 ****
--- 4016,4219 ----
      }
      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
*** ../vim-8.1.0518/src/testdir/test_tagjump.vim        2018-02-09 
18:30:21.000000000 +0100
--- src/testdir/test_tagjump.vim        2018-11-11 15:09:11.153557467 +0100
***************
*** 257,260 ****
--- 257,369 ----
    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
*** ../vim-8.1.0518/src/version.c       2018-11-10 20:47:43.394262832 +0100
--- src/version.c       2018-11-11 15:18:47.861449349 +0100
***************
*** 794,795 ****
--- 794,797 ----
  {   /* Add new patch number below this line */
+ /**/
+     519,
  /**/

-- 
Q: How does a UNIX Guru pick up a girl?
A: look; grep; which; eval; nice; uname; talk; date;

 /// Bram Moolenaar -- b...@moolenaar.net -- http://www.Moolenaar.net   \\\
///        sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\
\\\  an exciting new programming language -- http://www.Zimbu.org        ///
 \\\            help me help AIDS victims -- http://ICCF-Holland.org    ///

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to vim_dev+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Raspunde prin e-mail lui