vim-patch:8.1.0939: no completion for sign group names

Problem:    No completion for sign group names.
Solution:   Add completion for sign group names and buffer names. (Yegappan
            Lakshmanan, closes vim/vim#3980)
3678f65d43
This commit is contained in:
Jan Edmund Lazo 2020-02-25 23:03:14 -05:00
parent b376bb49b5
commit c40bb2de7a
No known key found for this signature in database
GPG Key ID: 64915E6E9F735B15
3 changed files with 138 additions and 42 deletions

View File

@ -1570,6 +1570,7 @@ char_u* skiptohex(char_u *q)
///
/// @return Pointer to the next whitespace or NUL character.
char_u *skiptowhite(const char_u *p)
FUNC_ATTR_NONNULL_ALL
{
while (*p != ' ' && *p != '\t' && *p != NUL) {
p++;

View File

@ -1608,10 +1608,44 @@ static enum
EXP_SUBCMD, // expand :sign sub-commands
EXP_DEFINE, // expand :sign define {name} args
EXP_PLACE, // expand :sign place {id} args
EXP_LIST, // expand :sign place args
EXP_UNPLACE, // expand :sign unplace"
EXP_SIGN_NAMES // expand with name of placed signs
EXP_SIGN_NAMES, // expand with name of placed signs
EXP_SIGN_GROUPS, // expand with name of placed sign groups
} expand_what;
// Return the n'th sign name (used for command line completion)
static char_u *get_nth_sign_name(int idx)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
// Complete with name of signs already defined
int current_idx = 0;
for (sign_T *sp = first_sign; sp != NULL; sp = sp->sn_next) {
if (current_idx++ == idx) {
return sp->sn_name;
}
}
return NULL;
}
// Return the n'th sign group name (used for command line completion)
static char_u *get_nth_sign_group_name(int idx)
{
// Complete with name of sign groups already defined
int current_idx = 0;
int todo = (int)sg_table.ht_used;
for (hashitem_T *hi = sg_table.ht_array; todo > 0; hi++) {
if (!HASHITEM_EMPTY(hi)) {
todo--;
if (current_idx++ == idx) {
signgroup_T *const group = HI2SG(hi);
return group->sg_name;
}
}
}
return NULL;
}
/// Function given to ExpandGeneric() to obtain the sign command
/// expansion.
char_u * get_sign_name(expand_T *xp, int idx)
@ -1629,20 +1663,18 @@ char_u * get_sign_name(expand_T *xp, int idx)
"buffer=", NULL };
return (char_u *)place_arg[idx];
}
case EXP_LIST: {
char *list_arg[] = { "group=", "file=", "buffer=", NULL };
return (char_u *)list_arg[idx];
}
case EXP_UNPLACE: {
char *unplace_arg[] = { "group=", "file=", "buffer=", NULL };
return (char_u *)unplace_arg[idx];
}
case EXP_SIGN_NAMES: {
// Complete with name of signs already defined
int current_idx = 0;
for (sign_T *sp = first_sign; sp != NULL; sp = sp->sn_next) {
if (current_idx++ == idx) {
return sp->sn_name;
}
}
}
return NULL;
case EXP_SIGN_NAMES:
return get_nth_sign_name(idx);
case EXP_SIGN_GROUPS:
return get_nth_sign_group_name(idx);
default:
return NULL;
}
@ -1651,7 +1683,6 @@ char_u * get_sign_name(expand_T *xp, int idx)
/// Handle command line completion for :sign command.
void set_context_in_sign_cmd(expand_T *xp, char_u *arg)
{
char_u *p;
char_u *end_subcmd;
char_u *last;
int cmd_idx;
@ -1675,26 +1706,6 @@ void set_context_in_sign_cmd(expand_T *xp, char_u *arg)
// |
// begin_subcmd_args
begin_subcmd_args = skipwhite(end_subcmd);
p = skiptowhite(begin_subcmd_args);
if (*p == NUL) {
//
// Expand first argument of subcmd when possible.
// For ":jump {id}" and ":unplace {id}", we could
// possibly expand the ids of all signs already placed.
//
xp->xp_pattern = begin_subcmd_args;
switch (cmd_idx) {
case SIGNCMD_LIST:
case SIGNCMD_UNDEFINE:
// :sign list <CTRL-D>
// :sign undefine <CTRL-D>
expand_what = EXP_SIGN_NAMES;
break;
default:
xp->xp_context = EXPAND_NOTHING;
}
return;
}
// Expand last argument of subcmd.
//
@ -1703,6 +1714,7 @@ void set_context_in_sign_cmd(expand_T *xp, char_u *arg)
// p
// Loop until reaching last argument.
char_u *p = begin_subcmd_args;
do {
p = skipwhite(p);
last = p;
@ -1722,7 +1734,20 @@ void set_context_in_sign_cmd(expand_T *xp, char_u *arg)
expand_what = EXP_DEFINE;
break;
case SIGNCMD_PLACE:
expand_what = EXP_PLACE;
// List placed signs
if (ascii_isdigit(*begin_subcmd_args)) {
// :sign place {id} {args}...
expand_what = EXP_PLACE;
} else {
// :sign place {args}...
expand_what = EXP_LIST;
}
break;
case SIGNCMD_LIST:
case SIGNCMD_UNDEFINE:
// :sign list <CTRL-D>
// :sign undefine <CTRL-D>
expand_what = EXP_SIGN_NAMES;
break;
case SIGNCMD_JUMP:
case SIGNCMD_UNPLACE:
@ -1736,19 +1761,33 @@ void set_context_in_sign_cmd(expand_T *xp, char_u *arg)
xp->xp_pattern = p + 1;
switch (cmd_idx) {
case SIGNCMD_DEFINE:
if (STRNCMP(last, "texthl", p - last) == 0
|| STRNCMP(last, "linehl", p - last) == 0
|| STRNCMP(last, "numhl", p - last) == 0) {
if (STRNCMP(last, "texthl", 6) == 0
|| STRNCMP(last, "linehl", 6) == 0
|| STRNCMP(last, "numhl", 5) == 0) {
xp->xp_context = EXPAND_HIGHLIGHT;
} else if (STRNCMP(last, "icon", p - last) == 0) {
} else if (STRNCMP(last, "icon", 4) == 0) {
xp->xp_context = EXPAND_FILES;
} else {
xp->xp_context = EXPAND_NOTHING;
}
break;
case SIGNCMD_PLACE:
if (STRNCMP(last, "name", p - last) == 0) {
if (STRNCMP(last, "name", 4) == 0) {
expand_what = EXP_SIGN_NAMES;
} else if (STRNCMP(last, "group", 5) == 0) {
expand_what = EXP_SIGN_GROUPS;
} else if (STRNCMP(last, "file", 4) == 0) {
xp->xp_context = EXPAND_BUFFERS;
} else {
xp->xp_context = EXPAND_NOTHING;
}
break;
case SIGNCMD_UNPLACE:
case SIGNCMD_JUMP:
if (STRNCMP(last, "group", 5) == 0) {
expand_what = EXP_SIGN_GROUPS;
} else if (STRNCMP(last, "file", 4) == 0) {
xp->xp_context = EXPAND_BUFFERS;
} else {
xp->xp_context = EXPAND_NOTHING;
}

View File

@ -212,13 +212,16 @@ func Test_sign_completion()
call assert_equal('"sign define Sign linehl=SpellBad SpellCap ' .
\ 'SpellLocal SpellRare', @:)
call writefile(['foo'], 'XsignOne')
call writefile(['bar'], 'XsignTwo')
call feedkeys(":sign define Sign texthl=Spell\<C-A>\<C-B>\"\<CR>", 'tx')
call assert_equal('"sign define Sign texthl=SpellBad SpellCap ' .
\ 'SpellLocal SpellRare', @:)
call writefile(repeat(["Sun is shining"], 30), "XsignOne")
call writefile(repeat(["Sky is blue"], 30), "XsignTwo")
call feedkeys(":sign define Sign icon=Xsig\<C-A>\<C-B>\"\<CR>", 'tx')
call assert_equal('"sign define Sign icon=XsignOne XsignTwo', @:)
call delete('XsignOne')
call delete('XsignTwo')
" Test for completion of arguments to ':sign undefine'
call feedkeys(":sign undefine \<C-A>\<C-B>\"\<CR>", 'tx')
call assert_equal('"sign undefine Sign1 Sign2', @:)
@ -229,17 +232,70 @@ func Test_sign_completion()
call feedkeys(":sign place 1 name=\<C-A>\<C-B>\"\<CR>", 'tx')
call assert_equal('"sign place 1 name=Sign1 Sign2', @:)
edit XsignOne
sign place 1 name=Sign1 line=5
sign place 1 name=Sign1 group=g1 line=10
edit XsignTwo
sign place 1 name=Sign2 group=g2 line=15
" Test for completion of group= and file= arguments to ':sign place'
call feedkeys(":sign place 1 name=Sign1 file=Xsign\<C-A>\<C-B>\"\<CR>", 'tx')
call assert_equal('"sign place 1 name=Sign1 file=XsignOne XsignTwo', @:)
call feedkeys(":sign place 1 name=Sign1 group=\<C-A>\<C-B>\"\<CR>", 'tx')
call assert_equal('"sign place 1 name=Sign1 group=g1 g2', @:)
" Test for completion of arguments to 'sign place' without sign identifier
call feedkeys(":sign place \<C-A>\<C-B>\"\<CR>", 'tx')
call assert_equal('"sign place buffer= file= group=', @:)
call feedkeys(":sign place file=Xsign\<C-A>\<C-B>\"\<CR>", 'tx')
call assert_equal('"sign place file=XsignOne XsignTwo', @:)
call feedkeys(":sign place group=\<C-A>\<C-B>\"\<CR>", 'tx')
call assert_equal('"sign place group=g1 g2', @:)
call feedkeys(":sign place group=g1 file=\<C-A>\<C-B>\"\<CR>", 'tx')
call assert_equal('"sign place group=g1 file=XsignOne XsignTwo', @:)
" Test for completion of arguments to ':sign unplace'
call feedkeys(":sign unplace 1 \<C-A>\<C-B>\"\<CR>", 'tx')
call assert_equal('"sign unplace 1 buffer= file= group=', @:)
call feedkeys(":sign unplace 1 file=Xsign\<C-A>\<C-B>\"\<CR>", 'tx')
call assert_equal('"sign unplace 1 file=XsignOne XsignTwo', @:)
call feedkeys(":sign unplace 1 group=\<C-A>\<C-B>\"\<CR>", 'tx')
call assert_equal('"sign unplace 1 group=g1 g2', @:)
call feedkeys(":sign unplace 1 group=g2 file=Xsign\<C-A>\<C-B>\"\<CR>", 'tx')
call assert_equal('"sign unplace 1 group=g2 file=XsignOne XsignTwo', @:)
" Test for completion of arguments to ':sign list'
call feedkeys(":sign list \<C-A>\<C-B>\"\<CR>", 'tx')
call assert_equal('"sign list Sign1 Sign2', @:)
" Test for completion of arguments to ':sign jump'
call feedkeys(":sign jump 1 \<C-A>\<C-B>\"\<CR>", 'tx')
call assert_equal('"sign jump 1 buffer= file= group=', @:)
call feedkeys(":sign jump 1 file=Xsign\<C-A>\<C-B>\"\<CR>", 'tx')
call assert_equal('"sign jump 1 file=XsignOne XsignTwo', @:)
call feedkeys(":sign jump 1 group=\<C-A>\<C-B>\"\<CR>", 'tx')
call assert_equal('"sign jump 1 group=g1 g2', @:)
" Error cases
call feedkeys(":sign here\<C-A>\<C-B>\"\<CR>", 'tx')
call assert_equal('"sign here', @:)
call feedkeys(":sign define Sign here=\<C-A>\<C-B>\"\<CR>", 'tx')
call assert_equal("\"sign define Sign here=\<C-A>", @:)
call feedkeys(":sign place 1 here=\<C-A>\<C-B>\"\<CR>", 'tx')
call assert_equal("\"sign place 1 here=\<C-A>", @:)
call feedkeys(":sign jump 1 here=\<C-A>\<C-B>\"\<CR>", 'tx')
call assert_equal("\"sign jump 1 here=\<C-A>", @:)
call feedkeys(":sign here there\<C-A>\<C-B>\"\<CR>", 'tx')
call assert_equal("\"sign here there\<C-A>", @:)
call feedkeys(":sign here there=\<C-A>\<C-B>\"\<CR>", 'tx')
call assert_equal("\"sign here there=\<C-A>", @:)
sign unplace * group=*
sign undefine Sign1
sign undefine Sign2
enew
call delete('XsignOne')
call delete('XsignTwo')
endfunc
func Test_sign_invalid_commands()