mirror of
https://github.com/vim/vim
synced 2025-03-15 06:17:51 +01:00
patch 9.1.1138: cmdline completion for :hi is too simplistic
Problem: Existing cmdline completion for :highlight was barebone and only completed the highlight group names. Solution: Implement full completion for the highlight group arguments such as guifg and cterm. If the user tries to complete immediately after the '=' (e.g. `hi Normal guifg=<Tab>`), the completion will fill in the existing value, similar to how cmdline completion for options work (Yee Cheng Chin). closes: #16712 Signed-off-by: Yee Cheng Chin <ychin.git@gmail.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
This commit is contained in:
parent
f4b36417e8
commit
a7b8120820
12 changed files with 627 additions and 89 deletions
|
@ -1,4 +1,4 @@
|
|||
*eval.txt* For Vim version 9.1. Last change: 2025 Jan 29
|
||||
*eval.txt* For Vim version 9.1. Last change: 2025 Feb 23
|
||||
|
||||
|
||||
VIM REFERENCE MANUAL by Bram Moolenaar
|
||||
|
@ -2081,7 +2081,7 @@ v:colornames A dictionary that maps color names to hex color strings. These
|
|||
|
||||
You can make changes to that file, but make sure to add new
|
||||
keys instead of updating existing ones, otherwise Vim will skip
|
||||
loading the file (thinking is hasn't been changed).
|
||||
loading the file (thinking it hasn't been changed).
|
||||
|
||||
*v:completed_item* *completed_item-variable*
|
||||
v:completed_item
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
*syntax.txt* For Vim version 9.1. Last change: 2025 Feb 20
|
||||
*syntax.txt* For Vim version 9.1. Last change: 2025 Feb 23
|
||||
|
||||
|
||||
VIM REFERENCE MANUAL by Bram Moolenaar
|
||||
|
@ -5432,6 +5432,10 @@ in their own color.
|
|||
See |:highlight-default| for the optional [default]
|
||||
argument.
|
||||
|
||||
:hi[ghlight][!] [default] link {from-group} {to-group}
|
||||
:hi[ghlight][!] [default] link {from-group} NONE
|
||||
See |:hi-link|.
|
||||
|
||||
Normally a highlight group is added once when starting up. This sets the
|
||||
default values for the highlighting. After that, you can use additional
|
||||
highlight commands to change the arguments that you want to set to non-default
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
*version9.txt* For Vim version 9.1. Last change: 2025 Feb 11
|
||||
*version9.txt* For Vim version 9.1. Last change: 2025 Feb 23
|
||||
|
||||
|
||||
VIM REFERENCE MANUAL by Bram Moolenaar
|
||||
|
@ -41573,8 +41573,11 @@ Include the "linematch" algorithm for the 'diffopt' setting. This aligns
|
|||
changes between buffers on similar lines improving the diff highlighting in
|
||||
Vim
|
||||
|
||||
Adjusted default values ~
|
||||
-----------------------
|
||||
*changed-9.2*
|
||||
Changed~
|
||||
-------
|
||||
|
||||
Default values: ~
|
||||
- the default 'history' option value has been increased to 200 and removed
|
||||
from |defaults.vim|
|
||||
- the default 'backspace' option for Vim has been set to "indent,eol,start"
|
||||
|
@ -41584,61 +41587,69 @@ Adjusted default values ~
|
|||
- the default value of the 'keyprotocol' option has been updated and support
|
||||
for the ghostty terminal emulator (using kitty protocol) has been added
|
||||
|
||||
*changed-9.2*
|
||||
Changed~
|
||||
-------
|
||||
- use 'smoothscroll' logic for CTRL-F and CTRL-B for pagewise scrolling
|
||||
- use 'smoothscroll' logic for CTRL-D and CTRL-U for half-pagewise scrolling
|
||||
- the default for 'commentstring' contains whitespace padding to have
|
||||
automatic comments look nicer |comment-install|
|
||||
- 'completeopt' is now a |global-local| option.
|
||||
- 'nrformats' accepts the new "blank" suboption, to determine a signed or
|
||||
unsigned number based on whitespace in front of a minus sign.
|
||||
- allow to specify a priority when defining a new sign |:sign-define|
|
||||
- provide information about function arguments using the get(func, "arity")
|
||||
function |get()-func|
|
||||
- |:bwipe| also wipes jumplist and tagstack data
|
||||
- moving in the buffer list using |:bnext| and similar commands, behaves as
|
||||
documented and skips help buffers (if not run from a help buffer, else
|
||||
moves to the next/previous help buffer).
|
||||
|
||||
Completion: ~
|
||||
- allow to complete directories from 'cdpath' for |:cd| and similar commands,
|
||||
add the "cd_in_path" completion type for e.g. |:command-complete| and
|
||||
|getcompletion()|
|
||||
- allow to complete shell commands and files using the new shellcmdline
|
||||
completion type using |:command-complete| and |getcmdcomplpat()|
|
||||
- add 'cpoptions' flag "z" |cpo-z|, to disable some (traditional) vi
|
||||
behaviour/inconsistency (see |d-special| and |cw|).
|
||||
- allow to specify additional attributes in the completion menu (allows to
|
||||
mark deprecated attributes from LSP server) |complete-items|
|
||||
- the regex engines match correctly case-insensitive multi-byte characters
|
||||
(and apply proper case folding)
|
||||
- the completed word and completion type are provided when handling the
|
||||
|CompleteDone| autocommand in the |v:event| dictionary
|
||||
- |complete_info()| returns the list of matches shown in the poppu menu via
|
||||
the "matches" key
|
||||
- New option value for 'completeopt':
|
||||
"nosort" - do not sort completion results
|
||||
"preinsert" - highlight to be inserted values
|
||||
- handle multi-line completion as expected
|
||||
- improved commandline completion for the |:hi| command
|
||||
|
||||
Options: ~
|
||||
- the default for 'commentstring' contains whitespace padding to have
|
||||
automatic comments look nicer |comment-install|
|
||||
- 'completeopt' is now a |global-local| option.
|
||||
- 'nrformats' accepts the new "blank" suboption, to determine a signed or
|
||||
unsigned number based on whitespace in front of a minus sign.
|
||||
- add 'cpoptions' flag "z" |cpo-z|, to disable some (traditional) vi
|
||||
behaviour/inconsistency (see |d-special| and |cw|).
|
||||
- 'rulerformat' now supports the |stl-%!| item
|
||||
- use 'smoothscroll' logic for CTRL-F / CTRL-B for pagewise scrolling
|
||||
and CTRL-D / CTRL-U for half-pagewise scrolling
|
||||
|
||||
Ex commands: ~
|
||||
- allow to specify a priority when defining a new sign |:sign-define|
|
||||
- |:bwipe| also wipes jumplist and tagstack data
|
||||
- moving in the buffer list using |:bnext| and similar commands, behaves as
|
||||
documented and skips help buffers (if not run from a help buffer, else
|
||||
moves to the next/previous help buffer).
|
||||
- |:keeppatterns| preserves the last substitute pattern when used with |:s|
|
||||
|
||||
Functions: ~
|
||||
- provide information about function arguments using the get(func, "arity")
|
||||
function |get()-func|
|
||||
- |setqflist()| and |setloclist()| can optionally try to preserve the current
|
||||
selection in the quickfix list with the "u" action.
|
||||
- allow to pass local Vim script variables to python interpreter |py3eval()|
|
||||
- |getwininfo()| now also returns the "leftcol" property for a window
|
||||
- |v:stacktrace| The stack trace of the exception most recently caught and
|
||||
not finished
|
||||
- Add the optional {opts} |Dict| argument to |getchar()| to control: cursor
|
||||
behaviour, return type and whether or not to simplify the returned key
|
||||
|
||||
Others: ~
|
||||
- the regex engines match correctly case-insensitive multi-byte characters
|
||||
(and apply proper case folding)
|
||||
- the putty terminal is detected using an |TermResponse| autocommand in
|
||||
|defaults.vim| and Vim switches to a dark background
|
||||
- the |help-TOC| package is included to ease navigating the documentation.
|
||||
- an interactive tutor plugin has been included |vim-tutor-mode|, can be
|
||||
started via |:Tutor|
|
||||
- improve the |vimtutor| and add a second chapter for more advanced tips
|
||||
- allow to pass local Vim script variables to python interpreter |py3eval()|
|
||||
- |getwininfo()| now also returns the "leftcol" property for a window
|
||||
- 'rulerformat' now supports the |stl-%!| item
|
||||
- the completed word and completion type are provided when handling the
|
||||
|CompleteDone| autocommand in the |v:event| dictionary
|
||||
- |complete_info()| returns the list of matches shown in the poppu menu via
|
||||
the "matches" key
|
||||
- |v:stacktrace| The stack trace of the exception most recently caught and
|
||||
not finished
|
||||
- New option value for 'completeopt':
|
||||
"nosort" - do not sort completion results
|
||||
"preinsert" - highlight to be inserted values
|
||||
- add |dist#vim9#Launch()| and |dist#vim9#Open()| to the |vim-script-library|
|
||||
and decouple it from |netrw|
|
||||
- new digraph "APPROACHES THE LIMIT" using ".="
|
||||
- Add the optional {opts} |Dict| argument to |getchar()| to control: cursor
|
||||
behaviour, return type and whether or not to simplify the returned key
|
||||
- handle multi-line completion as expected
|
||||
|
||||
*added-9.2*
|
||||
Added ~
|
||||
|
|
|
@ -3221,6 +3221,8 @@ ExpandFromContext(
|
|||
ret = ExpandMappings(pat, ®match, numMatches, matches);
|
||||
else if (xp->xp_context == EXPAND_ARGOPT)
|
||||
ret = expand_argopt(pat, xp, ®match, matches, numMatches);
|
||||
else if (xp->xp_context == EXPAND_HIGHLIGHT_GROUP)
|
||||
ret = expand_highlight_group(pat, xp, ®match, matches, numMatches);
|
||||
#if defined(FEAT_TERMINAL)
|
||||
else if (xp->xp_context == EXPAND_TERMINALOPT)
|
||||
ret = expand_terminal_opt(pat, xp, ®match, matches, numMatches);
|
||||
|
@ -3239,18 +3241,6 @@ ExpandFromContext(
|
|||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Expand a list of names.
|
||||
*
|
||||
* Generic function for command line completion. It calls a function to
|
||||
* obtain strings, one by one. The strings are matched against a regexp
|
||||
* program. Matching strings are copied into an array, which is returned.
|
||||
*
|
||||
* If 'fuzzy' is TRUE, then fuzzy matching is used. Otherwise, regex matching
|
||||
* is used.
|
||||
*
|
||||
* Returns OK when no problems encountered, FAIL for error (out of memory).
|
||||
*/
|
||||
int
|
||||
ExpandGeneric(
|
||||
char_u *pat,
|
||||
|
@ -3261,6 +3251,38 @@ ExpandGeneric(
|
|||
char_u *((*func)(expand_T *, int)),
|
||||
// returns a string from the list
|
||||
int escaped)
|
||||
{
|
||||
return ExpandGenericExt(
|
||||
pat, xp, regmatch, matches, numMatches, func, escaped, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Expand a list of names.
|
||||
*
|
||||
* Generic function for command line completion. It calls a function to
|
||||
* obtain strings, one by one. The strings are matched against a regexp
|
||||
* program. Matching strings are copied into an array, which is returned.
|
||||
*
|
||||
* If 'fuzzy' is TRUE, then fuzzy matching is used. Otherwise, regex matching
|
||||
* is used.
|
||||
*
|
||||
* 'sortStartIdx' allows the caller to control sorting behavior. Items before
|
||||
* the index will not be sorted. Pass 0 to sort all, and -1 to prevent any
|
||||
* sorting.
|
||||
*
|
||||
* Returns OK when no problems encountered, FAIL for error (out of memory).
|
||||
*/
|
||||
int
|
||||
ExpandGenericExt(
|
||||
char_u *pat,
|
||||
expand_T *xp,
|
||||
regmatch_T *regmatch,
|
||||
char_u ***matches,
|
||||
int *numMatches,
|
||||
char_u *((*func)(expand_T *, int)),
|
||||
// returns a string from the list
|
||||
int escaped,
|
||||
int sortStartIdx)
|
||||
{
|
||||
int i;
|
||||
garray_T ga;
|
||||
|
@ -3271,6 +3293,7 @@ ExpandGeneric(
|
|||
int match;
|
||||
int sort_matches = FALSE;
|
||||
int funcsort = FALSE;
|
||||
int sortStartMatchIdx = -1;
|
||||
|
||||
fuzzy = cmdline_fuzzy_complete(pat);
|
||||
*matches = NULL;
|
||||
|
@ -3346,6 +3369,12 @@ ExpandGeneric(
|
|||
}
|
||||
#endif
|
||||
|
||||
if (sortStartIdx >= 0 && i >= sortStartIdx && sortStartMatchIdx == -1)
|
||||
{
|
||||
// Found first item to start sorting from. This is usually 0.
|
||||
sortStartMatchIdx = ga.ga_len;
|
||||
}
|
||||
|
||||
++ga.ga_len;
|
||||
}
|
||||
|
||||
|
@ -3371,14 +3400,14 @@ ExpandGeneric(
|
|||
funcsort = TRUE;
|
||||
|
||||
// Sort the matches.
|
||||
if (sort_matches)
|
||||
if (sort_matches && sortStartMatchIdx != -1)
|
||||
{
|
||||
if (funcsort)
|
||||
// <SNR> functions should be sorted to the end.
|
||||
qsort((void *)ga.ga_data, (size_t)ga.ga_len, sizeof(char_u *),
|
||||
sort_func_compare);
|
||||
else
|
||||
sort_strings((char_u **)ga.ga_data, ga.ga_len);
|
||||
sort_strings((char_u **)ga.ga_data + sortStartMatchIdx, ga.ga_len - sortStartMatchIdx);
|
||||
}
|
||||
|
||||
if (!fuzzy)
|
||||
|
|
420
src/highlight.c
420
src/highlight.c
|
@ -1691,6 +1691,8 @@ do_highlight(
|
|||
break;
|
||||
}
|
||||
|
||||
// Note: Keep this in sync with get_highlight_group_key.
|
||||
|
||||
// Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg"
|
||||
// or "guibg").
|
||||
while (*linep && !VIM_ISWHITE(*linep) && *linep != '=')
|
||||
|
@ -3058,6 +3060,7 @@ highlight_list_one(int id)
|
|||
if (message_filtered(sgp->sg_name))
|
||||
return;
|
||||
|
||||
// Note: Keep this in sync with expand_highlight_group().
|
||||
didh = highlight_list_arg(id, didh, LIST_ATTR,
|
||||
sgp->sg_term, NULL, "term");
|
||||
didh = highlight_list_arg(id, didh, LIST_STRING,
|
||||
|
@ -3108,37 +3111,24 @@ highlight_list_one(int id)
|
|||
#endif
|
||||
}
|
||||
|
||||
static int
|
||||
highlight_list_arg(
|
||||
int id,
|
||||
int didh,
|
||||
static char_u*
|
||||
highlight_arg_to_string(
|
||||
int type,
|
||||
int iarg,
|
||||
char_u *sarg,
|
||||
char *name)
|
||||
char_u *buf)
|
||||
{
|
||||
char_u buf[MAX_ATTR_LEN];
|
||||
char_u *ts;
|
||||
int i;
|
||||
|
||||
if (got_int)
|
||||
return FALSE;
|
||||
|
||||
if (type == LIST_STRING ? (sarg == NULL) : (iarg == 0))
|
||||
return didh;
|
||||
|
||||
ts = buf;
|
||||
if (type == LIST_INT)
|
||||
sprintf((char *)buf, "%d", iarg - 1);
|
||||
else if (type == LIST_STRING)
|
||||
ts = sarg;
|
||||
return sarg;
|
||||
else // type == LIST_ATTR
|
||||
{
|
||||
size_t buflen;
|
||||
|
||||
buf[0] = NUL;
|
||||
buflen = 0;
|
||||
for (i = 0; i < (int)ARRAY_LENGTH(highlight_index_tab); ++i)
|
||||
for (int i = 0; i < (int)ARRAY_LENGTH(highlight_index_tab); ++i)
|
||||
{
|
||||
if (iarg & highlight_index_tab[i]->key)
|
||||
{
|
||||
|
@ -3153,6 +3143,28 @@ highlight_list_arg(
|
|||
}
|
||||
}
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
static int
|
||||
highlight_list_arg(
|
||||
int id,
|
||||
int didh,
|
||||
int type,
|
||||
int iarg,
|
||||
char_u *sarg,
|
||||
char *name)
|
||||
{
|
||||
char_u buf[MAX_ATTR_LEN];
|
||||
char_u *ts;
|
||||
|
||||
if (got_int)
|
||||
return FALSE;
|
||||
|
||||
if (type == LIST_STRING ? (sarg == NULL) : (iarg == 0))
|
||||
return didh;
|
||||
|
||||
ts = highlight_arg_to_string(type, iarg, sarg, buf);
|
||||
|
||||
(void)syn_list_header(didh,
|
||||
(int)(vim_strsize(ts) + STRLEN(name) + 1), id);
|
||||
|
@ -4078,6 +4090,15 @@ highlight_changed(void)
|
|||
static void highlight_list(void);
|
||||
static void highlight_list_two(int cnt, int attr);
|
||||
|
||||
// context for :highlight <group> <arg> expansion
|
||||
static int expand_hi_synid = 0; // ID for highlight group being completed
|
||||
static int expand_hi_equal_col = 0; // column where the '=' is
|
||||
static int expand_hi_include_orig = 0; // whether to fill the existing current value or not
|
||||
static char_u *expand_hi_curvalue = NULL; // the existing current value
|
||||
#if defined(FEAT_EVAL) && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS))
|
||||
static dict_iterator_T expand_colornames_iter; // iterator for looping through v:colornames
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Handle command line completion for :highlight command.
|
||||
*/
|
||||
|
@ -4085,10 +4106,12 @@ static void highlight_list_two(int cnt, int attr);
|
|||
set_context_in_highlight_cmd(expand_T *xp, char_u *arg)
|
||||
{
|
||||
char_u *p;
|
||||
int expand_group = TRUE;
|
||||
|
||||
// Default: expand group names
|
||||
xp->xp_context = EXPAND_HIGHLIGHT;
|
||||
xp->xp_pattern = arg;
|
||||
include_none = 0;
|
||||
include_link = 2;
|
||||
include_default = 1;
|
||||
|
||||
|
@ -4114,9 +4137,11 @@ set_context_in_highlight_cmd(expand_T *xp, char_u *arg)
|
|||
// past group name
|
||||
include_link = 0;
|
||||
if (arg[1] == 'i' && arg[0] == 'N')
|
||||
{
|
||||
highlight_list();
|
||||
if (STRNCMP("link", arg, p - arg) == 0
|
||||
|| STRNCMP("clear", arg, p - arg) == 0)
|
||||
expand_group = FALSE;
|
||||
}
|
||||
if (STRNCMP("link", arg, p - arg) == 0)
|
||||
{
|
||||
xp->xp_pattern = skipwhite(p);
|
||||
p = skiptowhite(xp->xp_pattern);
|
||||
|
@ -4124,10 +4149,67 @@ set_context_in_highlight_cmd(expand_T *xp, char_u *arg)
|
|||
{
|
||||
xp->xp_pattern = skipwhite(p);
|
||||
p = skiptowhite(xp->xp_pattern);
|
||||
include_none = 1;
|
||||
}
|
||||
expand_group = FALSE;
|
||||
}
|
||||
else if (STRNCMP("clear", arg, p - arg) == 0)
|
||||
{
|
||||
xp->xp_pattern = skipwhite(p);
|
||||
p = skiptowhite(xp->xp_pattern);
|
||||
expand_group = FALSE;
|
||||
}
|
||||
if (*p != NUL) // past group name(s)
|
||||
xp->xp_context = EXPAND_NOTHING;
|
||||
{
|
||||
if (expand_group)
|
||||
{
|
||||
// expansion will be done in expand_highlight_group()
|
||||
xp->xp_context = EXPAND_HIGHLIGHT_GROUP;
|
||||
|
||||
expand_hi_synid = syn_namen2id(arg, (int)(p - arg));
|
||||
|
||||
while (*p != NUL)
|
||||
{
|
||||
arg = skipwhite(p);
|
||||
p = skiptowhite(arg);
|
||||
}
|
||||
|
||||
p = vim_strchr(arg, '=');
|
||||
if (p == NULL)
|
||||
{
|
||||
// Didn't find a key=<value> pattern
|
||||
xp->xp_pattern = arg;
|
||||
expand_hi_equal_col = -1;
|
||||
expand_hi_include_orig = FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Found key=<value> pattern, record the exact location
|
||||
expand_hi_equal_col = (int)(p - xp->xp_line);
|
||||
|
||||
// Only include the original value if the pattern is empty
|
||||
if (*(p + 1) == NUL)
|
||||
expand_hi_include_orig = TRUE;
|
||||
else
|
||||
expand_hi_include_orig = FALSE;
|
||||
|
||||
// Account for comma-separated values
|
||||
if (STRNCMP(arg, "term=", 5) == 0 ||
|
||||
STRNCMP(arg, "cterm=", 6) == 0 ||
|
||||
STRNCMP(arg, "gui=", 4) == 0)
|
||||
{
|
||||
char_u *comma = vim_strrchr(p + 1, ',');
|
||||
if (comma != NULL)
|
||||
p = comma;
|
||||
}
|
||||
xp->xp_pattern = p + 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
xp->xp_context = EXPAND_NOTHING;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -4178,7 +4260,7 @@ get_highlight_name_ext(expand_T *xp UNUSED, int idx, int skip_cleared)
|
|||
return (char_u *)"";
|
||||
|
||||
if (idx == highlight_ga.ga_len && include_none != 0)
|
||||
return (char_u *)"none";
|
||||
return (char_u *)"NONE";
|
||||
if (idx == highlight_ga.ga_len + include_none && include_default != 0)
|
||||
return (char_u *)"default";
|
||||
if (idx == highlight_ga.ga_len + include_none + include_default
|
||||
|
@ -4192,6 +4274,300 @@ get_highlight_name_ext(expand_T *xp UNUSED, int idx, int skip_cleared)
|
|||
return HL_TABLE()[idx].sg_name;
|
||||
}
|
||||
|
||||
static char_u *
|
||||
get_highlight_attr_name(expand_T *xp UNUSED, int idx)
|
||||
{
|
||||
if (idx == 0)
|
||||
{
|
||||
// Fill with current value first
|
||||
if (expand_hi_curvalue != NULL)
|
||||
return expand_hi_curvalue;
|
||||
else
|
||||
return (char_u*)"";
|
||||
}
|
||||
if (idx < (int)ARRAY_LENGTH(highlight_index_tab) + 1)
|
||||
{
|
||||
char_u *value = highlight_index_tab[idx-1]->value.string;
|
||||
if (expand_hi_curvalue != NULL && STRCMP(expand_hi_curvalue, value) == 0)
|
||||
{
|
||||
// Already returned the current value above, just skip.
|
||||
return (char_u*)"";
|
||||
}
|
||||
return value;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static char_u *
|
||||
get_highlight_cterm_color(expand_T *xp UNUSED, int idx)
|
||||
{
|
||||
if (idx == 0)
|
||||
{
|
||||
// Fill with current value first
|
||||
if (expand_hi_curvalue != NULL)
|
||||
return expand_hi_curvalue;
|
||||
else
|
||||
return (char_u*)"";
|
||||
}
|
||||
// See highlight_set_cterm_color()
|
||||
else if (idx == 1)
|
||||
return (char_u*)"fg";
|
||||
else if (idx == 2)
|
||||
return (char_u*)"bg";
|
||||
if (idx < (int)ARRAY_LENGTH(color_name_tab) + 3)
|
||||
{
|
||||
char_u *value = color_name_tab[idx-3].value.string;
|
||||
return value;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#if defined(FEAT_EVAL) && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS))
|
||||
static char_u *
|
||||
get_highlight_gui_color(expand_T *xp UNUSED, int idx)
|
||||
{
|
||||
if (idx == 0)
|
||||
{
|
||||
// Fill with current value first
|
||||
if (expand_hi_curvalue != NULL)
|
||||
return expand_hi_curvalue;
|
||||
else
|
||||
return (char_u*)"";
|
||||
}
|
||||
// See color_name2handle()
|
||||
else if (idx == 1)
|
||||
return (char_u*)"fg";
|
||||
else if (idx == 2)
|
||||
return (char_u*)"bg";
|
||||
else if (idx == 3)
|
||||
return (char_u*)"NONE";
|
||||
|
||||
// Complete from v:colornames. Don't do platform specific names for now.
|
||||
typval_T *tv_result;
|
||||
char_u *colorname = dict_iterate_next(&expand_colornames_iter, &tv_result);
|
||||
if (colorname != NULL)
|
||||
{
|
||||
// :hi command doesn't allow space, so don't suggest any malformed items
|
||||
if (vim_strchr(colorname, ' ') != NULL)
|
||||
return (char_u*)"";
|
||||
|
||||
if (expand_hi_curvalue != NULL && STRICMP(expand_hi_curvalue, colorname) == 0)
|
||||
{
|
||||
// Already returned the current value above, just skip.
|
||||
return (char_u*)"";
|
||||
}
|
||||
}
|
||||
return colorname;
|
||||
}
|
||||
#endif
|
||||
|
||||
static char_u *
|
||||
get_highlight_group_key(expand_T *xp UNUSED, int idx)
|
||||
{
|
||||
// Note: Keep this in sync with do_highlight.
|
||||
static char *(p_hi_group_key_values[]) =
|
||||
{
|
||||
"term=",
|
||||
"start=",
|
||||
"stop=",
|
||||
"cterm=",
|
||||
"ctermfg=",
|
||||
"ctermbg=",
|
||||
"ctermul=",
|
||||
"ctermfont=",
|
||||
#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
|
||||
"gui=",
|
||||
"guifg=",
|
||||
"guibg=",
|
||||
"guisp=",
|
||||
#endif
|
||||
#ifdef FEAT_GUI
|
||||
"font=",
|
||||
#endif
|
||||
"NONE",
|
||||
};
|
||||
|
||||
if (idx < (int)ARRAY_LENGTH(p_hi_group_key_values))
|
||||
return (char_u*)p_hi_group_key_values[idx];
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Command-line expansion for :hi {group-name} <args>...
|
||||
*/
|
||||
int
|
||||
expand_highlight_group(
|
||||
char_u *pat,
|
||||
expand_T *xp,
|
||||
regmatch_T *rmp,
|
||||
char_u ***matches,
|
||||
int *numMatches)
|
||||
{
|
||||
if (expand_hi_equal_col != -1)
|
||||
{
|
||||
// List the values. First fill in the current value, then if possible colors
|
||||
// or attribute names.
|
||||
char_u *(*expandfunc)(expand_T *, int) = NULL;
|
||||
int type = 0;
|
||||
hl_group_T *sgp = NULL;
|
||||
int iarg = 0;
|
||||
char_u *sarg = NULL;
|
||||
|
||||
int unsortedItems = -1; // don't sort by default
|
||||
|
||||
if (expand_hi_synid != 0)
|
||||
sgp = &HL_TABLE()[expand_hi_synid - 1]; // index is ID minus one
|
||||
|
||||
// Note: Keep this in sync with highlight_list_one().
|
||||
char_u *name_end = xp->xp_line + expand_hi_equal_col;
|
||||
if (name_end - xp->xp_line >= 5
|
||||
&& STRNCMP(name_end - 5, " term", 5) == 0)
|
||||
{
|
||||
expandfunc = get_highlight_attr_name;
|
||||
if (sgp)
|
||||
{
|
||||
type = LIST_ATTR;
|
||||
iarg = sgp->sg_term;
|
||||
}
|
||||
}
|
||||
else if (name_end - xp->xp_line >= 6
|
||||
&& STRNCMP(name_end - 6, " cterm", 6) == 0)
|
||||
{
|
||||
expandfunc = get_highlight_attr_name;
|
||||
if (sgp)
|
||||
{
|
||||
type = LIST_ATTR;
|
||||
iarg = sgp->sg_cterm;
|
||||
}
|
||||
}
|
||||
#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
|
||||
else if (name_end - xp->xp_line >= 4
|
||||
&& STRNCMP(name_end - 4, " gui", 4) == 0)
|
||||
{
|
||||
expandfunc = get_highlight_attr_name;
|
||||
if (sgp)
|
||||
{
|
||||
type = LIST_ATTR;
|
||||
iarg = sgp->sg_gui;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
else if (name_end - xp->xp_line >= 8
|
||||
&& STRNCMP(name_end - 8, " ctermfg", 8) == 0)
|
||||
{
|
||||
expandfunc = get_highlight_cterm_color;
|
||||
if (sgp)
|
||||
{
|
||||
type = LIST_INT;
|
||||
iarg = sgp->sg_cterm_fg;
|
||||
}
|
||||
}
|
||||
else if (name_end - xp->xp_line >= 8
|
||||
&& STRNCMP(name_end - 8, " ctermbg", 8) == 0)
|
||||
{
|
||||
expandfunc = get_highlight_cterm_color;
|
||||
if (sgp)
|
||||
{
|
||||
type = LIST_INT;
|
||||
iarg = sgp->sg_cterm_bg;
|
||||
}
|
||||
}
|
||||
else if (name_end - xp->xp_line >= 8
|
||||
&& STRNCMP(name_end - 8, " ctermul", 8) == 0)
|
||||
{
|
||||
expandfunc = get_highlight_cterm_color;
|
||||
if (sgp)
|
||||
{
|
||||
type = LIST_INT;
|
||||
iarg = sgp->sg_cterm_ul;
|
||||
}
|
||||
}
|
||||
#if defined(FEAT_EVAL) && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS))
|
||||
else if (name_end - xp->xp_line >= 6
|
||||
&& STRNCMP(name_end - 6, " guifg", 6) == 0)
|
||||
{
|
||||
expandfunc = get_highlight_gui_color;
|
||||
if (sgp)
|
||||
{
|
||||
type = LIST_STRING;
|
||||
sarg = sgp->sg_gui_fg_name;
|
||||
}
|
||||
}
|
||||
else if (name_end - xp->xp_line >= 6
|
||||
&& STRNCMP(name_end - 6, " guibg", 6) == 0)
|
||||
{
|
||||
expandfunc = get_highlight_gui_color;
|
||||
if (sgp)
|
||||
{
|
||||
type = LIST_STRING;
|
||||
sarg = sgp->sg_gui_bg_name;
|
||||
}
|
||||
}
|
||||
else if (name_end - xp->xp_line >= 6
|
||||
&& STRNCMP(name_end - 6, " guisp", 6) == 0)
|
||||
{
|
||||
expandfunc = get_highlight_gui_color;
|
||||
if (sgp)
|
||||
{
|
||||
type = LIST_STRING;
|
||||
sarg = sgp->sg_gui_sp_name;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(FEAT_EVAL) && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS))
|
||||
if (expandfunc == get_highlight_gui_color)
|
||||
{
|
||||
// Top 4 items are special, after that sort all the color names
|
||||
unsortedItems = 4;
|
||||
|
||||
dict_T *colornames_table = get_vim_var_dict(VV_COLORNAMES);
|
||||
typval_T colornames_val;
|
||||
colornames_val.v_type = VAR_DICT;
|
||||
colornames_val.vval.v_dict = colornames_table;
|
||||
dict_iterate_start(&colornames_val, &expand_colornames_iter);
|
||||
}
|
||||
#endif
|
||||
|
||||
char_u buf[MAX_ATTR_LEN];
|
||||
|
||||
if (expand_hi_synid != 0 && type != 0 && expand_hi_include_orig)
|
||||
{
|
||||
// Retrieve the current value to go first in completion
|
||||
expand_hi_curvalue = highlight_arg_to_string(
|
||||
type, iarg, sarg, buf);
|
||||
}
|
||||
else
|
||||
expand_hi_curvalue = NULL;
|
||||
|
||||
if (expandfunc != NULL)
|
||||
{
|
||||
return ExpandGenericExt(
|
||||
pat,
|
||||
xp,
|
||||
rmp,
|
||||
matches,
|
||||
numMatches,
|
||||
expandfunc,
|
||||
FALSE,
|
||||
unsortedItems);
|
||||
}
|
||||
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
// List all the key names
|
||||
return ExpandGenericExt(
|
||||
pat,
|
||||
xp,
|
||||
rmp,
|
||||
matches,
|
||||
numMatches,
|
||||
get_highlight_group_key,
|
||||
FALSE,
|
||||
-1);
|
||||
}
|
||||
|
||||
#if defined(FEAT_GUI) || defined(PROTO)
|
||||
/*
|
||||
* Free all the highlight group fonts.
|
||||
|
|
|
@ -17,6 +17,7 @@ void set_expand_context(expand_T *xp);
|
|||
void set_cmd_context(expand_T *xp, char_u *str, int len, int col, int use_ccline);
|
||||
int expand_cmdline(expand_T *xp, char_u *str, int col, int *matchcount, char_u ***matches);
|
||||
int ExpandGeneric(char_u *pat, expand_T *xp, regmatch_T *regmatch, char_u ***matches, int *numMatches, char_u *((*func)(expand_T *, int)), int escaped);
|
||||
int ExpandGenericExt(char_u *pat, expand_T *xp, regmatch_T *regmatch, char_u ***matches, int *numMatches, char_u *((*func)(expand_T *, int)), int escaped, int sortStartIdx);
|
||||
void globpath(char_u *path, char_u *file, garray_T *ga, int expand_options, int dirs);
|
||||
int wildmenu_translate_key(cmdline_info_T *cclp, int key, expand_T *xp, int did_wild_list);
|
||||
int wildmenu_process_key(cmdline_info_T *cclp, int key, expand_T *xp);
|
||||
|
|
|
@ -43,6 +43,7 @@ int highlight_changed(void);
|
|||
void set_context_in_highlight_cmd(expand_T *xp, char_u *arg);
|
||||
char_u *get_highlight_name(expand_T *xp, int idx);
|
||||
char_u *get_highlight_name_ext(expand_T *xp, int idx, int skip_cleared);
|
||||
int expand_highlight_group( char_u *pat, expand_T *xp, regmatch_T *rmp, char_u ***matches, int *numMatches);
|
||||
void free_highlight_fonts(void);
|
||||
void f_hlget(typval_T *argvars, typval_T *rettv);
|
||||
void f_hlset(typval_T *argvars, typval_T *rettv);
|
||||
|
|
|
@ -6372,7 +6372,7 @@ reset_expand_highlight(void)
|
|||
}
|
||||
|
||||
/*
|
||||
* Handle command line completion for :match and :echohl command: Add "None"
|
||||
* Handle command line completion for :match and :echohl command: Add "NONE"
|
||||
* as highlight group.
|
||||
*/
|
||||
void
|
||||
|
|
|
@ -418,8 +418,8 @@ func Test_match_completion()
|
|||
hi Aardig ctermfg=green
|
||||
call feedkeys(":match \<Tab>\<Home>\"\<CR>", 'xt')
|
||||
call assert_equal('"match Aardig', @:)
|
||||
call feedkeys(":match \<S-Tab>\<Home>\"\<CR>", 'xt')
|
||||
call assert_equal('"match none', @:)
|
||||
call feedkeys(":match NON\<Tab>\<Home>\"\<CR>", 'xt')
|
||||
call assert_equal('"match NONE', @:)
|
||||
call feedkeys(":match | chist\<Tab>\<C-B>\"\<CR>", 'xt')
|
||||
call assert_equal('"match | chistory', @:)
|
||||
endfunc
|
||||
|
@ -428,20 +428,37 @@ func Test_highlight_completion()
|
|||
hi Aardig ctermfg=green
|
||||
call feedkeys(":hi \<Tab>\<Home>\"\<CR>", 'xt')
|
||||
call assert_equal('"hi Aardig', getreg(':'))
|
||||
|
||||
" hi default
|
||||
call feedkeys(":hi default \<Tab>\<Home>\"\<CR>", 'xt')
|
||||
call assert_equal('"hi default Aardig', getreg(':'))
|
||||
call feedkeys(":hi clear Aa\<Tab>\<Home>\"\<CR>", 'xt')
|
||||
call assert_equal('"hi clear Aardig', getreg(':'))
|
||||
call feedkeys(":hi li\<S-Tab>\<Home>\"\<CR>", 'xt')
|
||||
call assert_equal('"hi link', getreg(':'))
|
||||
call feedkeys(":hi d\<S-Tab>\<Home>\"\<CR>", 'xt')
|
||||
call assert_equal('"hi default', getreg(':'))
|
||||
call feedkeys(":hi default link Aa\<Tab>\<Home>\"\<CR>", 'xt')
|
||||
call assert_equal('"hi default link Aardig', getreg(':'))
|
||||
|
||||
" hi clear only accepts one parameter
|
||||
call feedkeys(":hi c\<S-Tab>\<Home>\"\<CR>", 'xt')
|
||||
call assert_equal('"hi clear', getreg(':'))
|
||||
call feedkeys(":hi clear Aa\<Tab>\<Home>\"\<CR>", 'xt')
|
||||
call assert_equal('"hi clear Aardig', getreg(':'))
|
||||
call feedkeys(":hi clear Aardig Aard\<Tab>\<C-B>\"\<CR>", 'xt')
|
||||
call assert_equal('"hi clear Aardig Aardig', getreg(':'))
|
||||
call feedkeys(":hi Aardig \<Tab>\<C-B>\"\<CR>", 'xt')
|
||||
call assert_equal("\"hi Aardig \t", getreg(':'))
|
||||
call assert_equal("\"hi clear Aardig Aard\<Tab>", getreg(':'))
|
||||
" hi link accepts up to two parameters
|
||||
call feedkeys(":hi li\<S-Tab>\<Home>\"\<CR>", 'xt')
|
||||
call assert_equal('"hi link', getreg(':'))
|
||||
call assert_equal('"hi link', getreg(':'))
|
||||
call feedkeys(":hi link Aa\<Tab>\<Home>\"\<CR>", 'xt')
|
||||
call assert_equal('"hi link Aardig', getreg(':'))
|
||||
call feedkeys(":hi link Aardig Aard\<Tab>\<C-B>\"\<CR>", 'xt')
|
||||
call assert_equal("\"hi link Aardig Aardig", getreg(':'))
|
||||
call feedkeys(":hi link Aardig Aardig Aard\<Tab>\<C-B>\"\<CR>", 'xt')
|
||||
call assert_equal("\"hi link Aardig Aardig Aard\<Tab>", getreg(':'))
|
||||
" hi link will complete to "NONE" for second parameter
|
||||
call feedkeys(":hi link NON\<Tab>\<Home>\"\<CR>", 'xt')
|
||||
call assert_equal("\"hi link NonText", getreg(':'))
|
||||
call feedkeys(":hi link Aardig NON\<Tab>\<Home>\"\<CR>", 'xt')
|
||||
call assert_equal("\"hi link Aardig NONE", getreg(':'))
|
||||
|
||||
" A cleared group does not show up in completions.
|
||||
hi Anders ctermfg=green
|
||||
|
@ -460,6 +477,102 @@ func Test_highlight_easter_egg()
|
|||
call test_override('ALL', 0)
|
||||
endfunc
|
||||
|
||||
func Test_highlight_group_completion()
|
||||
" Test completing keys
|
||||
call assert_equal('term=', getcompletion('hi Foo ', 'cmdline')[0])
|
||||
call assert_equal('ctermfg=', getcompletion('hi Foo c*fg', 'cmdline')[0])
|
||||
call assert_equal('NONE', getcompletion('hi Foo NON', 'cmdline')[0])
|
||||
set wildoptions+=fuzzy
|
||||
call assert_equal('ctermbg=', getcompletion('hi Foo cmbg', 'cmdline')[0])
|
||||
set wildoptions-=fuzzy
|
||||
|
||||
" Test completing the current value
|
||||
hi FooBar term=bold,underline cterm=undercurl ctermfg=lightgray ctermbg=12 ctermul=34
|
||||
call assert_equal('bold,underline', getcompletion('hi FooBar term=', 'cmdline')[0])
|
||||
call assert_equal('undercurl', getcompletion('hi FooBar cterm=', 'cmdline')[0])
|
||||
call assert_equal('7', getcompletion('hi FooBar ctermfg=', 'cmdline')[0])
|
||||
call assert_equal('12', getcompletion('hi FooBar ctermbg=', 'cmdline')[0])
|
||||
call assert_equal('34', getcompletion('hi FooBar ctermul=', 'cmdline')[0])
|
||||
|
||||
" "bold,underline" is unique and creates an extra item. "undercurl" and
|
||||
" should be de-duplicated
|
||||
call assert_equal(len(getcompletion('hi FooBar term=', 'cmdline')),
|
||||
\ 1 + len(getcompletion('hi FooBar cterm=', 'cmdline')))
|
||||
|
||||
" don't complete original value if we have user input already, similar to
|
||||
" behavior in :set <option>=<pattern>
|
||||
call assert_equal(['bold'], getcompletion('hi FooBar term=bol', 'cmdline'))
|
||||
call assert_equal([], getcompletion('hi FooBar ctermfg=1', 'cmdline'))
|
||||
|
||||
" start/stop do not fill their current value now as they are more
|
||||
" complicated
|
||||
hi FooBar start=123 stop=234
|
||||
call assert_equal([], getcompletion('hi FooBar start=', 'cmdline'))
|
||||
call assert_equal([], getcompletion('hi FooBar stop=', 'cmdline'))
|
||||
|
||||
if has("gui") || has("termguicolors")
|
||||
hi FooBar gui=italic guifg=#112233 guibg=brown1 guisp=green
|
||||
call assert_equal('italic', getcompletion('hi FooBar gui=', 'cmdline')[0])
|
||||
call assert_equal('#112233', getcompletion('hi FooBar guifg=', 'cmdline')[0])
|
||||
call assert_equal('brown1', getcompletion('hi FooBar guibg=', 'cmdline')[0])
|
||||
call assert_equal('green', getcompletion('hi FooBar guisp=', 'cmdline')[0])
|
||||
|
||||
" Check that existing value is de-duplicated and doesn't show up later
|
||||
call assert_equal(1, count(getcompletion('hi FooBar guibg=', 'cmdline'), 'brown1'))
|
||||
endif
|
||||
|
||||
" Test completing attributes
|
||||
call assert_equal(['underdouble', 'underdotted'], getcompletion('hi DoesNotExist term=un*erdo*', 'cmdline'))
|
||||
call assert_equal('NONE', getcompletion('hi DoesNotExist cterm=NON', 'cmdline')[0])
|
||||
call assert_equal('NONE', getcompletion('hi DoesNotExist cterm=', 'cmdline')[-1]) " NONE should be at the end and not sorted
|
||||
call assert_equal('bold', getcompletion('hi DoesNotExist cterm=underline,bo', 'cmdline')[0]) " complete after comma
|
||||
if has("gui") || has("termguicolors")
|
||||
set wildoptions+=fuzzy
|
||||
call assert_equal('italic', getcompletion('hi DoesNotExist gui=itic', 'cmdline')[0])
|
||||
set wildoptions-=fuzzy
|
||||
endif
|
||||
|
||||
" Test completing cterm colors
|
||||
call assert_equal('fg', getcompletion('hi FooBar ctermbg=f*g', 'cmdline')[0])
|
||||
call assert_equal('fg', getcompletion('hi DoesNotExist ctermbg=f*g', 'cmdline')[0])
|
||||
call assert_equal('NONE', getcompletion('hi FooBar ctermfg=NON', 'cmdline')[0])
|
||||
call assert_equal('NONE', getcompletion('hi DoesNotExist ctermfg=NON', 'cmdline')[0])
|
||||
set wildoptions+=fuzzy
|
||||
call assert_equal('Black', getcompletion('hi FooBar ctermul=blck', 'cmdline')[0])
|
||||
call assert_equal('Black', getcompletion('hi DoesNotExist ctermul=blck', 'cmdline')[0])
|
||||
set wildoptions-=fuzzy
|
||||
|
||||
" Test completing gui colors
|
||||
if has("gui") || has("termguicolors")
|
||||
call assert_equal('fg', getcompletion('hi FooBar guibg=f*g', 'cmdline')[0])
|
||||
call assert_equal('fg', getcompletion('hi DoesNotExist guibg=f*g', 'cmdline')[0])
|
||||
call assert_equal('NONE', getcompletion('hi FooBar guifg=NON', 'cmdline')[0])
|
||||
call assert_equal('NONE', getcompletion('hi DoesNotExist guifg=NON', 'cmdline')[0])
|
||||
set wildoptions=fuzzy
|
||||
call assert_equal('limegreen', getcompletion('hi FooBar guisp=limgrn', 'cmdline')[0])
|
||||
call assert_equal('limegreen', getcompletion('hi DoesNotExist guisp=limgrn', 'cmdline')[0])
|
||||
set wildoptions-=fuzzy
|
||||
|
||||
" Test pruning bad color names with space. Vim doesn't support them.
|
||||
let v:colornames['foobar with space'] = '#123456'
|
||||
let v:colornames['foobarwithoutspace'] = '#234567'
|
||||
call assert_equal(['foobarwithoutspace'], getcompletion('hi FooBar guibg=foobarw', 'cmdline'))
|
||||
|
||||
" Test specialized sorting. First few items are special values that
|
||||
" go first, after that it's a sorted list of color names.
|
||||
call assert_equal(['fg','bg','NONE'], getcompletion('hi DoesNotExist guifg=', 'cmdline')[0:2])
|
||||
let completed_colors=getcompletion('hi DoesNotExist guifg=', 'cmdline')[3:]
|
||||
let gui_colors_no_space=filter(copy(v:colornames), {key,val -> match(key, ' ')==-1})
|
||||
call assert_equal(len(gui_colors_no_space), len(completed_colors))
|
||||
call assert_equal(sort(copy(completed_colors)), completed_colors)
|
||||
|
||||
" Test that the specialized sorting still works if we have some pattern matches
|
||||
let completed_colors=getcompletion('hi DoesNotExist guifg=*blue*', 'cmdline')
|
||||
call assert_equal(sort(copy(completed_colors)), completed_colors)
|
||||
call assert_equal('aliceblue', completed_colors[0])
|
||||
endif
|
||||
endfunc
|
||||
|
||||
func Test_getcompletion()
|
||||
let groupcount = len(getcompletion('', 'event'))
|
||||
call assert_true(groupcount > 0)
|
||||
|
|
|
@ -209,7 +209,7 @@ endfunc
|
|||
|
||||
func Test_echohl_completion()
|
||||
call feedkeys(":echohl no\<C-A>\<C-B>\"\<CR>", 'tx')
|
||||
call assert_equal('"echohl NonText Normal none', @:)
|
||||
call assert_equal('"echohl NONE NonText Normal', @:)
|
||||
endfunc
|
||||
|
||||
func Test_syntax_arg_skipped()
|
||||
|
|
|
@ -704,6 +704,8 @@ static char *(features[]) =
|
|||
|
||||
static int included_patches[] =
|
||||
{ /* Add new patch number below this line */
|
||||
/**/
|
||||
1138,
|
||||
/**/
|
||||
1137,
|
||||
/**/
|
||||
|
|
|
@ -846,6 +846,7 @@ extern int (*dyn_libintl_wputenv)(const wchar_t *envstring);
|
|||
#define EXPAND_DIRS_IN_CDPATH 59
|
||||
#define EXPAND_SHELLCMDLINE 60
|
||||
#define EXPAND_FINDFUNC 61
|
||||
#define EXPAND_HIGHLIGHT_GROUP 62
|
||||
|
||||
|
||||
// Values for exmode_active (0 is no exmode)
|
||||
|
|
Loading…
Add table
Reference in a new issue