0
0
mirror of https://github.com/vim/vim.git synced 2025-08-31 20:53:42 -04:00

patch 8.1.0614: placing signs can be complicated

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)
This commit is contained in:
Bram Moolenaar 2018-12-21 15:17:36 +01:00
parent 48f377a476
commit 162b71479b
19 changed files with 2082 additions and 296 deletions

View File

@ -2408,6 +2408,15 @@ shellescape({string} [, {special}])
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}
@ -7858,7 +7867,217 @@ shiftwidth([{col}]) *shiftwidth()*
'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

View File

@ -1,4 +1,4 @@
*sign.txt* For Vim version 8.1. Last change: 2016 Aug 17
*sign.txt* For Vim version 8.1. Last change: 2018 Dec 21
VIM REFERENCE MANUAL by Gordon Prieur
@ -51,6 +51,20 @@ The color of the column is set with the SignColumn group |hl-SignColumn|.
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,6 +83,8 @@ comment. If you do need that, use the |:execute| command.
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,13 +122,18 @@ DEFINING A SIGN. *:sign-define* *E255* *E160* *E612*
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,6 +142,8 @@ LISTING SIGNS *:sign-list* *E156*
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,6 +159,25 @@ PLACING SIGNS *:sign-place* *E158*
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,31 +188,73 @@ PLACING SIGNS *:sign-place* *E158*
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.
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,15 +262,26 @@ REMOVING SIGNS *:sign-unplace* *E159*
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*

View File

@ -983,6 +983,14 @@ Jobs: *job-functions*
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

View File

