summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorYee Cheng Chin <ychin.git@gmail.com>2023-09-29 20:42:32 +0200
committerChristian Brabandt <cb@256bit.org>2023-09-29 20:42:32 +0200
commit900894b09a95398dfc75599e9f0aa2ea25723384 (patch)
tree62d287cb3235349c75c60884f280e3c5f47beb5e /src
parent3695d0e41ba26db074dd5680564a6f87d522fb61 (diff)
patch 9.0.1958: cannot complete option valuesv9.0.1958
Problem: cannot complete option values Solution: Add completion functions for several options Add cmdline tab-completion for setting string options Add tab-completion for setting string options on the cmdline using `:set=` (along with `:set+=` and `:set-=`). The existing tab completion for setting options currently only works when nothing is typed yet, and it only fills in with the existing value, e.g. when the user does `:set diffopt=<Tab>` it will be completed to `set diffopt=internal,filler,closeoff` and nothing else. This isn't too useful as a user usually wants auto-complete to suggest all the possible values, such as 'iblank', or 'algorithm:patience'. For set= and set+=, this adds a new optional callback function for each option that can be invoked when doing completion. This allows for each option to have control over how completion works. For example, in 'diffopt', it will suggest the default enumeration, but if `algorithm:` is selected, it will further suggest different algorithm types like 'meyers' and 'patience'. When using set=, the existing option value will be filled in as the first choice to preserve the existing behavior. When using set+= this won't happen as it doesn't make sense. For flag list options (e.g. 'mouse' and 'guioptions'), completion will take into account existing typed values (and in the case of set+=, the existing option value) to make sure it doesn't suggest duplicates. For set-=, there is a new `ExpandSettingSubtract` function which will handle flag list and comma-separated options smartly, by only suggesting values that currently exist in the option. Note that Vim has some existing code that adds special handling for 'filetype', 'syntax', and misc dir options like 'backupdir'. This change preserves them as they already work, instead of converting to the new callback API for each option. closes: #13182 Signed-off-by: Christian Brabandt <cb@256bit.org> Co-authored-by: Yee Cheng Chin <ychin.git@gmail.com>
Diffstat (limited to 'src')
-rw-r--r--src/autocmd.c10
-rw-r--r--src/clipboard.c1
-rw-r--r--src/cmdexpand.c12
-rw-r--r--src/diff.c2
-rw-r--r--src/ex_getln.c1
-rw-r--r--src/highlight.c1
-rw-r--r--src/indent.c1
-rw-r--r--src/mbyte.c13
-rw-r--r--src/option.c375
-rw-r--r--src/option.h3
-rw-r--r--src/optiondefs.h1239
-rw-r--r--src/optionstr.c1118
-rw-r--r--src/popupwin.c3
-rw-r--r--src/proto/autocmd.pro1
-rw-r--r--src/proto/cmdexpand.pro1
-rw-r--r--src/proto/mbyte.pro1
-rw-r--r--src/proto/option.pro2
-rw-r--r--src/proto/optionstr.pro65
-rw-r--r--src/proto/screen.pro2
-rw-r--r--src/screen.c101
-rw-r--r--src/spellsuggest.c1
-rw-r--r--src/structs.h28
-rw-r--r--src/term.c1
-rw-r--r--src/testdir/test_options.vim290
-rw-r--r--src/version.c2
-rw-r--r--src/vim.h16
26 files changed, 2566 insertions, 724 deletions
diff --git a/src/autocmd.c b/src/autocmd.c
index c09e12f404..a78e78b024 100644
--- a/src/autocmd.c
+++ b/src/autocmd.c
@@ -2737,6 +2737,16 @@ get_event_name(expand_T *xp UNUSED, int idx)
return (char_u *)event_names[idx - augroups.ga_len].name;
}
+/*
+ * Function given to ExpandGeneric() to obtain the list of event names. Don't
+ * include groups.
+ */
+ char_u *
+get_event_name_no_group(expand_T *xp UNUSED, int idx)
+{
+ return (char_u *)event_names[idx].name;
+}
+
#if defined(FEAT_EVAL) || defined(PROTO)
/*
diff --git a/src/clipboard.c b/src/clipboard.c
index 4ce536ac47..d80699b059 100644
--- a/src/clipboard.c
+++ b/src/clipboard.c
@@ -1266,6 +1266,7 @@ did_set_clipboard(optset_T *args UNUSED)
for (p = p_cb; *p != NUL; )
{
+ // Note: Keep this in sync with p_cb_values.
if (STRNCMP(p, "unnamed", 7) == 0 && (p[7] == ',' || p[7] == NUL))
{
new_unnamed |= CLIP_UNNAMED;
diff --git a/src/cmdexpand.c b/src/cmdexpand.c
index 40778cc219..b59610c02e 100644
--- a/src/cmdexpand.c
+++ b/src/cmdexpand.c
@@ -15,9 +15,6 @@
static int cmd_showtail; // Only show path tail in lists ?
-static int ExpandGeneric(char_u *pat, expand_T *xp, regmatch_T *regmatch,
- char_u ***matches, int *numMatches,
- char_u *((*func)(expand_T *, int)), int escaped);
static int ExpandFromContext(expand_T *xp, char_u *, char_u ***, int *, int);
static char_u *showmatches_gettail(char_u *s);
static int expand_showtail(expand_T *xp);
@@ -54,6 +51,8 @@ cmdline_fuzzy_completion_supported(expand_T *xp)
&& xp->xp_context != EXPAND_FILETYPE
&& xp->xp_context != EXPAND_HELP
&& xp->xp_context != EXPAND_OLD_SETTING
+ && xp->xp_context != EXPAND_STRING_SETTING
+ && xp->xp_context != EXPAND_SETTING_SUBTRACT
&& xp->xp_context != EXPAND_OWNSYNTAX
&& xp->xp_context != EXPAND_PACKADD
&& xp->xp_context != EXPAND_RUNTIME
@@ -3093,6 +3092,10 @@ ExpandFromContext(
if (xp->xp_context == EXPAND_SETTINGS
|| xp->xp_context == EXPAND_BOOL_SETTINGS)
ret = ExpandSettings(xp, &regmatch, pat, numMatches, matches, fuzzy);
+ else if (xp->xp_context == EXPAND_STRING_SETTING)
+ ret = ExpandStringSetting(xp, &regmatch, numMatches, matches);
+ else if (xp->xp_context == EXPAND_SETTING_SUBTRACT)
+ ret = ExpandSettingSubtract(xp, &regmatch, numMatches, matches);
else if (xp->xp_context == EXPAND_MAPPINGS)
ret = ExpandMappings(pat, &regmatch, numMatches, matches);
#if defined(FEAT_EVAL)
@@ -3121,7 +3124,7 @@ ExpandFromContext(
*
* Returns OK when no problems encountered, FAIL for error (out of memory).
*/
- static int
+ int
ExpandGeneric(
char_u *pat,
expand_T *xp,
@@ -3226,6 +3229,7 @@ ExpandGeneric(
// applies to the completion context. Menus and scriptnames should be kept
// in the specified order.
if (!fuzzy && xp->xp_context != EXPAND_MENUNAMES
+ && xp->xp_context != EXPAND_STRING_SETTING
&& xp->xp_context != EXPAND_MENUS
&& xp->xp_context != EXPAND_SCRIPTNAMES)
sort_matches = TRUE;
diff --git a/src/diff.c b/src/diff.c
index 1873767106..158870402b 100644
--- a/src/diff.c
+++ b/src/diff.c
@@ -2266,6 +2266,7 @@ diffopt_changed(void)
p = p_dip;
while (*p != NUL)
{
+ // Note: Keep this in sync with p_dip_values
if (STRNCMP(p, "filler", 6) == 0)
{
p += 6;
@@ -2343,6 +2344,7 @@ diffopt_changed(void)
}
else if (STRNCMP(p, "algorithm:", 10) == 0)
{
+ // Note: Keep this in sync with p_dip_algorithm_values.
p += 10;
if (STRNCMP(p, "myers", 5) == 0)
{
diff --git a/src/ex_getln.c b/src/ex_getln.c
index ad3a3106a7..5baffa77fb 100644
--- a/src/ex_getln.c
+++ b/src/ex_getln.c
@@ -2650,6 +2650,7 @@ check_opt_wim(void)
for (p = p_wim; *p; ++p)
{
+ // Note: Keep this in sync with p_wim_values.
for (i = 0; ASCII_ISALPHA(p[i]); ++i)
;
if (p[i] != NUL && p[i] != ',' && p[i] != ':')
diff --git a/src/highlight.c b/src/highlight.c
index 8b1e832d7b..31c3280e85 100644
--- a/src/highlight.c
+++ b/src/highlight.c
@@ -3814,6 +3814,7 @@ highlight_changed(void)
if (attr > HL_ALL) // Combination with ':' is not allowed.
return FAIL;
+ // Note: Keep this in sync with expand_set_highlight().
switch (*p)
{
case 'b': attr |= HL_BOLD;
diff --git a/src/indent.c b/src/indent.c
index 3c38b4ec37..1858ecf8f3 100644
--- a/src/indent.c
+++ b/src/indent.c
@@ -871,6 +871,7 @@ briopt_check(win_T *wp)
p = wp->w_p_briopt;
while (*p != NUL)
{
+ // Note: Keep this in sync with p_briopt_values
if (STRNCMP(p, "shift:", 6) == 0
&& ((p[6] == '-' && VIM_ISDIGIT(p[7])) || VIM_ISDIGIT(p[6])))
{
diff --git a/src/mbyte.c b/src/mbyte.c
index c4d1ddb3b8..4951f78323 100644
--- a/src/mbyte.c
+++ b/src/mbyte.c
@@ -5782,3 +5782,16 @@ f_charclass(typval_T *argvars, typval_T *rettv UNUSED)
rettv->vval.v_number = mb_get_class(argvars[0].vval.v_string);
}
#endif
+
+/*
+ * Function given to ExpandGeneric() to obtain the possible arguments of the
+ * encoding options.
+ */
+ char_u *
+get_encoding_name(expand_T *xp UNUSED, int idx)
+{
+ if (idx >= (int)(sizeof(enc_canon_table) / sizeof(enc_canon_table[0])))
+ return NULL;
+
+ return (char_u*)enc_canon_table[idx].name;
+}
diff --git a/src/option.c b/src/option.c
index 9f20bb244b..5140c39260 100644
--- a/src/option.c
+++ b/src/option.c
@@ -1632,7 +1632,7 @@ stropt_copy_value(
// For MS-DOS and WIN32 backslashes before normal file name characters
// are not removed, and keep backslash at start, for "\\machine\path",
// but do remove it for "\\\\machine\\path".
- // The reverse is found in ExpandOldSetting().
+ // The reverse is found in escape_option_str_cmdline().
while (*arg != NUL && !VIM_ISWHITE(*arg))
{
int i;
@@ -1837,7 +1837,7 @@ stropt_get_newval(
&(options[opt_idx]), OPT_GLOBAL));
else
{
- ++arg; // jump to after the '=' or ':'
+ ++arg; // joption_value2stringump to after the '=' or ':'
// Set 'keywordprg' to ":help" if an empty
// value was passed to :set by the user.
@@ -7232,8 +7232,10 @@ set_imsearch_global(void)
}
static int expand_option_idx = -1;
+static int expand_option_start_col = 0;
static char_u expand_option_name[5] = {'t', '_', NUL, NUL, NUL};
static int expand_option_flags = 0;
+static int expand_option_append = FALSE;
void
set_context_in_set_cmd(
@@ -7348,8 +7350,14 @@ set_context_in_set_cmd(
}
}
// handle "-=" and "+="
+ expand_option_append = FALSE;
+ int expand_option_subtract = FALSE;
if ((nextchar == '-' || nextchar == '+' || nextchar == '^') && p[1] == '=')
{
+ if (nextchar == '-')
+ expand_option_subtract = TRUE;
+ if (nextchar == '+' || nextchar == '^')
+ expand_option_append = TRUE;
++p;
nextchar = '=';
}
@@ -7359,22 +7367,20 @@ set_context_in_set_cmd(
xp->xp_context = EXPAND_UNSUCCESSFUL;
return;
}
- if (xp->xp_context != EXPAND_BOOL_SETTINGS && p[1] == NUL)
- {
- xp->xp_context = EXPAND_OLD_SETTING;
- if (is_term_option)
- expand_option_idx = -1;
- else
- expand_option_idx = opt_idx;
- xp->xp_pattern = p + 1;
- return;
- }
- xp->xp_context = EXPAND_NOTHING;
- if (is_term_option || (flags & P_NUM))
- return;
+
+ // Below are for handling expanding a specific option's value after the '='
+ // or ':'
+
+ if (is_term_option)
+ expand_option_idx = -1;
+ else
+ expand_option_idx = opt_idx;
xp->xp_pattern = p + 1;
+ expand_option_start_col = (int)(p + 1 - xp->xp_line);
+ // Certain options currently have special case handling to reuse the
+ // expansion logic with other commands.
#ifdef FEAT_SYN_HL
if (options[opt_idx].var == (char_u *)&p_syn)
{
@@ -7382,7 +7388,38 @@ set_context_in_set_cmd(
return;
}
#endif
+ if (options[opt_idx].var == (char_u *)&p_ft)
+ {
+ xp->xp_context = EXPAND_FILETYPE;
+ return;
+ }
+ // Now pick. If the option has a custom expander, use that. Otherwise, just
+ // fill with the existing option value.
+ if (expand_option_subtract)
+ {
+ xp->xp_context = EXPAND_SETTING_SUBTRACT;
+ return;
+ }
+ else if (expand_option_idx >= 0 &&
+ options[expand_option_idx].opt_expand_cb != NULL)
+ {
+ xp->xp_context = EXPAND_STRING_SETTING;
+ }
+ else if (*xp->xp_pattern == NUL)
+ {
+ xp->xp_context = EXPAND_OLD_SETTING;
+ return;
+ }
+ else
+ xp->xp_context = EXPAND_NOTHING;
+
+ if (is_term_option || (flags & P_NUM))
+ return;
+
+ // Only string options below
+
+ // Options that have P_EXPAND are considered to all use file/dir expansion.
if (flags & P_EXPAND)
{
p = options[opt_idx].var;
@@ -7403,10 +7440,6 @@ set_context_in_set_cmd(
else
xp->xp_backslash = XP_BS_ONE;
}
- else if (p == (char_u *)&p_ft)
- {
- xp->xp_context = EXPAND_FILETYPE;
- }
else
{
xp->xp_context = EXPAND_FILES;
@@ -7418,34 +7451,55 @@ set_context_in_set_cmd(
}
}
- // For an option that is a list of file names, find the start of the
- // last file name.
- for (p = arg + STRLEN(arg) - 1; p > xp->xp_pattern; --p)
+ // For an option that is a list of file names, or comma/colon-separated
+ // values, split it by the delimiter and find the start of the current
+ // pattern, while accounting for backslash-escaped space/commas/colons.
+ // Triple-backslashed escaped file names (e.g. 'path') can also be
+ // delimited by space.
+ if ((flags & P_EXPAND) || (flags & P_COMMA) || (flags & P_COLON))
{
- // count number of backslashes before ' ' or ','
- if (*p == ' ' || *p == ',')
+ for (p = arg + STRLEN(arg) - 1; p >= xp->xp_pattern; --p)
{
- s = p;
- while (s > xp->xp_pattern && *(s - 1) == '\\')
- --s;
- if ((*p == ' ' && (xp->xp_backslash == XP_BS_THREE && (p - s) < 3))
- || (*p == ',' && (flags & P_COMMA) && ((p - s) & 1) == 0))
+ // count number of backslashes before ' ' or ',' or ':'
+ if (*p == ' ' || *p == ',' ||
+ (*p == ':' && (flags & P_COLON)))
{
- xp->xp_pattern = p + 1;
- break;
+ s = p;
+ while (s > xp->xp_pattern && *(s - 1) == '\\')
+ --s;
+ if ((*p == ' ' && (xp->xp_backslash == XP_BS_THREE && (p - s) < 3))
+ || (*p == ',' && (flags & P_COMMA) && ((p - s) % 1) == 0)
+ || (*p == ':' && (flags & P_COLON)))
+ {
+ xp->xp_pattern = p + 1;
+ break;
+ }
}
}
+ }
+
+ // An option that is a list of single-character flags should always start
+ // at the end as we don't complete words.
+ if (flags & P_FLAGLIST)
+ xp->xp_pattern = arg + STRLEN(arg);
+ // Some options can either be using file/dir expansions, or custom value
+ // expansion depending on what the user typed. Unfortunately we have to
+ // manually handle it here to make sure we have the correct xp_context set.
#ifdef FEAT_SPELL
- // for 'spellsuggest' start at "file:"
- if (options[opt_idx].var == (char_u *)&p_sps
- && STRNCMP(p, "file:", 5) == 0)
+ if (options[opt_idx].var == (char_u *)&p_sps)
+ {
+ if (STRNCMP(xp->xp_pattern, "file:", 5) == 0)
{
- xp->xp_pattern = p + 5;
- break;
+ xp->xp_pattern += 5;
+ return;
+ }
+ else if (options[expand_option_idx].opt_expand_cb != NULL)
+ {
+ xp->xp_context = EXPAND_STRING_SETTING;
}
-#endif
}
+#endif
}
/*
@@ -7464,7 +7518,7 @@ set_context_in_set_cmd(
* If 'test_only' is FALSE and 'fuzzy' is TRUE and if 'str' fuzzy matches
* 'fuzzystr', then stores the match details in fuzmatch[idx] and returns TRUE.
*/
- static int
+ int
match_str(
char_u *str,
regmatch_T *regmatch,
@@ -7711,6 +7765,37 @@ ExpandSettings(
return OK;
}
+// Escape an option value that can be used on the command-line with :set.
+// Caller needs to free the returned string, unless NULL is returned.
+ static char_u*
+escape_option_str_cmdline(char_u *var)
+{
+ char_u *buf;
+
+ // A backslash is required before some characters. This is the reverse of
+ // what happens in do_set().
+ buf = vim_strsave_escaped(var, escape_chars);
+ if (buf == NULL)
+ return NULL;
+
+#ifdef BACKSLASH_IN_FILENAME
+ // For MS-Windows et al. we don't double backslashes at the start and
+ // before a file name character.
+ // The reverse is found at stropt_copy_value().
+ for (var = buf; *var != NUL; MB_PTR_ADV(var))
+ if (var[0] == '\\' && var[1] == '\\'
+ && expand_option_idx >= 0
+ && (options[expand_option_idx].flags & P_EXPAND)
+ && vim_isfilec(var[2])
+ && (var[2] != '\\' || (var == buf && var[4] != '\\')))
+ STRMOVE(var, var + 1);
+#endif
+ return buf;
+}
+
+/*
+ * Expansion handler for :set= when we just want to fill in with the existing value.
+ */
int
ExpandOldSetting(int *numMatches, char_u ***matches)
{
@@ -7718,7 +7803,7 @@ ExpandOldSetting(int *numMatches, char_u ***matches)
char_u *buf;
*numMatches = 0;
- *matches = ALLOC_ONE(char_u *);
+ *matches = ALLOC_MULT(char_u *, 1);
if (*matches == NULL)
return FAIL;
@@ -7739,34 +7824,211 @@ ExpandOldSetting(int *numMatches, char_u ***matches)
else if (var == NULL)
var = (char_u *)"";
- // A backslash is required before some characters. This is the reverse of
- // what happens in do_set().
- buf = vim_strsave_escaped(var, escape_chars);
-
+ buf = escape_option_str_cmdline(var);
if (buf == NULL)
{
VIM_CLEAR(*matches);
return FAIL;
}
-#ifdef BACKSLASH_IN_FILENAME
- // For MS-Windows et al. we don't double backslashes at the start and
- // before a file name character.
- for (var = buf; *var != NUL; MB_PTR_ADV(var))
- if (var[0] == '\\' && var[1] == '\\'
- && expand_option_idx >= 0
- && (options[expand_option_idx].flags & P_EXPAND)
- && vim_isfilec(var[2])
- && (var[2] != '\\' || (var == buf && var[4] != '\\')))
- STRMOVE(var, var + 1);
-#endif
-
- *matches[0] = buf;
+ (*matches)[0] = buf;
*numMatches = 1;
return OK;
}
/*
+ * Expansion handler for :set=/:set+= when the option has a custom expansion handler.
+ */
+ int
+ExpandStringSetting(
+ expand_T *xp,
+ regmatch_T *regmatch,
+ int *numMatches,
+ char_u ***matches)
+{
+ char_u *var = NULL; // init for GCC
+ char_u *buf;
+
+ if (expand_option_idx < 0 ||
+ options[expand_option_idx].opt_expand_cb == NULL)
+ {
+ // Not supposed to reach this. This function is only for options with
+ // custom expansion callbacks.
+ return FAIL;
+ }
+
+ optexpand_T args;
+ args.oe_varp = get_varp_scope(&options[expand_option_idx], expand_option_flags);
+ args.oe_append = expand_option_append;
+ args.oe_regmatch = regmatch;
+ args.oe_xp = xp;
+ args.oe_set_arg = xp->xp_line + expand_option_start_col;
+ args.oe_include_orig_val =
+ !expand_option_append &&
+ (*args.oe_set_arg == NUL);
+
+ // Retrieve the existing value, but escape it as a reverse of setting it.
+ // We technically only need to do this when oe_append or
+ // oe_include_orig_val is true.
+ option_value2string(&options[expand_option_idx], expand_option_flags);
+ var = NameBuff;
+ buf = escape_option_str_cmdline(var);
+ if (buf == NULL)
+ return FAIL;
+
+ args.oe_opt_value = buf;
+
+ int num_ret = options[expand_option_idx].opt_expand_cb(&args, numMatches, matches);
+
+ vim_free(buf);
+ return num_ret;
+}
+
+/*
+ * Expansion handler for :set-=
+ */
+ int
+ExpandSettingSubtract(
+ expand_T *xp,
+ regmatch_T *regmatch,
+ int *numMatches,
+ char_u ***matches)
+{
+ if (expand_option_idx < 0)
+ // term option
+ return ExpandOldSetting(numMatches, matches);
+
+ char_u *option_val = *(char_u**)get_option_varp_scope(
+ expand_option_idx, expand_option_flags);
+
+ long_u option_flags = options[expand_option_idx].flags;
+
+ if (option_flags & P_NUM)
+ return ExpandOldSetting(numMatches, matches);
+ else if (option_flags & P_COMMA)
+ {
+ // Split the option by comma, then present each option to the user if
+ // it matches the pattern.
+ // This condition needs to go first, because 'whichwrap' has both
+ // P_COMMA and P_FLAGLIST.
+ garray_T ga;
+
+ char_u *item;
+ char_u *option_copy;
+ char_u *next_val;
+ char_u *comma;
+
+ if (*option_val == NUL)
+ return FAIL;
+
+ // Make a copy as we need to inject null characters destructively.
+ option_copy = vim_strsave(option_val);
+ if (option_copy == NULL)
+ return FAIL;
+ next_val = option_copy;
+
+ ga_init2(&ga, sizeof(char_u *), 10);
+
+ do
+ {
+ item = next_val;
+ comma = vim_strchr(next_val, ',');
+ while (comma != NULL && comma != next_val && *(comma - 1) == '\\')
+ {
+ // "\," is interpreted as a literal comma rather than option
+ // separator when reading options in copy_option_part(). Skip
+ // it.
+ comma = vim_strchr(comma + 1, ',');
+ }
+ if (comma != NULL)
+ {
+ *comma = NUL; // null-terminate this value, required by later functions
+ next_val = comma + 1;
+ }
+ else
+ next_val = NULL;
+
+ if (*item == NUL)
+ // empty value, don't add to list
+ continue;
+
+ if (!vim_regexec(regmatch, item, (colnr_T)0))
+ continue;
+
+ char_u *buf = escape_option_str_cmdline(item);
+ if (buf == NULL)
+ {
+ vim_free(option_copy);
+ ga_clear_strings(&ga);
+ return FAIL;
+ }
+ if (ga_add_string(&ga, buf) != OK)
+ {
+ vim_free(buf);
+ break;
+ }
+ } while (next_val != NULL);
+
+ vim_free(option_copy);
+
+ *matches = ga.ga_data;
+ *numMatches = ga.ga_len;
+ return OK;
+ }
+ else if (option_flags & P_FLAGLIST)
+ {
+ // Only present the flags that are set on the option as the other flags
+ // are not meaningful to do set-= on.
+
+ if (*xp->xp_pattern != NUL)
+ {
+ // Don't suggest anything if cmdline is non-empty. Vim's set-=
+ // behavior requires consecutive strings and it's usually
+ // unintuitive to users if ther try to subtract multiple flags at
+ // once.
+ return FAIL;
+ }
+
+ int num_flags = STRLEN(option_val);
+ if (num_flags == 0)
+ return FAIL;
+
+ *matches = ALLOC_MULT(char_u *, num_flags + 1);
+ if (*matches == NULL)
+ return FAIL;
+
+ int count = 0;
+ char_u *p;
+
+ p = vim_strsave(option_val);
+ if (p == NULL)
+ {
+ VIM_CLEAR(*matches);
+ return FAIL;
+ }
+ (*matches)[count++] = p;
+
+ if (num_flags > 1)
+ {
+ // If more than one flags, split the flags up and expose each
+ // character as individual choice.
+ for (char_u *flag = option_val; *flag != NUL; flag++)
+ {
+ char_u *p = vim_strnsave(flag, 1);
+ if (p == NULL)
+ break;
+ (*matches)[count++] = p;
+ }
+ }
+
+ *numMatches = count;
+ return OK;
+ }
+
+ return ExpandOldSetting(numMatches, matches);
+}
+
+/*
* Get the value for the numeric or string option *opp in a nice format into
* NameBuff[]. Must not be called with a hidden option!
*/
@@ -8097,6 +8359,7 @@ fill_culopt_flags(char_u *val, win_T *wp)
p = val;
while (*p != NUL)
{
+ // Note: Keep this in sync with p_culopt_values.
if (STRNCMP(p, "line", 4) == 0)
{
p += 4;
diff --git a/src/option.h b/src/option.h
index 68748304bf..ced4f3e4b7 100644
--- a/src/option.h
+++ b/src/option.h
@@ -60,6 +60,7 @@
#define P_RWINONLY 0x10000000L // only redraw current window
#define P_MLE 0x20000000L // under control of 'modelineexpr'
#define P_FUNC 0x40000000L // accept a function reference or a lambda
+#define P_COLON 0x80000000L // values use colons to create sublists
// Returned by get_option_value().
typedef enum {
@@ -230,7 +231,7 @@ typedef enum {
#define CPO_ALL "aAbBcCdDeEfFgHiIjJkKlLmMnoOpPqrRsStuvwWxXyZ$!%*-+<>#{|&/\\.;"
// characters for p_ww option:
-#define WW_ALL "bshl<>[],~"
+#define WW_ALL "bshl<>[]~"
// characters for p_mouse option:
#define MOUSE_NORMAL 'n' // use mouse in Normal mode
diff --git a/src/optiondefs.h b/src/optiondefs.h
index 4f3a1524a9..08de5bf406 100644
--- a/src/optiondefs.h
+++ b/src/optiondefs.h
@@ -272,6 +272,11 @@ struct vimoption
// callback function to invoke after an option is modified to validate and
// apply the new value.
opt_did_set_cb_T opt_did_set_cb;
+
+ // callback function to invoke when expanding possible values on the
+ // cmdline. Only useful for string options.
+ opt_expand_cb_T opt_expand_cb;
+
char_u *def_val[2]; // default values for variable (vi and vim)
#ifdef FEAT_EVAL
sctx_T script_ctx; // script context where the option was last set
@@ -324,7 +329,7 @@ static struct vimoption options[] =
#else
(char_u *)NULL, PV_NONE,
#endif
- NULL,
+ NULL, NULL,
{
#if defined(MSWIN) && !defined(FEAT_GUI_MSWIN)
(char_u *)128L,
@@ -333,72 +338,72 @@ static struct vimoption options[] =
#endif
(char_u *)0L} SCTX_INIT},
{"antialias", "anti", P_BOOL|P_VI_DEF|P_VIM|P_RCLR,
- (char_u *)NULL, PV_NONE, NULL,
+ (char_u *)NULL, PV_NONE, NULL, NULL,
{(char_u *)FALSE, (char_u *)FALSE}
SCTX_INIT},
{"arabic", "arab", P_BOOL|P_VI_DEF|P_VIM|P_CURSWANT,
#ifdef FEAT_ARABIC
- (char_u *)VAR_WIN, PV_ARAB, did_set_arabic,
+ (char_u *)VAR_WIN, PV_ARAB, did_set_arabic, NULL,
#else
- (char_u *)NULL, PV_NONE, NULL,
+ (char_u *)NULL, PV_NONE, NULL, NULL,
#endif
{(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
{"arabicshape", "arshape", P_BOOL|P_VI_DEF|P_VIM|P_RCLR,
#ifdef FEAT_ARABIC
- (char_u *)&p_arshape, PV_NONE, NULL,
+ (char_u *)&p_arshape, PV_NONE, NULL, NULL,
#else
- (char_u *)NULL, PV_NONE, NULL,
+ (char_u *)NULL, PV_NONE, NULL, NULL,
#endif
{(char_u *)TRUE, (char_u *)0L} SCTX_INIT},
{"allowrevins", "ari", P_BOOL|P_VI_DEF|P_VIM,
#ifdef FEAT_RIGHTLEFT
- (char_u *)&p_ari, PV_NONE, NULL,
+ (char_u *)&p_ari, PV_NONE, NULL, NULL,
#else
- (char_u *)NULL, PV_NONE, NULL,
+ (char_u *)NULL, PV_NONE, NULL, NULL,
#endif
{(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
{"altkeymap", "akm", P_BOOL|P_VI_DEF,
- (char_u *)NULL, PV_NONE, NULL,
+ (char_u *)NULL, PV_NONE, NULL, NULL,
{(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
{"ambiwidth", "ambw", P_STRING|P_VI_DEF|P_RCLR,
- (char_u *)&p_ambw, PV_NONE, did_set_ambiwidth,
+ (char_u *)&p_ambw, PV_NONE, did_set_ambiwidth, expand_set_ambiwidth,
{(char_u *)"single", (char_u *)0L}
SCTX_INIT},
{"autochdir", "acd", P_BOOL|P_VI_DEF,
#ifdef FEAT_AUTOCHDIR
- (char_u *)&p_acd, PV_NONE, did_set_autochdir,
+ (char_u *)&p_acd, PV_NONE, did_set_autochdir, NULL,
{(char_u *)FALSE, (char_u *)0L}
#else
- (char_u *)NULL, PV_NONE, NULL,
+ (char_u *)NULL, PV_NONE, NULL, NULL,
{(char_u *)0L, (char_u *)0L}
#endif
SCTX_INIT},
{"autoshelldir", "asd", P_BOOL|P_VI_DEF,
#ifdef FEAT_AUTOSHELLDIR
- (char_u *)&p_asd, PV_NONE, NULL,
+ (char_u *)&p_asd, PV_NONE, NULL, NULL,
{(char_u *)FALSE, (char_u *)0L}
#else
- (char_u *)NULL, PV_NONE, NULL,
+ (char_u *)NULL, PV_NONE, NULL, NULL,
{(char_u *)0L, (char_u *)0L}
#endif
SCTX_INIT},
{"autoindent", "ai", P_BOOL|P_VI_DEF,
- (char_u *)&p_ai, PV_AI, NULL,
+ (char_u *)&p_ai, PV_AI, NULL, NULL,
{(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
{"autoprint", "ap", P_BOOL|P_VI_DEF,
- (char_u *)NULL, PV_NONE, NULL,
+ (char_u *)NULL, PV_NONE, NULL, NULL,
{(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
{"autoread", "ar", P_BOOL|P_VI_DEF,
- (char_u *)&p_ar, PV_AR, NULL,
+ (char_u *)&p_ar, PV_AR, NULL, NULL,
{(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
{"autowrite", "aw", P_BOOL|P_VI_DEF,
- (char_u *)&p_aw, PV_NONE, NULL,
+ (char_u *)&p_aw, PV_NONE, NULL, NULL,
{(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
{"autowriteall","awa", P_BOOL|P_VI_DEF,
- (char_u *)&p_awa, PV_NONE, NULL,
+ (char_u *)&p_awa, PV_NONE, NULL, NULL,
{(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
{"background", "bg", P_STRING|P_VI_DEF|P_RCLR,
- (char_u *)&p_bg, PV_NONE, did_set_background,
+ (char_u *)&p_bg, PV_NONE, did_set_background, expand_set_background,
{
#if (defined(MSWIN)) && !defined(FEAT_GUI)
(char_u *)"dark",
@@ -407,13 +412,13 @@ static struct vimoption options[] =
#endif
(char_u *)0L} SCTX_INIT},
{"backspace", "bs", P_STRING|P_VI_DEF|P_VIM|P_ONECOMMA|P_NODUP,
- (char_u *)&p_bs, PV_NONE, did_set_backspace,
+ (char_u *)&p_bs, PV_NONE, did_set_backspace, expand_set_backspace,
{(char_u *)"", (char_u *)0L} SCTX_INIT},
{"backup", "bk", P_BOOL|P_VI_DEF|P_VIM,
- (char_u *)&p_bk, PV_NONE, NULL,
+ (char_u *)&p_bk, PV_NONE, NULL, NULL,
{(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
{"backupcopy", "bkc", P_STRING|P_VIM|P_ONECOMMA|P_NODUP,
- (char_u *)&p_bkc, PV_BKC, did_set_backupcopy,
+ (char_u *)&p_bkc, PV_BKC, did_set_backupcopy, expand_set_backupcopy,
#ifdef UNIX
{(char_u *)"yes", (char_u *)"auto"}
#else
@@ -422,11 +427,11 @@ static struct vimoption options[] =
SCTX_INIT},
{"backupdir", "bdir", P_STRING|P_EXPAND|P_VI_DEF|P_ONECOMMA
|P_NODUP|P_SECURE,
- (char_u *)&p_bdir, PV_NONE, NULL,
+ (char_u *)&p_bdir, PV_NONE, NULL, NULL,
{(char_u *)DFLT_BDIR, (char_u *)0L} SCTX_INIT},
{"backupext", "bex", P_STRING|P_VI_DEF|P_NFNAME,
(char_u *)&p_bex, PV_NONE,
- did_set_backupext_or_patchmode,
+ did_set_backupext_or_patchmode, NULL,
{
#ifdef VMS
(char_u *)"_",
@@ -435,77 +440,77 @@ static struct vimoption options[] =
#endif
(char_u *)0L} SCTX_INIT},
{"backupskip", "bsk", P_STRING|P_VI_DEF|P_ONECOMMA|P_NODUP,
- (char_u *)&p_bsk, PV_NONE, NULL,
+ (char_u *)&p_bsk, PV_NONE, NULL, NULL,
{(char_u *)"", (char_u *)0L}
SCTX_INIT},
{"balloondelay","bdlay",P_NUM|P_VI_DEF,
#ifdef FEAT_BEVAL