vim-patch:8.1.1682: placing a larger number of ...

...signs is slow

Problem:    Placing a larger number of signs is slow.
Solution:   Add functions for dealing with a list of signs. (Yegappan
            Lakshmanan, closes #4636)
This commit is contained in:
Lewis Russell
2021-03-06 00:50:07 +00:00
parent 0b7e5eeb62
commit 5257bce979
6 changed files with 607 additions and 173 deletions

View File

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

View File

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

View File

@@ -316,8 +316,10 @@ return {
sign_getplaced={args={0, 2}},
sign_jump={args={3, 3}},
sign_place={args={4, 5}},
sign_placelist={args={1}},
sign_undefine={args={0, 1}},
sign_unplace={args={1, 2}},
sign_unplacelist={args={1}},
simplify={args=1},
sin={args=1, func="float_op_wrapper", data="&sin"},
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)
{
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;
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;
}
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 (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 (argvars[1].v_type != VAR_UNKNOWN && argvars[1].v_type != VAR_DICT) {
EMSG(_(e_dictreq));
return;
}
if (sign_define_by_name((char_u *)name, (char_u *)icon, (char_u *)linehl,
(char_u *)text, (char_u *)texthl, (char_u *)numhl)
== OK) {
rettv->vval.v_number = 0;
}
xfree(icon);
xfree(linehl);
xfree(text);
xfree(texthl);
xfree(numhl);
rettv->vval.v_number = sign_define_from_dict(
name, argvars[1].v_type == VAR_DICT ? argvars[1].vval.v_dict : NULL);
}
/// "sign_getdefined()" function
@@ -9031,83 +9005,44 @@ cleanup:
/// "sign_place()" function
static void f_sign_place(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
int sign_id;
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;
dict_T *dict = NULL;
rettv->vval.v_number = -1;
// Sign identifier
sign_id = (int)tv_get_number_chk(&argvars[0], &notanum);
if (notanum) {
return;
}
if (sign_id < 0) {
EMSG(_(e_invarg));
if (argvars[4].v_type != VAR_UNKNOWN
&& (argvars[4].v_type != VAR_DICT
|| ((dict = argvars[4].vval.v_dict) == NULL))) {
EMSG(_(e_dictreq));
return;
}
// Sign group
const char *group_chk = tv_get_string_chk(&argvars[1]);
if (group_chk == NULL) {
rettv->vval.v_number = sign_place_from_dict(
&argvars[0], &argvars[1], &argvars[2], &argvars[3], dict);
}
/// "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;
}
if (group_chk[0] == '\0') {
group = NULL; // global sign group
} else {
group = vim_strsave((const char_u *)group_chk);
}
// Sign name
sign_name = tv_get_string_chk(&argvars[2]);
if (sign_name == NULL) {
goto cleanup;
}
// Buffer to place the sign
buf = get_buf_arg(&argvars[3]);
if (buf == NULL) {
goto cleanup;
}
if (argvars[4].v_type != VAR_UNKNOWN) {
if (argvars[4].v_type != VAR_DICT
|| ((dict = argvars[4].vval.v_dict) == NULL)) {
// Process the List of sign attributes
TV_LIST_ITER_CONST(argvars[0].vval.v_list, li, {
sign_id = -1;
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);
} else {
EMSG(_(e_dictreq));
goto cleanup;
}
// 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);
tv_list_append_number(rettv->vval.v_list, sign_id);
});
}
/// "sign_undefine()" function
@@ -9115,6 +9050,14 @@ static void f_sign_undefine(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
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;
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
static void f_sign_unplace(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
dict_T *dict;
dictitem_T *di;
int sign_id = 0;
buf_T *buf = NULL;
char_u *group = NULL;
dict_T *dict = NULL;
rettv->vval.v_number = -1;
@@ -9150,46 +9089,38 @@ static void f_sign_unplace(typval_T *argvars, typval_T *rettv, FunPtr fptr)
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_DICT) {
EMSG(_(e_dictreq));
goto cleanup;
return;
}
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) {
// Delete the sign in all the buffers
FOR_ALL_BUFFERS(cbuf) {
if (sign_unplace(sign_id, group, cbuf, 0) == OK) {
rettv->vval.v_number = 0;
}
}
} else {
if (sign_unplace(sign_id, group, buf, 0) == OK) {
rettv->vval.v_number = 0;
}
rettv->vval.v_number = sign_unplace_from_dict(&argvars[0], dict);
}
/// "sign_unplacelist()" function
static void f_sign_unplacelist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
int retval;
tv_list_alloc_ret(rettv, kListLenMayKnow);
if (argvars[0].v_type != VAR_LIST) {
EMSG(_(e_listreq));
return;
}
cleanup:
xfree(group);
TV_LIST_ITER_CONST(argvars[0].vval.v_list, li, {
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

@@ -398,7 +398,8 @@ linenr_T buf_change_sign_type(
buf_T *buf, // buffer to store sign in
int markId, // sign ID
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
@@ -406,6 +407,8 @@ linenr_T buf_change_sign_type(
FOR_ALL_SIGNS_IN_BUF(buf, sign) {
if (sign->id == markId && sign_in_group(sign, group)) {
sign->typenr = typenr;
sign->priority = prio;
sign_sort_by_prio_on_line(buf, sign);
return sign->lnum;
}
}
@@ -1026,8 +1029,8 @@ int sign_place(
sp->sn_typenr,
has_text_or_icon);
} else {
// ":sign place {id} file={fname}": change sign type
lnum = buf_change_sign_type(buf, *sign_id, sign_group, sp->sn_typenr);
// ":sign place {id} file={fname}": change sign type and/or priority
lnum = buf_change_sign_type(buf, *sign_id, sign_group, sp->sn_typenr, prio);
}
if (lnum > 0) {
redraw_buf_line_later(buf, lnum);
@@ -1869,3 +1872,266 @@ 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 = (int)tv_get_number_chk(&di->di_tv, &notanum);
if (notanum) {
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

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