aboutsummaryrefslogtreecommitdiff
path: root/src/nvim
diff options
context:
space:
mode:
authorzeertzjq <zeertzjq@outlook.com>2023-10-01 21:05:12 +0800
committerGitHub <noreply@github.com>2023-10-01 21:05:12 +0800
commitb40c70f2fcaf709d2bf8b3209ae1e2f617d8e852 (patch)
tree489e52a8c585f8f1fccfdddf814ac763ad2b7522 /src/nvim
parent9b3045103f7d56e5ccd0574dcb93e953b72d5f50 (diff)
parent01c51a491330bd10202c73aff92c0978984c0692 (diff)
downloadrneovim-b40c70f2fcaf709d2bf8b3209ae1e2f617d8e852.tar.gz
rneovim-b40c70f2fcaf709d2bf8b3209ae1e2f617d8e852.tar.bz2
rneovim-b40c70f2fcaf709d2bf8b3209ae1e2f617d8e852.zip
Merge pull request #25456 from zeertzjq/vim-9.0.1958
vim-patch:9.0.{1958,1960,1961,1968}: string option completion
Diffstat (limited to 'src/nvim')
-rw-r--r--src/nvim/autocmd.c7
-rw-r--r--src/nvim/cmdexpand.c23
-rw-r--r--src/nvim/cmdexpand.h3
-rw-r--r--src/nvim/diff.c2
-rw-r--r--src/nvim/ex_getln.c1
-rw-r--r--src/nvim/generators/gen_options.lua5
-rw-r--r--src/nvim/highlight_group.c4
-rw-r--r--src/nvim/indent.c1
-rw-r--r--src/nvim/mbyte.c11
-rw-r--r--src/nvim/option.c310
-rw-r--r--src/nvim/option.h9
-rw-r--r--src/nvim/option_defs.h46
-rw-r--r--src/nvim/option_vars.h15
-rw-r--r--src/nvim/options.lua75
-rw-r--r--src/nvim/optionstr.c826
-rw-r--r--src/nvim/optionstr.h1
-rw-r--r--src/nvim/spellsuggest.c1
-rw-r--r--src/nvim/vim.h2
18 files changed, 1178 insertions, 164 deletions
diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c
index 657760914f..a40f7d8c26 100644
--- a/src/nvim/autocmd.c
+++ b/src/nvim/autocmd.c
@@ -2213,6 +2213,13 @@ char *expand_get_event_name(expand_T *xp, int idx)
return event_names[idx - next_augroup_id].name;
}
+/// Function given to ExpandGeneric() to obtain the list of event names. Don't
+/// include groups.
+char *get_event_name_no_group(expand_T *xp FUNC_ATTR_UNUSED, int idx)
+{
+ return event_names[idx].name;
+}
+
/// Check whether given autocommand is supported
///
/// @param[in] event Event to check.
diff --git a/src/nvim/cmdexpand.c b/src/nvim/cmdexpand.c
index d733ffe6ab..893deadd13 100644
--- a/src/nvim/cmdexpand.c
+++ b/src/nvim/cmdexpand.c
@@ -69,9 +69,6 @@
#include "nvim/vim.h"
#include "nvim/window.h"
-/// Type used by ExpandGeneric()
-typedef char *(*CompleteListItemGetter)(expand_T *, int);
-
/// Type used by call_user_expand_func
typedef void *(*user_expand_func_T)(const char *, int, typval_T *);
@@ -107,6 +104,8 @@ static bool cmdline_fuzzy_completion_supported(const expand_T *const xp)
&& xp->xp_context != EXPAND_HELP
&& xp->xp_context != EXPAND_LUA
&& 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
@@ -2599,7 +2598,7 @@ static int ExpandOther(char *pat, expand_T *xp, regmatch_T *rmp, char ***matches
{ EXPAND_MENUNAMES, get_menu_names, false, true },
{ EXPAND_SYNTAX, get_syntax_name, true, true },
{ EXPAND_SYNTIME, get_syntime_arg, true, true },
- { EXPAND_HIGHLIGHT, (ExpandFunc)get_highlight_name, true, false },
+ { EXPAND_HIGHLIGHT, get_highlight_name, true, false },
{ EXPAND_EVENTS, expand_get_event_name, true, false },
{ EXPAND_AUGROUP, expand_get_augroup_name, true, false },
{ EXPAND_SIGN, get_sign_name, true, true },
@@ -2694,8 +2693,7 @@ static int ExpandFromContext(expand_T *xp, char *pat, char ***matches, int *numM
return OK;
}
if (xp->xp_context == EXPAND_OLD_SETTING) {
- ExpandOldSetting(numMatches, matches);
- return OK;
+ return ExpandOldSetting(numMatches, matches);
}
if (xp->xp_context == EXPAND_BUFFERS) {
return ExpandBufnames(pat, numMatches, matches, options);
@@ -2765,6 +2763,10 @@ static int ExpandFromContext(expand_T *xp, char *pat, char ***matches, int *numM
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);
} else if (xp->xp_context == EXPAND_USER_DEFINED) {
@@ -2788,9 +2790,8 @@ static int ExpandFromContext(expand_T *xp, char *pat, char ***matches, int *numM
/// program. Matching strings are copied into an array, which is returned.
///
/// @param func returns a string from the list
-static void ExpandGeneric(const char *const pat, expand_T *xp, regmatch_T *regmatch,
- char ***matches, int *numMatches, CompleteListItemGetter func,
- int escaped)
+void ExpandGeneric(const char *const pat, expand_T *xp, regmatch_T *regmatch, char ***matches,
+ int *numMatches, CompleteListItemGetter func, bool escaped)
{
const bool fuzzy = cmdline_fuzzy_complete(pat);
*matches = NULL;
@@ -2863,6 +2864,7 @@ static void ExpandGeneric(const char *const pat, expand_T *xp, regmatch_T *regma
// in the specified order.
const bool sort_matches = !fuzzy
&& xp->xp_context != EXPAND_MENUNAMES
+ && xp->xp_context != EXPAND_STRING_SETTING
&& xp->xp_context != EXPAND_MENUS
&& xp->xp_context != EXPAND_SCRIPTNAMES;
@@ -3221,8 +3223,7 @@ void globpath(char *path, char *file, garray_T *ga, int expand_options, bool dir
char **p;
int num_p = 0;
- (void)ExpandFromContext(&xpc, buf, &p, &num_p,
- WILD_SILENT | expand_options);
+ (void)ExpandFromContext(&xpc, buf, &p, &num_p, WILD_SILENT | expand_options);
if (num_p > 0) {
ExpandEscape(&xpc, buf, num_p, p, WILD_SILENT | expand_options);
diff --git a/src/nvim/cmdexpand.h b/src/nvim/cmdexpand.h
index 32c23c5d66..f37c693a22 100644
--- a/src/nvim/cmdexpand.h
+++ b/src/nvim/cmdexpand.h
@@ -41,6 +41,9 @@ enum {
BUF_DIFF_FILTER = 0x2000,
};
+/// Type used by ExpandGeneric()
+typedef char *(*CompleteListItemGetter)(expand_T *, int);
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "cmdexpand.h.generated.h"
#endif
diff --git a/src/nvim/diff.c b/src/nvim/diff.c
index 3ab1da76f4..cb76cf17fc 100644
--- a/src/nvim/diff.c
+++ b/src/nvim/diff.c
@@ -2467,6 +2467,7 @@ int diffopt_changed(void)
char *p = p_dip;
while (*p != NUL) {
+ // Note: Keep this in sync with p_dip_values
if (strncmp(p, "filler", 6) == 0) {
p += 6;
diff_flags_new |= DIFF_FILLER;
@@ -2513,6 +2514,7 @@ int diffopt_changed(void)
p += 8;
diff_flags_new |= DIFF_INTERNAL;
} 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) {
p += 5;
diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c
index 6fa607d569..2d1633f312 100644
--- a/src/nvim/ex_getln.c
+++ b/src/nvim/ex_getln.c
@@ -2766,6 +2766,7 @@ int check_opt_wim(void)
}
for (char *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] != ':') {
return FAIL;
diff --git a/src/nvim/generators/gen_options.lua b/src/nvim/generators/gen_options.lua
index 0932a1357f..05def71caa 100644
--- a/src/nvim/generators/gen_options.lua
+++ b/src/nvim/generators/gen_options.lua
@@ -35,6 +35,8 @@ local redraw_flags={
local list_flags={
comma='P_COMMA',
onecomma='P_ONECOMMA',
+ commacolon='P_COMMA|P_COLON',
+ onecommacolon='P_ONECOMMA|P_COLON',
flags='P_FLAGLIST',
flagscomma='P_COMMA|P_FLAGLIST',
}
@@ -166,6 +168,9 @@ local function dump_option(i, o)
if o.cb then
w(' .opt_did_set_cb=' .. o.cb)
end
+ if o.expand_cb then
+ w(' .opt_expand_cb=' .. o.expand_cb)
+ end
if o.enable_if then
w('#else')
w(' .var=NULL')
diff --git a/src/nvim/highlight_group.c b/src/nvim/highlight_group.c
index 080dc79e0e..d2cdcb2516 100644
--- a/src/nvim/highlight_group.c
+++ b/src/nvim/highlight_group.c
@@ -2283,10 +2283,10 @@ static void highlight_list_two(int cnt, int attr)
}
/// Function given to ExpandGeneric() to obtain the list of group names.
-const char *get_highlight_name(expand_T *const xp, int idx)
+char *get_highlight_name(expand_T *const xp, int idx)
FUNC_ATTR_WARN_UNUSED_RESULT
{
- return get_highlight_name_ext(xp, idx, true);
+ return (char *)get_highlight_name_ext(xp, idx, true);
}
/// Obtain a highlight group name.
diff --git a/src/nvim/indent.c b/src/nvim/indent.c
index b7bc23cda5..55235e454c 100644
--- a/src/nvim/indent.c
+++ b/src/nvim/indent.c
@@ -760,6 +760,7 @@ bool briopt_check(win_T *wp)
char *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] == '-' && ascii_isdigit(p[7])) || ascii_isdigit(p[6]))) {
p += 6;
diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c
index 7b7c822b3b..124855fd08 100644
--- a/src/nvim/mbyte.c
+++ b/src/nvim/mbyte.c
@@ -2830,3 +2830,14 @@ void f_charclass(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
}
rettv->vval.v_number = mb_get_class(argvars[0].vval.v_string);
}
+
+/// Function given to ExpandGeneric() to obtain the possible arguments of the
+/// encoding options.
+char *get_encoding_name(expand_T *xp FUNC_ATTR_UNUSED, int idx)
+{
+ if (idx >= (int)ARRAY_SIZE(enc_canon_table)) {
+ return NULL;
+ }
+
+ return (char *)enc_canon_table[idx].name;
+}
diff --git a/src/nvim/option.c b/src/nvim/option.c
index eef5e66aeb..f771760822 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -136,13 +136,6 @@ static char *p_vsts_nopaste;
#define OPTION_COUNT ARRAY_SIZE(options)
-typedef enum {
- OP_NONE = 0,
- OP_ADDING, ///< "opt+=arg"
- OP_PREPENDING, ///< "opt^=arg"
- OP_REMOVING, ///< "opt-=arg"
-} set_op_T;
-
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "option.c.generated.h"
#endif
@@ -888,7 +881,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 +1161,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;
@@ -5305,8 +5298,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 +5400,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 +5417,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 +5477,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 +5488,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 +5651,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 +5697,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 +5963,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;
diff --git a/src/nvim/option.h b/src/nvim/option.h
index 9e3bf25bc3..8417d152e7 100644
--- a/src/nvim/option.h
+++ b/src/nvim/option.h
@@ -7,6 +7,7 @@
#include "nvim/eval/typval_defs.h"
#include "nvim/ex_cmds_defs.h"
#include "nvim/option_defs.h"
+#include "nvim/search.h"
/// The options that are local to a window or buffer have "indir" set to one of
/// these values. Special values:
@@ -45,7 +46,15 @@ typedef struct vimoption {
///< local option: indirect option index
///< callback function to invoke after an option is modified to validate and
///< apply the new value.
+
+ /// 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;
+
void *def_val; ///< default values for variable (neovim!!)
LastSet last_set; ///< script in which the option was last set
} vimoption_T;
diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h
index f078f6073c..dd637aacf5 100644
--- a/src/nvim/option_defs.h
+++ b/src/nvim/option_defs.h
@@ -3,6 +3,7 @@
#include "nvim/api/private/defs.h"
#include "nvim/eval/typval_defs.h"
+#include "nvim/regexp_defs.h"
#include "nvim/types.h"
/// Option value type
@@ -25,6 +26,14 @@ typedef struct {
} data;
} OptVal;
+/// :set operator types
+typedef enum {
+ OP_NONE = 0,
+ OP_ADDING, ///< "opt+=arg"
+ OP_PREPENDING, ///< "opt^=arg"
+ OP_REMOVING, ///< "opt-=arg"
+} set_op_T;
+
/// Argument for the callback function (opt_did_set_cb_T) invoked after an
/// option value is modified.
typedef struct {
@@ -33,6 +42,7 @@ typedef struct {
void *os_varp;
int os_idx;
int os_flags;
+ set_op_T os_op;
/// old value of the option (can be a string, number or a boolean)
union {
@@ -80,4 +90,40 @@ typedef struct {
/// Otherwise returns an error message.
typedef const char *(*opt_did_set_cb_T)(optset_T *args);
+/// Argument for the callback function (opt_expand_cb_T) invoked after a string
+/// option value is expanded for cmdline completion.
+typedef struct {
+ /// Pointer to the option variable. It's always a string.
+ char *oe_varp;
+ /// The original option value, escaped.
+ char *oe_opt_value;
+
+ /// true if using set+= instead of set=
+ bool oe_append;
+ /// true if we would like to add the original option value as the first choice.
+ bool oe_include_orig_val;
+
+ /// Regex from the cmdline, for matching potential options against.
+ regmatch_T *oe_regmatch;
+ /// The expansion context.
+ expand_T *oe_xp;
+
+ /// The full argument passed to :set. For example, if the user inputs
+ /// ":set dip=icase,algorithm:my<Tab>", oe_xp->xp_pattern will only have
+ /// "my", but oe_set_arg will contain the whole "icase,algorithm:my".
+ char *oe_set_arg;
+} optexpand_T;
+
+/// Type for the callback function that is invoked when expanding possible
+/// string option values during cmdline completion.
+///
+/// Strings in returned matches will be managed and freed by caller.
+///
+/// Returns OK if the expansion succeeded (numMatches and matches have to be
+/// set). Otherwise returns FAIL.
+///
+/// Note: If returned FAIL or *numMatches is 0, *matches will NOT be freed by
+/// caller.
+typedef int (*opt_expand_cb_T)(optexpand_T *args, int *numMatches, char ***matches);
+
#endif // NVIM_OPTION_DEFS_H
diff --git a/src/nvim/option_vars.h b/src/nvim/option_vars.h
index d8bbce21b3..86e7f4cee8 100644
--- a/src/nvim/option_vars.h
+++ b/src/nvim/option_vars.h
@@ -16,6 +16,7 @@
///< the same.
#define P_EXPAND 0x10U ///< environment expansion. NOTE: P_EXPAND can
///< never be used for local or hidden options
+#define P_NO_DEF_EXP 0x20U ///< do not expand default value
#define P_NODEFAULT 0x40U ///< don't set to default value
#define P_DEF_ALLOCED 0x80U ///< default value is in allocated memory, must
///< use free() when assigning new value
@@ -51,8 +52,10 @@
#define P_RWINONLY 0x10000000U ///< only redraw current window
#define P_MLE 0x20000000U ///< under control of 'modelineexpr'
#define P_FUNC 0x40000000U ///< accept a function reference or a lambda
-
-#define P_NO_DEF_EXP 0x80000000U ///< Do not expand default value.
+#define P_COLON 0x80000000U ///< values use colons to create sublists
+// Warning: Currently we have used all 32 bits for option flags, and adding more
+// flags will overflow it. Adding another flag will need to change how
+// it's stored first.
#define HIGHLIGHT_INIT \
"8:SpecialKey,~:EndOfBuffer,z:TermCursor,Z:TermCursorNC,@:NonText,d:Directory,e:ErrorMsg," \
@@ -183,7 +186,7 @@
#define CPO_VI "aAbBcCdDeEfFiIJKlLmMnoOpPqrRsStuvWxXyZ$!%+>;_"
// 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
@@ -757,9 +760,9 @@ extern char *p_vfile; ///< 'verbosefile'
EXTERN int p_warn; ///< 'warn'
EXTERN char *p_wop; ///< 'wildoptions'
EXTERN unsigned wop_flags;
-#define WOP_TAGFILE 0x01
-#define WOP_PUM 0x02
-#define WOP_FUZZY 0x04
+#define WOP_FUZZY 0x01
+#define WOP_TAGFILE 0x02
+#define WOP_PUM 0x04
EXTERN OptInt p_window; ///< 'window'
EXTERN char *p_wak; ///< 'winaltkeys'
EXTERN char *p_wig; ///< 'wildignore'
diff --git a/src/nvim/options.lua b/src/nvim/options.lua
index cd1d760836..bae9b56ef4 100644
--- a/src/nvim/options.lua
+++ b/src/nvim/options.lua
@@ -6,7 +6,7 @@
--- @field varname? string
--- @field pv_name? string
--- @field type 'bool'|'number'|'string'
---- @field list? 'comma'|'onecomma'|'flags'|'flagscomma'
+--- @field list? 'comma'|'onecomma'|'commacolon'|'onecommacolon'|'flags'|'flagscomma'
--- @field scope vim.option_scope[]
--- @field deny_duplicates? boolean
--- @field enable_if? string|false
@@ -25,6 +25,7 @@
--- @field alloced? true
--- @field redraw? vim.option_redraw[]
--- @field cb? string
+--- @field expand_cb? string
--- @field tags? string[]
--- @class vim.option_defaults
@@ -192,6 +193,7 @@ return {
set to one of CJK locales. See Unicode Standard Annex #11
(https://www.unicode.org/reports/tr11).
]=],
+ expand_cb = 'expand_set_ambiwidth',
full_name = 'ambiwidth',
redraw = { 'all_windows', 'ui_option' },
scope = { 'global' },
@@ -331,6 +333,7 @@ return {
option, you must load syntax.vim again to see the result. This can be
done with ":syntax on".
]=],
+ expand_cb = 'expand_set_background',
full_name = 'background',
scope = { 'global' },
short_desc = N_('"dark" or "light", used for highlight colors'),
@@ -357,6 +360,7 @@ return {
When the value is empty, Vi compatible backspacing is used, none of