Merge pull request #14063 from lewis6991/master

vim-patch:8.1.{1631,1682,1899}: sign improvements
This commit is contained in:
Jan Edmund Lazo
2021-04-04 10:28:16 -04:00
committed by GitHub
9 changed files with 740 additions and 276 deletions

View File

@@ -2395,6 +2395,7 @@ shellescape({string} [, {special}])
command argument command argument
shiftwidth([{col}]) Number effective value of 'shiftwidth' shiftwidth([{col}]) Number effective value of 'shiftwidth'
sign_define({name} [, {dict}]) Number define or update a sign sign_define({name} [, {dict}]) Number define or update a sign
sign_define({list}) List define or update a list of signs
sign_getdefined([{name}]) List get a list of defined signs sign_getdefined([{name}]) List get a list of defined signs
sign_getplaced([{expr} [, {dict}]]) sign_getplaced([{expr} [, {dict}]])
List get a list of placed signs List get a list of placed signs
@@ -2402,9 +2403,12 @@ sign_jump({id}, {group}, {expr})
Number jump to a sign Number jump to a sign
sign_place({id}, {group}, {name}, {expr} [, {dict}]) sign_place({id}, {group}, {name}, {expr} [, {dict}])
Number place a sign Number place a sign
sign_placelist({list}) List place a list of signs
sign_undefine([{name}]) Number undefine a sign sign_undefine([{name}]) Number undefine a sign
sign_undefine({list}) List undefine a list of signs
sign_unplace({group} [, {dict}]) sign_unplace({group} [, {dict}])
Number unplace a sign Number unplace a sign
sign_unplacelist({list}) List unplace a list of signs
simplify({filename}) String simplify filename as much as possible simplify({filename}) String simplify filename as much as possible
sin({expr}) Float sine of {expr} sin({expr}) Float sine of {expr}
sinh({expr}) Float hyperbolic sine of {expr} sinh({expr}) Float hyperbolic sine of {expr}
@@ -7927,6 +7931,7 @@ shiftwidth([{col}]) *shiftwidth()*
will be assumed. will be assumed.
sign_define({name} [, {dict}]) *sign_define()* sign_define({name} [, {dict}]) *sign_define()*
sign_define({list})
Define a new sign named {name} or modify the attributes of an Define a new sign named {name} or modify the attributes of an
existing sign. This is similar to the |:sign-define| command. existing sign. This is similar to the |:sign-define| command.
@@ -7936,24 +7941,38 @@ sign_define({name} [, {dict}]) *sign_define()*
The {name} can be a String or a Number. The optional {dict} The {name} can be a String or a Number. The optional {dict}
argument specifies the sign attributes. The following values argument specifies the sign attributes. The following values
are supported: are supported:
icon full path to the bitmap file for the sign. icon full path to the bitmap file for the sign.
linehl highlight group used for the whole line the linehl highlight group used for the whole line the
sign is placed in. sign is placed in.
text text that is displayed when there is no icon text text that is displayed when there is no icon
or the GUI is not being used. or the GUI is not being used.
texthl highlight group used for the text item texthl highlight group used for the text item
numhl highlight group used for 'number' column at the numhl highlight group used for 'number' column at the
associated line. Overrides |hl-LineNr|, associated line. Overrides |hl-LineNr|,
|hl-CursorLineNr|. |hl-CursorLineNr|.
If the sign named {name} already exists, then the attributes If the sign named {name} already exists, then the attributes
of the sign are updated. of the sign are updated.
Returns 0 on success and -1 on failure. The one argument {list} can be used to define a list of signs.
Each list item is a dictionary with the above items in {dict}
and a 'name' item for the sign name.
Returns 0 on success and -1 on failure. When the one argument
{list} is used, then returns a List of values one for each
defined sign.
Examples: > Examples: >
call sign_define("mySign", {"text" : "=>", "texthl" : call sign_define("mySign", {
\ "Error", "linehl" : "Search"}) \ "text" : "=>",
\ "texthl" : "Error",
\ "linehl" : "Search"})
call sign_define([
\ {'name' : 'sign1',
\ 'text' : '=>'},
\ {'name' : 'sign2',
\ 'text' : '!!'}
\ ])
< <
sign_getdefined([{name}]) *sign_getdefined()* sign_getdefined([{name}]) *sign_getdefined()*
Get a list of defined signs and their attributes. Get a list of defined signs and their attributes.
@@ -7965,14 +7984,14 @@ sign_getdefined([{name}]) *sign_getdefined()*
Each list item in the returned value is a dictionary with the Each list item in the returned value is a dictionary with the
following entries: following entries:
icon full path to the bitmap file of the sign icon full path to the bitmap file of the sign
linehl highlight group used for the whole line the linehl highlight group used for the whole line the
sign is placed in. sign is placed in.
name name of the sign name name of the sign
text text that is displayed when there is no icon text text that is displayed when there is no icon
or the GUI is not being used. or the GUI is not being used.
texthl highlight group used for the text item texthl highlight group used for the text item
numhl highlight group used for 'number' column at the numhl highlight group used for 'number' column at the
associated line. Overrides |hl-LineNr|, associated line. Overrides |hl-LineNr|,
|hl-CursorLineNr|. |hl-CursorLineNr|.
@@ -8063,25 +8082,25 @@ sign_jump({id}, {group}, {expr})
< <
*sign_place()* *sign_place()*
sign_place({id}, {group}, {name}, {expr} [, {dict}]) sign_place({id}, {group}, {name}, {expr} [, {dict}])
Place the sign defined as {name} at line {lnum} in file {expr} Place the sign defined as {name} at line {lnum} in file or
and assign {id} and {group} to sign. This is similar to the buffer {expr} and assign {id} and {group} to sign. This is
|:sign-place| command. similar to the |:sign-place| command.
If the sign identifier {id} is zero, then a new identifier is If the sign identifier {id} is zero, then a new identifier is
allocated. Otherwise the specified number is used. {group} is allocated. Otherwise the specified number is used. {group} is
the sign group name. To use the global sign group, use an the sign group name. To use the global sign group, use an
empty string. {group} functions as a namespace for {id}, thus empty string. {group} functions as a namespace for {id}, thus
two groups can use the same IDs. Refer to |sign-identifier| two groups can use the same IDs. Refer to |sign-identifier|
for more information. and |sign-group| for more information.
{name} refers to a defined sign. {name} refers to a defined sign.
{expr} refers to a buffer name or number. For the accepted {expr} refers to a buffer name or number. For the accepted
values, see |bufname()|. values, see |bufname()|.
The optional {dict} argument supports the following entries: The optional {dict} argument supports the following entries:
lnum line number in the buffer {expr} where lnum line number in the file or buffer
the sign is to be placed. For the {expr} where the sign is to be placed.
accepted values, see |line()|. For the accepted values, see |line()|.
priority priority of the sign. See priority priority of the sign. See
|sign-priority| for more information. |sign-priority| for more information.
@@ -8109,18 +8128,86 @@ sign_place({id}, {group}, {name}, {expr} [, {dict}])
" at line 40 in buffer json.c with priority 90 " at line 40 in buffer json.c with priority 90
call sign_place(10, 'g3', 'sign4', 'json.c', call sign_place(10, 'g3', 'sign4', 'json.c',
\ {'lnum' : 40, 'priority' : 90}) \ {'lnum' : 40, 'priority' : 90})
<
*sign_placelist()*
sign_placelist({list})
Place one or more signs. This is similar to the
|sign_place()| function. The {list} argument specifies the
List of signs to place. Each list item is a dict with the
following sign attributes:
buffer buffer name or number. For the accepted
values, see |bufname()|.
group sign group. {group} functions as a namespace
for {id}, thus two groups can use the same
IDs. If not specified or set to an empty
string, then the global group is used. See
|sign-group| for more information.
id sign identifier. If not specified or zero,
then a new unique identifier is allocated.
Otherwise the specified number is used. See
|sign-identifier| for more information.
lnum line number in the buffer {expr} where the
sign is to be placed. For the accepted values,
see |line()|.
name name of the sign to place. See |sign_define()|
for more information.
priority priority of the sign. When multiple signs are
placed on a line, the sign with the highest
priority is used. If not specified, the
default value of 10 is used. See
|sign-priority| for more information.
If {id} refers to an existing sign, then the existing sign is
modified to use the specified {name} and/or {priority}.
Returns a List of sign identifiers. If failed to place a
sign, the corresponding list item is set to -1.
Examples: >
" Place sign s1 with id 5 at line 20 and id 10 at line
" 30 in buffer a.c
let [n1, n2] = sign_place([
\ {'id' : 5,
\ 'name' : 's1',
\ 'buffer' : 'a.c',
\ 'lnum' : 20},
\ {'id' : 10,
\ 'name' : 's1',
\ 'buffer' : 'a.c',
\ 'lnum' : 30}
\ ])
" Place sign s1 in buffer a.c at line 40 and 50
" with auto-generated identifiers
let [n1, n2] = sign_place([
\ {'name' : 's1',
\ 'buffer' : 'a.c',
\ 'lnum' : 40},
\ {'name' : 's1',
\ 'buffer' : 'a.c',
\ 'lnum' : 50}
\ ])
< <
sign_undefine([{name}]) *sign_undefine()* sign_undefine([{name}]) *sign_undefine()*
sign_undefine({list})
Deletes a previously defined sign {name}. This is similar to Deletes a previously defined sign {name}. This is similar to
the |:sign-undefine| command. If {name} is not supplied, then the |:sign-undefine| command. If {name} is not supplied, then
deletes all the defined signs. deletes all the defined signs.
Returns 0 on success and -1 on failure. The one argument {list} can be used to undefine a list of
signs. Each list item is the name of a sign.
Returns 0 on success and -1 on failure. For the one argument
{list} call, returns a list of values one for each undefined
sign.
Examples: > Examples: >
" Delete a sign named mySign " Delete a sign named mySign
call sign_undefine("mySign") call sign_undefine("mySign")
" Delete signs 'sign1' and 'sign2'
call sign_undefine(["sign1", "sign2"])
" Delete all the signs " Delete all the signs
call sign_undefine() call sign_undefine()
< <
@@ -8166,6 +8253,32 @@ sign_unplace({group} [, {dict}]) *sign_unplace()*
" Remove all the placed signs from all the buffers " Remove all the placed signs from all the buffers
call sign_unplace('*') call sign_unplace('*')
< <
sign_unplacelist({list}) *sign_unplacelist()*
Remove previously placed signs from one or more buffers. This
is similar to the |sign_unplace()| function.
The {list} argument specifies the List of signs to remove.
Each list item is a dict with the following sign attributes:
buffer buffer name or number. For the accepted
values, see |bufname()|. If not specified,
then the specified sign is removed from all
the buffers.
group sign group name. If not specified or set to an
empty string, then the global sign group is
used. If set to '*', then all the groups
including the global group are used.
id sign identifier. If not specified, then all
the signs in the specified group are removed.
Returns a List where an entry is set to 0 if the corresponding
sign was successfully removed or -1 on failure.
Example: >
" Remove sign with id 10 from buffer a.vim and sign
" with id 20 from buffer b.vim
call sign_unplace([{'id' : 10, 'buffer' : "a.vim"},
\ {'id' : 20, 'buffer' : 'b.vim'}])
<
simplify({filename}) *simplify()* simplify({filename}) *simplify()*
Simplify the file name as much as possible without changing Simplify the file name as much as possible without changing
the meaning. Shortcuts (on MS-Windows) or symbolic links (on the meaning. Shortcuts (on MS-Windows) or symbolic links (on

View File

@@ -943,8 +943,10 @@ Signs: *sign-functions*
sign_getplaced() get a list of placed signs sign_getplaced() get a list of placed signs
sign_jump() jump to a sign sign_jump() jump to a sign
sign_place() place a sign sign_place() place a sign
sign_placelist() place a list of signs
sign_undefine() undefine a sign sign_undefine() undefine a sign
sign_unplace() unplace a sign sign_unplace() unplace a sign
sign_unplacelist() unplace a list of signs
Testing: *test-functions* Testing: *test-functions*

View File

@@ -316,8 +316,10 @@ return {
sign_getplaced={args={0, 2}}, sign_getplaced={args={0, 2}},
sign_jump={args={3, 3}}, sign_jump={args={3, 3}},
sign_place={args={4, 5}}, sign_place={args={4, 5}},
sign_placelist={args={1}},
sign_undefine={args={0, 1}}, sign_undefine={args={0, 1}},
sign_unplace={args={1, 2}}, sign_unplace={args={1, 2}},
sign_unplacelist={args={1}},
simplify={args=1}, simplify={args=1},
sin={args=1, func="float_op_wrapper", data="&sin"}, sin={args=1, func="float_op_wrapper", data="&sin"},
sinh={args=1, func="float_op_wrapper", data="&sinh"}, sinh={args=1, func="float_op_wrapper", data="&sinh"},

View File

@@ -8861,13 +8861,16 @@ static void f_shiftwidth(typval_T *argvars, typval_T *rettv, FunPtr fptr)
static void f_sign_define(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_sign_define(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{ {
const char *name; const char *name;
dict_T *dict;
char *icon = NULL;
char *linehl = NULL;
char *text = NULL;
char *texthl = NULL;
char *numhl = NULL;
if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_UNKNOWN) {
// Define multiple signs
tv_list_alloc_ret(rettv, kListLenMayKnow);
sign_define_multiple(argvars[0].vval.v_list, rettv->vval.v_list);
return;
}
// Define a single sign
rettv->vval.v_number = -1; rettv->vval.v_number = -1;
name = tv_get_string_chk(&argvars[0]); name = tv_get_string_chk(&argvars[0]);
@@ -8875,42 +8878,13 @@ static void f_sign_define(typval_T *argvars, typval_T *rettv, FunPtr fptr)
return; return;
} }
if (argvars[1].v_type != VAR_UNKNOWN) { if (argvars[1].v_type != VAR_UNKNOWN && argvars[1].v_type != VAR_DICT) {
if (argvars[1].v_type != VAR_DICT) { EMSG(_(e_dictreq));
EMSG(_(e_dictreq)); return;
return;
}
// sign attributes
dict = argvars[1].vval.v_dict;
if (tv_dict_find(dict, "icon", -1) != NULL) {
icon = tv_dict_get_string(dict, "icon", true);
}
if (tv_dict_find(dict, "linehl", -1) != NULL) {
linehl = tv_dict_get_string(dict, "linehl", true);
}
if (tv_dict_find(dict, "text", -1) != NULL) {
text = tv_dict_get_string(dict, "text", true);
}
if (tv_dict_find(dict, "texthl", -1) != NULL) {
texthl = tv_dict_get_string(dict, "texthl", true);
}
if (tv_dict_find(dict, "numhl", -1) != NULL) {
numhl = tv_dict_get_string(dict, "numhl", true);
}
} }
if (sign_define_by_name((char_u *)name, (char_u *)icon, (char_u *)linehl, rettv->vval.v_number = sign_define_from_dict(
(char_u *)text, (char_u *)texthl, (char_u *)numhl) name, argvars[1].v_type == VAR_DICT ? argvars[1].vval.v_dict : NULL);
== OK) {
rettv->vval.v_number = 0;
}
xfree(icon);
xfree(linehl);
xfree(text);
xfree(texthl);
xfree(numhl);
} }
/// "sign_getdefined()" function /// "sign_getdefined()" function
@@ -9031,83 +9005,44 @@ cleanup:
/// "sign_place()" function /// "sign_place()" function
static void f_sign_place(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_sign_place(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{ {
int sign_id; dict_T *dict = NULL;
char_u *group = NULL;
const char *sign_name;
buf_T *buf;
dict_T *dict;
dictitem_T *di;
linenr_T lnum = 0;
int prio = SIGN_DEF_PRIO;
bool notanum = false;
rettv->vval.v_number = -1; rettv->vval.v_number = -1;
// Sign identifier if (argvars[4].v_type != VAR_UNKNOWN
sign_id = (int)tv_get_number_chk(&argvars[0], &notanum); && (argvars[4].v_type != VAR_DICT
if (notanum) { || ((dict = argvars[4].vval.v_dict) == NULL))) {
return; EMSG(_(e_dictreq));
}
if (sign_id < 0) {
EMSG(_(e_invarg));
return; return;
} }
// Sign group rettv->vval.v_number = sign_place_from_dict(
const char *group_chk = tv_get_string_chk(&argvars[1]); &argvars[0], &argvars[1], &argvars[2], &argvars[3], dict);
if (group_chk == NULL) { }
/// "sign_placelist()" function. Place multiple signs.
static void f_sign_placelist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
int sign_id;
tv_list_alloc_ret(rettv, kListLenMayKnow);
if (argvars[0].v_type != VAR_LIST) {
EMSG(_(e_listreq));
return; return;
} }
if (group_chk[0] == '\0') {
group = NULL; // global sign group
} else {
group = vim_strsave((const char_u *)group_chk);
}
// Sign name // Process the List of sign attributes
sign_name = tv_get_string_chk(&argvars[2]); TV_LIST_ITER_CONST(argvars[0].vval.v_list, li, {
if (sign_name == NULL) { sign_id = -1;
goto cleanup; if (TV_LIST_ITEM_TV(li)->v_type == VAR_DICT) {
} sign_id = sign_place_from_dict(
NULL, NULL, NULL, NULL, TV_LIST_ITEM_TV(li)->vval.v_dict);
// Buffer to place the sign } else {
buf = get_buf_arg(&argvars[3]);
if (buf == NULL) {
goto cleanup;
}
if (argvars[4].v_type != VAR_UNKNOWN) {
if (argvars[4].v_type != VAR_DICT
|| ((dict = argvars[4].vval.v_dict) == NULL)) {
EMSG(_(e_dictreq)); EMSG(_(e_dictreq));
goto cleanup;
} }
tv_list_append_number(rettv->vval.v_list, sign_id);
// Line number where the sign is to be placed });
if ((di = tv_dict_find(dict, "lnum", -1)) != NULL) {
lnum = (linenr_T)tv_get_number_chk(&di->di_tv, &notanum);
if (notanum) {
goto cleanup;
}
(void)lnum;
lnum = tv_get_lnum(&di->di_tv);
}
if ((di = tv_dict_find(dict, "priority", -1)) != NULL) {
// Sign priority
prio = (int)tv_get_number_chk(&di->di_tv, &notanum);
if (notanum) {
goto cleanup;
}
}
}
if (sign_place(&sign_id, group, (const char_u *)sign_name, buf, lnum, prio)
== OK) {
rettv->vval.v_number = sign_id;
}
cleanup:
xfree(group);
} }
/// "sign_undefine()" function /// "sign_undefine()" function
@@ -9115,6 +9050,14 @@ static void f_sign_undefine(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{ {
const char *name; const char *name;
if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_UNKNOWN) {
// Undefine multiple signs
tv_list_alloc_ret(rettv, kListLenMayKnow);
sign_undefine_multiple(argvars[0].vval.v_list, rettv->vval.v_list);
return;
}
rettv->vval.v_number = -1; rettv->vval.v_number = -1;
if (argvars[0].v_type == VAR_UNKNOWN) { if (argvars[0].v_type == VAR_UNKNOWN) {
@@ -9137,11 +9080,7 @@ static void f_sign_undefine(typval_T *argvars, typval_T *rettv, FunPtr fptr)
/// "sign_unplace()" function /// "sign_unplace()" function
static void f_sign_unplace(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_sign_unplace(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{ {
dict_T *dict; dict_T *dict = NULL;
dictitem_T *di;
int sign_id = 0;
buf_T *buf = NULL;
char_u *group = NULL;
rettv->vval.v_number = -1; rettv->vval.v_number = -1;
@@ -9150,46 +9089,38 @@ static void f_sign_unplace(typval_T *argvars, typval_T *rettv, FunPtr fptr)
return; return;
} }
const char *group_chk = tv_get_string(&argvars[0]);
if (group_chk[0] == '\0') {
group = NULL; // global sign group
} else {
group = vim_strsave((const char_u *)group_chk);
}
if (argvars[1].v_type != VAR_UNKNOWN) { if (argvars[1].v_type != VAR_UNKNOWN) {
if (argvars[1].v_type != VAR_DICT) { if (argvars[1].v_type != VAR_DICT) {
EMSG(_(e_dictreq)); EMSG(_(e_dictreq));
goto cleanup; return;
} }
dict = argvars[1].vval.v_dict; dict = argvars[1].vval.v_dict;
if ((di = tv_dict_find(dict, "buffer", -1)) != NULL) {
buf = get_buf_arg(&di->di_tv);
if (buf == NULL) {
goto cleanup;
}
}
if (tv_dict_find(dict, "id", -1) != NULL) {
sign_id = tv_dict_get_number(dict, "id");
}
} }
if (buf == NULL) { rettv->vval.v_number = sign_unplace_from_dict(&argvars[0], dict);
// Delete the sign in all the buffers }
FOR_ALL_BUFFERS(cbuf) {
if (sign_unplace(sign_id, group, cbuf, 0) == OK) { /// "sign_unplacelist()" function
rettv->vval.v_number = 0; static void f_sign_unplacelist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
} {
} int retval;
} else {
if (sign_unplace(sign_id, group, buf, 0) == OK) { tv_list_alloc_ret(rettv, kListLenMayKnow);
rettv->vval.v_number = 0;
} if (argvars[0].v_type != VAR_LIST) {
EMSG(_(e_listreq));
return;
} }
cleanup: TV_LIST_ITER_CONST(argvars[0].vval.v_list, li, {
xfree(group); retval = -1;
if (TV_LIST_ITEM_TV(li)->v_type == VAR_DICT) {
retval = sign_unplace_from_dict(NULL, TV_LIST_ITEM_TV(li)->vval.v_dict);
} else {
EMSG(_(e_dictreq));
}
tv_list_append_number(rettv->vval.v_list, retval);
});
} }
/* /*

View File

@@ -7607,7 +7607,9 @@ int win_signcol_count(win_T *wp)
} }
} }
return MAX(minimum, MIN(maximum, needed_signcols)); int ret = MAX(minimum, MIN(maximum, needed_signcols));
assert(ret <= SIGN_SHOW_MAX);
return ret;
} }
/// Get window or buffer local options /// Get window or buffer local options

View File

@@ -2084,6 +2084,8 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow,
colnr_T trailcol = MAXCOL; // start of trailing spaces colnr_T trailcol = MAXCOL; // start of trailing spaces
colnr_T leadcol = 0; // start of leading spaces colnr_T leadcol = 0; // start of leading spaces
bool need_showbreak = false; // overlong line, skip first x chars bool need_showbreak = false; // overlong line, skip first x chars
sign_attrs_T sattrs[SIGN_SHOW_MAX]; // attributes for signs
int num_signs; // number of signs for line
int line_attr = 0; // attribute for the whole line int line_attr = 0; // attribute for the whole line
int line_attr_lowprio = 0; // low-priority attribute for the line int line_attr_lowprio = 0; // low-priority attribute for the line
matchitem_T *cur; // points to the match list matchitem_T *cur; // points to the match list
@@ -2375,11 +2377,14 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow,
wp->w_last_cursorline = wp->w_cursor.lnum; wp->w_last_cursorline = wp->w_cursor.lnum;
} }
memset(sattrs, 0, sizeof(sattrs));
num_signs = buf_get_signattrs(wp->w_buffer, lnum, sattrs);
// If this line has a sign with line highlighting set line_attr. // If this line has a sign with line highlighting set line_attr.
// TODO(bfredl, vigoux): this should not take priority over decoration! // TODO(bfredl, vigoux): this should not take priority over decoration!
v = buf_getsigntype(wp->w_buffer, lnum, SIGN_LINEHL, 0, 1); sign_attrs_T * sattr = sign_get_attr(SIGN_LINEHL, sattrs, 0, 1);
if (v != 0) { if (sattr != NULL) {
line_attr = sign_get_attr((int)v, SIGN_LINEHL); line_attr = sattr->linehl;
} }
// Highlight the current line in the quickfix window. // Highlight the current line in the quickfix window.
@@ -2696,7 +2701,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow,
int count = win_signcol_count(wp); int count = win_signcol_count(wp);
if (count > 0) { if (count > 0) {
get_sign_display_info( get_sign_display_info(
false, wp, lnum, row, false, wp, sattrs, row,
startrow, filler_lines, filler_todo, count, startrow, filler_lines, filler_todo, count,
&c_extra, &c_final, extra, sizeof(extra), &c_extra, &c_final, extra, sizeof(extra),
&p_extra, &n_extra, &p_extra, &n_extra,
@@ -2715,10 +2720,10 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow,
// in 'lnum', then display the sign instead of the line // in 'lnum', then display the sign instead of the line
// number. // number.
if (*wp->w_p_scl == 'n' && *(wp->w_p_scl + 1) == 'u' if (*wp->w_p_scl == 'n' && *(wp->w_p_scl + 1) == 'u'
&& buf_findsign_id(wp->w_buffer, lnum, (char_u *)"*") != 0) { && num_signs > 0) {
int count = win_signcol_count(wp); int count = win_signcol_count(wp);
get_sign_display_info( get_sign_display_info(
true, wp, lnum, row, true, wp, sattrs, row,
startrow, filler_lines, filler_todo, count, startrow, filler_lines, filler_todo, count,
&c_extra, &c_final, extra, sizeof(extra), &c_extra, &c_final, extra, sizeof(extra),
&p_extra, &n_extra, &p_extra, &n_extra,
@@ -2768,11 +2773,10 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow,
n_extra = number_width(wp) + 1; n_extra = number_width(wp) + 1;
char_attr = win_hl_attr(wp, HLF_N); char_attr = win_hl_attr(wp, HLF_N);
int num_sign = buf_getsigntype( sign_attrs_T *num_sattr = sign_get_attr(SIGN_NUMHL, sattrs, 0, 1);
wp->w_buffer, lnum, SIGN_NUMHL, 0, 1); if (num_sattr != NULL) {
if (num_sign != 0) {
// :sign defined with "numhl" highlight. // :sign defined with "numhl" highlight.
char_attr = sign_get_attr(num_sign, SIGN_NUMHL); char_attr = num_sattr->numhl;
} else if ((wp->w_p_cul || wp->w_p_rnu) } else if ((wp->w_p_cul || wp->w_p_rnu)
&& lnum == wp->w_cursor.lnum && lnum == wp->w_cursor.lnum
&& filler_todo == 0) { && filler_todo == 0) {
@@ -4451,7 +4455,7 @@ void screen_adjust_grid(ScreenGrid **grid, int *row_off, int *col_off)
static void get_sign_display_info( static void get_sign_display_info(
bool nrcol, bool nrcol,
win_T *wp, win_T *wp,
linenr_T lnum, sign_attrs_T sattrs[],
int row, int row,
int startrow, int startrow,
int filler_lines, int filler_lines,
@@ -4468,8 +4472,6 @@ static void get_sign_display_info(
int *sign_idxp int *sign_idxp
) )
{ {
int text_sign;
// Draw cells with the sign value or blank. // Draw cells with the sign value or blank.
*c_extrap = ' '; *c_extrap = ' ';
*c_finalp = NUL; *c_finalp = NUL;
@@ -4481,10 +4483,9 @@ static void get_sign_display_info(
} }
if (row == startrow + filler_lines && filler_todo <= 0) { if (row == startrow + filler_lines && filler_todo <= 0) {
text_sign = buf_getsigntype(wp->w_buffer, lnum, SIGN_TEXT, sign_attrs_T *sattr = sign_get_attr(SIGN_TEXT, sattrs, *sign_idxp, count);
*sign_idxp, count); if (sattr != NULL) {
if (text_sign != 0) { *pp_extra = sattr->text;
*pp_extra = sign_get_text(text_sign);
if (*pp_extra != NULL) { if (*pp_extra != NULL) {
*c_extrap = NUL; *c_extrap = NUL;
*c_finalp = NUL; *c_finalp = NUL;
@@ -4517,7 +4518,7 @@ static void get_sign_display_info(
(*pp_extra)[*n_extrap] = NUL; (*pp_extra)[*n_extrap] = NUL;
} }
} }
*char_attrp = sign_get_attr(text_sign, SIGN_TEXT); *char_attrp = sattr->texthl;
} }
} }

View File

@@ -398,7 +398,8 @@ linenr_T buf_change_sign_type(
buf_T *buf, // buffer to store sign in buf_T *buf, // buffer to store sign in
int markId, // sign ID int markId, // sign ID
const char_u *group, // sign group const char_u *group, // sign group
int typenr // typenr of sign we are adding int typenr, // typenr of sign we are adding
int prio // sign priority
) )
{ {
signlist_T *sign; // a sign in the signlist signlist_T *sign; // a sign in the signlist
@@ -406,6 +407,8 @@ linenr_T buf_change_sign_type(
FOR_ALL_SIGNS_IN_BUF(buf, sign) { FOR_ALL_SIGNS_IN_BUF(buf, sign) {
if (sign->id == markId && sign_in_group(sign, group)) { if (sign->id == markId && sign_in_group(sign, group)) {
sign->typenr = typenr; sign->typenr = typenr;
sign->priority = prio;
sign_sort_by_prio_on_line(buf, sign);
return sign->lnum; return sign->lnum;
} }
} }
@@ -413,54 +416,101 @@ linenr_T buf_change_sign_type(
return (linenr_T)0; return (linenr_T)0;
} }
/// Gets a sign from a given line. /// Return the sign attrs which has the attribute specified by 'type'. Returns
/// /// NULL if a sign is not found with the specified attribute.
/// Return the type number of the sign at line number 'lnum' in buffer 'buf'
/// which has the attribute specified by 'type'. Returns 0 if a sign is not
/// found at the line number or it doesn't have the specified attribute.
/// @param buf Buffer in which to search
/// @param lnum Line in which to search
/// @param type Type of sign to look for /// @param type Type of sign to look for
/// @param sattrs Sign attrs to search through
/// @param idx if there multiple signs, this index will pick the n-th /// @param idx if there multiple signs, this index will pick the n-th
// out of the most `max_signs` sorted ascending by Id. /// out of the most `max_signs` sorted ascending by Id.
/// @param max_signs the number of signs, with priority for the ones /// @param max_signs the number of signs, with priority for the ones
// with the highest Ids. /// with the highest Ids.
/// @return Identifier of the matching sign, or 0 /// @return Attrs of the matching sign, or NULL
int buf_getsigntype(buf_T *buf, linenr_T lnum, SignType type, sign_attrs_T * sign_get_attr(SignType type, sign_attrs_T sattrs[],
int idx, int max_signs) int idx, int max_signs)
{ {
signlist_T *sign; // a sign in a b_signlist sign_attrs_T *matches[SIGN_SHOW_MAX];
signlist_T *matches[9];
int nr_matches = 0; int nr_matches = 0;
FOR_ALL_SIGNS_IN_BUF(buf, sign) { for (int i = 0; i < SIGN_SHOW_MAX; i++) {
if (sign->lnum == lnum if ( (type == SIGN_TEXT && sattrs[i].text != NULL)
&& (type == SIGN_ANY || (type == SIGN_LINEHL && sattrs[i].linehl != 0)
|| (type == SIGN_TEXT || (type == SIGN_NUMHL && sattrs[i].numhl != 0)) {
&& sign_get_text(sign->typenr) != NULL) matches[nr_matches] = &sattrs[i];
|| (type == SIGN_LINEHL
&& sign_get_attr(sign->typenr, SIGN_LINEHL) != 0)
|| (type == SIGN_NUMHL
&& sign_get_attr(sign->typenr, SIGN_NUMHL) != 0))) {
matches[nr_matches] = sign;
nr_matches++; nr_matches++;
// signlist is sorted with most important (priority, id), thus we // attr list is sorted with most important (priority, id), thus we
// may stop as soon as we have max_signs matches // may stop as soon as we have max_signs matches
if (nr_matches == ARRAY_SIZE(matches) || nr_matches >= max_signs) { if (nr_matches >= max_signs) {
break; break;
} }
} }
} }
if (nr_matches > 0) { if (nr_matches > idx) {
if (idx >= nr_matches) { return matches[nr_matches - idx - 1];
return 0;
}
return matches[nr_matches - idx -1]->typenr;
} }
return 0; return NULL;
}
/// Lookup a sign by typenr. Returns NULL if sign is not found.
static sign_T * find_sign_by_typenr(int typenr)
{
sign_T *sp;
for (sp = first_sign; sp != NULL; sp = sp->sn_next) {
if (sp->sn_typenr == typenr) {
return sp;
}
}
return NULL;
}
/// Return the attributes of all the signs placed on line 'lnum' in buffer
/// 'buf'. Used when refreshing the screen. Returns the number of signs.
/// @param buf Buffer in which to search
/// @param lnum Line in which to search
/// @param sattrs Output array for attrs
/// @return Number of signs of which attrs were found
int buf_get_signattrs(buf_T *buf, linenr_T lnum, sign_attrs_T sattrs[])
{
signlist_T *sign;
sign_T *sp;
int nr_matches = 0;
FOR_ALL_SIGNS_IN_BUF(buf, sign) {
if (sign->lnum > lnum) {
// Signs are sorted by line number in the buffer. No need to check
// for signs after the specified line number 'lnum'.
break;
}
if (sign->lnum == lnum) {
sign_attrs_T sattr;
memset(&sattr, 0, sizeof(sattr));
sattr.typenr = sign->typenr;
sp = find_sign_by_typenr(sign->typenr);
if (sp != NULL) {
sattr.text = sp->sn_text;
if (sattr.text != NULL && sp->sn_text_hl != 0) {
sattr.texthl = syn_id2attr(sp->sn_text_hl);
}
if (sp->sn_line_hl != 0) {
sattr.linehl = syn_id2attr(sp->sn_line_hl);
}
if (sp->sn_num_hl != 0) {
sattr.numhl = syn_id2attr(sp->sn_num_hl);
}
}
sattrs[nr_matches] = sattr;
nr_matches++;
if (nr_matches == SIGN_SHOW_MAX) {
break;
}
}
}
return nr_matches;
} }
/// Delete sign 'id' in group 'group' from buffer 'buf'. /// Delete sign 'id' in group 'group' from buffer 'buf'.
@@ -557,6 +607,12 @@ static signlist_T * buf_getsign_at_line(
signlist_T *sign; // a sign in the signlist signlist_T *sign; // a sign in the signlist
FOR_ALL_SIGNS_IN_BUF(buf, sign) { FOR_ALL_SIGNS_IN_BUF(buf, sign) {
if (sign->lnum > lnum) {
// Signs are sorted by line number in the buffer. No need to check
// for signs after the specified line number 'lnum'.
break;
}
if (sign->lnum == lnum && sign_in_group(sign, groupname)) { if (sign->lnum == lnum && sign_in_group(sign, groupname)) {
return sign; return sign;
} }
@@ -973,8 +1029,8 @@ int sign_place(
sp->sn_typenr, sp->sn_typenr,
has_text_or_icon); has_text_or_icon);
} else { } else {
// ":sign place {id} file={fname}": change sign type // ":sign place {id} file={fname}": change sign type and/or priority
lnum = buf_change_sign_type(buf, *sign_id, sign_group, sp->sn_typenr); lnum = buf_change_sign_type(buf, *sign_id, sign_group, sp->sn_typenr, prio);
} }
if (lnum > 0) { if (lnum > 0) {
redraw_buf_line_later(buf, lnum); redraw_buf_line_later(buf, lnum);
@@ -1613,50 +1669,6 @@ static void sign_undefine(sign_T *sp, sign_T *sp_prev)
xfree(sp); xfree(sp);
} }
/// Gets highlighting attribute for sign "typenr" corresponding to "type".
int sign_get_attr(int typenr, SignType type)
{
sign_T *sp;
int sign_hl = 0;
for (sp = first_sign; sp != NULL; sp = sp->sn_next) {
if (sp->sn_typenr == typenr) {
switch (type) {
case SIGN_TEXT:
sign_hl = sp->sn_text_hl;
break;
case SIGN_LINEHL:
sign_hl = sp->sn_line_hl;
break;
case SIGN_NUMHL:
sign_hl = sp->sn_num_hl;
break;
default:
abort();
}
if (sign_hl > 0) {
return syn_id2attr(sign_hl);
}
break;
}
}
return 0;
}
/// Get text mark for sign "typenr".
/// Returns NULL if there isn't one.
char_u * sign_get_text(int typenr)
{
sign_T *sp;
for (sp = first_sign; sp != NULL; sp = sp->sn_next) {
if (sp->sn_typenr == typenr) {
return sp->sn_text;
}
}
return NULL;
}
/// Undefine/free all signs. /// Undefine/free all signs.
void free_signs(void) void free_signs(void)
{ {
@@ -1860,3 +1872,267 @@ void set_context_in_sign_cmd(expand_T *xp, char_u *arg)
} }
} }
/// Define a sign using the attributes in 'dict'. Returns 0 on success and -1 on
/// failure.
int sign_define_from_dict(const char *name_arg, dict_T *dict)
{
char *name = NULL;
char *icon = NULL;
char *linehl = NULL;
char *text = NULL;
char *texthl = NULL;
char *numhl = NULL;
int retval = -1;
if (name_arg == NULL) {
if (dict == NULL) {
return -1;
}
name = tv_dict_get_string(dict, "name", true);
} else {
name = xstrdup(name_arg);
}
if (name == NULL || name[0] == NUL) {
goto cleanup;
}
if (dict != NULL) {
icon = tv_dict_get_string(dict, "icon" , true);
linehl = tv_dict_get_string(dict, "linehl", true);
text = tv_dict_get_string(dict, "text" , true);
texthl = tv_dict_get_string(dict, "texthl", true);
numhl = tv_dict_get_string(dict, "numhl" , true);
}
if (sign_define_by_name((char_u *)name, (char_u *)icon, (char_u *)linehl,
(char_u *)text, (char_u *)texthl, (char_u *)numhl)
== OK) {
retval = 0;
}
cleanup:
xfree(name);
xfree(icon);
xfree(linehl);
xfree(text);
xfree(texthl);
xfree(numhl);
return retval;
}
/// Define multiple signs using attributes from list 'l' and store the return
/// values in 'retlist'.
void sign_define_multiple(list_T *l, list_T *retlist)
{
int retval;
TV_LIST_ITER_CONST(l, li, {
retval = -1;
if (TV_LIST_ITEM_TV(li)->v_type == VAR_DICT) {
retval = sign_define_from_dict(NULL, TV_LIST_ITEM_TV(li)->vval.v_dict);
} else {
EMSG(_(e_dictreq));
}
tv_list_append_number(retlist, retval);
});
}
/// Place a new sign using the values specified in dict 'dict'. Returns the sign
/// identifier if successfully placed, otherwise returns 0.
int sign_place_from_dict(
typval_T *id_tv,
typval_T *group_tv,
typval_T *name_tv,
typval_T *buf_tv,
dict_T *dict)
{
int sign_id = 0;
char_u *group = NULL;
char_u *sign_name = NULL;
buf_T *buf = NULL;
dictitem_T *di;
linenr_T lnum = 0;
int prio = SIGN_DEF_PRIO;
bool notanum = false;
int ret_sign_id = -1;
// sign identifier
if (id_tv == NULL) {
di = tv_dict_find(dict, "id", -1);
if (di != NULL) {
id_tv = &di->di_tv;
}
}
if (id_tv == NULL) {
sign_id = 0;
} else {
sign_id = (int)tv_get_number_chk(id_tv, &notanum);
if (notanum) {
return -1;
}
if (sign_id < 0) {
EMSG(_(e_invarg));
return -1;
}
}
// sign group
if (group_tv == NULL) {
di = tv_dict_find(dict, "group", -1);
if (di != NULL) {
group_tv = &di->di_tv;
}
}
if (group_tv == NULL) {
group = NULL; // global group
} else {
group = (char_u *)tv_get_string_chk(group_tv);
if (group == NULL) {
goto cleanup;
}
if (group[0] == '\0') { // global sign group
group = NULL;
} else {
group = vim_strsave(group);
if (group == NULL) {
return -1;
}
}
}
// sign name
if (name_tv == NULL) {
di = tv_dict_find(dict, "name", -1);
if (di != NULL) {
name_tv = &di->di_tv;
}
}
if (name_tv == NULL) {
goto cleanup;
}
sign_name = (char_u *)tv_get_string_chk(name_tv);
if (sign_name == NULL) {
goto cleanup;
}
// buffer to place the sign
if (buf_tv == NULL) {
di = tv_dict_find(dict, "buffer", -1);
if (di != NULL) {
buf_tv = &di->di_tv;
}
}
if (buf_tv == NULL) {
goto cleanup;
}
buf = get_buf_arg(buf_tv);
if (buf == NULL) {
goto cleanup;
}
// line number of the sign
di = tv_dict_find(dict, "lnum", -1);
if (di != NULL) {
lnum = tv_get_lnum(&di->di_tv);
if (lnum <= 0) {
EMSG(_(e_invarg));
goto cleanup;
}
}
// sign priority
di = tv_dict_find(dict, "priority", -1);
if (di != NULL) {
prio = (int)tv_get_number_chk(&di->di_tv, &notanum);
if (notanum) {
goto cleanup;
}
}
if (sign_place(&sign_id, group, sign_name, buf, lnum, prio) == OK) {
ret_sign_id = sign_id;
}
cleanup:
xfree(group);
return ret_sign_id;
}
/// Undefine multiple signs
void sign_undefine_multiple(list_T *l, list_T *retlist)
{
char_u *name;
int retval;
TV_LIST_ITER_CONST(l, li, {
retval = -1;
name = (char_u *)tv_get_string_chk(TV_LIST_ITEM_TV(li));
if (name != NULL && (sign_undefine_by_name(name) == OK)) {
retval = 0;
}
tv_list_append_number(retlist, retval);
});
}
/// Unplace the sign with attributes specified in 'dict'. Returns 0 on success
/// and -1 on failure.
int sign_unplace_from_dict(typval_T *group_tv, dict_T *dict)
{
dictitem_T *di;
int sign_id = 0;
buf_T *buf = NULL;
char_u *group = NULL;
int retval = -1;
// sign group
if (group_tv != NULL) {
group = (char_u *)tv_get_string(group_tv);
} else {
group = (char_u *)tv_dict_get_string(dict, "group", false);
}
if (group != NULL) {
if (group[0] == '\0') { // global sign group
group = NULL;
} else {
group = vim_strsave(group);
if (group == NULL) {
return -1;
}
}
}
if (dict != NULL) {
if ((di = tv_dict_find(dict, "buffer", -1)) != NULL) {
buf = get_buf_arg(&di->di_tv);
if (buf == NULL) {
goto cleanup;
}
}
if (tv_dict_find(dict, "id", -1) != NULL) {
sign_id = (int)tv_dict_get_number(dict, "id");
if (sign_id <= 0) {
EMSG(_(e_invarg));
goto cleanup;
}
}
}
if (buf == NULL) {
// Delete the sign in all the buffers
retval = 0;
FOR_ALL_BUFFERS(buf2) {
if (sign_unplace(sign_id, group, buf2, 0) != OK) {
retval = -1;
}
}
} else if (sign_unplace(sign_id, group, buf, 0) == OK) {
retval = 0;
}
cleanup:
xfree(group);
return retval;
}

View File

@@ -33,16 +33,25 @@ struct signlist
signlist_T *prev; // previous entry -- for easy reordering signlist_T *prev; // previous entry -- for easy reordering
}; };
/// Sign attributes. Used by the screen refresh routines.
typedef struct sign_attrs_S {
int typenr;
char_u *text;
int texthl;
int linehl;
int numhl;
} sign_attrs_T;
#define SIGN_SHOW_MAX 9
// Default sign priority for highlighting // Default sign priority for highlighting
#define SIGN_DEF_PRIO 10 #define SIGN_DEF_PRIO 10
// type argument for buf_getsigntype() and sign_get_attr() // type argument for sign_get_attr()
typedef enum { typedef enum {
SIGN_ANY,
SIGN_LINEHL, SIGN_LINEHL,
SIGN_ICON,
SIGN_TEXT,
SIGN_NUMHL, SIGN_NUMHL,
SIGN_TEXT,
} SignType; } SignType;

View File

@@ -415,7 +415,7 @@ func Test_sign_funcs()
" Tests for invalid arguments to sign_define() " Tests for invalid arguments to sign_define()
call assert_fails('call sign_define("sign4", {"text" : "===>"})', 'E239:') call assert_fails('call sign_define("sign4", {"text" : "===>"})', 'E239:')
" call assert_fails('call sign_define("sign5", {"text" : ""})', 'E239:') " call assert_fails('call sign_define("sign5", {"text" : ""})', 'E239:')
call assert_fails('call sign_define([])', 'E730:') call assert_fails('call sign_define({})', 'E731:')
call assert_fails('call sign_define("sign6", [])', 'E715:') call assert_fails('call sign_define("sign6", [])', 'E715:')
" Tests for sign_getdefined() " Tests for sign_getdefined()
@@ -444,7 +444,7 @@ func Test_sign_funcs()
call assert_fails('call sign_place([], "", "mySign", 1)', 'E745:') 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(5, "", "mySign", -1)', 'E158:')
call assert_fails('call sign_place(-1, "", "sign1", "Xsign", [])', call assert_fails('call sign_place(-1, "", "sign1", "Xsign", [])',
\ 'E474:') \ 'E715:')
call assert_fails('call sign_place(-1, "", "sign1", "Xsign", call assert_fails('call sign_place(-1, "", "sign1", "Xsign",
\ {"lnum" : 30})', 'E474:') \ {"lnum" : 30})', 'E474:')
call assert_fails('call sign_place(10, "", "xsign1x", "Xsign", call assert_fails('call sign_place(10, "", "xsign1x", "Xsign",
@@ -460,11 +460,11 @@ func Test_sign_funcs()
call assert_fails('call sign_place(5, "", "sign1", [], {"lnum" : 10})', call assert_fails('call sign_place(5, "", "sign1", [], {"lnum" : 10})',
\ 'E158:') \ 'E158:')
call assert_fails('call sign_place(21, "", "sign1", "Xsign", call assert_fails('call sign_place(21, "", "sign1", "Xsign",
\ {"lnum" : -1})', 'E885:') \ {"lnum" : -1})', 'E474:')
call assert_fails('call sign_place(22, "", "sign1", "Xsign", call assert_fails('call sign_place(22, "", "sign1", "Xsign",
\ {"lnum" : 0})', 'E885:') \ {"lnum" : 0})', 'E474:')
call assert_fails('call sign_place(22, "", "sign1", "Xsign", call assert_fails('call sign_place(22, "", "sign1", "Xsign",
\ {"lnum" : []})', 'E745:') \ {"lnum" : []})', 'E474:')
call assert_equal(-1, sign_place(1, "*", "sign1", "Xsign", {"lnum" : 10})) call assert_equal(-1, sign_place(1, "*", "sign1", "Xsign", {"lnum" : 10}))
" Tests for sign_getplaced() " Tests for sign_getplaced()
@@ -504,11 +504,21 @@ func Test_sign_funcs()
\ {'id' : 20, 'buffer' : 200})", 'E158:') \ {'id' : 20, 'buffer' : 200})", 'E158:')
call assert_fails("call sign_unplace('g1', 'mySign')", 'E715:') call assert_fails("call sign_unplace('g1', 'mySign')", 'E715:')
call sign_unplace('*')
" Test for modifying a placed sign
call assert_equal(15, sign_place(15, '', 'sign1', 'Xsign', {'lnum' : 20}))
call assert_equal(15, sign_place(15, '', 'sign2', 'Xsign'))
call assert_equal([{'bufnr' : bufnr(''), 'signs' :
\ [{'id' : 15, 'group' : '', 'lnum' : 20, 'name' : 'sign2',
\ 'priority' : 10}]}],
\ sign_getplaced())
" Tests for sign_undefine() " Tests for sign_undefine()
call assert_equal(0, sign_undefine("sign1")) call assert_equal(0, sign_undefine("sign1"))
call assert_equal([], sign_getdefined("sign1")) call assert_equal([], sign_getdefined("sign1"))
call assert_fails('call sign_undefine("none")', 'E155:') call assert_fails('call sign_undefine("none")', 'E155:')
call assert_fails('call sign_undefine([])', 'E730:') call assert_fails('call sign_undefine({})', 'E731:')
" Test for using '.' as the line number for sign_place() " Test for using '.' as the line number for sign_place()
call Sign_define_ignore_error("sign1", attr) call Sign_define_ignore_error("sign1", attr)
@@ -644,7 +654,7 @@ func Test_sign_group()
call assert_equal([], sign_getplaced(bnum, {'group' : '*'})[0].signs) call assert_equal([], sign_getplaced(bnum, {'group' : '*'})[0].signs)
" Error case " Error case
call assert_fails("call sign_unplace([])", 'E474:') call assert_fails("call sign_unplace({})", 'E474:')
" Place a sign in the global group and try to delete it using a group " 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(5, sign_place(5, '', 'sign1', bnum, {'lnum' : 10}))
@@ -1131,7 +1141,7 @@ func Test_sign_unplace()
endfunc endfunc
" Tests for auto-generating the sign identifier " Tests for auto-generating the sign identifier
func Test_sign_id_autogen() func Test_aaa_sign_id_autogen()
enew | only enew | only
call sign_unplace('*') call sign_unplace('*')
call sign_undefine() call sign_undefine()
@@ -1868,3 +1878,121 @@ func Test_sign_numcol()
set number& set number&
enew! | close enew! | close
endfunc endfunc
" Test for managing multiple signs using the sign functions
func Test_sign_funcs_multi()
call writefile(repeat(["Sun is shining"], 30), "Xsign")
edit Xsign
let bnum = bufnr('')
" Define multiple signs at once
call assert_equal([0, 0, 0, 0], sign_define([
\ {'name' : 'sign1', 'text' : '=>', 'linehl' : 'Search',
\ 'texthl' : 'Search'},
\ {'name' : 'sign2', 'text' : '=>', 'linehl' : 'Search',
\ 'texthl' : 'Search'},
\ {'name' : 'sign3', 'text' : '=>', 'linehl' : 'Search',
\ 'texthl' : 'Search'},
\ {'name' : 'sign4', 'text' : '=>', 'linehl' : 'Search',
\ 'texthl' : 'Search'}]))
" Negative cases for sign_define()
call assert_equal([], sign_define([]))
call assert_equal([-1], sign_define([{}]))
call assert_fails('call sign_define([6])', 'E715:')
call assert_fails('call sign_define(["abc"])', 'E715:')
call assert_fails('call sign_define([[]])', 'E715:')
" Place multiple signs at once with specific sign identifier
let l = sign_placelist([{'id' : 1, 'group' : 'g1', 'name' : 'sign1',
\ 'buffer' : 'Xsign', 'lnum' : 11, 'priority' : 50},
\ {'id' : 2, 'group' : 'g2', 'name' : 'sign2',
\ 'buffer' : 'Xsign', 'lnum' : 11, 'priority' : 100},
\ {'id' : 3, 'group' : '', 'name' : 'sign3',
\ 'buffer' : 'Xsign', 'lnum' : 11}])
call assert_equal([1, 2, 3], l)
let s = sign_getplaced('Xsign', {'group' : '*'})
call assert_equal([
\ {'id' : 2, 'name' : 'sign2', 'lnum' : 11,
\ 'group' : 'g2', 'priority' : 100},
\ {'id' : 1, 'name' : 'sign1', 'lnum' : 11,
\ 'group' : 'g1', 'priority' : 50},
\ {'id' : 3, 'name' : 'sign3', 'lnum' : 11,
\ 'group' : '', 'priority' : 10}], s[0].signs)
call sign_unplace('*')
" Place multiple signs at once with auto-generated sign identifier
call assert_equal([1, 1, 5], sign_placelist([
\ {'group' : 'g1', 'name' : 'sign1',
\ 'buffer' : 'Xsign', 'lnum' : 11},
\ {'group' : 'g2', 'name' : 'sign2',
\ 'buffer' : 'Xsign', 'lnum' : 11},
\ {'group' : '', 'name' : 'sign3',
\ 'buffer' : 'Xsign', 'lnum' : 11}]))
let s = sign_getplaced('Xsign', {'group' : '*'})
call assert_equal([
\ {'id' : 5, 'name' : 'sign3', 'lnum' : 11,
\ 'group' : '', 'priority' : 10},
\ {'id' : 1, 'name' : 'sign2', 'lnum' : 11,
\ 'group' : 'g2', 'priority' : 10},
\ {'id' : 1, 'name' : 'sign1', 'lnum' : 11,
\ 'group' : 'g1', 'priority' : 10}], s[0].signs)
" Change an existing sign without specifying the group
call assert_equal([5], sign_placelist([
\ {'id' : 5, 'name' : 'sign1', 'buffer' : 'Xsign'}]))
let s = sign_getplaced('Xsign', {'id' : 5, 'group' : ''})
call assert_equal([{'id' : 5, 'name' : 'sign1', 'lnum' : 11,
\ 'group' : '', 'priority' : 10}], s[0].signs)
" Place a sign using '.' as the line number
call cursor(23, 1)
call assert_equal([7], sign_placelist([
\ {'id' : 7, 'name' : 'sign1', 'buffer' : '%', 'lnum' : '.'}]))
let s = sign_getplaced('%', {'lnum' : '.'})
call assert_equal([{'id' : 7, 'name' : 'sign1', 'lnum' : 23,
\ 'group' : '', 'priority' : 10}], s[0].signs)
" Place sign without a sign name
call assert_equal([-1], sign_placelist([{'id' : 10, 'buffer' : 'Xsign',
\ 'lnum' : 12, 'group' : ''}]))
" Place sign without a buffer
call assert_equal([-1], sign_placelist([{'id' : 10, 'name' : 'sign1',
\ 'lnum' : 12, 'group' : ''}]))
" Invalid arguments
call assert_equal([], sign_placelist([]))
call assert_fails('call sign_placelist({})', "E714:")
call assert_fails('call sign_placelist([[]])', "E715:")
call assert_fails('call sign_placelist(["abc"])', "E715:")
call assert_fails('call sign_placelist([100])', "E715:")
" Unplace multiple signs
call assert_equal([0, 0, 0], sign_unplacelist([{'id' : 5},
\ {'id' : 1, 'group' : 'g1'}, {'id' : 1, 'group' : 'g2'}]))
" Invalid arguments
call assert_equal([], sign_unplacelist([]))
call assert_fails('call sign_unplacelist({})', "E714:")
call assert_fails('call sign_unplacelist([[]])', "E715:")
call assert_fails('call sign_unplacelist(["abc"])', "E715:")
call assert_fails('call sign_unplacelist([100])', "E715:")
call assert_fails("call sign_unplacelist([{'id' : -1}])", 'E474')
call assert_equal([0, 0, 0, 0],
\ sign_undefine(['sign1', 'sign2', 'sign3', 'sign4']))
call assert_equal([], sign_getdefined())
" Invalid arguments
call assert_equal([], sign_undefine([]))
call assert_fails('call sign_undefine([[]])', 'E730:')
call assert_fails('call sign_undefine([{}])', 'E731:')
call assert_fails('call sign_undefine(["1abc2"])', 'E155:')
call sign_unplace('*')
call sign_undefine()
enew!
call delete("Xsign")
endfunc