summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--runtime/doc/cmdline.txt20
-rw-r--r--runtime/doc/options.txt9
-rw-r--r--runtime/doc/tags2
-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
29 files changed, 2590 insertions, 731 deletions
diff --git a/runtime/doc/cmdline.txt b/runtime/doc/cmdline.txt
index d2e476f006..3d63b1964b 100644
--- a/runtime/doc/cmdline.txt
+++ b/runtime/doc/cmdline.txt
@@ -517,16 +517,26 @@ example, to match only files that end in ".c": >
:e *.c$
This will not match a file ending in ".cpp". Without the "$" it does match.
-The old value of an option can be obtained by hitting 'wildchar' just after
-the '='. For example, typing 'wildchar' after ":set dir=" will insert the
-current value of 'dir'. This overrules file name completion for the options
-that take a file name.
-
If you would like using <S-Tab> for CTRL-P in an xterm, put this command in
your .cshrc: >
xmodmap -e "keysym Tab = Tab Find"
And this in your .vimrc: >
:cmap <Esc>[1~ <C-P>
+< *complete-set-option*
+When setting an option using |:set=|, the old value of an option can be
+obtained by hitting 'wildchar' just after the '='. For example, typing
+'wildchar' after ":set dir=" will insert the current value of 'dir'. This
+overrules file name completion for the options that take a file name.
+
+When using |:set=|, |:set+=|, or |:set^=|, string options that have
+pre-defined names or syntax (e.g. 'diffopt', 'listchars') or are a list of
+single-character flags (e.g. 'shortmess') will also present a list of possible
+values for completion when using 'wildchar'.
+
+When using |:set-=|, comma-separated options like 'diffopt' or 'backupdir'
+will show each item separately. Flag list options like 'shortmess' will show
+both the entire old value and the individual flags. Otherwise completion will
+just fill in with the entire old value.
==============================================================================
3. Ex command-lines *cmdline-lines*
diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt
index f7606397d9..090399af03 100644
--- a/runtime/doc/options.txt
+++ b/runtime/doc/options.txt
@@ -71,7 +71,7 @@ achieve special effects. These options come in three forms:
'ttytype'
Warning: This may have a lot of side effects.
- *:set-args* *E487* *E521*
+ *:set-args* *:set=* *E487* *E521*
:se[t] {option}={value} or
:se[t] {option}:{value}
Set string or number option to {value}.
@@ -79,7 +79,9 @@ achieve special effects. These options come in three forms:
hex (preceded with 0x) or octal (preceded with '0').
The old value can be inserted by typing 'wildchar' (by
default this is a <Tab> or CTRL-E if 'compatible' is
- set). See |cmdline-completion|.
+ set). Many string options with fixed syntax and names
+ also support completing known values. See
+ |cmdline-completion| and |complete-set-option|.
White space between {option} and '=' is allowed and
will be ignored. White space between '=' and {value}
is not allowed.
@@ -113,6 +115,9 @@ achieve special effects. These options come in three forms:
When the option is a list of flags, {value} must be
exactly as they appear in the option. Remove flags
one by one to avoid problems.
+ The individual values from a comma separated list or
+ list of flags can be inserted by typing 'wildchar'.
+ See |complete-set-option|.
Also see |:set-args| above.
The {option} arguments to ":set" may be repeated. For example: >
diff --git a/runtime/doc/tags b/runtime/doc/tags
index 48fe75fb8d..1df34d3a3d 100644
--- a/runtime/doc/tags
+++ b/runtime/doc/tags
@@ -3208,6 +3208,7 @@ $quote eval.txt /*$quote*
:set-inv options.txt /*:set-inv*
:set-termcap options.txt /*:set-termcap*
:set-verbose options.txt /*:set-verbose*
+:set= options.txt /*:set=*
:set^= options.txt /*:set^=*
:set_env options.txt /*:set_env*
:setf options.txt /*:setf*
@@ -6484,6 +6485,7 @@ complete-items insert.txt /*complete-items*
complete-popup insert.txt /*complete-popup*
complete-popuphidden insert.txt /*complete-popuphidden*
complete-script-local-functions cmdline.txt /*complete-script-local-functions*
+complete-set-option cmdline.txt /*complete-set-option*
complete_CTRL-E insert.txt /*complete_CTRL-E*
complete_CTRL-Y insert.txt /*complete_CTRL-Y*
complete_add() builtin.txt /*complete_add()*
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",