]> granicus.if.org Git - vim/commitdiff
patch 8.1.1682: placing a larger number of signs is slow v8.1.1682
authorBram Moolenaar <Bram@vim.org>
Sat, 13 Jul 2019 19:21:40 +0000 (21:21 +0200)
committerBram Moolenaar <Bram@vim.org>
Sat, 13 Jul 2019 19:21:40 +0000 (21:21 +0200)
Problem:    Placing a larger number of signs is slow.
Solution:   Add functions for dealing with a list of signs. (Yegappan
            Lakshmanan, closes #4636)

runtime/doc/eval.txt
runtime/doc/usr_41.txt
src/evalfunc.c
src/proto/sign.pro
src/sign.c
src/testdir/test_signs.vim
src/version.c

index aa68fc7fdb4a57e8a8b814b6a26aab969acb544d..dc25d4a21c61bd54faa5410599e413033e5f7191 100644 (file)
@@ -1,4 +1,4 @@
-*eval.txt*     For Vim version 8.1.  Last change: 2019 Jul 04
+*eval.txt*     For Vim version 8.1.  Last change: 2019 Jul 13
 
 
                  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -2660,6 +2660,7 @@ shellescape({string} [, {special}])
                                        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
@@ -2667,9 +2668,12 @@ sign_jump({id}, {group}, {expr})
                                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}
@@ -6494,6 +6498,8 @@ listener_flush([{buf}])                                   *listener_flush()*
 
 listener_remove({id})                                  *listener_remove()*
                Remove a listener previously added with listener_add().
+               Returns zero when {id} could not be found, one when {id} was
+               removed.
 
 localtime()                                            *localtime()*
                Return the current time, measured as seconds since 1st Jan
@@ -8624,6 +8630,7 @@ shiftwidth([{col}])                                               *shiftwidth()*
                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.
 
@@ -8643,11 +8650,25 @@ sign_define({name} [, {dict}])                          *sign_define()*
                If the sign named {name} already exists, then the attributes
                of the sign are updated.
 
