aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/option.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim/option.c')
-rw-r--r--src/nvim/option.c386
1 files changed, 294 insertions, 92 deletions
diff --git a/src/nvim/option.c b/src/nvim/option.c
index eef5e66aeb..c0353e52be 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -40,6 +40,7 @@
#include "nvim/change.h"
#include "nvim/charset.h"
#include "nvim/cmdexpand.h"
+#include "nvim/cmdexpand_defs.h"
#include "nvim/cursor_shape.h"
#include "nvim/decoration_provider.h"
#include "nvim/diff.h"
@@ -136,12 +137,12 @@ static char *p_vsts_nopaste;
#define OPTION_COUNT ARRAY_SIZE(options)
+/// :set boolean option prefix
typedef enum {
- OP_NONE = 0,
- OP_ADDING, ///< "opt+=arg"
- OP_PREPENDING, ///< "opt^=arg"
- OP_REMOVING, ///< "opt-=arg"
-} set_op_T;
+ PREFIX_NO = 0, ///< "no" prefix
+ PREFIX_NONE, ///< no prefix
+ PREFIX_INV, ///< "inv" prefix
+} set_prefix_T;
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "option.c.generated.h"
@@ -557,7 +558,7 @@ static char *find_dup_item(char *origval, const char *newval, uint32_t flags)
/// Set the Vi-default value of a number option.
/// Used for 'lines' and 'columns'.
-void set_number_default(char *name, long val)
+void set_number_default(char *name, OptInt val)
{
int opt_idx = findoption(name);
if (opt_idx >= 0) {
@@ -752,8 +753,8 @@ void ex_set(exarg_T *eap)
(void)do_set(eap->arg, flags);
}
-static void do_set_bool(int opt_idx, int opt_flags, int prefix, int nextchar, const void *varp,
- const char **errmsg)
+static void do_set_bool(int opt_idx, int opt_flags, set_prefix_T prefix, int nextchar,
+ const void *varp, const char **errmsg)
{
varnumber_T value;
@@ -772,10 +773,12 @@ static void do_set_bool(int opt_idx, int opt_flags, int prefix, int nextchar, co
value = *(int *)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL);
}
} else {
- if (prefix == 2) {
- value = *(int *)varp ^ 1; // ":set invopt": invert
+ // ":set invopt": invert
+ // ":set opt" or ":set noopt": set or reset
+ if (prefix == PREFIX_INV) {
+ value = *(int *)varp ^ 1;
} else {
- value = prefix; // ":set opt" or ":set noopt": set or reset
+ value = prefix == PREFIX_NO ? 0 : 1;
}
}
@@ -797,7 +800,7 @@ static void do_set_num(int opt_idx, int opt_flags, char **argp, int nextchar, co
// other error
arg++;
if (nextchar == '&') {
- value = (long)(intptr_t)options[opt_idx].def_val;
+ value = (varnumber_T)options[opt_idx].def_val;
} else if (nextchar == '<') {
if ((OptInt *)varp == &curbuf->b_p_ul && opt_flags == OPT_LOCAL) {
// for 'undolevels' NO_LOCAL_UNDOLEVEL means using the global value
@@ -843,7 +846,7 @@ static void do_set_num(int opt_idx, int opt_flags, char **argp, int nextchar, co
if (op == OP_REMOVING) {
value = *(OptInt *)varp - value;
}
- *errmsg = set_num_option(opt_idx, (void *)varp, (long)value,
+ *errmsg = set_num_option(opt_idx, (void *)varp, value,
errbuf, errbuflen, opt_flags);
}
@@ -888,7 +891,7 @@ static char *stropt_copy_value(char *origval, char **argp, set_op_T op,
// For MS-Windows 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 && !ascii_iswhite(*arg)) {
if (*arg == '\\' && arg[1] != NUL
#ifdef BACKSLASH_IN_FILENAME
@@ -1168,7 +1171,7 @@ static void do_set_option_string(int opt_idx, int opt_flags, char **argp, int ne
// be triggered that can cause havoc.
*errmsg = did_set_string_option(curbuf, curwin, opt_idx, (char **)varp, oldval,
errbuf, errbuflen,
- opt_flags, value_checked);
+ opt_flags, op, value_checked);
secure = secure_saved;
@@ -1204,17 +1207,17 @@ static set_op_T get_op(const char *arg)
return op;
}
-static int get_option_prefix(char **argp)
+static set_prefix_T get_option_prefix(char **argp)
{
if (strncmp(*argp, "no", 2) == 0) {
*argp += 2;
- return 0;
+ return PREFIX_NO;
} else if (strncmp(*argp, "inv", 3) == 0) {
*argp += 3;
- return 2;
+ return PREFIX_INV;
}
- return 1;
+ return PREFIX_NONE;
}
/// @param[in] arg Pointer to start option name
@@ -1273,11 +1276,11 @@ static int parse_option_name(char *arg, int *keyp, int *lenp, int *opt_idxp)
return OK;
}
-static int validate_opt_idx(win_T *win, int opt_idx, int opt_flags, uint32_t flags, int prefix,
- const char **errmsg)
+static int validate_opt_idx(win_T *win, int opt_idx, int opt_flags, uint32_t flags,
+ set_prefix_T prefix, const char **errmsg)
{
// Only bools can have a prefix of 'inv' or 'no'
- if (!(flags & P_BOOL) && prefix != 1) {
+ if (!(flags & P_BOOL) && prefix != PREFIX_NONE) {
*errmsg = e_invarg;
return FAIL;
}
@@ -1325,8 +1328,8 @@ static int validate_opt_idx(win_T *win, int opt_idx, int opt_flags, uint32_t fla
return OK;
}
-static void do_set_option_value(int opt_idx, int opt_flags, char **argp, int prefix, int nextchar,
- set_op_T op, uint32_t flags, void *varp, char *errbuf,
+static void do_set_option_value(int opt_idx, int opt_flags, char **argp, set_prefix_T prefix,
+ int nextchar, set_op_T op, uint32_t flags, void *varp, char *errbuf,
size_t errbuflen, const char **errmsg)
{
bool value_checked = false;
@@ -1355,7 +1358,7 @@ static void do_set_option(int opt_flags, char **argp, bool *did_show, char *errb
size_t errbuflen, const char **errmsg)
{
// 1: nothing, 0: "no", 2: "inv" in front of name
- int prefix = get_option_prefix(argp);
+ set_prefix_T prefix = get_option_prefix(argp);
char *arg = *argp;
@@ -1434,7 +1437,7 @@ static void do_set_option(int opt_flags, char **argp, bool *did_show, char *errb
// '=' character per "set" command line. grrr. (jw)
//
if (nextchar == '?'
- || (prefix == 1
+ || (prefix == PREFIX_NONE
&& vim_strchr("=:&<", nextchar) == NULL
&& !(flags & P_BOOL))) {
// print value
@@ -1976,8 +1979,8 @@ void set_option_sctx_idx(int opt_idx, int opt_flags, sctx_T script_ctx)
}
/// Apply the OptionSet autocommand.
-static void apply_optionset_autocmd(int opt_idx, long opt_flags, OptInt oldval, OptInt oldval_g,
- long newval, const char *errmsg)
+static void apply_optionset_autocmd(int opt_idx, int opt_flags, OptInt oldval, OptInt oldval_g,
+ OptInt newval, const char *errmsg)
{
// Don't do this while starting up, failure or recursively.
if (starting || errmsg != NULL || *get_vim_var_str(VV_OPTION_TYPE) != NUL) {
@@ -1988,7 +1991,7 @@ static void apply_optionset_autocmd(int opt_idx, long opt_flags, OptInt oldval,
vim_snprintf(buf_old, sizeof(buf_old), "%" PRId64, oldval);
vim_snprintf(buf_old_global, sizeof(buf_old_global), "%" PRId64, oldval_g);
- vim_snprintf(buf_new, sizeof(buf_new), "%ld", newval);
+ vim_snprintf(buf_new, sizeof(buf_new), "%" PRId64, newval);
vim_snprintf(buf_type, sizeof(buf_type), "%s",
(opt_flags & OPT_LOCAL) ? "local" : "global");
set_vim_var_string(VV_OPTION_NEW, buf_new, -1);
@@ -2899,9 +2902,9 @@ static const char *set_bool_option(const int opt_idx, char *const varp, const in
options[opt_idx].flags |= P_WAS_SET;
apply_optionset_autocmd(opt_idx, opt_flags,
- (long)(old_value ? true : false),
- (long)(old_global_value ? true : false),
- (long)(value ? true : false), NULL);
+ (old_value ? true : false),
+ (old_global_value ? true : false),
+ (value ? true : false), NULL);
if (options[opt_idx].flags & P_UI_OPTION) {
ui_call_option_set(cstr_as_string(options[opt_idx].fullname),
@@ -2921,8 +2924,8 @@ static const char *set_bool_option(const int opt_idx, char *const varp, const in
}
/// Check the bounds of numeric options.
-static const char *check_num_option_bounds(OptInt *pp, OptInt old_value, long old_Rows,
- char *errbuf, size_t errbuflen, const char *errmsg)
+static const char *check_num_option_bounds(OptInt *pp, OptInt old_value, int old_Rows, char *errbuf,
+ size_t errbuflen, const char *errmsg)
{
// Check the (new) bounds for Rows and Columns here.
if (p_lines < min_rows() && full_screen) {
@@ -2997,9 +3000,9 @@ static const char *check_num_option_bounds(OptInt *pp, OptInt old_value, long ol
}
/// Options that need some validation.
-static const char *validate_num_option(const OptInt *pp, long *valuep)
+static const char *validate_num_option(const OptInt *pp, OptInt *valuep)
{
- long value = *valuep;
+ OptInt value = *valuep;
// Many number options assume their value is in the signed int range.
if (value < INT_MIN || value > INT_MAX) {
@@ -3157,12 +3160,12 @@ static const char *validate_num_option(const OptInt *pp, long *valuep)
/// @param[in] opt_flags OPT_LOCAL, OPT_GLOBAL or OPT_MODELINE.
///
/// @return NULL on success, error message on error.
-static const char *set_num_option(int opt_idx, void *varp, long value, char *errbuf,
+static const char *set_num_option(int opt_idx, void *varp, OptInt value, char *errbuf,
size_t errbuflen, int opt_flags)
{
OptInt old_value = *(OptInt *)varp;
OptInt old_global_value = 0; // only used when setting a local and global option
- long old_Rows = Rows; // remember old Rows
+ int old_Rows = Rows; // remember old Rows
OptInt *pp = (OptInt *)varp;
// Disallow changing some options from secure mode.
@@ -3184,7 +3187,7 @@ static const char *set_num_option(int opt_idx, void *varp, long value, char *err
return errmsg;
}
- *pp = (OptInt)value;
+ *pp = value;
// Remember where the option was set.
set_option_sctx_idx(opt_idx, opt_flags, current_sctx);
@@ -3195,7 +3198,7 @@ static const char *set_num_option(int opt_idx, void *varp, long value, char *err
.os_varp = varp,
.os_flags = opt_flags,
.os_oldval.number = old_value,
- .os_newval.number = (OptInt)value,
+ .os_newval.number = value,
.os_errbuf = NULL,
.os_errbuflen = 0,
.os_buf = curbuf,
@@ -3215,7 +3218,7 @@ static const char *set_num_option(int opt_idx, void *varp, long value, char *err
options[opt_idx].flags |= P_WAS_SET;
apply_optionset_autocmd(opt_idx, opt_flags, old_value, old_global_value,
- value, errmsg);
+ (int)value, errmsg);
if (errmsg == NULL && options[opt_idx].flags & P_UI_OPTION) {
ui_call_option_set(cstr_as_string(options[opt_idx].fullname),
@@ -3786,7 +3789,7 @@ static const char *set_option(int opt_idx, void *varp, OptVal *v, int opt_flags,
if (v->type == kOptValTypeBoolean) {
errmsg = set_bool_option(opt_idx, varp, (int)v->data.boolean, opt_flags);
} else if (v->type == kOptValTypeNumber) {
- errmsg = set_num_option(opt_idx, varp, (long)v->data.number, errbuf, errbuflen, opt_flags);
+ errmsg = set_num_option(opt_idx, varp, v->data.number, errbuf, errbuflen, opt_flags);
} else if (v->type == kOptValTypeString) {
errmsg = set_string_option(opt_idx, varp, v->data.string.data, opt_flags, &value_checked,
errbuf, errbuflen);
@@ -5305,8 +5308,10 @@ void set_imsearch_global(buf_T *buf)
}
static int expand_option_idx = -1;
+static int expand_option_start_col = 0;
static char expand_option_name[5] = { 't', '_', NUL, NUL, NUL };
static int expand_option_flags = 0;
+static bool expand_option_append = false;
/// @param opt_flags OPT_GLOBAL and/or OPT_LOCAL
void set_context_in_set_cmd(expand_T *xp, char *arg, int opt_flags)
@@ -5405,7 +5410,15 @@ void set_context_in_set_cmd(expand_T *xp, char *arg, int opt_flags)
}
}
// handle "-=" and "+="
+ expand_option_append = false;
+ bool 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 = '=';
}
@@ -5414,28 +5427,51 @@ void set_context_in_set_cmd(expand_T *xp, char *arg, int opt_flags)
xp->xp_context = EXPAND_UNSUCCESSFUL;
return;
}
- if (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.
if (options[opt_idx].var == &p_syn) {
xp->xp_context = EXPAND_OWNSYNTAX;
return;
}
+ if (options[opt_idx].var == &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;
if (p == (char *)&p_bdir
@@ -5451,8 +5487,6 @@ void set_context_in_set_cmd(expand_T *xp, char *arg, int opt_flags)
} else {
xp->xp_backslash = XP_BS_ONE;
}
- } else if (p == (char *)&p_ft) {
- xp->xp_context = EXPAND_FILETYPE;
} else {
xp->xp_context = EXPAND_FILES;
// for 'tags' need three backslashes for a space
@@ -5464,27 +5498,45 @@ void set_context_in_set_cmd(expand_T *xp, char *arg, int opt_flags)
}
}
- // 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--) {
- // count number of backslashes before ' ' or ','
- if (*p == ' ' || *p == ',') {
- char *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)) {
- xp->xp_pattern = p + 1;
- break;
+ // 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)) {
+ for (p = arg + strlen(arg) - 1; p > xp->xp_pattern; p--) {
+ // count number of backslashes before ' ' or ','
+ if (*p == ' ' || *p == ',' || (*p == ':' && (flags & P_COLON))) {
+ char *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;
+ }
}
}
+ }
- // for 'spellsuggest' start at "file:"
- if (options[opt_idx].var == &p_sps
- && strncmp(p, "file:", 5) == 0) {
- xp->xp_pattern = p + 5;
- 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.
+ // for 'spellsuggest' start at "file:"
+ if (options[opt_idx].var == &p_sps) {
+ if (strncmp(xp->xp_pattern, "file:", 5) == 0) {
+ xp->xp_pattern += 5;
+ return;
+ } else if (options[expand_option_idx].opt_expand_cb != NULL) {
+ xp->xp_context = EXPAND_STRING_SETTING;
}
}
}
@@ -5609,7 +5661,33 @@ int ExpandSettings(expand_T *xp, regmatch_T *regmatch, char *fuzzystr, int *numM
return OK;
}
-void ExpandOldSetting(int *numMatches, char ***matches)
+/// 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 *escape_option_str_cmdline(char *var)
+{
+ // A backslash is required before some characters. This is the reverse of
+ // what happens in do_set().
+ char *buf = vim_strsave_escaped(var, escape_chars);
+
+#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((uint8_t)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 ***matches)
{
char *var = NULL;
@@ -5629,26 +5707,149 @@ void ExpandOldSetting(int *numMatches, char ***matches)
var = "";
}
- // A backslash is required before some characters. This is the reverse of
- // what happens in do_set().
- char *buf = vim_strsave_escaped(var, escape_chars);
+ char *buf = escape_option_str_cmdline(var);
-#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((uint8_t)var[2])
- && (var[2] != '\\' || (var == buf && var[4] != '\\'))) {
- STRMOVE(var, var + 1);
+ (*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 ***matches)
+{
+ 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 = {
+ .oe_varp = get_varp_scope(&options[expand_option_idx], expand_option_flags),
+ .oe_append = expand_option_append,
+ .oe_regmatch = regmatch,
+ .oe_xp = xp,
+ .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);
+ char *var = NameBuff;
+ char *buf = escape_option_str_cmdline(var);
+ args.oe_opt_value = buf;
+
+ int num_ret = options[expand_option_idx].opt_expand_cb(&args, numMatches, matches);
+
+ xfree(buf);
+ return num_ret;
+}
+
+/// Expansion handler for :set-=
+int ExpandSettingSubtract(expand_T *xp, regmatch_T *regmatch, int *numMatches, char ***matches)
+{
+ if (expand_option_idx < 0) {
+ // term option
+ return ExpandOldSetting(numMatches, matches);
+ }
+
+ char *option_val = *(char **)get_option_varp_scope_from(expand_option_idx,
+ expand_option_flags,
+ curbuf, curwin);
+
+ uint32_t 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.
+
+ if (*option_val == NUL) {
+ return FAIL;
}
+
+ // Make a copy as we need to inject null characters destructively.
+ char *option_copy = xstrdup(option_val);
+ char *next_val = option_copy;
+
+ garray_T ga;
+ ga_init(&ga, sizeof(char *), 10);
+
+ do {
+ char *item = next_val;
+ char *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 *buf = escape_option_str_cmdline(item);
+ GA_APPEND(char *, &ga, buf);
+ } while (next_val != NULL);
+
+ xfree(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;
+ }
+
+ size_t num_flags = strlen(option_val);
+ if (num_flags == 0) {
+ return FAIL;
+ }
+
+ *matches = xmalloc(sizeof(char *) * (num_flags + 1));
+
+ int count = 0;
+
+ (*matches)[count++] = xstrdup(option_val);
+
+ if (num_flags > 1) {
+ // If more than one flags, split the flags up and expose each
+ // character as individual choice.
+ for (char *flag = option_val; *flag != NUL; flag++) {
+ (*matches)[count++] = xstrnsave(flag, 1);
+ }
+ }
+
+ *numMatches = count;
+ return OK;
}
-#endif
- *matches[0] = buf;
- *numMatches = 1;
+ return ExpandOldSetting(numMatches, matches);
}
/// Get the value for the numeric or string option///opp in a nice format into
@@ -5772,6 +5973,7 @@ int fill_culopt_flags(char *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;
culopt_flags_new |= CULOPT_LINE;