# HG changeset patch # User Bram Moolenaar # Date 1563046204 -7200 # Node ID 9843fbfa0ee5bf369ac59a3556b6a859fc0702d7 # Parent c6491636f12e9b51eebb0098a2bc90bc27c9af70 patch 8.1.1682: placing a larger number of signs is slow commit https://github.com/vim/vim/commit/809ce4d317fe12db0b2c17f16b4f77200fb060c4 Author: Bram Moolenaar Date: Sat Jul 13 21:21:40 2019 +0200 patch 8.1.1682: placing a larger number of signs is slow Problem: Placing a larger number of signs is slow. Solution: Add functions for dealing with a list of signs. (Yegappan Lakshmanan, closes #4636) diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -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_fl 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_ 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. @@ -8801,17 +8822,85 @@ sign_place({id}, {group}, {name}, {expr} 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 " 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} diff --git a/runtime/doc/usr_41.txt b/runtime/doc/usr_41.txt --- a/runtime/doc/usr_41.txt +++ b/runtime/doc/usr_41.txt @@ -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 diff --git a/src/evalfunc.c b/src/evalfunc.c --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -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 diff --git a/src/proto/sign.pro b/src/proto/sign.pro --- a/src/proto/sign.pro +++ b/src/proto/sign.pro @@ -22,6 +22,8 @@ void f_sign_getdefined(typval_T *argvars 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 : */ diff --git a/src/sign.c b/src/sign.c --- a/src/sign.c +++ b/src/sign.c @@ -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, ch # 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; - dict_T *dict; - char_u *icon = NULL; - char_u *linehl = NULL; - char_u *text = NULL; - char_u *texthl = NULL; + 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) + if (argvars[1].v_type != VAR_UNKNOWN && argvars[1].v_type != VAR_DICT) { - 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); + emsg(_(e_dictreq)); + return; } - 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); + 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: } /* + * 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, ¬anum); + 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, ¬anum); + 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, ¬anum); + 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) { - 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; + dict_T *dict = NULL; rettv->vval.v_number = -1; - // Sign identifier - sign_id = (int)tv_get_number_chk(&argvars[0], ¬anum); - if (notanum) - return; - if (sign_id < 0) + if (argvars[4].v_type != VAR_UNKNOWN + && (argvars[4].v_type != VAR_DICT + || ((dict = argvars[4].vval.v_dict) == NULL))) { - emsg(_(e_invarg)); + emsg(_(e_dictreq)); return; } - // Sign group - group = tv_get_string_chk(&argvars[1]); - if (group == NULL) + 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 (group[0] == '\0') - group = NULL; // global sign group - else + + if (argvars[0].v_type != VAR_LIST) { - group = vim_strsave(group); - if (group == NULL) - return; + emsg(_(e_listreq)); + 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) + // Process the List of sign attributes + for (li = argvars[0].vval.v_list->lv_first; li != NULL; li = li->li_next) { - if (argvars[4].v_type != VAR_DICT || - ((dict = argvars[4].vval.v_dict) == NULL)) - { + 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)); - goto cleanup; - } + list_append_number(rettv->vval.v_list, sign_id); + } +} - // 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, ¬anum); - 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, ¬anum); - if (notanum) - goto cleanup; - } +/* + * 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); } - - if (sign_place(&sign_id, group, sign_name, buf, lnum, prio) == OK) - rettv->vval.v_number = sign_id; - -cleanup: - vim_free(group); } /* @@ -2360,6 +2526,16 @@ f_sign_undefine(typval_T *argvars, typva { 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,16 +2557,78 @@ f_sign_undefine(typval_T *argvars, typva } /* + * 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); + if (buf == NULL) + 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; - dictitem_T *di; - int sign_id = 0; - buf_T *buf = NULL; - char_u *group = NULL; + dict_T *dict = NULL; rettv->vval.v_number = -1; @@ -2400,50 +2638,46 @@ f_sign_unplace(typval_T *argvars, typval 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; + return; } dict = argvars[1].vval.v_dict; - - if ((di = dict_find(dict, (char_u *)"buffer", -1)) != NULL) - { - buf = get_buf_arg(&di->di_tv); - if (buf == NULL) - goto cleanup; - } - if (dict_find(dict, (char_u *)"id", -1) != NULL) - sign_id = dict_get_number(dict, (char_u *)"id"); } - if (buf == NULL) + 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) { - // 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; + emsg(_(e_listreq)); + return; } -cleanup: - vim_free(group); + 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 */ diff --git a/src/testdir/test_signs.vim b/src/testdir/test_signs.vim --- a/src/testdir/test_signs.vim +++ b/src/testdir/test_signs.vim @@ -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 diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -778,6 +778,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1682, +/**/ 1681, /**/ 1680,