-               Returns 0 on success and -1 on failure.
+               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("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.
@@ -8800,18 +8821,86 @@ sign_place({id}, {group}, {name}, {expr} [, {dict}])
                        " at line 40 in buffer json.c with priority 90
                        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.
 
-               Returns 0 on success and -1 on failure.
+               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()
 <
@@ -8857,6 +8946,32 @@ sign_unplace({group} [, {dict}])                 *sign_unplace()*
                        " 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
@@ -9917,8 +10032,8 @@ timer_stop({timer})                                      *timer_stop()*
 
 timer_stopall()                                                *timer_stopall()*
                Stop all timers.  The timer callbacks will no longer be
-               invoked.  Useful if some timers is misbehaving.  If there are
-               no timers there is no error.
+               invoked.  Useful if a timer is misbehaving.  If there are no
+               timers there is no error.
 
                {only available when compiled with the |+timers| feature}
 
index 34d60f101ba01124235b52ddc01cc6b64fbe5f4a..0f677e44f969835603f99b615abe8e71b2a0171a 100644 (file)
@@ -1015,8 +1015,10 @@ Signs:                                           *sign-functions*
        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
index 43c8028b6ed8ffb87c2755a85d950644e1ab78f3..9ac67b8c9fa7b291e7266db40820aa7d42b9dffa 100644 (file)
@@ -888,8 +888,10 @@ static struct fst
     {"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
index 51aa0bbaf354204d1fc3c24f8a92897f02090cd2..50b74aaa428fa02640a41acd7abbfa5751f65292 100644 (file)
@@ -22,6 +22,8 @@ void f_sign_getdefined(typval_T *argvars, typval_T *rettv);
 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 : */
index cf7581951c76c91fd0e064af53f331eeab3b7a35..bf238d907cb2a66c8670f96a3116be129df243b4 100644 (file)
@@ -442,7 +442,8 @@ buf_change_sign_type(
     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                typenr,         // typenr of sign we are adding
+    int                prio)           // sign priority
 {
     signlist_T *sign;          // a sign in the signlist
 
@@ -451,6 +452,8 @@ buf_change_sign_type(
        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,8 +1107,9 @@ sign_place(
        // 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);
+       // ":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,51 +2100,104 @@ set_context_in_sign_cmd(expand_T *xp, char_u *arg)
 # endif
 
 /*
- * "sign_define()" function
+ * Define a sign using the attributes in 'dict'. Returns 0 on success and -1 on
+ * failure.
  */
-    void
-f_sign_define(typval_T *argvars, typval_T *rettv)
+    static int
+sign_define_from_dict(char_u *name_arg, dict_T *dict)
 {
-    char_u     *name;
-    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;
 
-    rettv->vval.v_number = -1;
-
-    name = tv_get_string_chk(&argvars[0]);
-    if (name == NULL)
-       return;
-
-    if (argvars[1].v_type != VAR_UNKNOWN)
+    if (name_arg == NULL)
     {
-       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 (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)
-       rettv->vval.v_number = 0;
+       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,87 +2326,196 @@ cleanup:
 }
 
 /*
- * "sign_place()" function
+ * Place a new sign using the values specified in dict 'dict'. Returns the sign
+ * identifier if successfully placed, otherwise returns 0.
  */
-    void
-f_sign_place(typval_T *argvars, typval_T *rettv)
+    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;
+    int                sign_id = 0;
     char_u     *group = NULL;
-    char_u     *sign_name;
-    buf_T      *buf;
-    dict_T     *dict;
+    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;
 
-    rettv->vval.v_number = -1;
-
-    // Sign identifier
-    sign_id = (int)tv_get_number_chk(&argvars[0], &notanum);
-    if (notanum)
-       return;
-    if (sign_id < 0)
+    // sign identifier
+    if (id_tv == NULL)
     {
-       emsg(_(e_invarg));
-       return;
+       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
-    group = tv_get_string_chk(&argvars[1]);
-    if (group == NULL)
-       return;
-    if (group[0] == '\0')
-       group = NULL;                   // global sign group
+    // 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 = vim_strsave(group);
+       group = tv_get_string_chk(group_tv);
        if (group == NULL)
-           return;
+           goto cleanup;
+       if (group[0] == '\0')                   // global sign group
+           group = NULL;
+       else
+       {
+           group = vim_strsave(group);
+           if (group == NULL)
+               return -1;
+       }
     }
 
-    // Sign name
-    sign_name = tv_get_string_chk(&argvars[2]);
+    // 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
-    buf = get_buf_arg(&argvars[3]);
+    // 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;
 
-    if (argvars[4].v_type != VAR_UNKNOWN)
+    // line number of the sign
+    di = dict_find(dict, (char_u *)"lnum", -1);
+    if (di != NULL)
     {
-       if (argvars[4].v_type != VAR_DICT ||
-                               ((dict = argvars[4].vval.v_dict) == NULL))
-       {
-           emsg(_(e_dictreq));
+       lnum = (int)tv_get_number_chk(&di->di_tv, &notanum);
+       if (notanum)
            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;
-       }
+    // 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)
-       rettv->vval.v_number = sign_id;
+       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,6 +2526,16 @@ f_sign_undefine(typval_T *argvars, typval_T *rettv)
 {
     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,44 +2557,37 @@ f_sign_undefine(typval_T *argvars, typval_T *rettv)
 }
 
 /*
- * "sign_unplace()" function
+ * Unplace the sign with attributes specified in 'dict'. Returns 0 on success
+ * and -1 on failure.
  */
-    void
-f_sign_unplace(typval_T *argvars, typval_T *rettv)
+    static int
+sign_unplace_from_dict(typval_T *group_tv, dict_T *dict)
 {
-    dict_T     *dict;
     dictitem_T *di;
     int                sign_id = 0;
     buf_T      *buf = NULL;
     char_u     *group = NULL;
+    int                retval = -1;
 
-    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
+    // 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)
     {
-       group = vim_strsave(group);
-       if (group == NULL)
-           return;
-    }
-
-    if (argvars[1].v_type != VAR_UNKNOWN)
-    {
-       if (argvars[1].v_type != VAR_DICT)
+       if (group[0] == '\0')                   // global sign group
+           group = NULL;
+       else
        {
-           emsg(_(e_dictreq));
-           goto cleanup;
+           group = vim_strsave(group);
+           if (group == NULL)
+               return -1;
        }
-       dict = argvars[1].vval.v_dict;
+    }
 
+    if (dict != NULL)
+    {
        if ((di = dict_find(dict, (char_u *)"buffer", -1)) != NULL)
        {
            buf = get_buf_arg(&di->di_tv);
@@ -2426,24 +2595,89 @@ f_sign_unplace(typval_T *argvars, typval_T *rettv)
                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)
-               rettv->vval.v_number = 0;
-    }
-    else
-    {
-       if (sign_unplace(sign_id, group, buf, 0) == OK)
-           rettv->vval.v_number = 0;
+           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 */
index 898acbc38e610ef5277be393e4b1a7002f1901ff..1b9a29d9fef0a0b766f6da3879b979f27766f16e 100644 (file)
@@ -412,7 +412,7 @@ func Test_sign_funcs()
   " 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({})', 'E731:')
   call assert_fails('call sign_define("sign6", [])', 'E715:')
 
   " Tests for sign_getdefined()
@@ -441,7 +441,7 @@ func Test_sign_funcs()
   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:')
+             \ 'E715:')
   call assert_fails('call sign_place(-1, "", "sign1", "Xsign",
              \ {"lnum" : 30})', 'E474:')
   call assert_fails('call sign_place(10, "", "xsign1x", "Xsign",
@@ -501,11 +501,21 @@ func Test_sign_funcs()
              \ {'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([])', 'E730:')
+  call assert_fails('call sign_undefine({})', 'E731:')
 
   call delete("Xsign")
   call sign_unplace('*')
@@ -631,7 +641,7 @@ func Test_sign_group()
   call assert_equal([], sign_getplaced(bnum, {'group' : '*'})[0].signs)
 
   " Error case
-  call assert_fails("call sign_unplace([])", 'E474:')
+  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,8 +1127,8 @@ func Test_sign_unplace()
   call delete("Xsign2")
 endfunc
 
-" Tests for auto-generating the sign identifier
-func Test_sign_id_autogen()
+" Tests for auto-generating the sign identifier.
+func Test_aaa_sign_id_autogen()
   enew | only
   call sign_unplace('*')
   call sign_undefine()
@@ -1843,3 +1853,113 @@ func Test_sign_numcol()
   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
index fff8d22a23a8822dc4aac1ff0de4cfb762aadb44..efcdb3f0ee424522e11874fb68ebc775397fc739 100644 (file)
@@ -777,6 +777,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1682,
 /**/
     1681,
 /**/