@ -21,5 +21,13 @@ typedef enum {
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;

View File

@ -936,7 +936,7 @@ free_buffer_stuff(
uc_clear(&buf->b_ucmds); /* clear local user commands */
#endif
#ifdef FEAT_SIGNS
buf_delete_signs(buf); /* delete any signs */
buf_delete_signs(buf, (char_u *)"*"); // delete any signs */
#endif
#ifdef FEAT_NETBEANS_INTG
netbeans_file_killed(buf);
@ -5866,25 +5866,34 @@ win_found:
#if defined(FEAT_SIGNS) || defined(PROTO)
/*
* Insert the sign into the signlist.
* 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 */
linenr_T lnum, /* line number which gets the mark */
int typenr) /* typenr of sign we are adding */
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((long_u)sizeof(signlist_T), FALSE);
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,15 +5901,15 @@ insert_sign(
if (prev == NULL)
{
/* When adding first sign need to redraw the windows to create the
* column for signs. */
// 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 */
// first sign in signlist
buf->b_signlist = newsign;
#ifdef FEAT_NETBEANS_INTG
if (netbeans_active())
@ -5913,50 +5922,100 @@ insert_sign(
}
/*
* Add the sign into the signlist. Find the right spot to do it though.
* Insert a new sign sorted by line number and sign priority.
*/
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 */
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; /* a sign in the signlist */
signlist_T *prev; /* the previous sign */
signlist_T *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)
// 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, lnum, typenr);
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,15 +6026,16 @@ buf_addsign(
*/
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 */
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 */
signlist_T *sign; // a sign in the signlist
for (sign = buf->b_signlist; sign != NULL; sign = sign->next)
FOR_ALL_SIGNS_IN_BUF(buf)
{
if (sign->id == markId)
if (sign->id == markId && sign_in_group(sign, group))
{
sign->typenr = typenr;
return sign->lnum;
@ -5985,6 +6045,11 @@ buf_change_sign_type(
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,7 +6058,7 @@ buf_getsigntype(
{
signlist_T *sign; /* a sign in a b_signlist */
for (sign = buf->b_signlist; sign != NULL; sign = sign->next)
FOR_ALL_SIGNS_IN_BUF(buf)
if (sign->lnum == lnum
&& (type == SIGN_ANY
# ifdef FEAT_SIGN_ICONS
@ -6008,37 +6073,51 @@ buf_getsigntype(
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 */
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 */
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)
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);
break;
// 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. */
// 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,39 +6129,78 @@ buf_delsign(
/*
* 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.
* 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 */
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 */
signlist_T *sign; // a sign in the signlist
for (sign = buf->b_signlist; sign != NULL; sign = sign->next)
if (sign->id == id)
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 */
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 */
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;
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,7 +6213,7 @@ buf_findsigntype_id(
{
signlist_T *sign; /* a sign in the signlist */
for (sign = buf->b_signlist; sign != NULL; sign = sign->next)
FOR_ALL_SIGNS_IN_BUF(buf)
if (sign->lnum == lnum && sign->typenr == typenr)
return sign->id;
@ -6110,10 +6228,10 @@ buf_findsigntype_id(
int
buf_signcount(buf_T *buf, linenr_T lnum)
{
signlist_T *sign; /* a sign in the signlist */
signlist_T *sign; // a sign in the signlist
int count = 0;
for (sign = buf->b_signlist; sign != NULL; sign = sign->next)
FOR_ALL_SIGNS_IN_BUF(buf)
if (sign->lnum == lnum)
if (sign_get_image(sign->typenr) != NULL)
count++;
@ -6123,28 +6241,39 @@ buf_signcount(buf_T *buf, linenr_T lnum)
# endif /* FEAT_SIGN_ICONS */
# endif /* FEAT_NETBEANS_INTG */
/*
* Delete signs in buffer "buf".
* Delete signs in group 'group' in buffer "buf". If 'group' is '*', then
* delete all the signs.
*/
void
buf_delete_signs(buf_T *buf)
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). */
// 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)
lastp = &buf->b_signlist;
for (sign = buf->b_signlist; sign != NULL; sign = next)
{
next = buf->b_signlist->next;
vim_free(buf->b_signlist);
buf->b_signlist = 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,18 +6287,19 @@ buf_delete_all_signs(void)
FOR_ALL_BUFFERS(buf)
if (buf->b_signlist != NULL)
buf_delete_signs(buf);
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)
sign_list_placed(buf_T *rbuf, char_u *sign_group)
{
buf_T *buf;
signlist_T *p;
signlist_T *sign;
char lbuf[BUFSIZ];
char group[BUFSIZ];
MSG_PUTS_TITLE(_("\n--- Signs ---"));
msg_putchar('\n');
@ -6185,10 +6315,18 @@ sign_list_placed(buf_T *rbuf)
MSG_PUTS_ATTR(lbuf, HL_ATTR(HLF_D));
msg_putchar('\n');
}
for (p = buf->b_signlist; p != NULL && !got_int; p = p->next)
FOR_ALL_SIGNS_IN_BUF(buf)
{
vim_snprintf(lbuf, BUFSIZ, _(" line=%ld id=%d name=%s"),
(long)p->lnum, p->id, sign_typenr2name(p->typenr));
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,7 +6348,7 @@ sign_mark_adjust(
{
signlist_T *sign; /* a sign in a b_signlist */
for (sign = curbuf->b_signlist; sign != NULL; sign = sign->next)
FOR_ALL_SIGNS_IN_BUF(curbuf)
{
if (sign->lnum >= line1 && sign->lnum <= line2)
{

View File

@ -367,6 +367,14 @@ static void f_sha256(typval_T *argvars, typval_T *rettv);
#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,6 +855,14 @@ static struct fst
#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,19 +4433,12 @@ f_get(typval_T *argvars, typval_T *rettv)
get_buffer_signs(buf_T *buf, list_T *l)
{
signlist_T *sign;
dict_T *d;
for (sign = buf->b_signlist; sign; sign = sign->next)
FOR_ALL_SIGNS_IN_BUF(buf)
{
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));
if ((d = sign_get_info(sign)) != NULL)
list_append_dict(l, d);
}
}
}
#endif
@ -11285,6 +11294,319 @@ f_shiftwidth(typval_T *argvars UNUSED, typval_T *rettv)
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, &notanum);
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, &notanum);
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], &notanum);
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, &notanum);
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, &notanum);
if (notanum)
goto cleanup;
}
}
if (sign_place(&sign_id, group, sign_name, buf, lnum, prio) == OK)
rettv->vval.v_number = sign_id;
cleanup:
vim_free(group);
}
/*
* "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
*/

View File

@ -7643,6 +7643,289 @@ sign_cmd_idx(
return idx;
}
/*
* 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
*/
@ -7653,7 +7936,6 @@ ex_sign(exarg_T *eap)
char_u *p;
int idx;
sign_T *sp;
sign_T *sp_prev;
buf_T *buf = NULL;
/* Parse the subcommand. */
@ -7681,6 +7963,12 @@ ex_sign(exarg_T *eap)
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,66 +7976,13 @@ ex_sign(exarg_T *eap)
*p++ = NUL;
while (arg[0] == '0' && arg[1] != NUL)
++arg;
name = vim_strsave(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)
{
int failed = FALSE;
/* ":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 (;;)
@ -7759,96 +7994,48 @@ ex_sign(exarg_T *eap)
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
icon = vim_strnsave(arg, (int)(p - arg));
}
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, " ");
text = vim_strnsave(arg, (int)(p - arg));
}
else if (STRNCMP(arg, "linehl=", 7) == 0)
{
arg += 7;
sp->sn_line_hl = syn_check_group(arg, (int)(p - arg));
linehl = vim_strnsave(arg, (int)(p - arg));
}
else if (STRNCMP(arg, "texthl=", 7) == 0)
{
arg += 7;
sp->sn_text_hl = syn_check_group(arg, (int)(p - arg));
texthl = vim_strnsave(arg, (int)(p - arg));
}
else
{
EMSG2(_(e_invarg2), arg);
return;
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 (sp == NULL)
EMSG2(_("E155: Unknown sign: %s"), arg);
else if (idx == SIGNCMD_LIST)
/* ":sign list {name}" */
sign_list_defined(sp);
sign_list_by_name(name);
else
/* ":sign undefine {name}" */
sign_undefine(sp, sp_prev);
sign_undefine_by_name(name);
vim_free(name);
return;
}
}
else
@ -7856,24 +8043,24 @@ ex_sign(exarg_T *eap)
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);
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)
{
buf_delsign(curwin->w_buffer, id);
update_debug_sign(curwin->w_buffer, curwin->w_cursor.lnum);
}
sign_unplace(id, NULL, curwin->w_buffer);
else
EMSG(_("E159: Missing sign number"));
}
@ -7906,18 +8093,17 @@ ex_sign(exarg_T *eap)
{
/* ":sign unplace {id}": remove placed sign by number */
FOR_ALL_BUFFERS(buf)
if ((lnum = buf_delsign(buf, id)) != 0)
update_debug_sign(buf, lnum);
sign_unplace(id, NULL, buf);
return;
}
}
}
/*
* Check for line={lnum} name={name} and file={fname} or buffer={nr}.
* Leave "arg" pointing to {fname}.
* Check for line={lnum} name={name} group={group} priority={prio}
* and file={fname} or buffer={nr}. Leave "arg" pointing to {fname}.
*/
for (;;)
while (*arg != NUL)
{
if (STRNCMP(arg, "line=", 5) == 0)
{
@ -7945,10 +8131,25 @@ ex_sign(exarg_T *eap)
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(arg);
buf = buflist_findname_exp(arg);
bufarg = TRUE;
break;
}
else if (STRNCMP(arg, "buffer=", 7) == 0)
@ -7957,6 +8158,7 @@ ex_sign(exarg_T *eap)
buf = buflist_findnr((int)getdigits(&arg));
if (*skipwhite(arg) != NUL)
EMSG(_(e_trailing));
bufarg = TRUE;
break;
}
else
@ -7967,24 +8169,33 @@ ex_sign(exarg_T *eap)
arg = skipwhite(arg);
}
if (buf == NULL)
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 (lnum >= 0 || sign_name != NULL)
if ((group == NULL) && (lnum >= 0 || sign_name != NULL))
EMSG(_(e_invarg));
else
/* ":sign place file={fname}": list placed signs in one file */
sign_list_placed(buf);
// ":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)) > 0)
else if ((lnum = buf_findsign(buf, id, group)) > 0)
{ /* goto a sign ... */
if (buf_jump_open_win(buf) != NULL)
{ /* ... in a current window */
@ -8021,45 +8232,164 @@ ex_sign(exarg_T *eap)
EMSG(_(e_invarg));
else if (id == -2)
{
/* ":sign unplace * file={fname}" */
redraw_buf_later(buf, NOT_VALID);
buf_delete_signs(buf);
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
{
/* ":sign unplace {id} file={fname}" */
lnum = buf_delsign(buf, id);
update_debug_sign(buf, lnum);
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)
{
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 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,7 +8544,6 @@ sign_typenr2name(int typenr)
return (char_u *)_("[Deleted]");
}
# if defined(EXITFREE) || defined(PROTO)
/*
* Undefine/free all signs.
*/
@ -8224,7 +8553,6 @@ free_signs(void)
while (first_sign != NULL)
sign_undefine(first_sign, NULL);
}
# endif
# if defined(FEAT_CMDL_COMPL) || defined(PROTO)
static enum

View File

@ -606,6 +606,10 @@ EXTERN buf_T *curbuf INIT(= NULL); /* currently active buffer */
#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 */

View File

@ -115,6 +115,20 @@ rettv_list_alloc(typval_T *rettv)
return OK;
}
/*
* 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
*/

View File

@ -894,6 +894,19 @@ alloc_clear(unsigned size)
return p;
}
/*
* 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
*/

