To: vim_dev@googlegroups.com Subject: Patch 8.1.1682 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ 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], ¬anum); ! 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, ¬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; ! } } 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, ¬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) + { + 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 -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\ \\\ an exciting new programming language -- http://www.Zimbu.org /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///