To: vim_dev@googlegroups.com Subject: Patch 8.1.0614 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.1.0614 Problem: Placing signs can be complicated. Solution: Add functions for defining and placing signs. Introduce a group name to avoid different plugins using the same signs. (Yegappan Lakshmanan, closes #3652) Files: runtime/doc/eval.txt, runtime/doc/sign.txt, runtime/doc/usr_41.txt, src/alloc.h, src/buffer.c, src/evalfunc.c, src/ex_cmds.c, src/globals.h, src/list.c, src/misc2.c, src/netbeans.c, src/proto/buffer.pro, src/proto/ex_cmds.pro, src/proto/list.pro, src/proto/misc2.pro, src/structs.h, src/testdir/test_signs.vim, src/workshop.c *** ../vim-8.1.0613/runtime/doc/eval.txt 2018-12-13 22:17:52.869941530 +0100 --- runtime/doc/eval.txt 2018-12-21 14:49:21.627875310 +0100 *************** *** 2395,2400 **** --- 2408,2422 ---- String escape {string} for use as shell command argument shiftwidth([{col}]) Number effective value of 'shiftwidth' + sign_define({name} [, {dict}]) Number define or update a sign + sign_getdefined([{name}]) List get a list of defined signs + sign_getplaced([{expr} [, {dict}]]) + List get a list of placed signs + sign_place({id}, {group}, {name}, {expr} [, {dict}]) + Number place a sign + sign_undefine([{name}]) Number undefine a sign + sign_unplace({group} [, {dict}]) + Number unplace a sign simplify({filename}) String simplify filename as much as possible sin({expr}) Float sine of {expr} sinh({expr}) Float hyperbolic sine of {expr} *************** *** 7841,7847 **** --- 7867,8083 ---- 'vartabstop' feature. If the 'vartabstop' setting is enabled and no {col} argument is given, column 1 will be assumed. + sign_define({name} [, {dict}]) *sign_define()* + Define a new sign named {name} or modify the attributes of an + existing sign. This is similar to the |:sign-define| command. + Prefix {name} with a unique text to avoid name collisions. + There is no {group} like with placing signs. + + The {name} can be a String or a Number. The optional {dict} + argument specifies the sign attributes. The following values + are supported: + icon full path to the bitmap file for the sign. + linehl highlight group used for the whole line the + sign is placed in. + text text that is displayed when there is no icon + or the GUI is not being used. + texthl highlight group used for the text item + For an existing sign, the attributes 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. + This is similar to the |:sign-list| command. + + If the {name} is not supplied, then a list of all the defined + signs is returned. Otherwise the attribute of the specified + sign is returned. + + Each list item in the returned value is a dictionary with the + following entries: + icon full path to the bitmap file of the sign + linehl highlight group used for the whole line the + sign is placed in. + name name of the sign + text text that is displayed when there is no icon + or the GUI is not being used. + texthl highlight group used for the text item + + Returns an empty List if there are no signs and when {name} is + not found. + + Examples: > + " Get a list of all the defined signs + echo sign_getdefined() + + " Get the attribute of the sign named mySign + echo sign_getdefined("mySign") + < + sign_getplaced([{expr} [, {dict}]]) *sign_getplaced()* + Return a list of signs placed in a buffer or all the buffers. + This is similar to the |:sign-place-list| command. + + If the optional buffer name {expr} is specified, then only the + list of signs placed in that buffer is returned. For the use + of {expr}, see |bufname()|. The optional {dict} can contain + the following entries: + group select only signs in this group + id select sign with this identifier + lnum select signs placed in this line. For the use + of {lnum}, see |line()|. + If {group} is '*', then signs in all the groups including the + global group are returned. If {group} is not supplied, then + only signs in the global group are returned. If no arguments + are supplied, then signs in the global group placed in all the + buffers are returned. + + Each list item in the returned value is a dictionary with the + following entries: + bufnr number of the buffer with the sign + signs list of signs placed in {bufnr}. Each list + item is a dictionary with the below listed + entries + + The dictionary for each sign contains the following entries: + group sign group. Set to '' for the global group. + id identifier of the sign + lnum line number where the sign is placed + name name of the defined sign + priority sign priority + + Returns an empty list on failure. + + Examples: > + " Get a List of signs placed in eval.c in the + " global group + echo sign_getplaced("eval.c") + + " Get a List of signs in group 'g1' placed in eval.c + echo sign_getplaced("eval.c", {'group' : 'g1'}) + + " Get a List of signs placed at line 10 in eval.c + echo sign_getplaced("eval.c", {'lnum' : 10}) + + " Get sign with identifier 10 placed in a.py + echo sign_getplaced("a.py", {'id' : 10'}) + + " Get sign with id 20 in group 'g1' placed in a.py + echo sign_getplaced("a.py", {'group' : 'g1', + \ 'id' : 20'}) + + " Get a List of all the placed signs + echo sign_getplaced() + < + *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 + the sign group name. To use the global sign group, use an + empty string. {group} functions as a namespace for {id}, thus + two groups can use the same IDs. + + {name} refers to a defined sign. + {expr} refers to a buffer name or number. For the accepted + 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. + + If the optional {dict} is not specified, then it modifies the + placed sign {id} in group {group} to use the defined sign + {name}. + + Returns the sign identifier on success and -1 on failure. + + Examples: > + " Place a sign named sign1 with id 5 at line 20 in + " buffer json.c + call sign_place(5, '', 'sign1', 'json.c', + \ {'lnum' : 20}) + + " Updates sign 5 in buffer json.c to use sign2 + call sign_place(5, '', 'sign2', 'json.c') + + " Place a sign named sign3 at line 30 in + " buffer json.c with a new identifier + let id = sign_place(0, '', 'sign3', 'json.c', + \ {'lnum' : 30}) + + " Place a sign named sign4 with id 10 in group 'g3' + " at line 40 in buffer json.c with priority 90 + 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() + < + sign_unplace({group} [, {dict}]) *sign_unplace()* + Remove a previously placed sign in one or more buffers. This + is similar to the |:sign-unplace()| command. + + {group} is the sign group name. To use the global sign group, + use an empty string. If {group} is set to '*', then all the + groups including the global group are used. + The signs in {group} are selected based on the entries in + {dict}. The following optional entries in {dict} are + supported: + buffer buffer name or number. See |bufname()|. + id sign identifier + If {dict} is not supplied, then all the signs in {group} are + removed. + + Returns 0 on success and -1 on failure. + + Examples: > + " Remove sign 10 from buffer a.vim + call sign_unplace('', {'buffer' : "a.vim", 'id' : 10}) + + " Remove sign 20 in group 'g1' from buffer 3 + call sign_unplace('g1', {'buffer' : 3, 'id' : 20}) + + " Remove all the signs in group 'g2' from buffer 10 + call sign_unplace('g2', {'buffer' : 10}) + + " Remove sign 30 in group 'g3' from all the buffers + call sign_unplace('g3', {'id' : 30}) + + " Remove all the signs placed in buffer 5 + call sign_unplace('*', {'buffer' : 5}) + + " Remove the signs in group 'g4' from all the buffers + call sign_unplace('g4') + + " Remove sign 40 from all the buffers + call sign_unplace('*', {'id' : 40}) + + " Remove all the placed signs from all the buffers + call sign_unplace('*') + < 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.0613/runtime/doc/sign.txt 2018-05-17 13:42:02.000000000 +0200 --- runtime/doc/sign.txt 2018-12-21 14:52:11.058495079 +0100 *************** *** 51,56 **** --- 51,70 ---- Example to set the color: > :highlight SignColumn guibg=darkgrey + < + *sign-group* + Each sign can be assigned to either the global group or a named group. When + placing a sign, if a group name is not supplied, or an empty string is used, + then the sign is placed in the global group. Otherwise the sign is placed in + the named group. The sign identifier is unique within a group. The sign group + allows Vim plugins to use unique signs without interfering with other plugins + using signs. + + *sign-priority* + Each placed sign is assigned a priority value. When multiple signs are placed + on the same line, the attributes of the sign with the highest priority is used + independent of the sign group. The default priority for a sign is 10. The + priority is assigned at the time of placing a sign. ============================================================================== 2. Commands *sign-commands* *:sig* *:sign* *************** *** 69,74 **** --- 83,90 ---- DEFINING A SIGN. *:sign-define* *E255* *E160* *E612* + See |sign_define()| for the equivalent Vim script function. + :sign define {name} {argument}... Define a new sign or set attributes for an existing sign. The {name} can either be a number (all digits) or a name *************** *** 106,118 **** --- 122,139 ---- DELETING A SIGN *:sign-undefine* *E155* + See |sign_undefine()| for the equivalent Vim script function. + :sign undefine {name} Deletes a previously defined sign. If signs with this {name} are still placed this will cause trouble. + LISTING SIGNS *:sign-list* *E156* + See |sign_getdefined()| for the equivalent Vim script function. + :sign list Lists all defined signs and their attributes. :sign list {name} *************** *** 121,126 **** --- 142,149 ---- PLACING SIGNS *:sign-place* *E158* + See |sign_place()| for the equivalent Vim script function. + :sign place {id} line={lnum} name={name} file={fname} Place sign defined as {name} at line {lnum} in file {fname}. *:sign-fname* *************** *** 136,141 **** --- 159,183 ---- to be done several times and making changes may not work as expected). + The following optional sign attributes can be specified before + "file=": + group={group} Place sign in sign group {group} + priority={prio} Assign priority {prio} to sign + + By default, the sign is placed in the global sign group. + + By default, the sign is assigned a default priority of 10. To + assign a different priority value, use "priority={prio}" to + specify a value. The priority is used to determine the + highlight group used when multiple signs are placed on the + same line. + + Examples: > + :sign place 5 line=3 name=sign1 file=a.py + :sign place 6 group=g2 line=2 name=sign2 file=x.py + :sign place 9 group=g2 priority=50 line=5 + \ name=sign1 file=a.py + < :sign place {id} line={lnum} name={name} buffer={nr} Same, but use buffer {nr}. *************** *** 146,176 **** This can be used to change the displayed sign without moving it (e.g., when the debugger has stopped at a breakpoint). :sign place {id} name={name} buffer={nr} Same, but use buffer {nr}. REMOVING SIGNS *:sign-unplace* *E159* :sign unplace {id} file={fname} Remove the previously placed sign {id} from file {fname}. See remark above about {fname} |:sign-fname|. :sign unplace * file={fname} Remove all placed signs in file {fname}. :sign unplace {id} buffer={nr} Remove the previously placed sign {id} from buffer {nr}. :sign unplace * buffer={nr} Remove all placed signs in buffer {nr}. :sign unplace {id} Remove the previously placed sign {id} from all files it appears in. :sign unplace * ! Remove all placed signs. :sign unplace Remove the placed sign at the cursor position. --- 188,260 ---- This can be used to change the displayed sign without moving it (e.g., when the debugger has stopped at a breakpoint). + The optional "group={group}" attribute can be used before + "file=" to select a sign in a particular group. + :sign place {id} name={name} buffer={nr} Same, but use buffer {nr}. REMOVING SIGNS *:sign-unplace* *E159* + See |sign_unplace()| for the equivalent Vim script function. + :sign unplace {id} file={fname} Remove the previously placed sign {id} from file {fname}. See remark above about {fname} |:sign-fname|. + :sign unplace {id} group={group} file={fname} + Same but remove the sign {id} in sign group {group}. + + :sign unplace {id} group=* file={fname} + Same but remove the sign {id} from all the sign groups. + :sign unplace * file={fname} Remove all placed signs in file {fname}. + :sign unplace * group={group} file={fname} + Remove all placed signs in group {group} from file {fname}. + + :sign unplace * group=* file={fname} + Remove all placed signs in all the groups from file {fname}. + :sign unplace {id} buffer={nr} Remove the previously placed sign {id} from buffer {nr}. + :sign unplace {id} group={group} buffer={nr} + Remove the previously placed sign {id} in group {group} from + buffer {nr}. + + :sign unplace {id} group=* buffer={nr} + Remove the previously placed sign {id} in all the groups from + buffer {nr}. + :sign unplace * buffer={nr} Remove all placed signs in buffer {nr}. + :sign unplace * group={group} buffer={nr} + Remove all placed signs in group {group} from buffer {nr}. + + :sign unplace * group=* buffer={nr} + Remove all placed signs in all the groups from buffer {nr}. + :sign unplace {id} Remove the previously placed sign {id} from all files it appears in. + :sign unplace {id} group={group} + Remove the previously placed sign {id} in group {group} from + all files it appears in. + + :sign unplace {id} group=* + Remove the previously placed sign {id} in all the groups from + all the files it appears in. + :sign unplace * ! Remove all placed signs in the global group. ! ! :sign unplace * group=* ! Remove all placed signs in all the groups. :sign unplace Remove the placed sign at the cursor position. *************** *** 178,192 **** --- 262,287 ---- LISTING PLACED SIGNS *:sign-place-list* + See |sign_getplaced()| for the equivalent Vim script function. + :sign place file={fname} List signs placed in file {fname}. See remark above about {fname} |:sign-fname|. + :sign place group={group} file={fname} + List signs in group {group} placed in file {fname}. + :sign place buffer={nr} List signs placed in buffer {nr}. + :sign place group={group} buffer={nr} + List signs in group {group} placed in buffer {nr}. + :sign place List placed signs in all files. + :sign place group={group} + List placed signs in all sign groups in all the files. + JUMPING TO A SIGN *:sign-jump* *E157* *************** *** 202,205 **** have a name. ! vim:tw=78:ts=8:ft=help:norl: --- 297,300 ---- have a name. ! vim:tw=78:ts=8:noet:ft=help:norl: *** ../vim-8.1.0613/runtime/doc/usr_41.txt 2018-11-11 15:20:32.432704446 +0100 --- runtime/doc/usr_41.txt 2018-12-21 14:36:57.238303578 +0100 *************** *** 973,978 **** --- 983,996 ---- job_info() get information about a job job_setoptions() set options for a job + Signs: *sign-functions* + sign_define() define or update a sign + sign_getdefined() get a list of defined signs + sign_getplaced() get a list of placed signs + sign_place() place a sign + sign_undefine() undefine a sign + sign_unplace() unplace a sign + Terminal window: *terminal-functions* term_start() open a terminal window and run a job term_list() get the list of terminal buffers *** ../vim-8.1.0613/src/alloc.h 2018-11-11 15:20:32.432704446 +0100 --- src/alloc.h 2018-12-21 14:36:57.238303578 +0100 *************** *** 21,25 **** --- 21,33 ---- aid_tagstack_items, aid_tagstack_from, aid_tagstack_details, + aid_sign_getdefined, + aid_sign_getplaced, + aid_sign_define_by_name, + aid_sign_getlist, + aid_sign_getplaced_dict, + aid_sign_getplaced_list, + aid_insert_sign, + aid_sign_getinfo, aid_last } alloc_id_T; *** ../vim-8.1.0613/src/buffer.c 2018-12-21 13:03:24.292337854 +0100 --- src/buffer.c 2018-12-21 14:54:24.453442611 +0100 *************** *** 936,942 **** uc_clear(&buf->b_ucmds); /* clear local user commands */ #endif #ifdef FEAT_SIGNS ! buf_delete_signs(buf); /* delete any signs */ #endif #ifdef FEAT_NETBEANS_INTG netbeans_file_killed(buf); --- 936,942 ---- uc_clear(&buf->b_ucmds); /* clear local user commands */ #endif #ifdef FEAT_SIGNS ! buf_delete_signs(buf, (char_u *)"*"); // delete any signs */ #endif #ifdef FEAT_NETBEANS_INTG netbeans_file_killed(buf); *************** *** 5866,5890 **** #if defined(FEAT_SIGNS) || defined(PROTO) /* ! * Insert the sign into the signlist. */ static void insert_sign( ! buf_T *buf, /* buffer to store sign in */ ! signlist_T *prev, /* previous sign entry */ ! signlist_T *next, /* next sign entry */ ! int id, /* sign ID */ ! linenr_T lnum, /* line number which gets the mark */ ! int typenr) /* typenr of sign we are adding */ { signlist_T *newsign; ! newsign = (signlist_T *)lalloc((long_u)sizeof(signlist_T), FALSE); if (newsign != NULL) { newsign->id = id; newsign->lnum = lnum; newsign->typenr = typenr; newsign->next = next; newsign->prev = prev; if (next != NULL) --- 5866,5899 ---- #if defined(FEAT_SIGNS) || defined(PROTO) /* ! * Insert a new sign into the signlist for buffer 'buf' between the 'prev' and ! * 'next' signs. */ static void insert_sign( ! buf_T *buf, // buffer to store sign in ! signlist_T *prev, // previous sign entry ! signlist_T *next, // next sign entry ! int id, // sign ID ! char_u *group, // sign group; NULL for global group ! int prio, // sign priority ! linenr_T lnum, // line number which gets the mark ! int typenr) // typenr of sign we are adding { signlist_T *newsign; ! newsign = (signlist_T *)lalloc_id((long_u)sizeof(signlist_T), FALSE, ! aid_insert_sign); if (newsign != NULL) { newsign->id = id; newsign->lnum = lnum; newsign->typenr = typenr; + if (group != NULL) + newsign->group = vim_strsave(group); + else + newsign->group = NULL; + newsign->priority = prio; newsign->next = next; newsign->prev = prev; if (next != NULL) *************** *** 5892,5906 **** if (prev == NULL) { ! /* When adding first sign need to redraw the windows to create the ! * column for signs. */ if (buf->b_signlist == NULL) { redraw_buf_later(buf, NOT_VALID); changed_cline_bef_curs(); } ! /* first sign in signlist */ buf->b_signlist = newsign; #ifdef FEAT_NETBEANS_INTG if (netbeans_active()) --- 5901,5915 ---- if (prev == NULL) { ! // When adding first sign need to redraw the windows to create the ! // column for signs. if (buf->b_signlist == NULL) { redraw_buf_later(buf, NOT_VALID); changed_cline_bef_curs(); } ! // first sign in signlist buf->b_signlist = newsign; #ifdef FEAT_NETBEANS_INTG if (netbeans_active()) *************** *** 5913,5962 **** } /* * Add the sign into the signlist. Find the right spot to do it though. */ void buf_addsign( ! buf_T *buf, /* buffer to store sign in */ ! int id, /* sign ID */ ! linenr_T lnum, /* line number which gets the mark */ ! int typenr) /* typenr of sign we are adding */ { ! signlist_T *sign; /* a sign in the signlist */ ! signlist_T *prev; /* the previous sign */ prev = NULL; ! for (sign = buf->b_signlist; sign != NULL; sign = sign->next) { ! if (lnum == sign->lnum && id == sign->id) { sign->typenr = typenr; return; } else if (lnum < sign->lnum) { ! // keep signs sorted by lnum: insert new sign at head of list for ! // this lnum ! while (prev != NULL && prev->lnum == lnum) ! prev = prev->prev; ! if (prev == NULL) ! sign = buf->b_signlist; ! else ! sign = prev->next; ! insert_sign(buf, prev, sign, id, lnum, typenr); return; } prev = sign; } ! // insert new sign at head of list for this lnum ! while (prev != NULL && prev->lnum == lnum) ! prev = prev->prev; ! if (prev == NULL) ! sign = buf->b_signlist; ! else ! sign = prev->next; ! insert_sign(buf, prev, sign, id, lnum, typenr); return; } --- 5922,6021 ---- } /* + * Insert a new sign sorted by line number and sign priority. + */ + static void + insert_sign_by_lnum_prio( + buf_T *buf, // buffer to store sign in + signlist_T *prev, // previous sign entry + int id, // sign ID + char_u *group, // sign group; NULL for global group + int prio, // sign priority + linenr_T lnum, // line number which gets the mark + int typenr) // typenr of sign we are adding + { + signlist_T *sign; + + // keep signs sorted by lnum and by priority: insert new sign at + // the proper position in the list for this lnum. + while (prev != NULL && prev->lnum == lnum && prev->priority <= prio) + prev = prev->prev; + if (prev == NULL) + sign = buf->b_signlist; + else + sign = prev->next; + + insert_sign(buf, prev, sign, id, group, prio, lnum, typenr); + } + + /* + * Returns TRUE if 'sign' is in 'group'. + * A sign can either be in the global group (sign->group == NULL) + * or in a named group. If 'group' is '*', then the sign is part of the group. + */ + int + sign_in_group(signlist_T *sign, char_u *group) + { + return ((group != NULL && STRCMP(group, "*") == 0) || + (group == NULL && sign->group == NULL) || + (group != NULL && sign->group != NULL && + STRCMP(group, sign->group) == 0)); + } + + /* + * Return information about a sign in a Dict + */ + dict_T * + sign_get_info(signlist_T *sign) + { + dict_T *d; + + if ((d = dict_alloc_id(aid_sign_getinfo)) == NULL) + return NULL; + dict_add_number(d, "id", sign->id); + dict_add_string(d, "group", (sign->group == NULL) ? + (char_u *)"" : sign->group); + dict_add_number(d, "lnum", sign->lnum); + dict_add_string(d, "name", sign_typenr2name(sign->typenr)); + dict_add_number(d, "priority", sign->priority); + + return d; + } + + /* * Add the sign into the signlist. Find the right spot to do it though. */ void buf_addsign( ! buf_T *buf, // buffer to store sign in ! int id, // sign ID ! char_u *group, // sign group ! int prio, // sign priority ! linenr_T lnum, // line number which gets the mark ! int typenr) // typenr of sign we are adding { ! signlist_T *sign; // a sign in the signlist ! signlist_T *prev; // the previous sign prev = NULL; ! FOR_ALL_SIGNS_IN_BUF(buf) { ! if (lnum == sign->lnum && id == sign->id && ! sign_in_group(sign, group)) { + // Update an existing sign sign->typenr = typenr; return; } else if (lnum < sign->lnum) { ! insert_sign_by_lnum_prio(buf, prev, id, group, prio, lnum, typenr); return; } prev = sign; } ! insert_sign_by_lnum_prio(buf, prev, id, group, prio, lnum, typenr); return; } *************** *** 5967,5981 **** */ linenr_T buf_change_sign_type( ! buf_T *buf, /* buffer to store sign in */ ! int markId, /* sign ID */ ! int typenr) /* typenr of sign we are adding */ { ! signlist_T *sign; /* a sign in the signlist */ ! for (sign = buf->b_signlist; sign != NULL; sign = sign->next) { ! if (sign->id == markId) { sign->typenr = typenr; return sign->lnum; --- 6026,6041 ---- */ linenr_T 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 { ! signlist_T *sign; // a sign in the signlist ! FOR_ALL_SIGNS_IN_BUF(buf) { ! if (sign->id == markId && sign_in_group(sign, group)) { sign->typenr = typenr; return sign->lnum; *************** *** 5985,5990 **** --- 6045,6055 ---- return (linenr_T)0; } + /* + * Return the type number of the sign at line number 'lnum' in buffer 'buf' + * which has the attribute specifed by 'type'. Returns 0 if a sign is not found + * at the line number or it doesn't have the specified attribute. + */ int buf_getsigntype( buf_T *buf, *************** *** 5993,5999 **** { signlist_T *sign; /* a sign in a b_signlist */ ! for (sign = buf->b_signlist; sign != NULL; sign = sign->next) if (sign->lnum == lnum && (type == SIGN_ANY # ifdef FEAT_SIGN_ICONS --- 6058,6064 ---- { signlist_T *sign; /* a sign in a b_signlist */ ! FOR_ALL_SIGNS_IN_BUF(buf) if (sign->lnum == lnum && (type == SIGN_ANY # ifdef FEAT_SIGN_ICONS *************** *** 6008,6044 **** return 0; } ! linenr_T buf_delsign( ! buf_T *buf, /* buffer sign is stored in */ ! int id) /* sign id */ ! { ! signlist_T **lastp; /* pointer to pointer to current sign */ ! signlist_T *sign; /* a sign in a b_signlist */ ! signlist_T *next; /* the next sign in a b_signlist */ ! linenr_T lnum; /* line number whose sign was deleted */ lastp = &buf->b_signlist; lnum = 0; for (sign = buf->b_signlist; sign != NULL; sign = next) { next = sign->next; ! if (sign->id == id) { *lastp = next; if (next != NULL) next->prev = sign->prev; lnum = sign->lnum; vim_free(sign); ! break; } else lastp = &sign->next; } ! /* When deleted the last sign need to redraw the windows to remove the ! * sign column. */ if (buf->b_signlist == NULL) { redraw_buf_later(buf, NOT_VALID); --- 6073,6123 ---- return 0; } ! /* ! * Delete sign 'id' in group 'group' from buffer 'buf'. ! * If 'id' is zero, then delete all the signs in group 'group'. Otherwise ! * delete only the specified sign. ! * If 'group' is '*', then delete the sign in all the groups. If 'group' is ! * NULL, then delete the sign in the global group. Otherwise delete the sign in ! * the specified group. ! * Returns the line number of the deleted sign. If multiple signs are deleted, ! * then returns the line number of the last sign deleted. ! */ linenr_T buf_delsign( ! buf_T *buf, // buffer sign is stored in ! int id, // sign id ! char_u *group) // sign group ! { ! signlist_T **lastp; // pointer to pointer to current sign ! signlist_T *sign; // a sign in a b_signlist ! signlist_T *next; // the next sign in a b_signlist ! linenr_T lnum; // line number whose sign was deleted lastp = &buf->b_signlist; lnum = 0; for (sign = buf->b_signlist; sign != NULL; sign = next) { next = sign->next; ! if ((id == 0 || sign->id == id) && sign_in_group(sign, group)) ! { *lastp = next; if (next != NULL) next->prev = sign->prev; lnum = sign->lnum; + vim_free(sign->group); vim_free(sign); ! // Check whether only one sign needs to be deleted ! if (group == NULL || (*group != '*' && id != 0)) ! break; } else lastp = &sign->next; } ! // When deleted the last sign need to redraw the windows to remove the ! // sign column. if (buf->b_signlist == NULL) { redraw_buf_later(buf, NOT_VALID); *************** *** 6050,6088 **** /* ! * Find the line number of the sign with the requested id. If the sign does ! * not exist, return 0 as the line number. This will still let the correct file ! * get loaded. */ int buf_findsign( ! buf_T *buf, /* buffer to store sign in */ ! int id) /* sign ID */ { ! signlist_T *sign; /* a sign in the signlist */ ! for (sign = buf->b_signlist; sign != NULL; sign = sign->next) ! if (sign->id == id) return sign->lnum; return 0; } int buf_findsign_id( ! buf_T *buf, /* buffer whose sign we are searching for */ ! linenr_T lnum) /* line number of sign */ { ! signlist_T *sign; /* a sign in the signlist */ ! for (sign = buf->b_signlist; sign != NULL; sign = sign->next) ! if (sign->lnum == lnum) ! return sign->id; return 0; } - # if defined(FEAT_NETBEANS_INTG) || defined(PROTO) /* * See if a given type of sign exists on a specific line. --- 6129,6206 ---- /* ! * Find the line number of the sign with the requested id in group 'group'. If ! * the sign does not exist, return 0 as the line number. This will still let ! * the correct file get loaded. */ int buf_findsign( ! buf_T *buf, // buffer to store sign in ! int id, // sign ID ! char_u *group) // sign group { ! signlist_T *sign; // a sign in the signlist ! FOR_ALL_SIGNS_IN_BUF(buf) ! if (sign->id == id && sign_in_group(sign, group)) return sign->lnum; return 0; } + /* + * Return the sign at line 'lnum' in buffer 'buf'. Returns NULL if a sign is + * not found at the line. + */ + static signlist_T * + buf_getsign_at_line( + buf_T *buf, // buffer whose sign we are searching for + linenr_T lnum) // line number of sign + { + signlist_T *sign; // a sign in the signlist + + FOR_ALL_SIGNS_IN_BUF(buf) + if (sign->lnum == lnum) + return sign; + + return NULL; + } + + /* + * Return the sign with identifier 'id' in group 'group' placed in buffer 'buf' + */ + signlist_T * + buf_getsign_with_id( + buf_T *buf, // buffer whose sign we are searching for + int id, // sign identifier + char_u *group) // sign group + { + signlist_T *sign; // a sign in the signlist + + FOR_ALL_SIGNS_IN_BUF(buf) + if (sign->id == id && sign_in_group(sign, group)) + return sign; + + return NULL; + } + + /* + * Return the identifier of the sign at line number 'lnum' in buffer 'buf'. + */ int buf_findsign_id( ! buf_T *buf, // buffer whose sign we are searching for ! linenr_T lnum) // line number of sign { ! signlist_T *sign; // a sign in the signlist ! sign = buf_getsign_at_line(buf, lnum); ! if (sign != NULL) ! return sign->id; return 0; } # if defined(FEAT_NETBEANS_INTG) || defined(PROTO) /* * See if a given type of sign exists on a specific line. *************** *** 6095,6101 **** { signlist_T *sign; /* a sign in the signlist */ ! for (sign = buf->b_signlist; sign != NULL; sign = sign->next) if (sign->lnum == lnum && sign->typenr == typenr) return sign->id; --- 6213,6219 ---- { signlist_T *sign; /* a sign in the signlist */ ! FOR_ALL_SIGNS_IN_BUF(buf) if (sign->lnum == lnum && sign->typenr == typenr) return sign->id; *************** *** 6110,6119 **** int buf_signcount(buf_T *buf, linenr_T lnum) { ! signlist_T *sign; /* a sign in the signlist */ int count = 0; ! for (sign = buf->b_signlist; sign != NULL; sign = sign->next) if (sign->lnum == lnum) if (sign_get_image(sign->typenr) != NULL) count++; --- 6228,6237 ---- int buf_signcount(buf_T *buf, linenr_T lnum) { ! signlist_T *sign; // a sign in the signlist int count = 0; ! FOR_ALL_SIGNS_IN_BUF(buf) if (sign->lnum == lnum) if (sign_get_image(sign->typenr) != NULL) count++; *************** *** 6123,6150 **** # endif /* FEAT_SIGN_ICONS */ # endif /* FEAT_NETBEANS_INTG */ - /* ! * Delete signs in buffer "buf". */ void ! buf_delete_signs(buf_T *buf) { signlist_T *next; ! /* When deleting the last sign need to redraw the windows to remove the ! * sign column. Not when curwin is NULL (this means we're exiting). */ if (buf->b_signlist != NULL && curwin != NULL) { redraw_buf_later(buf, NOT_VALID); changed_cline_bef_curs(); } ! while (buf->b_signlist != NULL) { ! next = buf->b_signlist->next; ! vim_free(buf->b_signlist); ! buf->b_signlist = next; } } --- 6241,6279 ---- # endif /* FEAT_SIGN_ICONS */ # endif /* FEAT_NETBEANS_INTG */ /* ! * Delete signs in group 'group' in buffer "buf". If 'group' is '*', then ! * delete all the signs. */ void ! buf_delete_signs(buf_T *buf, char_u *group) { + signlist_T *sign; + signlist_T **lastp; // pointer to pointer to current sign signlist_T *next; ! // When deleting the last sign need to redraw the windows to remove the ! // sign column. Not when curwin is NULL (this means we're exiting). if (buf->b_signlist != NULL && curwin != NULL) { redraw_buf_later(buf, NOT_VALID); changed_cline_bef_curs(); } ! lastp = &buf->b_signlist; ! for (sign = buf->b_signlist; sign != NULL; sign = next) { ! next = sign->next; ! if (sign_in_group(sign, group)) ! { ! *lastp = next; ! if (next != NULL) ! next->prev = sign->prev; ! vim_free(sign->group); ! vim_free(sign); ! } ! else ! lastp = &sign->next; } } *************** *** 6158,6175 **** FOR_ALL_BUFFERS(buf) if (buf->b_signlist != NULL) ! buf_delete_signs(buf); } /* * List placed signs for "rbuf". If "rbuf" is NULL do it for all buffers. */ void ! sign_list_placed(buf_T *rbuf) { buf_T *buf; ! signlist_T *p; char lbuf[BUFSIZ]; MSG_PUTS_TITLE(_("\n--- Signs ---")); msg_putchar('\n'); --- 6287,6305 ---- FOR_ALL_BUFFERS(buf) if (buf->b_signlist != NULL) ! buf_delete_signs(buf, (char_u *)"*"); } /* * List placed signs for "rbuf". If "rbuf" is NULL do it for all buffers. */ void ! sign_list_placed(buf_T *rbuf, char_u *sign_group) { buf_T *buf; ! signlist_T *sign; char lbuf[BUFSIZ]; + char group[BUFSIZ]; MSG_PUTS_TITLE(_("\n--- Signs ---")); msg_putchar('\n'); *************** *** 6185,6194 **** MSG_PUTS_ATTR(lbuf, HL_ATTR(HLF_D)); msg_putchar('\n'); } ! for (p = buf->b_signlist; p != NULL && !got_int; p = p->next) { ! vim_snprintf(lbuf, BUFSIZ, _(" line=%ld id=%d name=%s"), ! (long)p->lnum, p->id, sign_typenr2name(p->typenr)); MSG_PUTS(lbuf); msg_putchar('\n'); } --- 6315,6332 ---- MSG_PUTS_ATTR(lbuf, HL_ATTR(HLF_D)); msg_putchar('\n'); } ! FOR_ALL_SIGNS_IN_BUF(buf) { ! if (!sign_in_group(sign, sign_group)) ! continue; ! if (sign->group != NULL) ! vim_snprintf(group, BUFSIZ, " group=%s", sign->group); ! else ! group[0] = '\0'; ! vim_snprintf(lbuf, BUFSIZ, _(" line=%ld id=%d%s name=%s " ! "priority=%d"), ! (long)sign->lnum, sign->id, group, ! sign_typenr2name(sign->typenr), sign->priority); MSG_PUTS(lbuf); msg_putchar('\n'); } *************** *** 6210,6216 **** { signlist_T *sign; /* a sign in a b_signlist */ ! for (sign = curbuf->b_signlist; sign != NULL; sign = sign->next) { if (sign->lnum >= line1 && sign->lnum <= line2) { --- 6348,6354 ---- { signlist_T *sign; /* a sign in a b_signlist */ ! FOR_ALL_SIGNS_IN_BUF(curbuf) { if (sign->lnum >= line1 && sign->lnum <= line2) { *** ../vim-8.1.0613/src/evalfunc.c 2018-12-14 15:38:28.327597664 +0100 --- src/evalfunc.c 2018-12-21 14:36:57.242303552 +0100 *************** *** 367,372 **** --- 367,380 ---- #endif /* FEAT_CRYPT */ static void f_shellescape(typval_T *argvars, typval_T *rettv); static void f_shiftwidth(typval_T *argvars, typval_T *rettv); + #ifdef FEAT_SIGNS + static void f_sign_define(typval_T *argvars, typval_T *rettv); + static void f_sign_getdefined(typval_T *argvars, typval_T *rettv); + static void f_sign_getplaced(typval_T *argvars, typval_T *rettv); + static void f_sign_place(typval_T *argvars, typval_T *rettv); + static void f_sign_undefine(typval_T *argvars, typval_T *rettv); + static void f_sign_unplace(typval_T *argvars, typval_T *rettv); + #endif static void f_simplify(typval_T *argvars, typval_T *rettv); #ifdef FEAT_FLOAT static void f_sin(typval_T *argvars, typval_T *rettv); *************** *** 847,852 **** --- 855,868 ---- #endif {"shellescape", 1, 2, f_shellescape}, {"shiftwidth", 0, 1, f_shiftwidth}, + #ifdef FEAT_SIGNS + {"sign_define", 1, 2, f_sign_define}, + {"sign_getdefined", 0, 1, f_sign_getdefined}, + {"sign_getplaced", 0, 2, f_sign_getplaced}, + {"sign_place", 4, 5, f_sign_place}, + {"sign_undefine", 0, 1, f_sign_undefine}, + {"sign_unplace", 1, 2, f_sign_unplace}, + #endif {"simplify", 1, 1, f_simplify}, #ifdef FEAT_FLOAT {"sin", 1, 1, f_sin}, *************** *** 4417,4435 **** get_buffer_signs(buf_T *buf, list_T *l) { signlist_T *sign; ! for (sign = buf->b_signlist; sign; sign = sign->next) { ! dict_T *d = dict_alloc(); ! ! if (d != NULL) ! { ! dict_add_number(d, "id", sign->id); ! dict_add_number(d, "lnum", sign->lnum); ! dict_add_string(d, "name", sign_typenr2name(sign->typenr)); ! list_append_dict(l, d); - } } } #endif --- 4433,4444 ---- get_buffer_signs(buf_T *buf, list_T *l) { signlist_T *sign; + dict_T *d; ! FOR_ALL_SIGNS_IN_BUF(buf) { ! if ((d = sign_get_info(sign)) != NULL) list_append_dict(l, d); } } #endif *************** *** 11285,11290 **** --- 11294,11612 ---- rettv->vval.v_number = get_sw_value(curbuf); } + #ifdef FEAT_SIGNS + /* + * "sign_define()" function + */ + static 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 = get_tv_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); + } + + /* + * "sign_getdefined()" function + */ + static void + f_sign_getdefined(typval_T *argvars, typval_T *rettv) + { + char_u *name = NULL; + + if (rettv_list_alloc_id(rettv, aid_sign_getdefined) != OK) + return; + + if (argvars[0].v_type != VAR_UNKNOWN) + name = get_tv_string(&argvars[0]); + + sign_getlist(name, rettv->vval.v_list); + } + + /* + * "sign_getplaced()" function + */ + static void + f_sign_getplaced(typval_T *argvars, typval_T *rettv) + { + buf_T *buf = NULL; + dict_T *dict; + dictitem_T *di; + linenr_T lnum = 0; + int sign_id = 0; + char_u *group = NULL; + int notanum = FALSE; + + if (rettv_list_alloc_id(rettv, aid_sign_getplaced) != OK) + return; + + if (argvars[0].v_type != VAR_UNKNOWN) + { + // get signs placed in this buffer + buf = find_buffer(&argvars[0]); + if (buf == NULL) + { + EMSG2(_("E158: Invalid buffer name: %s"), + get_tv_string(&argvars[0])); + return; + } + + if (argvars[1].v_type != VAR_UNKNOWN) + { + if (argvars[1].v_type != VAR_DICT || + ((dict = argvars[1].vval.v_dict) == NULL)) + { + EMSG(_(e_dictreq)); + return; + } + if ((di = dict_find(dict, (char_u *)"lnum", -1)) != NULL) + { + // get signs placed at this line + (void)get_tv_number_chk(&di->di_tv, ¬anum); + if (notanum) + return; + lnum = get_tv_lnum(&di->di_tv); + } + if ((di = dict_find(dict, (char_u *)"id", -1)) != NULL) + { + // get sign placed with this identifier + sign_id = (int)get_tv_number_chk(&di->di_tv, ¬anum); + if (notanum) + return; + } + if ((di = dict_find(dict, (char_u *)"group", -1)) != NULL) + { + group = get_tv_string_chk(&di->di_tv); + if (group == NULL) + return; + } + } + } + + sign_get_placed(buf, lnum, sign_id, group, rettv->vval.v_list); + } + + /* + * "sign_place()" function + */ + static 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 identifer + sign_id = (int)get_tv_number_chk(&argvars[0], ¬anum); + if (notanum) + return; + if (sign_id < 0) + { + EMSG(_(e_invarg)); + return; + } + + // Sign group + group = get_tv_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 = get_tv_string_chk(&argvars[2]); + if (sign_name == NULL) + goto cleanup; + + // Buffer to place the sign + buf = find_buffer(&argvars[3]); + if (buf == NULL) + { + EMSG2(_("E158: Invalid buffer name: %s"), get_tv_string(&argvars[2])); + 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)get_tv_number_chk(&di->di_tv, ¬anum); + if (notanum) + goto cleanup; + lnum = get_tv_lnum(&di->di_tv); + } + if ((di = dict_find(dict, (char_u *)"priority", -1)) != NULL) + { + // Sign priority + prio = (int)get_tv_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); + } + + /* + * "sign_undefine()" function + */ + static void + f_sign_undefine(typval_T *argvars, typval_T *rettv) + { + char_u *name; + + rettv->vval.v_number = -1; + + if (argvars[0].v_type == VAR_UNKNOWN) + { + // Free all the signs + free_signs(); + rettv->vval.v_number = 0; + } + else + { + // Free only the specified sign + name = get_tv_string_chk(&argvars[0]); + if (name == NULL) + return; + + if (sign_undefine_by_name(name) == OK) + rettv->vval.v_number = 0; + } + } + + /* + * "sign_unplace()" function + */ + static 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 = get_tv_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)); + return; + } + dict = argvars[1].vval.v_dict; + + if ((di = dict_find(dict, (char_u *)"buffer", -1)) != NULL) + { + buf = find_buffer(&di->di_tv); + if (buf == NULL) + { + EMSG2(_("E158: Invalid buffer name: %s"), + get_tv_string(&di->di_tv)); + return; + } + } + 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) == OK) + rettv->vval.v_number = 0; + } + else + { + if (sign_unplace(sign_id, group, buf) == OK) + rettv->vval.v_number = 0; + } + vim_free(group); + } + #endif + /* * "simplify()" function */ *** ../vim-8.1.0613/src/ex_cmds.c 2018-12-14 19:37:03.677899412 +0100 --- src/ex_cmds.c 2018-12-21 14:36:57.242303552 +0100 *************** *** 7644,7649 **** --- 7644,7932 ---- } /* + * Find a sign by name. Also returns pointer to the previous sign. + */ + static sign_T * + sign_find(char_u *name, sign_T **sp_prev) + { + sign_T *sp; + + if (sp_prev != NULL) + *sp_prev = NULL; + for (sp = first_sign; sp != NULL; sp = sp->sn_next) + { + if (STRCMP(sp->sn_name, name) == 0) + break; + if (sp_prev != NULL) + *sp_prev = sp; + } + + return sp; + } + + /* + * Define a new sign or update an existing sign + */ + int + sign_define_by_name( + char_u *name, + char_u *icon, + char_u *linehl, + char_u *text, + char_u *texthl) + { + sign_T *sp_prev; + sign_T *sp; + + sp = sign_find(name, &sp_prev); + if (sp == NULL) + { + sign_T *lp; + int start = next_sign_typenr; + + // Allocate a new sign. + sp = (sign_T *)alloc_clear_id((unsigned)sizeof(sign_T), + aid_sign_define_by_name); + if (sp == NULL) + return FAIL; + + // Check that next_sign_typenr is not already being used. + // This only happens after wrapping around. Hopefully + // another one got deleted and we can use its number. + for (lp = first_sign; lp != NULL; ) + { + if (lp->sn_typenr == next_sign_typenr) + { + ++next_sign_typenr; + if (next_sign_typenr == MAX_TYPENR) + next_sign_typenr = 1; + if (next_sign_typenr == start) + { + vim_free(sp); + EMSG(_("E612: Too many signs defined")); + return FAIL; + } + lp = first_sign; // start all over + continue; + } + lp = lp->sn_next; + } + + sp->sn_typenr = next_sign_typenr; + if (++next_sign_typenr == MAX_TYPENR) + next_sign_typenr = 1; // wrap around + + sp->sn_name = vim_strsave(name); + if (sp->sn_name == NULL) // out of memory + { + vim_free(sp); + return FAIL; + } + + // add the new sign to the list of signs + if (sp_prev == NULL) + first_sign = sp; + else + sp_prev->sn_next = sp; + } + + // set values for a defined sign. + if (icon != NULL) + { + vim_free(sp->sn_icon); + sp->sn_icon = vim_strsave(icon); + backslash_halve(sp->sn_icon); + # ifdef FEAT_SIGN_ICONS + if (gui.in_use) + { + out_flush(); + if (sp->sn_image != NULL) + gui_mch_destroy_sign(sp->sn_image); + sp->sn_image = gui_mch_register_sign(sp->sn_icon); + } + # endif + } + + if (text != NULL) + { + char_u *s; + char_u *endp; + int cells; + int len; + + endp = text + (int)STRLEN(text); + for (s = text; s + 1 < endp; ++s) + if (*s == '\\') + { + // Remove a backslash, so that it is possible + // to use a space. + STRMOVE(s, s + 1); + --endp; + } + # ifdef FEAT_MBYTE + // Count cells and check for non-printable chars + if (has_mbyte) + { + cells = 0; + for (s = text; s < endp; s += (*mb_ptr2len)(s)) + { + if (!vim_isprintc((*mb_ptr2char)(s))) + break; + cells += (*mb_ptr2cells)(s); + } + } + else + # endif + { + for (s = text; s < endp; ++s) + if (!vim_isprintc(*s)) + break; + cells = (int)(s - text); + } + // Currently must be one or two display cells + if (s != endp || cells < 1 || cells > 2) + { + EMSG2(_("E239: Invalid sign text: %s"), text); + return FAIL; + } + + vim_free(sp->sn_text); + // Allocate one byte more if we need to pad up + // with a space. + len = (int)(endp - text + ((cells == 1) ? 1 : 0)); + sp->sn_text = vim_strnsave(text, len); + + if (sp->sn_text != NULL && cells == 1) + STRCPY(sp->sn_text + len - 1, " "); + } + + if (linehl != NULL) + sp->sn_line_hl = syn_check_group(linehl, (int)STRLEN(linehl)); + + if (texthl != NULL) + sp->sn_text_hl = syn_check_group(texthl, (int)STRLEN(texthl)); + + return OK; + } + + /* + * Free the sign specified by 'name'. + */ + int + sign_undefine_by_name(char_u *name) + { + sign_T *sp_prev; + sign_T *sp; + + sp = sign_find(name, &sp_prev); + if (sp == NULL) + { + EMSG2(_("E155: Unknown sign: %s"), name); + return FAIL; + } + sign_undefine(sp, sp_prev); + + return OK; + } + + /* + * List the signs matching 'name' + */ + static void + sign_list_by_name(char_u *name) + { + sign_T *sp; + + sp = sign_find(name, NULL); + if (sp != NULL) + sign_list_defined(sp); + else + EMSG2(_("E155: Unknown sign: %s"), name); + } + + /* + * Place a sign at the specifed file location or update a sign. + */ + int + sign_place( + int *sign_id, + char_u *sign_group, + char_u *sign_name, + buf_T *buf, + linenr_T lnum, + int prio) + { + sign_T *sp; + + // Check for reserved character '*' in group name + if (sign_group != NULL && (*sign_group == '*' || *sign_group == '\0')) + return FAIL; + + for (sp = first_sign; sp != NULL; sp = sp->sn_next) + if (STRCMP(sp->sn_name, sign_name) == 0) + break; + if (sp == NULL) + { + EMSG2(_("E155: Unknown sign: %s"), sign_name); + return FAIL; + } + if (*sign_id == 0) + { + // Allocate a new sign id + int id = 1; + signlist_T *sign; + + while ((sign = buf_getsign_with_id(buf, id, sign_group)) != NULL) + id++; + + *sign_id = id; + } + + if (lnum > 0) + // ":sign place {id} line={lnum} name={name} file={fname}": + // 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) + update_debug_sign(buf, lnum); + else + { + EMSG2(_("E885: Not possible to change sign %s"), sign_name); + return FAIL; + } + + return OK; + } + + /* + * Unplace the specified sign + */ + int + sign_unplace(int sign_id, char_u *sign_group, buf_T *buf) + { + if (sign_id == 0) + { + // Delete all the signs in the specified buffer + redraw_buf_later(buf, NOT_VALID); + buf_delete_signs(buf, sign_group); + } + else + { + linenr_T lnum; + + // Delete only the specified signs + lnum = buf_delsign(buf, sign_id, sign_group); + if (lnum == 0) + return FAIL; + update_debug_sign(buf, lnum); + } + + return OK; + } + + /* * ":sign" command */ void *************** *** 7653,7659 **** char_u *p; int idx; sign_T *sp; - sign_T *sp_prev; buf_T *buf = NULL; /* Parse the subcommand. */ --- 7936,7941 ---- *************** *** 7681,7686 **** --- 7963,7974 ---- EMSG(_("E156: Missing sign name")); else { + char_u *name; + char_u *icon = NULL; + char_u *text = NULL; + char_u *linehl = NULL; + char_u *texthl = NULL; + /* Isolate the sign name. If it's a number skip leading zeroes, * so that "099" and "99" are the same sign. But keep "0". */ p = skiptowhite(arg); *************** *** 7688,7753 **** *p++ = NUL; while (arg[0] == '0' && arg[1] != NUL) ++arg; - sp_prev = NULL; - for (sp = first_sign; sp != NULL; sp = sp->sn_next) - { - if (STRCMP(sp->sn_name, arg) == 0) - break; - sp_prev = sp; - } if (idx == SIGNCMD_DEFINE) { ! /* ":sign define {name} ...": define a sign */ ! if (sp == NULL) ! { ! sign_T *lp; ! int start = next_sign_typenr; ! ! /* Allocate a new sign. */ ! sp = (sign_T *)alloc_clear((unsigned)sizeof(sign_T)); ! if (sp == NULL) ! return; ! ! /* Check that next_sign_typenr is not already being used. ! * This only happens after wrapping around. Hopefully ! * another one got deleted and we can use its number. */ ! for (lp = first_sign; lp != NULL; ) ! { ! if (lp->sn_typenr == next_sign_typenr) ! { ! ++next_sign_typenr; ! if (next_sign_typenr == MAX_TYPENR) ! next_sign_typenr = 1; ! if (next_sign_typenr == start) ! { ! vim_free(sp); ! EMSG(_("E612: Too many signs defined")); ! return; ! } ! lp = first_sign; /* start all over */ ! continue; ! } ! lp = lp->sn_next; ! } ! ! sp->sn_typenr = next_sign_typenr; ! if (++next_sign_typenr == MAX_TYPENR) ! next_sign_typenr = 1; /* wrap around */ ! ! sp->sn_name = vim_strsave(arg); ! if (sp->sn_name == NULL) /* out of memory */ ! { ! vim_free(sp); ! return; ! } ! /* add the new sign to the list of signs */ ! if (sp_prev == NULL) ! first_sign = sp; ! else ! sp_prev->sn_next = sp; ! } /* set values for a defined sign. */ for (;;) --- 7976,7988 ---- *p++ = NUL; while (arg[0] == '0' && arg[1] != NUL) ++arg; + name = vim_strsave(arg); if (idx == SIGNCMD_DEFINE) { ! int failed = FALSE; ! /* ":sign define {name} ...": define a sign */ /* set values for a defined sign. */ for (;;) *************** *** 7759,7854 **** if (STRNCMP(arg, "icon=", 5) == 0) { arg += 5; ! vim_free(sp->sn_icon); ! sp->sn_icon = vim_strnsave(arg, (int)(p - arg)); ! backslash_halve(sp->sn_icon); ! # ifdef FEAT_SIGN_ICONS ! if (gui.in_use) ! { ! out_flush(); ! if (sp->sn_image != NULL) ! gui_mch_destroy_sign(sp->sn_image); ! sp->sn_image = gui_mch_register_sign(sp->sn_icon); ! } ! # endif } else if (STRNCMP(arg, "text=", 5) == 0) { - char_u *s; - int cells; - int len; - arg += 5; ! for (s = arg; s + 1 < p; ++s) ! if (*s == '\\') ! { ! // Remove a backslash, so that it is possible ! // to use a space. ! STRMOVE(s, s + 1); ! --p; ! } ! # ifdef FEAT_MBYTE ! /* Count cells and check for non-printable chars */ ! if (has_mbyte) ! { ! cells = 0; ! for (s = arg; s < p; s += (*mb_ptr2len)(s)) ! { ! if (!vim_isprintc((*mb_ptr2char)(s))) ! break; ! cells += (*mb_ptr2cells)(s); ! } ! } ! else ! # endif ! { ! for (s = arg; s < p; ++s) ! if (!vim_isprintc(*s)) ! break; ! cells = (int)(s - arg); ! } ! /* Currently must be one or two display cells */ ! if (s != p || cells < 1 || cells > 2) ! { ! *p = NUL; ! EMSG2(_("E239: Invalid sign text: %s"), arg); ! return; ! } ! ! vim_free(sp->sn_text); ! /* Allocate one byte more if we need to pad up ! * with a space. */ ! len = (int)(p - arg + ((cells == 1) ? 1 : 0)); ! sp->sn_text = vim_strnsave(arg, len); ! ! if (sp->sn_text != NULL && cells == 1) ! STRCPY(sp->sn_text + len - 1, " "); } else if (STRNCMP(arg, "linehl=", 7) == 0) { arg += 7; ! sp->sn_line_hl = syn_check_group(arg, (int)(p - arg)); } else if (STRNCMP(arg, "texthl=", 7) == 0) { arg += 7; ! sp->sn_text_hl = syn_check_group(arg, (int)(p - arg)); } else { EMSG2(_(e_invarg2), arg); ! return; } } } - else if (sp == NULL) - EMSG2(_("E155: Unknown sign: %s"), arg); else if (idx == SIGNCMD_LIST) /* ":sign list {name}" */ ! sign_list_defined(sp); else /* ":sign undefine {name}" */ ! sign_undefine(sp, sp_prev); } } else --- 7994,8041 ---- if (STRNCMP(arg, "icon=", 5) == 0) { arg += 5; ! icon = vim_strnsave(arg, (int)(p - arg)); } else if (STRNCMP(arg, "text=", 5) == 0) { arg += 5; ! text = vim_strnsave(arg, (int)(p - arg)); } else if (STRNCMP(arg, "linehl=", 7) == 0) { arg += 7; ! linehl = vim_strnsave(arg, (int)(p - arg)); } else if (STRNCMP(arg, "texthl=", 7) == 0) { arg += 7; ! texthl = vim_strnsave(arg, (int)(p - arg)); } else { EMSG2(_(e_invarg2), arg); ! failed = TRUE; ! break; } } + + if (!failed) + sign_define_by_name(name, icon, linehl, text, texthl); + + vim_free(icon); + vim_free(text); + vim_free(linehl); + vim_free(texthl); } else if (idx == SIGNCMD_LIST) /* ":sign list {name}" */ ! sign_list_by_name(name); else /* ":sign undefine {name}" */ ! sign_undefine_by_name(name); ! ! vim_free(name); ! return; } } else *************** *** 7856,7879 **** int id = -1; linenr_T lnum = -1; char_u *sign_name = NULL; char_u *arg1; if (*arg == NUL) { if (idx == SIGNCMD_PLACE) { /* ":sign place": list placed signs in all buffers */ ! sign_list_placed(NULL); } else if (idx == SIGNCMD_UNPLACE) { /* ":sign unplace": remove placed sign at cursor */ id = buf_findsign_id(curwin->w_buffer, curwin->w_cursor.lnum); if (id > 0) ! { ! buf_delsign(curwin->w_buffer, id); ! update_debug_sign(curwin->w_buffer, curwin->w_cursor.lnum); ! } else EMSG(_("E159: Missing sign number")); } --- 8043,8066 ---- int id = -1; linenr_T lnum = -1; char_u *sign_name = NULL; + char_u *group = NULL; + int prio = SIGN_DEF_PRIO; char_u *arg1; + int bufarg = FALSE; if (*arg == NUL) { if (idx == SIGNCMD_PLACE) { /* ":sign place": list placed signs in all buffers */ ! sign_list_placed(NULL, NULL); } else if (idx == SIGNCMD_UNPLACE) { /* ":sign unplace": remove placed sign at cursor */ id = buf_findsign_id(curwin->w_buffer, curwin->w_cursor.lnum); if (id > 0) ! sign_unplace(id, NULL, curwin->w_buffer); else EMSG(_("E159: Missing sign number")); } *************** *** 7906,7923 **** { /* ":sign unplace {id}": remove placed sign by number */ FOR_ALL_BUFFERS(buf) ! if ((lnum = buf_delsign(buf, id)) != 0) ! update_debug_sign(buf, lnum); return; } } } /* ! * Check for line={lnum} name={name} and file={fname} or buffer={nr}. ! * Leave "arg" pointing to {fname}. */ ! for (;;) { if (STRNCMP(arg, "line=", 5) == 0) { --- 8093,8109 ---- { /* ":sign unplace {id}": remove placed sign by number */ FOR_ALL_BUFFERS(buf) ! sign_unplace(id, NULL, buf); return; } } } /* ! * Check for line={lnum} name={name} group={group} priority={prio} ! * and file={fname} or buffer={nr}. Leave "arg" pointing to {fname}. */ ! while (*arg != NUL) { if (STRNCMP(arg, "line=", 5) == 0) { *************** *** 7945,7954 **** while (sign_name[0] == '0' && sign_name[1] != NUL) ++sign_name; } else if (STRNCMP(arg, "file=", 5) == 0) { arg += 5; ! buf = buflist_findname(arg); break; } else if (STRNCMP(arg, "buffer=", 7) == 0) --- 8131,8155 ---- while (sign_name[0] == '0' && sign_name[1] != NUL) ++sign_name; } + else if (STRNCMP(arg, "group=", 6) == 0) + { + arg += 6; + group = arg; + arg = skiptowhite(arg); + if (*arg != NUL) + *arg++ = NUL; + } + else if (STRNCMP(arg, "priority=", 9) == 0) + { + arg += 9; + prio = atoi((char *)arg); + arg = skiptowhite(arg); + } else if (STRNCMP(arg, "file=", 5) == 0) { arg += 5; ! buf = buflist_findname_exp(arg); ! bufarg = TRUE; break; } else if (STRNCMP(arg, "buffer=", 7) == 0) *************** *** 7957,7962 **** --- 8158,8164 ---- buf = buflist_findnr((int)getdigits(&arg)); if (*skipwhite(arg) != NUL) EMSG(_(e_trailing)); + bufarg = TRUE; break; } else *************** *** 7967,7990 **** arg = skipwhite(arg); } ! if (buf == NULL) { EMSG2(_("E158: Invalid buffer name: %s"), arg); } else if (id <= 0 && !(idx == SIGNCMD_UNPLACE && id == -2)) { ! if (lnum >= 0 || sign_name != NULL) EMSG(_(e_invarg)); else ! /* ":sign place file={fname}": list placed signs in one file */ ! sign_list_placed(buf); } else if (idx == SIGNCMD_JUMP) { /* ":sign jump {id} file={fname}" */ if (lnum >= 0 || sign_name != NULL) EMSG(_(e_invarg)); ! else if ((lnum = buf_findsign(buf, id)) > 0) { /* goto a sign ... */ if (buf_jump_open_win(buf) != NULL) { /* ... in a current window */ --- 8169,8201 ---- arg = skipwhite(arg); } ! if ((!bufarg && group == NULL) || (group != NULL && *group == '\0')) ! { ! // File or buffer is not specified or an empty group is used ! EMSG(_(e_invarg)); ! return; ! } ! ! if (bufarg && buf == NULL) { EMSG2(_("E158: Invalid buffer name: %s"), arg); } else if (id <= 0 && !(idx == SIGNCMD_UNPLACE && id == -2)) { ! if ((group == NULL) && (lnum >= 0 || sign_name != NULL)) EMSG(_(e_invarg)); else ! // ":sign place file={fname}": list placed signs in one file ! // ":sign place group={group} file={fname}" ! // ":sign place group=* file={fname}" ! sign_list_placed(buf, group); } else if (idx == SIGNCMD_JUMP) { /* ":sign jump {id} file={fname}" */ if (lnum >= 0 || sign_name != NULL) EMSG(_(e_invarg)); ! else if ((lnum = buf_findsign(buf, id, group)) > 0) { /* goto a sign ... */ if (buf_jump_open_win(buf) != NULL) { /* ... in a current window */ *************** *** 8021,8065 **** EMSG(_(e_invarg)); else if (id == -2) { ! /* ":sign unplace * file={fname}" */ ! redraw_buf_later(buf, NOT_VALID); ! buf_delete_signs(buf); } else { ! /* ":sign unplace {id} file={fname}" */ ! lnum = buf_delsign(buf, id); ! update_debug_sign(buf, lnum); } } /* idx == SIGNCMD_PLACE */ ! else if (sign_name != NULL) ! { ! for (sp = first_sign; sp != NULL; sp = sp->sn_next) ! if (STRCMP(sp->sn_name, sign_name) == 0) ! break; ! if (sp == NULL) ! { ! EMSG2(_("E155: Unknown sign: %s"), sign_name); ! return; ! } ! if (lnum > 0) ! /* ":sign place {id} line={lnum} name={name} file={fname}": ! * place a sign */ ! buf_addsign(buf, id, lnum, sp->sn_typenr); ! else ! /* ":sign place {id} file={fname}": change sign type */ ! lnum = buf_change_sign_type(buf, id, sp->sn_typenr); ! if (lnum > 0) ! update_debug_sign(buf, lnum); ! else ! EMSG2(_("E885: Not possible to change sign %s"), sign_name); ! } else EMSG(_(e_invarg)); } } # if defined(FEAT_SIGN_ICONS) || defined(PROTO) /* * Allocate the icons. Called when the GUI has started. Allows defining --- 8232,8395 ---- EMSG(_(e_invarg)); else if (id == -2) { ! if (buf != NULL) ! // ":sign unplace * file={fname}" ! sign_unplace(0, group, buf); ! else ! // ":sign unplace * group=*": remove all placed signs ! FOR_ALL_BUFFERS(buf) ! if (buf->b_signlist != NULL) ! buf_delete_signs(buf, group); } else { ! if (buf != NULL) ! // ":sign unplace {id} file={fname}" ! // ":sign unplace {id} group={group} file={fname}" ! // ":sign unplace {id} group=* file={fname}" ! sign_unplace(id, group, buf); ! else ! // ":sign unplace {id} group={group}": ! // ":sign unplace {id} group=*": ! // remove all placed signs in this group. ! FOR_ALL_BUFFERS(buf) ! if (buf->b_signlist != NULL) ! sign_unplace(id, group, buf); } } /* idx == SIGNCMD_PLACE */ ! else if (sign_name != NULL && buf != NULL) ! sign_place(&id, group, sign_name, buf, lnum, prio); else EMSG(_(e_invarg)); } } + /* + * Return information about a specified sign + */ + static void + sign_getinfo(sign_T *sp, dict_T *retdict) + { + char_u *p; + + dict_add_string(retdict, "name", (char_u *)sp->sn_name); + if (sp->sn_icon != NULL) + dict_add_string(retdict, "icon", (char_u *)sp->sn_icon); + if (sp->sn_text != NULL) + dict_add_string(retdict, "text", (char_u *)sp->sn_text); + if (sp->sn_line_hl > 0) + { + p = get_highlight_name_ext(NULL, sp->sn_line_hl - 1, FALSE); + if (p == NULL) + p = (char_u *)"NONE"; + dict_add_string(retdict, "linehl", (char_u *)p); + } + if (sp->sn_text_hl > 0) + { + p = get_highlight_name_ext(NULL, sp->sn_text_hl - 1, FALSE); + if (p == NULL) + p = (char_u *)"NONE"; + dict_add_string(retdict, "texthl", (char_u *)p); + } + } + + /* + * If 'name' is NULL, return a list of all the defined signs. + * Otherwise, return information about the specified sign. + */ + void + sign_getlist(char_u *name, list_T *retlist) + { + sign_T *sp = first_sign; + dict_T *dict; + + if (name != NULL) + { + sp = sign_find(name, NULL); + if (sp == NULL) + return; + } + + for (; sp != NULL && !got_int; sp = sp->sn_next) + { + if ((dict = dict_alloc_id(aid_sign_getlist)) == NULL) + return; + if (list_append_dict(retlist, dict) == FAIL) + return; + sign_getinfo(sp, dict); + + if (name != NULL) // handle only the specified sign + break; + } + } + + /* + * Return information about all the signs placed in a buffer + */ + static void + sign_get_placed_in_buf( + buf_T *buf, + linenr_T lnum, + int sign_id, + char_u *sign_group, + list_T *retlist) + { + dict_T *d; + list_T *l; + signlist_T *sign; + dict_T *sdict; + + if ((d = dict_alloc_id(aid_sign_getplaced_dict)) == NULL) + return; + list_append_dict(retlist, d); + + dict_add_number(d, "bufnr", (long)buf->b_fnum); + + if ((l = list_alloc_id(aid_sign_getplaced_list)) == NULL) + return; + dict_add_list(d, "signs", l); + + FOR_ALL_SIGNS_IN_BUF(buf) + { + if (!sign_in_group(sign, sign_group)) + continue; + if ((lnum == 0 && sign_id == 0) || + (sign_id == 0 && lnum == sign->lnum) || + (lnum == 0 && sign_id == sign->id) || + (lnum == sign->lnum && sign_id == sign->id)) + { + if ((sdict = sign_get_info(sign)) != NULL) + list_append_dict(l, sdict); + } + } + } + + /* + * Get a list of signs placed in buffer 'buf'. If 'num' is non-zero, return the + * sign placed at the line number. If 'lnum' is zero, return all the signs + * placed in 'buf'. If 'buf' is NULL, return signs placed in all the buffers. + */ + void + sign_get_placed( + buf_T *buf, + linenr_T lnum, + int sign_id, + char_u *sign_group, + list_T *retlist) + { + if (buf != NULL) + sign_get_placed_in_buf(buf, lnum, sign_id, sign_group, retlist); + else + { + FOR_ALL_BUFFERS(buf) + { + if (buf->b_signlist != NULL) + sign_get_placed_in_buf(buf, 0, sign_id, sign_group, retlist); + } + } + } + # if defined(FEAT_SIGN_ICONS) || defined(PROTO) /* * Allocate the icons. Called when the GUI has started. Allows defining *************** *** 8214,8220 **** return (char_u *)_("[Deleted]"); } - # if defined(EXITFREE) || defined(PROTO) /* * Undefine/free all signs. */ --- 8544,8549 ---- *************** *** 8224,8230 **** while (first_sign != NULL) sign_undefine(first_sign, NULL); } - # endif # if defined(FEAT_CMDL_COMPL) || defined(PROTO) static enum --- 8553,8558 ---- *** ../vim-8.1.0613/src/globals.h 2018-11-10 17:33:23.087518814 +0100 --- src/globals.h 2018-12-21 14:36:57.242303552 +0100 *************** *** 606,611 **** --- 606,615 ---- #define FOR_ALL_BUFFERS(buf) for (buf = firstbuf; buf != NULL; buf = buf->b_next) + // Iterate through all the signs placed in a buffer + #define FOR_ALL_SIGNS_IN_BUF(buf) \ + for (sign = buf->b_signlist; sign != NULL; sign = sign->next) + /* Flag that is set when switching off 'swapfile'. It means that all blocks * are to be loaded into memory. Shouldn't be global... */ EXTERN int mf_dont_release INIT(= FALSE); /* don't release blocks */ *** ../vim-8.1.0613/src/list.c 2018-11-11 15:20:32.432704446 +0100 --- src/list.c 2018-12-21 14:36:57.242303552 +0100 *************** *** 116,121 **** --- 116,135 ---- } /* + * Same as rettv_list_alloc() but uses an allocation id for testing. + */ + int + rettv_list_alloc_id(typval_T *rettv, alloc_id_T id UNUSED) + { + #ifdef FEAT_EVAL + if (alloc_fail_id == id && alloc_does_fail((long_u)sizeof(list_T))) + return FAIL; + #endif + return rettv_list_alloc(rettv); + } + + + /* * Set a list as the return value */ void *** ../vim-8.1.0613/src/misc2.c 2018-12-16 16:30:17.810717978 +0100 --- src/misc2.c 2018-12-21 14:36:57.246303525 +0100 *************** *** 895,900 **** --- 895,913 ---- } /* + * Same as alloc_clear() but with allocation id for testing + */ + char_u * + alloc_clear_id(unsigned size, alloc_id_T id UNUSED) + { + #ifdef FEAT_EVAL + if (alloc_fail_id == id && alloc_does_fail((long_u)size)) + return NULL; + #endif + return alloc_clear(size); + } + + /* * alloc() with check for maximum line length */ char_u * *** ../vim-8.1.0613/src/netbeans.c 2018-09-30 21:43:17.199693265 +0200 --- src/netbeans.c 2018-12-21 14:36:57.246303525 +0100 *************** *** 1042,1048 **** cp = (char *)args; serNum = strtol(cp, &cp, 10); /* If the sign isn't found linenum will be zero. */ ! linenum = (long)buf_findsign(buf->bufp, serNum); } #endif nb_reply_nr(cmdno, linenum); --- 1042,1048 ---- cp = (char *)args; serNum = strtol(cp, &cp, 10); /* If the sign isn't found linenum will be zero. */ ! linenum = (long)buf_findsign(buf->bufp, serNum, NULL); } #endif nb_reply_nr(cmdno, linenum); *************** *** 1256,1262 **** { nbdebug((" Deleting sign %d on line %d\n", id, i)); ! buf_delsign(buf->bufp, id); } else { --- 1256,1262 ---- { nbdebug((" Deleting sign %d on line %d\n", id, i)); ! buf_delsign(buf->bufp, id, NULL); } else { *** ../vim-8.1.0613/src/proto/buffer.pro 2018-12-18 21:56:25.084495836 +0100 --- src/proto/buffer.pro 2018-12-21 14:36:57.246303525 +0100 *************** *** 69,85 **** void switch_to_win_for_buf(buf_T *buf, win_T **save_curwinp, tabpage_T **save_curtabp, bufref_T *save_curbuf); void restore_win_for_buf(win_T *save_curwin, tabpage_T *save_curtab, bufref_T *save_curbuf); int find_win_for_buf(buf_T *buf, win_T **wp, tabpage_T **tp); ! void buf_addsign(buf_T *buf, int id, linenr_T lnum, int typenr); ! linenr_T buf_change_sign_type(buf_T *buf, int markId, int typenr); int buf_getsigntype(buf_T *buf, linenr_T lnum, int type); ! linenr_T buf_delsign(buf_T *buf, int id); ! int buf_findsign(buf_T *buf, int id); int buf_findsign_id(buf_T *buf, linenr_T lnum); int buf_findsigntype_id(buf_T *buf, linenr_T lnum, int typenr); int buf_signcount(buf_T *buf, linenr_T lnum); ! void buf_delete_signs(buf_T *buf); void buf_delete_all_signs(void); ! void sign_list_placed(buf_T *rbuf); void sign_mark_adjust(linenr_T line1, linenr_T line2, long amount, long amount_after); void set_buflisted(int on); int buf_contents_changed(buf_T *buf); --- 69,90 ---- void switch_to_win_for_buf(buf_T *buf, win_T **save_curwinp, tabpage_T **save_curtabp, bufref_T *save_curbuf); void restore_win_for_buf(win_T *save_curwin, tabpage_T *save_curtab, bufref_T *save_curbuf); int find_win_for_buf(buf_T *buf, win_T **wp, tabpage_T **tp); ! void buf_addsign(buf_T *buf, int id, char_u *group, int prio, linenr_T lnum, int typenr); ! linenr_T buf_change_sign_type(buf_T *buf, int markId, char_u *group, int typenr); int buf_getsigntype(buf_T *buf, linenr_T lnum, int type); ! linenr_T buf_delsign(buf_T *buf, int id, char_u *group); ! int buf_findsign(buf_T *buf, int id, char_u *group); ! #ifdef FEAT_SIGNS ! int sign_in_group(signlist_T *sign, char_u *group); ! dict_T *sign_get_info(signlist_T *sign); ! signlist_T *buf_getsign_with_id(buf_T *buf, int id, char_u *group); ! #endif int buf_findsign_id(buf_T *buf, linenr_T lnum); int buf_findsigntype_id(buf_T *buf, linenr_T lnum, int typenr); int buf_signcount(buf_T *buf, linenr_T lnum); ! void buf_delete_signs(buf_T *buf, char_u *group); void buf_delete_all_signs(void); ! void sign_list_placed(buf_T *rbuf, char_u *sign_group); void sign_mark_adjust(linenr_T line1, linenr_T line2, long amount, long amount_after); void set_buflisted(int on); int buf_contents_changed(buf_T *buf); *** ../vim-8.1.0613/src/proto/ex_cmds.pro 2018-12-18 21:56:25.084495836 +0100 --- src/proto/ex_cmds.pro 2018-12-21 14:36:57.246303525 +0100 *************** *** 61,70 **** --- 61,77 ---- void *sign_get_image(int typenr); char_u *sign_typenr2name(int typenr); void free_signs(void); + void free_signs(void); char_u *get_sign_name(expand_T *xp, int idx); void set_context_in_sign_cmd(expand_T *xp, char_u *arg); void ex_smile(exarg_T *eap); void ex_drop(exarg_T *eap); char_u *skip_vimgrep_pat(char_u *p, char_u **s, int *flags); void ex_oldfiles(exarg_T *eap); + int sign_define_by_name(char_u *name, char_u *icon, char_u *linehl, char_u *text, char_u *texthl); + int sign_undefine_by_name(char_u *name); + void sign_getlist(char_u *name, list_T *retlist); + int sign_place(int *sign_id, char_u *group, char_u *sign_name, buf_T *buf, linenr_T lnum, int prio); + int sign_unplace(int id, char_u *group, buf_T *buf); + void sign_get_placed(buf_T *buf, linenr_T lnum, int id, char_u *group, list_T *retlist); /* vim: set ft=c : */ *** ../vim-8.1.0613/src/proto/list.pro 2018-11-11 15:20:32.436704418 +0100 --- src/proto/list.pro 2018-12-21 14:36:57.246303525 +0100 *************** *** 5,10 **** --- 5,11 ---- list_T *list_alloc(void); list_T *list_alloc_id(alloc_id_T id); int rettv_list_alloc(typval_T *rettv); + int rettv_list_alloc_id(typval_T *rettv, alloc_id_T id); void rettv_list_set(typval_T *rettv, list_T *l); void list_unref(list_T *l); int list_free_nonref(int copyID); *** ../vim-8.1.0613/src/proto/misc2.pro 2018-12-18 21:56:25.084495836 +0100 --- src/proto/misc2.pro 2018-12-21 14:36:57.246303525 +0100 *************** *** 24,29 **** --- 24,30 ---- char_u *alloc(unsigned size); char_u *alloc_id(unsigned size, alloc_id_T id); char_u *alloc_clear(unsigned size); + char_u * alloc_clear_id(unsigned size, alloc_id_T id); char_u *alloc_check(unsigned size); char_u *lalloc_clear(long_u size, int message); char_u *lalloc(long_u size, int message); *** ../vim-8.1.0613/src/structs.h 2018-12-13 22:17:52.881941445 +0100 --- src/structs.h 2018-12-21 14:36:57.246303525 +0100 *************** *** 740,749 **** --- 740,754 ---- int id; /* unique identifier for each placed sign */ linenr_T lnum; /* line number which has this sign */ int typenr; /* typenr of sign */ + char_u *group; /* sign group */ + int priority; /* priority for highlighting */ signlist_T *next; /* next signlist entry */ signlist_T *prev; /* previous entry -- for easy reordering */ }; + // Default sign priority for highlighting + #define SIGN_DEF_PRIO 10 + /* type argument for buf_getsigntype() */ #define SIGN_ANY 0 #define SIGN_LINEHL 1 *** ../vim-8.1.0613/src/testdir/test_signs.vim 2018-12-14 19:37:03.677899412 +0100 --- src/testdir/test_signs.vim 2018-12-21 14:44:27.834440393 +0100 *************** *** 16,23 **** try sign define Sign2 text=xy texthl=Title linehl=Error icon=../../pixmaps/stock_vim_find_help.png catch /E255:/ ! " ignore error: E255: Couldn't read in sign data! ! " This error can happen when running in gui. " Some gui like Motif do not support the png icon format. endtry --- 16,23 ---- try sign define Sign2 text=xy texthl=Title linehl=Error icon=../../pixmaps/stock_vim_find_help.png catch /E255:/ ! " Ignore error: E255: Couldn't read in sign data! ! " This error can happen when running in the GUI. " Some gui like Motif do not support the png icon format. endtry *************** *** 63,69 **** " Check placed signs let a=execute('sign place') ! call assert_equal("\n--- Signs ---\nSigns for [NULL]:\n line=3 id=41 name=Sign1\n", a) " Unplace the sign and try jumping to it again should fail. sign unplace 41 --- 63,69 ---- " Check placed signs let a=execute('sign place') ! call assert_equal("\n--- Signs ---\nSigns for [NULL]:\n line=3 id=41 name=Sign1 priority=10\n", a) " Unplace the sign and try jumping to it again should fail. sign unplace 41 *************** *** 112,118 **** " Only 1 or 2 character text is allowed call assert_fails("sign define Sign4 text=abc linehl=Comment", 'E239:') call assert_fails("sign define Sign4 text= linehl=Comment", 'E239:') ! call assert_fails("sign define Sign4 text=\ ab linehl=Comment", 'E239:') " define sign with whitespace sign define Sign4 text=\ X linehl=Comment --- 112,118 ---- " Only 1 or 2 character text is allowed call assert_fails("sign define Sign4 text=abc linehl=Comment", 'E239:') call assert_fails("sign define Sign4 text= linehl=Comment", 'E239:') ! call assert_fails("sign define Sign4 text=\\ ab linehl=Comment", 'E239:') " define sign with whitespace sign define Sign4 text=\ X linehl=Comment *************** *** 131,136 **** --- 131,158 ---- sign define Sign4 text=\\ linehl=Comment sign undefine Sign4 + " Error cases + call assert_fails("exe 'sign place abc line=3 name=Sign1 buffer=' . bufnr('%')", 'E474:') + call assert_fails("exe 'sign unplace abc name=Sign1 buffer=' . bufnr('%')", 'E474:') + call assert_fails("exe 'sign place 1abc line=3 name=Sign1 buffer=' . bufnr('%')", 'E474:') + call assert_fails("exe 'sign unplace 2abc name=Sign1 buffer=' . bufnr('%')", 'E474:') + call assert_fails("sign unplace 2 *", 'E474:') + call assert_fails("exe 'sign place 1 line=3 name=Sign1 buffer=' . bufnr('%') a", 'E488:') + call assert_fails("exe 'sign place name=Sign1 buffer=' . bufnr('%')", 'E474:') + call assert_fails("exe 'sign place line=10 buffer=' . bufnr('%')", 'E474:') + call assert_fails("exe 'sign unplace 2 line=10 buffer=' . bufnr('%')", 'E474:') + call assert_fails("exe 'sign unplace 2 name=Sign1 buffer=' . bufnr('%')", 'E474:') + call assert_fails("exe 'sign place 2 line=3 buffer=' . bufnr('%')", 'E474:') + call assert_fails("sign place 2", 'E474:') + call assert_fails("sign place abc", 'E474:') + call assert_fails("sign place 5 line=3", 'E474:') + call assert_fails("sign place 5 name=Sign1", 'E474:') + call assert_fails("sign place 5 group=g1", 'E474:') + call assert_fails("sign place 5 group=*", 'E474:') + call assert_fails("sign place 5 priority=10", 'E474:') + call assert_fails("sign place 5 line=3 name=Sign1", 'E474:') + call assert_fails("sign place 5 group=g1 line=3 name=Sign1", 'E474:') + " After undefining the sign, we should no longer be able to place it. sign undefine Sign1 sign undefine Sign2 *************** *** 152,158 **** " Listing placed sign should show that sign is deleted. let a=execute('sign place') ! call assert_equal("\n--- Signs ---\nSigns for foobar:\n line=1 id=41 name=[Deleted]\n", a) sign unplace 41 let a=execute('sign place') --- 174,180 ---- " Listing placed sign should show that sign is deleted. let a=execute('sign place') ! call assert_equal("\n--- Signs ---\nSigns for foobar:\n line=1 id=41 name=[Deleted] priority=10\n", a) sign unplace 41 let a=execute('sign place') *************** *** 202,207 **** --- 224,231 ---- endfunc func Test_sign_invalid_commands() + sign define Sign1 text=x + call assert_fails('sign', 'E471:') call assert_fails('sign jump', 'E471:') call assert_fails('sign xxx', 'E160:') *************** *** 211,216 **** --- 235,286 ---- call assert_fails('sign list xxx', 'E155:') call assert_fails('sign place 1 buffer=999', 'E158:') call assert_fails('sign define Sign2 text=', 'E239:') + " Non-numeric identifier for :sign place + call assert_fails("exe 'sign place abc line=3 name=Sign1 buffer=' . bufnr('%')", 'E474:') + " Non-numeric identifier for :sign unplace + call assert_fails("exe 'sign unplace abc name=Sign1 buffer=' . bufnr('%')", 'E474:') + " Number followed by an alphabet as sign identifier for :sign place + call assert_fails("exe 'sign place 1abc line=3 name=Sign1 buffer=' . bufnr('%')", 'E474:') + " Number followed by an alphabet as sign identifier for :sign unplace + call assert_fails("exe 'sign unplace 2abc name=Sign1 buffer=' . bufnr('%')", 'E474:') + " Sign identifier and '*' for :sign unplace + call assert_fails("sign unplace 2 *", 'E474:') + " Trailing characters after buffer number for :sign place + call assert_fails("exe 'sign place 1 line=3 name=Sign1 buffer=' . bufnr('%') . 'xxx'", 'E488:') + " Trailing characters after buffer number for :sign unplace + call assert_fails("exe 'sign unplace 1 buffer=' . bufnr('%') . 'xxx'", 'E488:') + call assert_fails("exe 'sign unplace * buffer=' . bufnr('%') . 'xxx'", 'E488:') + call assert_fails("sign unplace 1 xxx", 'E474:') + call assert_fails("sign unplace * xxx", 'E474:') + call assert_fails("sign unplace xxx", 'E474:') + " Placing a sign without line number + call assert_fails("exe 'sign place name=Sign1 buffer=' . bufnr('%')", 'E474:') + " Placing a sign without sign name + call assert_fails("exe 'sign place line=10 buffer=' . bufnr('%')", 'E474:') + " Unplacing a sign with line number + call assert_fails("exe 'sign unplace 2 line=10 buffer=' . bufnr('%')", 'E474:') + " Unplacing a sign with sign name + call assert_fails("exe 'sign unplace 2 name=Sign1 buffer=' . bufnr('%')", 'E474:') + " Placing a sign without sign name + call assert_fails("exe 'sign place 2 line=3 buffer=' . bufnr('%')", 'E474:') + " Placing a sign with only sign identifier + call assert_fails("sign place 2", 'E474:') + " Placing a sign with only a name + call assert_fails("sign place abc", 'E474:') + " Placing a sign with only line number + call assert_fails("sign place 5 line=3", 'E474:') + " Placing a sign with only sign name + call assert_fails("sign place 5 name=Sign1", 'E474:') + " Placing a sign with only sign group + call assert_fails("sign place 5 group=g1", 'E474:') + call assert_fails("sign place 5 group=*", 'E474:') + " Placing a sign with only sign priority + call assert_fails("sign place 5 priority=10", 'E474:') + " Placing a sign without buffer number or file name + call assert_fails("sign place 5 line=3 name=Sign1", 'E474:') + call assert_fails("sign place 5 group=g1 line=3 name=Sign1", 'E474:') + + sign undefine Sign1 endfunc func Test_sign_delete_buffer() *************** *** 224,226 **** --- 294,842 ---- sign unplace 61 sign undefine Sign endfunc + + " Test for VimL functions for managing signs + func Test_sign_funcs() + " Remove all the signs + call sign_unplace('*') + call sign_undefine() + + " Tests for sign_define() + let attr = {'text' : '=>', 'linehl' : 'Search', 'texthl' : 'Error'} + call assert_equal(0, sign_define("sign1", attr)) + call assert_equal([{'name' : 'sign1', 'texthl' : 'Error', + \ 'linehl' : 'Search', 'text' : '=>'}], sign_getdefined()) + + " Define a new sign without attributes and then update it + call sign_define("sign2") + let attr = {'text' : '!!', 'linehl' : 'DiffAdd', 'texthl' : 'DiffChange', + \ 'icon' : 'sign2.ico'} + try + call sign_define("sign2", attr) + catch /E255:/ + " ignore error: E255: Couldn't read in sign data! + " This error can happen when running in gui. + endtry + call assert_equal([{'name' : 'sign2', 'texthl' : 'DiffChange', + \ 'linehl' : 'DiffAdd', 'text' : '!!', 'icon' : 'sign2.ico'}], + \ sign_getdefined("sign2")) + + " Test for a sign name with digits + call assert_equal(0, sign_define(0002, {'linehl' : 'StatusLine'})) + call assert_equal([{'name' : '2', 'linehl' : 'StatusLine'}], + \ sign_getdefined(0002)) + call sign_undefine(0002) + + " 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() + call assert_equal([], sign_getdefined("none")) + call assert_fails('call sign_getdefined({})', 'E731:') + + " Tests for sign_place() + call writefile(repeat(["Sun is shining"], 30), "Xsign") + edit Xsign + + call assert_equal(10, sign_place(10, '', 'sign1', 'Xsign', + \ {'lnum' : 20})) + call assert_equal([{'bufnr' : bufnr(''), 'signs' : + \ [{'id' : 10, 'group' : '', 'lnum' : 20, 'name' : 'sign1', + \ 'priority' : 10}]}], sign_getplaced('Xsign')) + call assert_equal([{'bufnr' : bufnr(''), 'signs' : + \ [{'id' : 10, 'group' : '', 'lnum' : 20, 'name' : 'sign1', + \ 'priority' : 10}]}], + \ sign_getplaced('Xsign', {'lnum' : 20})) + call assert_equal([{'bufnr' : bufnr(''), 'signs' : + \ [{'id' : 10, 'group' : '', 'lnum' : 20, 'name' : 'sign1', + \ 'priority' : 10}]}], + \ sign_getplaced('Xsign', {'id' : 10})) + + " Tests for invalid arguments to sign_place() + 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", + \ {"lnum" : 30})', 'E155:') + call assert_fails('call sign_place(10, "", "", "Xsign", + \ {"lnum" : 30})', 'E155:') + call assert_fails('call sign_place(10, "", [], "Xsign", + \ {"lnum" : 30})', 'E730:') + call assert_fails('call sign_place(5, "", "sign1", "abcxyz.xxx", + \ {"lnum" : 10})', 'E158:') + call assert_fails('call sign_place(5, "", "sign1", "", {"lnum" : 10})', + \ 'E158:') + call assert_fails('call sign_place(5, "", "sign1", [], {"lnum" : 10})', + \ 'E158:') + call assert_fails('call sign_place(21, "", "sign1", "Xsign", + \ {"lnum" : -1})', 'E885:') + call assert_fails('call sign_place(22, "", "sign1", "Xsign", + \ {"lnum" : 0})', 'E885:') + call assert_equal(-1, sign_place(1, "*", "sign1", "Xsign", {"lnum" : 10})) + + " Tests for sign_getplaced() + call assert_equal([{'bufnr' : bufnr(''), 'signs' : + \ [{'id' : 10, 'group' : '', 'lnum' : 20, 'name' : 'sign1', + \ 'priority' : 10}]}], + \ sign_getplaced(bufnr('Xsign'))) + call assert_equal([{'bufnr' : bufnr(''), 'signs' : + \ [{'id' : 10, 'group' : '', 'lnum' : 20, 'name' : 'sign1', + \ 'priority' : 10}]}], + \ sign_getplaced()) + call assert_fails("call sign_getplaced('dummy.sign')", 'E158:') + call assert_fails('call sign_getplaced("")', 'E158:') + call assert_fails('call sign_getplaced(-1)', 'E158:') + call assert_fails('call sign_getplaced("Xsign", [])', 'E715:') + call assert_equal([{'bufnr' : bufnr(''), 'signs' : []}], + \ sign_getplaced('Xsign', {'lnum' : 1000000})) + call assert_fails("call sign_getplaced('Xsign', {'lnum' : []})", + \ 'E745:') + call assert_equal([{'bufnr' : bufnr(''), 'signs' : []}], + \ sign_getplaced('Xsign', {'id' : 44})) + call assert_fails("call sign_getplaced('Xsign', {'id' : []})", + \ 'E745:') + + " Tests for sign_unplace() + call sign_place(20, '', 'sign2', 'Xsign', {"lnum" : 30}) + call assert_equal(0, sign_unplace('', + \ {'id' : 20, 'buffer' : 'Xsign'})) + call assert_equal(-1, sign_unplace('', + \ {'id' : 30, 'buffer' : 'Xsign'})) + call sign_place(20, '', 'sign2', 'Xsign', {"lnum" : 30}) + call assert_fails("call sign_unplace('', + \ {'id' : 20, 'buffer' : 'buffer.c'})", 'E158:') + call assert_fails("call sign_unplace('', + \ {'id' : 20, 'buffer' : ''})", 'E158:') + call assert_fails("call sign_unplace('', + \ {'id' : 20, 'buffer' : 200})", 'E158:') + call assert_fails("call sign_unplace('', '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('*') + call sign_undefine() + enew | only + endfunc + + " Tests for sign groups + func Test_sign_group() + enew | only + " Remove all the signs + call sign_unplace('*') + call sign_undefine() + + call writefile(repeat(["Sun is shining"], 30), "Xsign") + + let attr = {'text' : '=>', 'linehl' : 'Search', 'texthl' : 'Error'} + call assert_equal(0, sign_define("sign1", attr)) + + edit Xsign + let bnum = bufnr('%') + let fname = fnamemodify('Xsign', ':p') + + " Error case + call assert_fails("call sign_place(5, [], 'sign1', 'Xsign', + \ {'lnum' : 30})", 'E730:') + + " place three signs with the same identifier. One in the global group and + " others in the named groups + call assert_equal(5, sign_place(5, '', 'sign1', 'Xsign', + \ {'lnum' : 10})) + call assert_equal(5, sign_place(5, 'g1', 'sign1', bnum, {'lnum' : 20})) + call assert_equal(5, sign_place(5, 'g2', 'sign1', bnum, {'lnum' : 30})) + + " Test for sign_getplaced() with group + let s = sign_getplaced('Xsign') + call assert_equal(1, len(s[0].signs)) + call assert_equal(s[0].signs[0].group, '') + let s = sign_getplaced(bnum, {'group' : 'g2'}) + call assert_equal('g2', s[0].signs[0].group) + let s = sign_getplaced(bnum, {'group' : 'g3'}) + call assert_equal([], s[0].signs) + let s = sign_getplaced(bnum, {'group' : '*'}) + call assert_equal([{'id' : 5, 'group' : '', 'name' : 'sign1', 'lnum' : 10, + \ 'priority' : 10}, + \ {'id' : 5, 'group' : 'g1', 'name' : 'sign1', 'lnum' : 20, + \ 'priority' : 10}, + \ {'id' : 5, 'group' : 'g2', 'name' : 'sign1', 'lnum' : 30, + \ 'priority' : 10}], + \ s[0].signs) + + " Test for sign_getplaced() with id + let s = sign_getplaced(bnum, {'id' : 5}) + call assert_equal([{'id' : 5, 'group' : '', 'name' : 'sign1', 'lnum' : 10, + \ 'priority' : 10}], + \ s[0].signs) + let s = sign_getplaced(bnum, {'id' : 5, 'group' : 'g2'}) + call assert_equal( + \ [{'id' : 5, 'name' : 'sign1', 'lnum' : 30, 'group' : 'g2', + \ 'priority' : 10}], + \ s[0].signs) + let s = sign_getplaced(bnum, {'id' : 5, 'group' : '*'}) + call assert_equal([{'id' : 5, 'group' : '', 'name' : 'sign1', 'lnum' : 10, + \ 'priority' : 10}, + \ {'id' : 5, 'group' : 'g1', 'name' : 'sign1', 'lnum' : 20, + \ 'priority' : 10}, + \ {'id' : 5, 'group' : 'g2', 'name' : 'sign1', 'lnum' : 30, + \ 'priority' : 10}], + \ s[0].signs) + let s = sign_getplaced(bnum, {'id' : 5, 'group' : 'g3'}) + call assert_equal([], s[0].signs) + + " Test for sign_getplaced() with lnum + let s = sign_getplaced(bnum, {'lnum' : 20}) + call assert_equal([], s[0].signs) + let s = sign_getplaced(bnum, {'lnum' : 20, 'group' : 'g1'}) + call assert_equal( + \ [{'id' : 5, 'name' : 'sign1', 'lnum' : 20, 'group' : 'g1', + \ 'priority' : 10}], + \ s[0].signs) + let s = sign_getplaced(bnum, {'lnum' : 30, 'group' : '*'}) + call assert_equal( + \ [{'id' : 5, 'name' : 'sign1', 'lnum' : 30, 'group' : 'g2', + \ 'priority' : 10}], + \ s[0].signs) + let s = sign_getplaced(bnum, {'lnum' : 40, 'group' : '*'}) + call assert_equal([], s[0].signs) + + " Error case + call assert_fails("call sign_getplaced(bnum, {'group' : []})", + \ 'E730:') + + " Clear the sign in global group + call sign_unplace('', {'id' : 5, 'buffer' : bnum}) + let s = sign_getplaced(bnum, {'group' : '*'}) + call assert_equal([ + \ {'id' : 5, 'name' : 'sign1', 'lnum' : 20, 'group' : 'g1', + \ 'priority' : 10}, + \ {'id' : 5, 'name' : 'sign1', 'lnum' : 30, 'group' : 'g2', + \ 'priority' : 10}], + \ s[0].signs) + + " Clear the sign in one of the groups + call sign_unplace('g1', {'buffer' : 'Xsign'}) + let s = sign_getplaced(bnum, {'group' : '*'}) + call assert_equal([ + \ {'id' : 5, 'name' : 'sign1', 'lnum' : 30, 'group' : 'g2', + \ 'priority' : 10}], + \ s[0].signs) + + " Clear all the signs from the buffer + call sign_unplace('*', {'buffer' : bnum}) + call assert_equal([], sign_getplaced(bnum, {'group' : '*'})[0].signs) + + " Clear sign across groups using an identifier + call sign_place(25, '', 'sign1', bnum, {'lnum' : 10}) + call sign_place(25, 'g1', 'sign1', bnum, {'lnum' : 11}) + call sign_place(25, 'g2', 'sign1', bnum, {'lnum' : 12}) + call assert_equal(0, sign_unplace('*', {'id' : 25})) + 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})) + call assert_equal(-1, sign_unplace('g1', {'id' : 5})) + + " Place signs in multiple groups and delete all the signs in one of the + " group + call assert_equal(5, sign_place(5, '', 'sign1', bnum, {'lnum' : 10})) + call assert_equal(6, sign_place(6, '', 'sign1', bnum, {'lnum' : 11})) + call assert_equal(5, sign_place(5, 'g1', 'sign1', bnum, {'lnum' : 10})) + call assert_equal(5, sign_place(5, 'g2', 'sign1', bnum, {'lnum' : 10})) + call assert_equal(6, sign_place(6, 'g1', 'sign1', bnum, {'lnum' : 11})) + call assert_equal(6, sign_place(6, 'g2', 'sign1', bnum, {'lnum' : 11})) + call assert_equal(0, sign_unplace('g1')) + let s = sign_getplaced(bnum, {'group' : 'g1'}) + call assert_equal([], s[0].signs) + let s = sign_getplaced(bnum) + call assert_equal(2, len(s[0].signs)) + let s = sign_getplaced(bnum, {'group' : 'g2'}) + call assert_equal('g2', s[0].signs[0].group) + call assert_equal(0, sign_unplace('', {'id' : 5})) + call assert_equal(0, sign_unplace('', {'id' : 6})) + let s = sign_getplaced(bnum, {'group' : 'g2'}) + call assert_equal('g2', s[0].signs[0].group) + call assert_equal(0, sign_unplace('', {'buffer' : bnum})) + + call sign_unplace('*') + + " Test for :sign command and groups + exe 'sign place 5 line=10 name=sign1 file=' . fname + exe 'sign place 5 group=g1 line=10 name=sign1 file=' . fname + exe 'sign place 5 group=g2 line=10 name=sign1 file=' . fname + + " Test for :sign place group={group} file={fname} + let a = execute('sign place file=' . fname) + call assert_equal("\n--- Signs ---\nSigns for Xsign:\n line=10 id=5 name=sign1 priority=10\n", a) + + let a = execute('sign place group=g2 file=' . fname) + call assert_equal("\n--- Signs ---\nSigns for Xsign:\n line=10 id=5 group=g2 name=sign1 priority=10\n", a) + + let a = execute('sign place group=* file=' . fname) + call assert_equal("\n--- Signs ---\nSigns for Xsign:\n" . + \ " line=10 id=5 group=g2 name=sign1 priority=10\n" . + \ " line=10 id=5 group=g1 name=sign1 priority=10\n" . + \ " line=10 id=5 name=sign1 priority=10\n", a) + + let a = execute('sign place group=xyz file=' . fname) + call assert_equal("\n--- Signs ---\nSigns for Xsign:\n", a) + + call sign_unplace('*') + + " Test for :sign place group={group} buffer={nr} + let bnum = bufnr('Xsign') + exe 'sign place 5 line=10 name=sign1 buffer=' . bnum + exe 'sign place 5 group=g1 line=11 name=sign1 buffer=' . bnum + exe 'sign place 5 group=g2 line=12 name=sign1 buffer=' . bnum + + let a = execute('sign place buffer=' . bnum) + call assert_equal("\n--- Signs ---\nSigns for Xsign:\n line=10 id=5 name=sign1 priority=10\n", a) + + let a = execute('sign place group=g2 buffer=' . bnum) + call assert_equal("\n--- Signs ---\nSigns for Xsign:\n line=12 id=5 group=g2 name=sign1 priority=10\n", a) + + let a = execute('sign place group=* buffer=' . bnum) + call assert_equal("\n--- Signs ---\nSigns for Xsign:\n" . + \ " line=10 id=5 name=sign1 priority=10\n" . + \ " line=11 id=5 group=g1 name=sign1 priority=10\n" . + \ " line=12 id=5 group=g2 name=sign1 priority=10\n", a) + + let a = execute('sign place group=xyz buffer=' . bnum) + call assert_equal("\n--- Signs ---\nSigns for Xsign:\n", a) + + let a = execute('sign place group=*') + call assert_equal("\n--- Signs ---\nSigns for Xsign:\n" . + \ " line=10 id=5 name=sign1 priority=10\n" . + \ " line=11 id=5 group=g1 name=sign1 priority=10\n" . + \ " line=12 id=5 group=g2 name=sign1 priority=10\n", a) + + " Test for :sign unplace + exe 'sign unplace 5 group=g2 file=' . fname + call assert_equal([], sign_getplaced(bnum, {'group' : 'g2'})[0].signs) + + exe 'sign unplace 5 group=g1 buffer=' . bnum + call assert_equal([], sign_getplaced(bnum, {'group' : 'g1'})[0].signs) + + exe 'sign unplace 5 group=xy file=' . fname + call assert_equal(1, len(sign_getplaced(bnum, {'group' : '*'})[0].signs)) + + " Test for removing all the signs. Place the signs again for this test + exe 'sign place 5 group=g1 line=11 name=sign1 file=' . fname + exe 'sign place 5 group=g2 line=12 name=sign1 file=' . fname + exe 'sign place 6 line=20 name=sign1 file=' . fname + exe 'sign place 6 group=g1 line=21 name=sign1 file=' . fname + exe 'sign place 6 group=g2 line=22 name=sign1 file=' . fname + exe 'sign unplace 5 group=* file=' . fname + let a = execute('sign place group=*') + call assert_equal("\n--- Signs ---\nSigns for Xsign:\n" . + \ " line=20 id=6 name=sign1 priority=10\n" . + \ " line=21 id=6 group=g1 name=sign1 priority=10\n" . + \ " line=22 id=6 group=g2 name=sign1 priority=10\n", a) + + " Remove all the signs from the global group + exe 'sign unplace * file=' . fname + let a = execute('sign place group=*') + call assert_equal("\n--- Signs ---\nSigns for Xsign:\n" . + \ " line=21 id=6 group=g1 name=sign1 priority=10\n" . + \ " line=22 id=6 group=g2 name=sign1 priority=10\n", a) + + " Remove all the signs from a particular group + exe 'sign place 5 line=10 name=sign1 file=' . fname + exe 'sign place 5 group=g1 line=11 name=sign1 file=' . fname + exe 'sign place 5 group=g2 line=12 name=sign1 file=' . fname + exe 'sign unplace * group=g1 file=' . fname + let a = execute('sign place group=*') + call assert_equal("\n--- Signs ---\nSigns for Xsign:\n" . + \ " line=10 id=5 name=sign1 priority=10\n" . + \ " line=12 id=5 group=g2 name=sign1 priority=10\n" . + \ " line=22 id=6 group=g2 name=sign1 priority=10\n", a) + + " Remove all the signs from all the groups in a file + exe 'sign place 5 group=g1 line=11 name=sign1 file=' . fname + exe 'sign place 6 line=20 name=sign1 file=' . fname + exe 'sign place 6 group=g1 line=21 name=sign1 file=' . fname + exe 'sign unplace * group=* file=' . fname + let a = execute('sign place group=*') + call assert_equal("\n--- Signs ---\n", a) + + " Remove a particular sign id in a group from all the files + exe 'sign place 5 group=g1 line=11 name=sign1 file=' . fname + sign unplace 5 group=g1 + let a = execute('sign place group=*') + call assert_equal("\n--- Signs ---\n", a) + + " Remove a particular sign id in all the groups from all the files + exe 'sign place 5 line=10 name=sign1 file=' . fname + exe 'sign place 5 group=g1 line=11 name=sign1 file=' . fname + exe 'sign place 5 group=g2 line=12 name=sign1 file=' . fname + exe 'sign place 6 line=20 name=sign1 file=' . fname + exe 'sign place 6 group=g1 line=21 name=sign1 file=' . fname + exe 'sign place 6 group=g2 line=22 name=sign1 file=' . fname + sign unplace 5 group=* + let a = execute('sign place group=*') + call assert_equal("\n--- Signs ---\nSigns for Xsign:\n" . + \ " line=20 id=6 name=sign1 priority=10\n" . + \ " line=21 id=6 group=g1 name=sign1 priority=10\n" . + \ " line=22 id=6 group=g2 name=sign1 priority=10\n", a) + + " Remove all the signs from all the groups in all the files + exe 'sign place 5 line=10 name=sign1 file=' . fname + exe 'sign place 5 group=g1 line=11 name=sign1 file=' . fname + sign unplace * group=* + let a = execute('sign place group=*') + call assert_equal("\n--- Signs ---\n", a) + + " Error cases + call assert_fails("exe 'sign place 3 group= name=sign1 buffer=' . bnum", 'E474:') + + call delete("Xsign") + call sign_unplace('*') + call sign_undefine() + enew | only + endfunc + + " Tests for auto-generating the sign identifier + func Test_sign_id_autogen() + enew | only + call sign_unplace('*') + call sign_undefine() + + let attr = {'text' : '=>', 'linehl' : 'Search', 'texthl' : 'Error'} + call assert_equal(0, sign_define("sign1", attr)) + + call writefile(repeat(["Sun is shining"], 30), "Xsign") + edit Xsign + + call assert_equal(1, sign_place(0, '', 'sign1', 'Xsign', + \ {'lnum' : 10})) + call assert_equal(2, sign_place(2, '', 'sign1', 'Xsign', + \ {'lnum' : 12})) + call assert_equal(3, sign_place(0, '', 'sign1', 'Xsign', + \ {'lnum' : 14})) + call sign_unplace('', {'buffer' : 'Xsign', 'id' : 2}) + call assert_equal(2, sign_place(0, '', 'sign1', 'Xsign', + \ {'lnum' : 12})) + + call assert_equal(1, sign_place(0, 'g1', 'sign1', 'Xsign', + \ {'lnum' : 11})) + call assert_equal(0, sign_unplace('g1', {'id' : 1})) + call assert_equal(10, + \ sign_getplaced('Xsign', {'id' : 1})[0].signs[0].lnum) + + call delete("Xsign") + call sign_unplace('*') + call sign_undefine() + enew | only + endfunc + + " Test for sign priority + func Test_sign_priority() + enew | only + call sign_unplace('*') + call sign_undefine() + + let attr = {'text' : '=>', 'linehl' : 'Search', 'texthl' : 'Search'} + call sign_define("sign1", attr) + call sign_define("sign2", attr) + call sign_define("sign3", attr) + + " Place three signs with different priority in the same line + call writefile(repeat(["Sun is shining"], 30), "Xsign") + edit Xsign + let fname = fnamemodify('Xsign', ':p') + + call sign_place(1, 'g1', 'sign1', 'Xsign', + \ {'lnum' : 11, 'priority' : 50}) + call sign_place(2, 'g2', 'sign2', 'Xsign', + \ {'lnum' : 11, 'priority' : 100}) + call sign_place(3, '', 'sign3', 'Xsign', {'lnum' : 11}) + 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) + + " Error case + call assert_fails("call sign_place(1, 'g1', 'sign1', 'Xsign', + \ [])", 'E715:') + call sign_unplace('*') + + " Tests for the :sign place command with priority + sign place 5 line=10 name=sign1 priority=30 file=Xsign + sign place 5 group=g1 line=10 name=sign1 priority=20 file=Xsign + sign place 5 group=g2 line=10 name=sign1 priority=25 file=Xsign + let a = execute('sign place group=*') + call assert_equal("\n--- Signs ---\nSigns for Xsign:\n" . + \ " line=10 id=5 name=sign1 priority=30\n" . + \ " line=10 id=5 group=g2 name=sign1 priority=25\n" . + \ " line=10 id=5 group=g1 name=sign1 priority=20\n", a) + + " Test for :sign place group={group} + let a = execute('sign place group=g1') + call assert_equal("\n--- Signs ---\nSigns for Xsign:\n" . + \ " line=10 id=5 group=g1 name=sign1 priority=20\n", a) + + call sign_unplace('*') + call sign_undefine() + enew | only + call delete("Xsign") + endfunc + + " Tests for memory allocation failures in sign functions + func Test_sign_memfailures() + call writefile(repeat(["Sun is shining"], 30), "Xsign") + edit Xsign + + call test_alloc_fail(GetAllocId('sign_getdefined'), 0, 0) + call assert_fails('call sign_getdefined("sign1")', 'E342:') + call test_alloc_fail(GetAllocId('sign_getplaced'), 0, 0) + call assert_fails('call sign_getplaced("Xsign")', 'E342:') + call test_alloc_fail(GetAllocId('sign_define_by_name'), 0, 0) + let attr = {'text' : '=>', 'linehl' : 'Search', 'texthl' : 'Error'} + call assert_fails('call sign_define("sign1", attr)', 'E342:') + + let attr = {'text' : '=>', 'linehl' : 'Search', 'texthl' : 'Error'} + call sign_define("sign1", attr) + call test_alloc_fail(GetAllocId('sign_getlist'), 0, 0) + call assert_fails('call sign_getdefined("sign1")', 'E342:') + + call sign_place(3, 'g1', 'sign1', 'Xsign', {'lnum' : 10}) + call test_alloc_fail(GetAllocId('sign_getplaced_dict'), 0, 0) + call assert_fails('call sign_getplaced("Xsign")', 'E342:') + call test_alloc_fail(GetAllocId('sign_getplaced_list'), 0, 0) + call assert_fails('call sign_getplaced("Xsign")', 'E342:') + + call test_alloc_fail(GetAllocId('insert_sign'), 0, 0) + call assert_fails('call sign_place(4, "g1", "sign1", "Xsign", {"lnum" : 11})', + \ 'E342:') + + call test_alloc_fail(GetAllocId('sign_getinfo'), 0, 0) + call assert_fails('call getbufinfo()', 'E342:') + call sign_place(4, 'g1', 'sign1', 'Xsign', {'lnum' : 11}) + call test_alloc_fail(GetAllocId('sign_getinfo'), 0, 0) + call assert_fails('let binfo=getbufinfo("Xsign")', 'E342:') + call assert_equal([{'lnum': 11, 'id': 4, 'name': 'sign1', + \ 'priority': 10, 'group': 'g1'}], binfo[0].signs) + + call sign_unplace('*') + call sign_undefine() + enew | only + call delete("Xsign") + endfunc *** ../vim-8.1.0613/src/workshop.c 2018-09-30 21:43:17.207693209 +0200 --- src/workshop.c 2018-12-21 14:36:57.246303525 +0100 *************** *** 491,497 **** lineno = 0; buf = buflist_findname((char_u *)filename); if (buf != NULL) ! lineno = buf_findsign(buf, markId); return lineno; } --- 491,497 ---- lineno = 0; buf = buflist_findname((char_u *)filename); if (buf != NULL) ! lineno = buf_findsign(buf, markId, NULL); return lineno; } *** ../vim-8.1.0613/src/version.c 2018-12-21 13:03:24.296337825 +0100 --- src/version.c 2018-12-21 14:37:53.945927817 +0100 *************** *** 801,802 **** --- 801,804 ---- { /* Add new patch number below this line */ + /**/ + 614, /**/ -- hundred-and-one symptoms of being an internet addict: 31. You code your homework in HTML and give your instructor the URL. /// 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 ///