View File

@ -1042,7 +1042,7 @@ nb_do_cmd(
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);
linenum = (long)buf_findsign(buf->bufp, serNum, NULL);
}
#endif
nb_reply_nr(cmdno, linenum);
@ -1256,7 +1256,7 @@ nb_do_cmd(
{
nbdebug((" Deleting sign %d on line %d\n",
id, i));
buf_delsign(buf->bufp, id);
buf_delsign(buf->bufp, id, NULL);
}
else
{

View File

@ -69,17 +69,22 @@ char_u *buf_spname(buf_T *buf);
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);
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);
int buf_findsign(buf_T *buf, int id);
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);
void buf_delete_signs(buf_T *buf, char_u *group);
void buf_delete_all_signs(void);
void sign_list_placed(buf_T *rbuf);
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);

View File

@ -61,10 +61,17 @@ char_u *sign_get_text(int typenr);
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 : */

View File

@ -5,6 +5,7 @@ void list_fix_watch(list_T *l, listitem_T *item);
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);

View File

@ -24,6 +24,7 @@ int alloc_does_fail(long_u size);
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);

View File

@ -740,10 +740,15 @@ struct signlist
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

View File

@ -16,8 +16,8 @@ func Test_sign()
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.
" 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,7 +63,7 @@ func Test_sign()
" 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)
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,7 +112,7 @@ func Test_sign()
" 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:')
call assert_fails("sign define Sign4 text=\\ ab linehl=Comment", 'E239:')
" define sign with whitespace
sign define Sign4 text=\ X linehl=Comment
@ -131,6 +131,28 @@ func Test_sign()
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,7 +174,7 @@ func Test_sign_undefine_still_placed()
" 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)
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,6 +224,8 @@ func Test_sign_completion()
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,6 +235,52 @@ func Test_sign_invalid_commands()
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,3 +294,549 @@ func Test_sign_delete_buffer()
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

View File

@ -799,6 +799,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
614,
/**/
613,
/**/

View File

@ -491,7 +491,7 @@ workshop_get_mark_lineno(
lineno = 0;
buf = buflist_findname((char_u *)filename);
if (buf != NULL)
lineno = buf_findsign(buf, markId);
lineno = buf_findsign(buf, markId, NULL);
return lineno;
}