Patch 8.1.1682
Problem:    Placing a larger number of signs is slow.
Solution:   Add functions for dealing with a list of signs. (Yegappan
            Lakshmanan, closes #4636)
Files:      runtime/doc/eval.txt, runtime/doc/usr_41.txt, src/evalfunc.c,
            src/proto/sign.pro, src/sign.c, src/testdir/test_signs.vim


*** ../vim-8.1.1681/runtime/doc/eval.txt        2019-07-07 18:27:52.369277108 
+0200
--- runtime/doc/eval.txt        2019-07-13 21:14:51.013577260 +0200
***************
*** 2659,2664 ****
--- 2660,2666 ----
                                        command argument
  shiftwidth([{col}])           Number  effective value of 'shiftwidth'
  sign_define({name} [, {dict}])        Number  define or update a sign
+ sign_define({list})           List    define or update a list of signs
  sign_getdefined([{name}])     List    get a list of defined signs
  sign_getplaced([{expr} [, {dict}]])
                                List    get a list of placed signs
***************
*** 2666,2674 ****
--- 2668,2679 ----
                                Number  jump to a sign
  sign_place({id}, {group}, {name}, {expr} [, {dict}])
                                Number  place a sign
+ sign_placelist({list})                List    place a list of signs
  sign_undefine([{name}])               Number  undefine a sign
+ sign_undefine({list})         List    undefine a list of signs
  sign_unplace({group} [, {dict}])
                                Number  unplace a sign
+ sign_unplacelist({list})      List    unplace a list of signs
  simplify({filename})          String  simplify filename as much as possible
  sin({expr})                   Float   sine of {expr}
  sinh({expr})                  Float   hyperbolic sine of {expr}
***************
*** 8623,8628 ****
--- 8630,8636 ----
                no {col} argument is given, column 1 will be assumed.
  
  sign_define({name} [, {dict}])                                *sign_define()*
+ sign_define({list})
                Define a new sign named {name} or modify the attributes of an
                existing sign.  This is similar to the |:sign-define| command.
  
***************
*** 8642,8652 ****
                If the sign named {name} already exists, then the attributes
                of the sign are updated.
  
!               Returns 0 on success and -1 on failure.
! 
!               Examples: >
!                       call sign_define("mySign", {"text" : "=>", "texthl" :
!                                       \ "Error", "linehl" : "Search"})
  <
  sign_getdefined([{name}])                             *sign_getdefined()*
                Get a list of defined signs and their attributes.
--- 8650,8674 ----
                If the sign named {name} already exists, then the attributes
                of the sign are updated.
  
!               The one argument {list} can be used to define a list of signs.
!               Each list item is a dictionary with the above items in {dict}
!               and a 'name' item for the sign name.
! 
!               Returns 0 on success and -1 on failure.  When the one argument
!               {list} is used, then returns a List of values one for each
!               defined sign.
! 
!               Examples: >
!                       call sign_define("mySign", {
!                               \ "text" : "=>",
!                               \ "texthl" : "Error",
!                               \ "linehl" : "Search"})
!                       call sign_define([
!                               \ {'name' : 'sign1',
!                               \  'text' : '=>'},
!                               \ {'name' : 'sign2',
!                               \  'text' : '!!'}
!                               \ ])
  <
  sign_getdefined([{name}])                             *sign_getdefined()*
                Get a list of defined signs and their attributes.
***************
*** 8753,8761 ****
  <
                                                        *sign_place()*
  sign_place({id}, {group}, {name}, {expr} [, {dict}])
!               Place the sign defined as {name} at line {lnum} in file {expr}
!               and assign {id} and {group} to sign.  This is similar to the
!               |:sign-place| command.
  
                If the sign identifier {id} is zero, then a new identifier is
                allocated.  Otherwise the specified number is used. {group} is
--- 8775,8783 ----
  <
                                                        *sign_place()*
  sign_place({id}, {group}, {name}, {expr} [, {dict}])
!               Place the sign defined as {name} at line {lnum} in file or
!               buffer {expr} and assign {id} and {group} to sign.  This is
!               similar to the |:sign-place| command.
  
                If the sign identifier {id} is zero, then a new identifier is
                allocated.  Otherwise the specified number is used. {group} is
***************
*** 8769,8777 ****
                values, see |bufname()|.
  
                The optional {dict} argument supports the following entries:
!                       lnum            line number in the buffer {expr} where
!                                       the sign is to be placed. For the
!                                       accepted values, see |line()|.
                        priority        priority of the sign. See
                                        |sign-priority| for more information.
  
--- 8791,8799 ----
                values, see |bufname()|.
  
                The optional {dict} argument supports the following entries:
!                       lnum            line number in the file or buffer
!                                       {expr} where the sign is to be placed.
!                                       For the accepted values, see |line()|.
                        priority        priority of the sign. See
                                        |sign-priority| for more information.
  
***************
*** 8800,8816 ****
                        call sign_place(10, 'g3', 'sign4', 'json.c',
                                        \ {'lnum' : 40, 'priority' : 90})
  <
  sign_undefine([{name}])                                       
*sign_undefine()*
                Deletes a previously defined sign {name}. This is similar to
                the |:sign-undefine| command. If {name} is not supplied, then
                deletes all the defined signs.
  
!               Returns 0 on success and -1 on failure.
  
                Examples: >
                        " Delete a sign named mySign
                        call sign_undefine("mySign")
  
                        " Delete all the signs
                        call sign_undefine()
  <
--- 8822,8906 ----
                        call sign_place(10, 'g3', 'sign4', 'json.c',
                                        \ {'lnum' : 40, 'priority' : 90})
  <
+                                                       *sign_placelist()*
+ sign_placelist({list})
+               Place one or more signs.  This is similar to the
+               |sign_place()| function.  The {list} argument specifies the
+               List of signs to place. Each list item is a dict with the
+               following sign attributes:
+                   buffer      buffer name or number. For the accepted
+                               values, see |bufname()|.
+                   group       sign group. {group} functions as a namespace
+                               for {id}, thus two groups can use the same
+                               IDs. If not specified or set to an empty
+                               string, then the global group is used.   See
+                               |sign-group| for more information.
+                   id          sign identifier. If not specified or zero,
+                               then a new unique identifier is allocated.
+                               Otherwise the specified number is used. See
+                               |sign-identifier| for more information.
+                   lnum        line number in the buffer {expr} where the
+                               sign is to be placed. For the accepted values,
+                               see |line()|.
+                   name        name of the sign to place. See |sign_define()|
+                               for more information.
+                   priority    priority of the sign. When multiple signs are
+                               placed on a line, the sign with the highest
+                               priority is used. If not specified, the
+                               default value of 10 is used. See
+                               |sign-priority| for more information.
+ 
+               If {id} refers to an existing sign, then the existing sign is
+               modified to use the specified {name} and/or {priority}.
+ 
+               Returns a List of sign identifiers. If failed to place a
+               sign, the corresponding list item is set to -1.
+ 
+               Examples: >
+                       " Place sign s1 with id 5 at line 20 and id 10 at line
+                       " 30 in buffer a.c
+                       let [n1, n2] = sign_place([
+                               \ {'id' : 5,
+                               \  'name' : 's1',
+                               \  'buffer' : 'a.c',
+                               \  'lnum' : 20},
+                               \ {'id' : 10,
+                               \  'name' : 's1',
+                               \  'buffer' : 'a.c',
+                               \  'lnum' : 30}
+                               \ ])
+ 
+                       " Place sign s1 in buffer a.c at line 40 and 50
+                       " with auto-generated identifiers
+                       let [n1, n2] = sign_place([
+                               \ {'name' : 's1',
+                               \  'buffer' : 'a.c',
+                               \  'lnum' : 40},
+                               \ {'name' : 's1',
+                               \  'buffer' : 'a.c',
+                               \  'lnum' : 50}
+                               \ ])
+ <
  sign_undefine([{name}])                                       
*sign_undefine()*
+ sign_undefine({list})
                Deletes a previously defined sign {name}. This is similar to
                the |:sign-undefine| command. If {name} is not supplied, then
                deletes all the defined signs.
  
!               The one argument {list} can be used to undefine a list of
!               signs. Each list item is the name of a sign.
! 
!               Returns 0 on success and -1 on failure.  For the one argument
!               {list} call, returns a list of values one for each undefined
!               sign.
  
                Examples: >
                        " Delete a sign named mySign
                        call sign_undefine("mySign")
  
+                       " Delete signs 'sign1' and 'sign2'
+                       call sign_undefine(["sign1", "sign2"])
+ 
                        " Delete all the signs
                        call sign_undefine()
  <
***************
*** 8856,8861 ****
--- 8946,8977 ----
                        " Remove all the placed signs from all the buffers
                        call sign_unplace('*')
  <
+ sign_unplacelist({list})                              *sign_unplacelist()*
+               Remove previously placed signs from one or more buffers.  This
+               is similar to the |sign_unplace()| function.
+ 
+               The {list} argument specifies the List of signs to remove.
+               Each list item is a dict with the following sign attributes:
+                   buffer      buffer name or number. For the accepted
+                               values, see |bufname()|. If not specified,
+                               then the specified sign is removed from all
+                               the buffers.
+                   group       sign group name. If not specified or set to an
+                               empty string, then the global sign group is
+                               used. If set to '*', then all the groups
+                               including the global group are used.
+                   id          sign identifier. If not specified, then all
+                               the signs in the specified group are removed.
+ 
+               Returns a List where an entry is set to 0 if the corresponding
+               sign was successfully removed or -1 on failure.
+ 
+               Example: >
+                       " Remove sign with id 10 from buffer a.vim and sign
+                       " with id 20 from buffer b.vim
+                       call sign_unplace([{'id' : 10, 'buffer' : "a.vim"},
+                                       \ {'id' : 20, 'buffer' : 'b.vim'}])
+ <
  simplify({filename})                                  *simplify()*
                Simplify the file name as much as possible without changing
                the meaning.  Shortcuts (on MS-Windows) or symbolic links (on
*** ../vim-8.1.1681/runtime/doc/usr_41.txt      2019-07-07 18:27:52.369277108 
+0200
--- runtime/doc/usr_41.txt      2019-07-13 20:45:29.466278471 +0200
***************
*** 1015,1022 ****
--- 1015,1024 ----
        sign_getplaced()        get a list of placed signs
        sign_jump()             jump to a sign
        sign_place()            place a sign
+       sign_placelist()        place a list of signs
        sign_undefine()         undefine a sign
        sign_unplace()          unplace a sign
+       sign_unplacelist()      unplace a list of signs
  
  Terminal window:                              *terminal-functions*
        term_start()            open a terminal window and run a job
*** ../vim-8.1.1681/src/evalfunc.c      2019-07-12 21:07:49.529756954 +0200
--- src/evalfunc.c      2019-07-13 20:45:29.466278471 +0200
***************
*** 888,895 ****
--- 888,897 ----
      {"sign_getplaced",        0, 2, f_sign_getplaced},
      {"sign_jump",     3, 3, f_sign_jump},
      {"sign_place",    4, 5, f_sign_place},
+     {"sign_placelist",        1, 1, f_sign_placelist},
      {"sign_undefine", 0, 1, f_sign_undefine},
      {"sign_unplace",  1, 2, f_sign_unplace},
+     {"sign_unplacelist",      1, 2, f_sign_unplacelist},
  #endif
      {"simplify",      1, 1, f_simplify},
  #ifdef FEAT_FLOAT
*** ../vim-8.1.1681/src/proto/sign.pro  2019-07-04 18:28:31.605084894 +0200
--- src/proto/sign.pro  2019-07-13 20:45:29.466278471 +0200
***************
*** 22,27 ****
--- 22,29 ----
  void f_sign_getplaced(typval_T *argvars, typval_T *rettv);
  void f_sign_jump(typval_T *argvars, typval_T *rettv);
  void f_sign_place(typval_T *argvars, typval_T *rettv);
+ void f_sign_placelist(typval_T *argvars, typval_T *rettv);
  void f_sign_undefine(typval_T *argvars, typval_T *rettv);
  void f_sign_unplace(typval_T *argvars, typval_T *rettv);
+ void f_sign_unplacelist(typval_T *argvars, typval_T *rettv);
  /* vim: set ft=c : */
*** ../vim-8.1.1681/src/sign.c  2019-07-04 18:28:31.609084867 +0200
--- src/sign.c  2019-07-13 20:45:29.466278471 +0200
***************
*** 442,448 ****
      buf_T     *buf,           // buffer to store sign in
      int               markId,         // sign ID
      char_u    *group,         // sign group
!     int               typenr)         // typenr of sign we are adding
  {
      signlist_T        *sign;          // a sign in the signlist
  
--- 442,449 ----
      buf_T     *buf,           // buffer to store sign in
      int               markId,         // sign ID
      char_u    *group,         // sign group
!     int               typenr,         // typenr of sign we are adding
!     int               prio)           // sign priority
  {
      signlist_T        *sign;          // a sign in the signlist
  
***************
*** 451,456 ****
--- 452,459 ----
        if (sign->id == markId && sign_in_group(sign, group))
        {
            sign->typenr = typenr;
+           sign->priority = prio;
+           sign_sort_by_prio_on_line(buf, sign);
            return sign->lnum;
        }
      }
***************
*** 1104,1111 ****
        // place a sign
        buf_addsign(buf, *sign_id, sign_group, prio, lnum, sp->sn_typenr);
      else
!       // ":sign place {id} file={fname}": change sign type
!       lnum = buf_change_sign_type(buf, *sign_id, sign_group, sp->sn_typenr);
      if (lnum > 0)
      {
        redraw_buf_line_later(buf, lnum);
--- 1107,1115 ----
        // place a sign
        buf_addsign(buf, *sign_id, sign_group, prio, lnum, sp->sn_typenr);
      else
!       // ":sign place {id} file={fname}": change sign type and/or priority
!       lnum = buf_change_sign_type(buf, *sign_id, sign_group, sp->sn_typenr,
!                                                               prio);
      if (lnum > 0)
      {
        redraw_buf_line_later(buf, lnum);
***************
*** 2096,2146 ****
  # endif
  
  /*
!  * "sign_define()" function
   */
!     void
! f_sign_define(typval_T *argvars, typval_T *rettv)
  {
!     char_u    *name;
!     dict_T    *dict;
      char_u    *icon = NULL;
      char_u    *linehl = NULL;
      char_u    *text = NULL;
      char_u    *texthl = NULL;
  
!     rettv->vval.v_number = -1;
! 
!     name = tv_get_string_chk(&argvars[0]);
!     if (name == NULL)
!       return;
! 
!     if (argvars[1].v_type != VAR_UNKNOWN)
      {
!       if (argvars[1].v_type != VAR_DICT)
!       {
!           emsg(_(e_dictreq));
!           return;
!       }
! 
!       // sign attributes
!       dict = argvars[1].vval.v_dict;
!       if (dict_find(dict, (char_u *)"icon", -1) != NULL)
!           icon = dict_get_string(dict, (char_u *)"icon", TRUE);
!       if (dict_find(dict, (char_u *)"linehl", -1) != NULL)
!           linehl = dict_get_string(dict, (char_u *)"linehl", TRUE);
!       if (dict_find(dict, (char_u *)"text", -1) != NULL)
!           text = dict_get_string(dict, (char_u *)"text", TRUE);
!       if (dict_find(dict, (char_u *)"texthl", -1) != NULL)
!           texthl = dict_get_string(dict, (char_u *)"texthl", TRUE);
      }
  
      if (sign_define_by_name(name, icon, linehl, text, texthl) == OK)
!       rettv->vval.v_number = 0;
  
      vim_free(icon);
      vim_free(linehl);
      vim_free(text);
      vim_free(texthl);
  }
  
  /*
--- 2100,2203 ----
  # endif
  
  /*
!  * Define a sign using the attributes in 'dict'. Returns 0 on success and -1 
on
!  * failure.
   */
!     static int
! sign_define_from_dict(char_u *name_arg, dict_T *dict)
  {
!     char_u    *name = NULL;
      char_u    *icon = NULL;
      char_u    *linehl = NULL;
      char_u    *text = NULL;
      char_u    *texthl = NULL;
+     int               retval = -1;
  
!     if (name_arg == NULL)
      {
!       if (dict == NULL)
!           return -1;
!       name = dict_get_string(dict, (char_u *)"name", TRUE);
!     }
!     else
!       name = vim_strsave(name_arg);
!     if (name == NULL || name[0] == NUL)
!       goto cleanup;
!     if (dict != NULL)
!     {
!       icon = dict_get_string(dict, (char_u *)"icon", TRUE);
!       linehl = dict_get_string(dict, (char_u *)"linehl", TRUE);
!       text = dict_get_string(dict, (char_u *)"text", TRUE);
!       texthl = dict_get_string(dict, (char_u *)"texthl", TRUE);
      }
  
      if (sign_define_by_name(name, icon, linehl, text, texthl) == OK)
!       retval = 0;
  
+ cleanup:
+     vim_free(name);
      vim_free(icon);
      vim_free(linehl);
      vim_free(text);
      vim_free(texthl);
+ 
+     return retval;
+ }
+ 
+ /*
+  * Define multiple signs using attributes from list 'l' and store the return
+  * values in 'retlist'.
+  */
+     static void
+ sign_define_multiple(list_T *l, list_T *retlist)
+ {
+     listitem_T        *li;
+     int               retval;
+ 
+     for (li = l->lv_first; li != NULL; li = li->li_next)
+     {
+       retval = -1;
+       if (li->li_tv.v_type == VAR_DICT)
+           retval = sign_define_from_dict(NULL, li->li_tv.vval.v_dict);
+       else
+           emsg(_(e_dictreq));
+       list_append_number(retlist, retval);
+     }
+ }
+ 
+ /*
+  * "sign_define()" function
+  */
+     void
+ f_sign_define(typval_T *argvars, typval_T *rettv)
+ {
+     char_u    *name;
+ 
+     if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_UNKNOWN)
+     {
+       // Define multiple signs
+       if (rettv_list_alloc(rettv) != OK)
+           return;
+ 
+       sign_define_multiple(argvars[0].vval.v_list, rettv->vval.v_list);
+       return;
+     }
+ 
+     // Define a single sign
+     rettv->vval.v_number = -1;
+ 
+     name = tv_get_string_chk(&argvars[0]);
+     if (name == NULL)
+       return;
+ 
+     if (argvars[1].v_type != VAR_UNKNOWN && argvars[1].v_type != VAR_DICT)
+     {
+       emsg(_(e_dictreq));
+       return;
+     }
+ 
+     rettv->vval.v_number = sign_define_from_dict(name,
+           argvars[1].v_type == VAR_DICT ? argvars[1].vval.v_dict : NULL);
  }
  
  /*
***************
*** 2269,2355 ****
  }
  
  /*
!  * "sign_place()" function
   */
!     void
! f_sign_place(typval_T *argvars, typval_T *rettv)
  {
!     int               sign_id;
      char_u    *group = NULL;
!     char_u    *sign_name;
!     buf_T     *buf;
!     dict_T    *dict;
      dictitem_T        *di;
      linenr_T  lnum = 0;
      int               prio = SIGN_DEF_PRIO;
      int               notanum = FALSE;
  
!     rettv->vval.v_number = -1;
! 
!     // Sign identifier
!     sign_id = (int)tv_get_number_chk(&argvars[0], &notanum);
!     if (notanum)
!       return;
!     if (sign_id < 0)
      {
!       emsg(_(e_invarg));
!       return;
      }
  
!     // Sign group
!     group = tv_get_string_chk(&argvars[1]);
!     if (group == NULL)
!       return;
!     if (group[0] == '\0')
!       group = NULL;                   // global sign group
      else
      {
!       group = vim_strsave(group);
        if (group == NULL)
!           return;
      }
  
!     // Sign name
!     sign_name = tv_get_string_chk(&argvars[2]);
      if (sign_name == NULL)
        goto cleanup;
  
!     // Buffer to place the sign
!     buf = get_buf_arg(&argvars[3]);
      if (buf == NULL)
        goto cleanup;
  
!     if (argvars[4].v_type != VAR_UNKNOWN)
      {
!       if (argvars[4].v_type != VAR_DICT ||
!                               ((dict = argvars[4].vval.v_dict) == NULL))
!       {
!           emsg(_(e_dictreq));
            goto cleanup;
!       }
  
!       // Line number where the sign is to be placed
!       if ((di = dict_find(dict, (char_u *)"lnum", -1)) != NULL)
!       {
!           (void)tv_get_number_chk(&di->di_tv, &notanum);
!           if (notanum)
!               goto cleanup;
!           lnum = tv_get_lnum(&di->di_tv);
!       }
!       if ((di = dict_find(dict, (char_u *)"priority", -1)) != NULL)
!       {
!           // Sign priority
!           prio = (int)tv_get_number_chk(&di->di_tv, &notanum);
!           if (notanum)
!               goto cleanup;
!       }
      }
  
      if (sign_place(&sign_id, group, sign_name, buf, lnum, prio) == OK)
!       rettv->vval.v_number = sign_id;
  
  cleanup:
      vim_free(group);
  }
  
  /*
--- 2326,2521 ----
  }
  
  /*
!  * Place a new sign using the values specified in dict 'dict'. Returns the 
sign
!  * identifier if successfully placed, otherwise returns 0.
   */
!     static int
! sign_place_from_dict(
!       typval_T        *id_tv,
!       typval_T        *group_tv,
!       typval_T        *name_tv,
!       typval_T        *buf_tv,
!       dict_T          *dict)
  {
!     int               sign_id = 0;
      char_u    *group = NULL;
!     char_u    *sign_name = NULL;
!     buf_T     *buf = NULL;
      dictitem_T        *di;
      linenr_T  lnum = 0;
      int               prio = SIGN_DEF_PRIO;
      int               notanum = FALSE;
+     int               ret_sign_id = -1;
  
!     // sign identifier
!     if (id_tv == NULL)
      {
!       di = dict_find(dict, (char_u *)"id", -1);
!       if (di != NULL)
!           id_tv = &di->di_tv;
!     }
!     if (id_tv == NULL)
!       sign_id = 0;
!     else
!     {
!       sign_id = tv_get_number_chk(id_tv, &notanum);
!       if (notanum)
!           return -1;
!       if (sign_id < 0)
!       {
!           emsg(_(e_invarg));
!           return -1;
!       }
      }
  
!     // sign group
!     if (group_tv == NULL)
!     {
!       di = dict_find(dict, (char_u *)"group", -1);
!       if (di != NULL)
!           group_tv = &di->di_tv;
!     }
!     if (group_tv == NULL)
!       group = NULL;                           // global group
      else
      {
!       group = tv_get_string_chk(group_tv);
        if (group == NULL)
!           goto cleanup;
!       if (group[0] == '\0')                   // global sign group
!           group = NULL;
!       else
!       {
!           group = vim_strsave(group);
!           if (group == NULL)
!               return -1;
!       }
      }
  
!     // sign name
!     if (name_tv == NULL)
!     {
!       di = dict_find(dict, (char_u *)"name", -1);
!       if (di != NULL)
!           name_tv = &di->di_tv;
!     }
!     if (name_tv == NULL)
!       goto cleanup;
!     sign_name = tv_get_string_chk(name_tv);
      if (sign_name == NULL)
        goto cleanup;
  
!     // buffer to place the sign
!     if (buf_tv == NULL)
!     {
!       di = dict_find(dict, (char_u *)"buffer", -1);
!       if (di != NULL)
!           buf_tv = &di->di_tv;
!     }
!     if (buf_tv == NULL)
!       goto cleanup;
!     buf = get_buf_arg(buf_tv);
      if (buf == NULL)
        goto cleanup;
  
!     // line number of the sign
!     di = dict_find(dict, (char_u *)"lnum", -1);
!     if (di != NULL)
      {
!       lnum = (int)tv_get_number_chk(&di->di_tv, &notanum);
!       if (notanum)
            goto cleanup;
!     }
  
!     // sign priority
!     di = dict_find(dict, (char_u *)"priority", -1);
!     if (di != NULL)
!     {
!       prio = (int)tv_get_number_chk(&di->di_tv, &notanum);
!       if (notanum)
!           goto cleanup;
      }
  
      if (sign_place(&sign_id, group, sign_name, buf, lnum, prio) == OK)
!       ret_sign_id = sign_id;
  
  cleanup:
      vim_free(group);
+ 
+     return ret_sign_id;
+ }
+ 
+ /*
+  * "sign_place()" function
+  */
+     void
+ f_sign_place(typval_T *argvars, typval_T *rettv)
+ {
+     dict_T    *dict = NULL;
+ 
+     rettv->vval.v_number = -1;
+ 
+     if (argvars[4].v_type != VAR_UNKNOWN
+           && (argvars[4].v_type != VAR_DICT
+               || ((dict = argvars[4].vval.v_dict) == NULL)))
+     {
+       emsg(_(e_dictreq));
+       return;
+     }
+ 
+     rettv->vval.v_number = sign_place_from_dict(&argvars[0], &argvars[1],
+                                       &argvars[2], &argvars[3], dict);
+ }
+ 
+ /*
+  * "sign_placelist()" function.  Place multiple signs.
+  */
+     void
+ f_sign_placelist(typval_T *argvars, typval_T *rettv)
+ {
+     listitem_T        *li;
+     int               sign_id;
+ 
+     if (rettv_list_alloc(rettv) != OK)
+       return;
+ 
+     if (argvars[0].v_type != VAR_LIST)
+     {
+       emsg(_(e_listreq));
+       return;
+     }
+ 
+     // Process the List of sign attributes
+     for (li = argvars[0].vval.v_list->lv_first; li != NULL; li = li->li_next)
+     {
+       sign_id = -1;
+       if (li->li_tv.v_type == VAR_DICT)
+           sign_id = sign_place_from_dict(NULL, NULL, NULL, NULL,
+                                               li->li_tv.vval.v_dict);
+       else
+           emsg(_(e_dictreq));
+       list_append_number(rettv->vval.v_list, sign_id);
+     }
+ }
+ 
+ /*
+  * Undefine multiple signs
+  */
+     static void
+ sign_undefine_multiple(list_T *l, list_T *retlist)
+ {
+     char_u    *name;
+     listitem_T        *li;
+     int               retval;
+ 
+     for (li = l->lv_first; li != NULL; li = li->li_next)
+     {
+       retval = -1;
+       name = tv_get_string_chk(&li->li_tv);
+       if (name != NULL && (sign_undefine_by_name(name) == OK))
+           retval = 0;
+       list_append_number(retlist, retval);
+     }
  }
  
  /*
***************
*** 2360,2365 ****
--- 2526,2541 ----
  {
      char_u *name;
  
+     if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_UNKNOWN)
+     {
+       // Undefine multiple signs
+       if (rettv_list_alloc(rettv) != OK)
+           return;
+ 
+       sign_undefine_multiple(argvars[0].vval.v_list, rettv->vval.v_list);
+       return;
+     }
+ 
      rettv->vval.v_number = -1;
  
      if (argvars[0].v_type == VAR_UNKNOWN)
***************
*** 2381,2424 ****
  }
  
  /*
!  * "sign_unplace()" function
   */
!     void
! f_sign_unplace(typval_T *argvars, typval_T *rettv)
  {
-     dict_T    *dict;
      dictitem_T        *di;
      int               sign_id = 0;
      buf_T     *buf = NULL;
      char_u    *group = NULL;
  
!     rettv->vval.v_number = -1;
! 
!     if (argvars[0].v_type != VAR_STRING)
!     {
!       emsg(_(e_invarg));
!       return;
!     }
! 
!     group = tv_get_string(&argvars[0]);
!     if (group[0] == '\0')
!       group = NULL;                   // global sign group
      else
      {
!       group = vim_strsave(group);
!       if (group == NULL)
!           return;
!     }
! 
!     if (argvars[1].v_type != VAR_UNKNOWN)
!     {
!       if (argvars[1].v_type != VAR_DICT)
        {
!           emsg(_(e_dictreq));
!           goto cleanup;
        }
!       dict = argvars[1].vval.v_dict;
  
        if ((di = dict_find(dict, (char_u *)"buffer", -1)) != NULL)
        {
            buf = get_buf_arg(&di->di_tv);
--- 2557,2593 ----
  }
  
  /*
!  * Unplace the sign with attributes specified in 'dict'. Returns 0 on success
!  * and -1 on failure.
   */
!     static int
! sign_unplace_from_dict(typval_T *group_tv, dict_T *dict)
  {
      dictitem_T        *di;
      int               sign_id = 0;
      buf_T     *buf = NULL;
      char_u    *group = NULL;
+     int               retval = -1;
  
!     // sign group
!     if (group_tv != NULL)
!       group = tv_get_string(group_tv);
      else
+       group = dict_get_string(dict, (char_u *)"group", FALSE);
+     if (group != NULL)
      {
!       if (group[0] == '\0')                   // global sign group
!           group = NULL;
!       else
        {
!           group = vim_strsave(group);
!           if (group == NULL)
!               return -1;
        }
!     }
  
+     if (dict != NULL)
+     {
        if ((di = dict_find(dict, (char_u *)"buffer", -1)) != NULL)
        {
            buf = get_buf_arg(&di->di_tv);
***************
*** 2426,2449 ****
                goto cleanup;
        }
        if (dict_find(dict, (char_u *)"id", -1) != NULL)
            sign_id = dict_get_number(dict, (char_u *)"id");
      }
  
      if (buf == NULL)
      {
        // Delete the sign in all the buffers
        FOR_ALL_BUFFERS(buf)
!           if (sign_unplace(sign_id, group, buf, 0) == OK)
!               rettv->vval.v_number = 0;
!     }
!     else
!     {
!       if (sign_unplace(sign_id, group, buf, 0) == OK)
!           rettv->vval.v_number = 0;
      }
  
  cleanup:
      vim_free(group);
  }
  
  #endif /* FEAT_SIGNS */
--- 2595,2683 ----
                goto cleanup;
        }
        if (dict_find(dict, (char_u *)"id", -1) != NULL)
+       {
            sign_id = dict_get_number(dict, (char_u *)"id");
+           if (sign_id <= 0)
+           {
+               emsg(_(e_invarg));
+               goto cleanup;
+           }
+       }
      }
  
      if (buf == NULL)
      {
        // Delete the sign in all the buffers
+       retval = 0;
        FOR_ALL_BUFFERS(buf)
!           if (sign_unplace(sign_id, group, buf, 0) != OK)
!               retval = -1;
      }
+     else if (sign_unplace(sign_id, group, buf, 0) == OK)
+       retval = 0;
  
  cleanup:
      vim_free(group);
+ 
+     return retval;
+ }
+ 
+ /*
+  * "sign_unplace()" function
+  */
+     void
+ f_sign_unplace(typval_T *argvars, typval_T *rettv)
+ {
+     dict_T    *dict = NULL;
+ 
+     rettv->vval.v_number = -1;
+ 
+     if (argvars[0].v_type != VAR_STRING)
+     {
+       emsg(_(e_invarg));
+       return;
+     }
+ 
+     if (argvars[1].v_type != VAR_UNKNOWN)
+     {
+       if (argvars[1].v_type != VAR_DICT)
+       {
+           emsg(_(e_dictreq));
+           return;
+       }
+       dict = argvars[1].vval.v_dict;
+     }
+ 
+     rettv->vval.v_number = sign_unplace_from_dict(&argvars[0], dict);
+ }
+ 
+ /*
+  * "sign_unplacelist()" function
+  */
+     void
+ f_sign_unplacelist(typval_T *argvars, typval_T *rettv)
+ {
+     listitem_T        *li;
+     int               retval;
+ 
+     if (rettv_list_alloc(rettv) != OK)
+       return;
+ 
+     if (argvars[0].v_type != VAR_LIST)
+     {
+       emsg(_(e_listreq));
+       return;
+     }
+ 
+     for (li = argvars[0].vval.v_list->lv_first; li != NULL; li = li->li_next)
+     {
+       retval = -1;
+       if (li->li_tv.v_type == VAR_DICT)
+           retval = sign_unplace_from_dict(NULL, li->li_tv.vval.v_dict);
+       else
+           emsg(_(e_dictreq));
+       list_append_number(rettv->vval.v_list, retval);
+     }
  }
  
  #endif /* FEAT_SIGNS */
*** ../vim-8.1.1681/src/testdir/test_signs.vim  2019-07-04 11:59:25.420462567 
+0200
--- src/testdir/test_signs.vim  2019-07-13 20:45:29.466278471 +0200
***************
*** 412,418 ****
    " Tests for invalid arguments to sign_define()
    call assert_fails('call sign_define("sign4", {"text" : "===>"})', 'E239:')
    call assert_fails('call sign_define("sign5", {"text" : ""})', 'E239:')
!   call assert_fails('call sign_define([])', 'E730:')
    call assert_fails('call sign_define("sign6", [])', 'E715:')
  
    " Tests for sign_getdefined()
--- 412,418 ----
    " Tests for invalid arguments to sign_define()
    call assert_fails('call sign_define("sign4", {"text" : "===>"})', 'E239:')
    call assert_fails('call sign_define("sign5", {"text" : ""})', 'E239:')
!   call assert_fails('call sign_define({})', 'E731:')
    call assert_fails('call sign_define("sign6", [])', 'E715:')
  
    " Tests for sign_getdefined()
***************
*** 441,447 ****
    call assert_fails('call sign_place([], "", "mySign", 1)', 'E745:')
    call assert_fails('call sign_place(5, "", "mySign", -1)', 'E158:')
    call assert_fails('call sign_place(-1, "", "sign1", "Xsign", [])',
!             \ 'E474:')
    call assert_fails('call sign_place(-1, "", "sign1", "Xsign",
              \ {"lnum" : 30})', 'E474:')
    call assert_fails('call sign_place(10, "", "xsign1x", "Xsign",
--- 441,447 ----
    call assert_fails('call sign_place([], "", "mySign", 1)', 'E745:')
    call assert_fails('call sign_place(5, "", "mySign", -1)', 'E158:')
    call assert_fails('call sign_place(-1, "", "sign1", "Xsign", [])',
!             \ 'E715:')
    call assert_fails('call sign_place(-1, "", "sign1", "Xsign",
              \ {"lnum" : 30})', 'E474:')
    call assert_fails('call sign_place(10, "", "xsign1x", "Xsign",
***************
*** 501,511 ****
              \ {'id' : 20, 'buffer' : 200})", 'E158:')
    call assert_fails("call sign_unplace('g1', 'mySign')", 'E715:')
  
    " Tests for sign_undefine()
    call assert_equal(0, sign_undefine("sign1"))
    call assert_equal([], sign_getdefined("sign1"))
    call assert_fails('call sign_undefine("none")', 'E155:')
!   call assert_fails('call sign_undefine([])', 'E730:')
  
    call delete("Xsign")
    call sign_unplace('*')
--- 501,521 ----
              \ {'id' : 20, 'buffer' : 200})", 'E158:')
    call assert_fails("call sign_unplace('g1', 'mySign')", 'E715:')
  
+   call sign_unplace('*')
+ 
+   " Test for modifying a placed sign
+   call assert_equal(15, sign_place(15, '', 'sign1', 'Xsign', {'lnum' : 20}))
+   call assert_equal(15, sign_place(15, '', 'sign2', 'Xsign'))
+   call assert_equal([{'bufnr' : bufnr(''), 'signs' :
+             \ [{'id' : 15, 'group' : '', 'lnum' : 20, 'name' : 'sign2',
+             \ 'priority' : 10}]}],
+             \ sign_getplaced())
+ 
    " Tests for sign_undefine()
    call assert_equal(0, sign_undefine("sign1"))
    call assert_equal([], sign_getdefined("sign1"))
    call assert_fails('call sign_undefine("none")', 'E155:')
!   call assert_fails('call sign_undefine({})', 'E731:')
  
    call delete("Xsign")
    call sign_unplace('*')
***************
*** 631,637 ****
    call assert_equal([], sign_getplaced(bnum, {'group' : '*'})[0].signs)
  
    " Error case
!   call assert_fails("call sign_unplace([])", 'E474:')
  
    " Place a sign in the global group and try to delete it using a group
    call assert_equal(5, sign_place(5, '', 'sign1', bnum, {'lnum' : 10}))
--- 641,647 ----
    call assert_equal([], sign_getplaced(bnum, {'group' : '*'})[0].signs)
  
    " Error case
!   call assert_fails("call sign_unplace({})", 'E474:')
  
    " Place a sign in the global group and try to delete it using a group
    call assert_equal(5, sign_place(5, '', 'sign1', bnum, {'lnum' : 10}))
***************
*** 1117,1124 ****
    call delete("Xsign2")
  endfunc
  
! " Tests for auto-generating the sign identifier
! func Test_sign_id_autogen()
    enew | only
    call sign_unplace('*')
    call sign_undefine()
--- 1127,1134 ----
    call delete("Xsign2")
  endfunc
  
! " Tests for auto-generating the sign identifier.
! func Test_aaa_sign_id_autogen()
    enew | only
    call sign_unplace('*')
    call sign_undefine()
***************
*** 1843,1845 ****
--- 1853,1965 ----
    set number&
    enew!  | close
  endfunc
+ 
+ " Test for managing multiple signs using the sign functions
+ func Test_sign_funcs_multi()
+   call writefile(repeat(["Sun is shining"], 30), "Xsign")
+   edit Xsign
+   let bnum = bufnr('')
+ 
+   " Define multiple signs at once
+   call assert_equal([0, 0, 0, 0], sign_define([
+             \ {'name' : 'sign1', 'text' : '=>', 'linehl' : 'Search',
+             \ 'texthl' : 'Search'},
+             \ {'name' : 'sign2', 'text' : '=>', 'linehl' : 'Search',
+             \ 'texthl' : 'Search'},
+             \ {'name' : 'sign3', 'text' : '=>', 'linehl' : 'Search',
+             \ 'texthl' : 'Search'},
+             \ {'name' : 'sign4', 'text' : '=>', 'linehl' : 'Search',
+             \ 'texthl' : 'Search'}]))
+ 
+   " Negative cases for sign_define()
+   call assert_equal([], sign_define([]))
+   call assert_equal([-1], sign_define([{}]))
+   call assert_fails('call sign_define([6])', 'E715:')
+   call assert_fails('call sign_define(["abc"])', 'E715:')
+   call assert_fails('call sign_define([[]])', 'E715:')
+ 
+   " Place multiple signs at once with specific sign identifier
+   let l = sign_placelist([{'id' : 1, 'group' : 'g1', 'name' : 'sign1',
+             \ 'buffer' : 'Xsign', 'lnum' : 11, 'priority' : 50},
+             \ {'id' : 2, 'group' : 'g2', 'name' : 'sign2',
+             \ 'buffer' : 'Xsign', 'lnum' : 11, 'priority' : 100},
+             \ {'id' : 3, 'group' : '', 'name' : 'sign3',
+             \ 'buffer' : 'Xsign', 'lnum' : 11}])
+   call assert_equal([1, 2, 3], l)
+   let s = sign_getplaced('Xsign', {'group' : '*'})
+   call assert_equal([
+             \ {'id' : 2, 'name' : 'sign2', 'lnum' : 11,
+             \ 'group' : 'g2', 'priority' : 100},
+             \ {'id' : 1, 'name' : 'sign1', 'lnum' : 11,
+             \ 'group' : 'g1', 'priority' : 50},
+             \ {'id' : 3, 'name' : 'sign3', 'lnum' : 11,
+             \ 'group' : '', 'priority' : 10}], s[0].signs)
+ 
+   call sign_unplace('*')
+ 
+   " Place multiple signs at once with auto-generated sign identifier
+   call assert_equal([1, 1, 5], sign_placelist([
+             \ {'group' : 'g1', 'name' : 'sign1',
+             \ 'buffer' : 'Xsign', 'lnum' : 11},
+             \ {'group' : 'g2', 'name' : 'sign2',
+             \ 'buffer' : 'Xsign', 'lnum' : 11},
+             \ {'group' : '', 'name' : 'sign3',
+             \ 'buffer' : 'Xsign', 'lnum' : 11}]))
+   let s = sign_getplaced('Xsign', {'group' : '*'})
+   call assert_equal([
+             \ {'id' : 5, 'name' : 'sign3', 'lnum' : 11,
+             \ 'group' : '', 'priority' : 10},
+             \ {'id' : 1, 'name' : 'sign2', 'lnum' : 11,
+             \ 'group' : 'g2', 'priority' : 10},
+             \ {'id' : 1, 'name' : 'sign1', 'lnum' : 11,
+             \ 'group' : 'g1', 'priority' : 10}], s[0].signs)
+ 
+   " Change an existing sign without specifying the group
+   call assert_equal([5], sign_placelist([
+             \ {'id' : 5, 'name' : 'sign1', 'buffer' : 'Xsign'}]))
+   let s = sign_getplaced('Xsign', {'id' : 5, 'group' : ''})
+   call assert_equal([{'id' : 5, 'name' : 'sign1', 'lnum' : 11,
+             \ 'group' : '', 'priority' : 10}], s[0].signs)
+ 
+   " Place sign without a sign name
+   call assert_equal([-1], sign_placelist([{'id' : 10, 'buffer' : 'Xsign',
+             \ 'lnum' : 12, 'group' : ''}]))
+ 
+   " Place sign without a buffer
+   call assert_equal([-1], sign_placelist([{'id' : 10, 'name' : 'sign1',
+             \ 'lnum' : 12, 'group' : ''}]))
+ 
+   " Invalid arguments
+   call assert_equal([], sign_placelist([]))
+   call assert_fails('call sign_placelist({})', "E714:")
+   call assert_fails('call sign_placelist([[]])', "E715:")
+   call assert_fails('call sign_placelist(["abc"])', "E715:")
+   call assert_fails('call sign_placelist([100])', "E715:")
+ 
+   " Unplace multiple signs
+   call assert_equal([0, 0, 0], sign_unplacelist([{'id' : 5},
+             \ {'id' : 1, 'group' : 'g1'}, {'id' : 1, 'group' : 'g2'}]))
+ 
+   " Invalid arguments
+   call assert_equal([], sign_unplacelist([]))
+   call assert_fails('call sign_unplacelist({})', "E714:")
+   call assert_fails('call sign_unplacelist([[]])', "E715:")
+   call assert_fails('call sign_unplacelist(["abc"])', "E715:")
+   call assert_fails('call sign_unplacelist([100])', "E715:")
+   call assert_fails("call sign_unplacelist([{'id' : -1}])", 'E474')
+ 
+   call assert_equal([0, 0, 0, 0],
+             \ sign_undefine(['sign1', 'sign2', 'sign3', 'sign4']))
+   call assert_equal([], sign_getdefined())
+ 
+   " Invalid arguments
+   call assert_equal([], sign_undefine([]))
+   call assert_fails('call sign_undefine([[]])', 'E730:')
+   call assert_fails('call sign_undefine([{}])', 'E731:')
+   call assert_fails('call sign_undefine(["1abc2"])', 'E155:')
+ 
+   call sign_unplace('*')
+   call sign_undefine()
+   enew!
+   call delete("Xsign")
+ endfunc
*** ../vim-8.1.1681/src/version.c       2019-07-13 20:14:39.626623070 +0200
--- src/version.c       2019-07-13 20:53:34.595655948 +0200
***************
*** 779,780 ****
--- 779,782 ----
  {   /* Add new patch number below this line */
+ /**/
+     1682,
  /**/

-- 
TALL KNIGHT:   Firstly.  You must get us another shrubbery!
OTHER KNIGHTS: More shrubberies!  More shrubberies for the ex-Knights of Ni!
ARTHUR:        Not another shrubbery -
                 "Monty Python and the Holy Grail" PYTHON (MONTY) PICTURES LTD

 /// Bram Moolenaar -- [email protected] -- 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 [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/vim_dev/201907131922.x6DJM0KU021079%40masaka.moolenaar.net.
For more options, visit https://groups.google.com/d/optout.

Raspunde prin e-mail lui