diff options
author | zeertzjq <zeertzjq@outlook.com> | 2023-09-30 08:13:58 +0800 |
---|---|---|
committer | zeertzjq <zeertzjq@outlook.com> | 2023-10-01 20:00:23 +0800 |
commit | f06af5e66981095f3244f67d1587ce7e9853eb4c (patch) | |
tree | bfd07406c05904fc540e08456d7f3e63543a2003 /src | |
parent | 9b3045103f7d56e5ccd0574dcb93e953b72d5f50 (diff) | |
download | rneovim-f06af5e66981095f3244f67d1587ce7e9853eb4c.tar.gz rneovim-f06af5e66981095f3244f67d1587ce7e9853eb4c.tar.bz2 rneovim-f06af5e66981095f3244f67d1587ce7e9853eb4c.zip |
vim-patch:9.0.1958: cannot complete option values
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: vim/vim#13182
https://github.com/vim/vim/commit/900894b09a95398dfc75599e9f0aa2ea25723384
Co-authored-by: Yee Cheng Chin <ychin.git@gmail.com>
Diffstat (limited to 'src')
-rw-r--r-- | src/nvim/autocmd.c | 7 | ||||
-rw-r--r-- | src/nvim/cmdexpand.c | 21 | ||||
-rw-r--r-- | src/nvim/cmdexpand.h | 3 | ||||
-rw-r--r-- | src/nvim/diff.c | 2 | ||||
-rw-r--r-- | src/nvim/ex_getln.c | 1 | ||||
-rw-r--r-- | src/nvim/generators/gen_options.lua | 5 | ||||
-rw-r--r-- | src/nvim/indent.c | 1 | ||||
-rw-r--r-- | src/nvim/mbyte.c | 11 | ||||
-rw-r--r-- | src/nvim/option.c | 307 | ||||
-rw-r--r-- | src/nvim/option.h | 9 | ||||
-rw-r--r-- | src/nvim/option_defs.h | 37 | ||||
-rw-r--r-- | src/nvim/option_vars.h | 12 | ||||
-rw-r--r-- | src/nvim/options.lua | 66 | ||||
-rw-r--r-- | src/nvim/optionstr.c | 650 | ||||
-rw-r--r-- | src/nvim/optionstr.h | 1 | ||||
-rw-r--r-- | src/nvim/spellsuggest.c | 1 | ||||
-rw-r--r-- | src/nvim/vim.h | 2 |
17 files changed, 1047 insertions, 89 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..3969162b6e 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 @@ -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, ®match, pat, numMatches, matches, fuzzy); + } else if (xp->xp_context == EXPAND_STRING_SETTING) { + ret = ExpandStringSetting(xp, ®match, numMatches, matches); + } else if (xp->xp_context == EXPAND_SETTING_SUBTRACT) { + ret = ExpandSettingSubtract(xp, ®match, numMatches, matches); } else if (xp->xp_context == EXPAND_MAPPINGS) { ret = ExpandMappings(pat, ®match, 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/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..89e797d580 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -888,7 +888,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 @@ -5305,8 +5305,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 +5407,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 +5424,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 +5484,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 +5495,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; } } } @@ -5503,9 +5552,9 @@ void set_context_in_set_cmd(expand_T *xp, char *arg, int opt_flags) /// /// 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 bool match_str(char *const str, regmatch_T *const regmatch, char **const matches, - const int idx, const bool test_only, const bool fuzzy, - const char *const fuzzystr, fuzmatch_str_T *const fuzmatch) +bool match_str(char *const str, regmatch_T *const regmatch, char **const matches, const int idx, + const bool test_only, const bool fuzzy, const char *const fuzzystr, + fuzmatch_str_T *const fuzmatch) { if (!fuzzy) { if (vim_regexec(regmatch, str, (colnr_T)0)) { @@ -5609,7 +5658,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 +5704,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 +5970,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..7820cbaf4a 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 @@ -80,4 +81,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..13caba221f 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,7 @@ #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 #define HIGHLIGHT_INIT \ "8:SpecialKey,~:EndOfBuffer,z:TermCursor,Z:TermCursorNC,@:NonText,d:Directory,e:ErrorMsg," \ @@ -183,7 +183,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 +757,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..fd7e1586ac 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 the ways mentioned for the items above are possible. ]=], + expand_cb = 'expand_set_backspace', full_name = 'backspace', list = 'onecomma', scope = { 'global' }, @@ -453,6 +457,7 @@ return { the system may refuse to do this. In that case the "auto" value will again not rename the file. ]=], + expand_cb = 'expand_set_backupcopy', full_name = 'backupcopy', list = 'onecomma', scope = { 'global', 'buffer' }, @@ -621,6 +626,7 @@ return { indicate that an error occurred. It can be silenced by adding the "error" keyword. ]=], + expand_cb = 'expand_set_belloff', full_name = 'belloff', list = 'comma', scope = { 'global' }, @@ -763,6 +769,7 @@ return { added for the 'showbreak' setting. (default: off) ]=], + expand_cb = 'expand_set_breakindentopt', full_name = 'breakindentopt', list = 'onecomma', redraw = { 'current_buffer' }, @@ -816,6 +823,7 @@ return { This option is used together with 'buftype' and 'swapfile' to specify special kinds of buffers. See |special-buffers|. ]=], + expand_cb = 'expand_set_bufhidden', full_name = 'bufhidden', noglob = true, scope = { 'buffer' }, @@ -893,6 +901,7 @@ return { without saving. For writing there must be matching |BufWriteCmd|, |FileWriteCmd| or |FileAppendCmd| autocommands. ]=], + expand_cb = 'expand_set_buftype', full_name = 'buftype', noglob = true, scope = { 'buffer' }, @@ -917,6 +926,7 @@ return { case mapping, the current locale is not effective. This probably only matters for Turkish. ]=], + expand_cb = 'expand_set_casemap', full_name = 'casemap', list = 'onecomma', scope = { 'global' }, @@ -1183,6 +1193,7 @@ return { "*". See |clipboard|. ]=], deny_duplicates = true, + expand_cb = 'expand_set_clipboard', full_name = 'clipboard', list = 'onecomma', scope = { 'global' }, @@ -1369,6 +1380,7 @@ return { based expansion (e.g., dictionary |i_CTRL-X_CTRL-K|, included patterns |i_CTRL-X_CTRL-I|, tags |i_CTRL-X_CTRL-]| and normal expansions). ]=], + expand_cb = 'expand_set_complete', full_name = 'complete', list = 'onecomma', scope = { 'buffer' }, @@ -1399,7 +1411,9 @@ return { Keep in mind that the cursor position is not always where it's displayed. E.g., when moving vertically it may change column. ]=], + expand_cb = 'expand_set_concealcursor', full_name = 'concealcursor', + list = 'flags', redraw = { 'current_window' }, scope = { 'window' }, short_desc = N_('whether concealable text is hidden in cursor line'), @@ -1492,6 +1506,7 @@ return { select one from the menu. Only works in combination with "menu" or "menuone". ]=], + expand_cb = 'expand_set_completeopt', full_name = 'completeopt', list = 'onecomma', scope = { 'global' }, @@ -1517,6 +1532,7 @@ return { command line completion the global value is used. ]=], enable_if = 'BACKSLASH_IN_FILENAME', + expand_cb = 'expand_set_completeslash', full_name = 'completeslash', scope = { 'buffer' }, type = 'string', @@ -1797,6 +1813,7 @@ return { _ When using |cw| on a word, do not include the whitespace following the word in the motion. ]=], + expand_cb = 'expand_set_cpoptions', full_name = 'cpoptions', list = 'flags', redraw = { 'all_windows' }, @@ -1878,6 +1895,7 @@ return { "line" and "screenline" cannot be used together. ]=], + expand_cb = 'expand_set_cursorlineopt', full_name = 'cursorlineopt', list = 'onecomma', redraw = { 'current_window_only' }, @@ -1900,6 +1918,7 @@ return { "msg" and "throw" are useful for debugging 'foldexpr', 'formatexpr' or 'indentexpr'. ]=], + expand_cb = 'expand_set_debug', full_name = 'debug', scope = { 'global' }, short_desc = N_('to "msg" to see all error messages'), @@ -2140,8 +2159,9 @@ return { :set diffopt-=internal " do NOT use the internal diff parser < ]=], + expand_cb = 'expand_set_diffopt', full_name = 'diffopt', - list = 'onecomma', + list = 'onecommacolon', redraw = { 'current_window' }, scope = { 'global' }, short_desc = N_('options for using diff mode'), @@ -2242,6 +2262,7 @@ return { The "@" character can be changed by setting the "lastline" item in 'fillchars'. The character is highlighted with |hl-NonText|. ]=], + expand_cb = 'expand_set_display', full_name = 'display', list = 'onecomma', redraw = { 'all_windows' }, @@ -2260,6 +2281,7 @@ return { hor horizontally, height of windows is not affected both width and height of windows is affected ]=], + expand_cb = 'expand_set_eadirection', full_name = 'eadirection', scope = { 'global' }, short_desc = N_("in which direction 'equalalways' works"), @@ -2470,6 +2492,7 @@ return { :set ei=WinEnter,WinLeave < ]=], + expand_cb = 'expand_set_eventignore', full_name = 'eventignore', list = 'onecomma', scope = { 'global' }, @@ -2558,6 +2581,7 @@ return { This option cannot be changed when 'modifiable' is off. ]=], + expand_cb = 'expand_set_encoding', full_name = 'fileencoding', no_mkrc = true, redraw = { 'statuslines', 'current_buffer' }, @@ -2619,6 +2643,7 @@ return { Setting this option does not have an effect until the next time a file is read. ]=], + expand_cb = 'expand_set_encoding', full_name = 'fileencodings', list = 'onecomma', scope = { 'global' }, @@ -2651,6 +2676,7 @@ return { option is set, because the file would be different when written. This option cannot be changed when 'modifiable' is off. ]=], + expand_cb = 'expand_set_fileformat', full_name = 'fileformat', no_mkrc = true, redraw = { 'curswant', 'statuslines' }, @@ -2714,6 +2740,7 @@ return { used. Also see |file-formats|. ]=], + expand_cb = 'expand_set_fileformat', full_name = 'fileformats', list = 'onecomma', scope = { 'global' }, @@ -2846,6 +2873,7 @@ return { eob EndOfBuffer |hl-EndOfBuffer| lastline NonText |hl-NonText| ]=], + expand_cb = 'expand_set_chars_option', full_name = 'fillchars', list = 'onecomma', redraw = { 'current_window' }, @@ -2884,6 +2912,7 @@ return { its level is higher than 'foldlevel'. Useful if you want folds to automatically close when moving out of them. ]=], + expand_cb = 'expand_set_foldclose', full_name = 'foldclose', list = 'onecomma', redraw = { 'current_window' }, @@ -3045,6 +3074,7 @@ return { |fold-syntax| syntax Syntax highlighting items specify folds. |fold-diff| diff Fold text that is not changed. ]=], + expand_cb = 'expand_set_foldmethod', full_name = 'foldmethod', redraw = { 'current_window' }, scope = { 'window' }, @@ -3122,6 +3152,7 @@ return { To close folds you can re-apply 'foldlevel' with the |zx| command or set the 'foldclose' option to "all". ]=], + expand_cb = 'expand_set_foldopen', full_name = 'foldopen', list = 'onecomma', redraw = { 'curswant' }, @@ -3218,6 +3249,7 @@ return { To avoid problems with flags that are added in the future, use the "+=" and "-=" feature of ":set" |add-option-flags|. ]=], + expand_cb = 'expand_set_formatoptions', full_name = 'formatoptions', list = 'flags', scope = { 'buffer' }, @@ -4451,6 +4483,7 @@ return { |alternate-file| or using |mark-motions| try to restore the |mark-view| in which the action occurred. ]=], + expand_cb = 'expand_set_jumpoptions', full_name = 'jumpoptions', list = 'onecomma', scope = { 'global' }, @@ -4495,6 +4528,7 @@ return { Special keys in this context are the cursor keys, <End>, <Home>, <PageUp> and <PageDown>. ]=], + expand_cb = 'expand_set_keymodel', full_name = 'keymodel', list = 'onecomma', scope = { 'global' }, @@ -4779,6 +4813,7 @@ return { Note that when using 'indentexpr' the `=` operator indents all the lines, otherwise the first line is not indented (Vi-compatible). ]=], + expand_cb = 'expand_set_lispoptions', full_name = 'lispoptions', list = 'onecomma', pv_name = 'p_lop', @@ -4933,6 +4968,7 @@ return { "precedes". |hl-Whitespace| for "nbsp", "space", "tab", "multispace", "lead" and "trail". ]=], + expand_cb = 'expand_set_chars_option', full_name = 'listchars', list = 'onecomma', redraw = { 'current_window' }, @@ -5015,6 +5051,7 @@ return { :set makeencoding=char " system locale is used < ]=], + expand_cb = 'expand_set_encoding', full_name = 'makeencoding', scope = { 'global', 'buffer' }, short_desc = N_('Converts the output of external commands'), @@ -5377,6 +5414,7 @@ return { 'mousehide' hide mouse pointer while typing text 'selectmode' whether to start Select mode or Visual mode ]=], + expand_cb = 'expand_set_mouse', full_name = 'mouse', list = 'flags', scope = { 'global' }, @@ -5468,6 +5506,7 @@ return { "g<LeftMouse>" is "<C-LeftMouse> (jump to tag under mouse click) "g<RightMouse>" is "<C-RightMouse> ("CTRL-T") ]=], + expand_cb = 'expand_set_mousemodel', full_name = 'mousemodel', scope = { 'global' }, short_desc = N_('changes meaning of mouse buttons'), @@ -5645,6 +5684,7 @@ return { considered decimal. This also happens for numbers that are not recognized as octal or hex. ]=], + expand_cb = 'expand_set_nrformats', full_name = 'nrformats', list = 'onecomma', scope = { 'buffer' }, @@ -6307,6 +6347,7 @@ return { This is useful for languages such as Hebrew, Arabic and Farsi. The 'rightleft' option must be set for 'rightleftcmd' to take effect. ]=], + expand_cb = 'expand_set_rightleftcmd', full_name = 'rightleftcmd', redraw = { 'current_window' }, scope = { 'window' }, @@ -6634,6 +6675,7 @@ return { When 'diff' mode is active there always is vertical scroll binding, even when "ver" isn't there. ]=], + expand_cb = 'expand_set_scrollopt', full_name = 'scrollopt', list = 'onecomma', scope = { 'global' }, @@ -6687,6 +6729,7 @@ return { backwards, you cannot include the last character of a line, when starting in Normal mode and 'virtualedit' empty. ]=], + expand_cb = 'expand_set_selection', full_name = 'selection', scope = { 'global' }, short_desc = N_('what type of selection to use'), @@ -6707,6 +6750,7 @@ return { cmd when using "v", "V" or CTRL-V See |Select-mode|. ]=], + expand_cb = 'expand_set_selectmode', full_name = 'selectmode', list = 'onecomma', scope = { 'global' }, @@ -6758,6 +6802,7 @@ return { If you leave out "options" many things won't work well after restoring the session. ]=], + expand_cb = 'expand_set_sessionoptions', full_name = 'sessionoptions', list = 'onecomma', scope = { 'global' }, @@ -7292,6 +7337,7 @@ return { shm=a Abbreviation, but no loss of information. shm=at Abbreviation, and truncate message when necessary. ]=], + expand_cb = 'expand_set_shortmess', full_name = 'shortmess', list = 'flags', scope = { 'global' }, @@ -7368,6 +7414,7 @@ return { place the text. Without a custom 'statusline' or 'tabline' it will be displayed in a convenient location. ]=], + expand_cb = 'expand_set_showcmdloc', full_name = 'showcmdloc', scope = { 'global' }, short_desc = N_('change location of partial command'), @@ -7530,6 +7577,7 @@ return { This is done in order for the signcolumn appearance not appear weird during line deletion. ]=], + expand_cb = 'expand_set_signcolumn', full_name = 'signcolumn', redraw = { 'current_window' }, scope = { 'window' }, @@ -7828,6 +7876,7 @@ return { security reasons. ]=], expand = true, + expand_cb = 'expand_set_spellsuggest', full_name = 'spellsuggest', list = 'onecomma', scope = { 'global' }, @@ -7852,7 +7901,7 @@ return { designated regions of the buffer are spellchecked in this case. ]=], - expand = true, + expand_cb = 'expand_set_spelloptions', full_name = 'spelloptions', list = 'onecomma', redraw = { 'current_buffer' }, @@ -7892,6 +7941,7 @@ return { with the previous cursor position. For "screen", the text cannot always be kept on the same screen line when 'wrap' is enabled. ]=], + expand_cb = 'expand_set_splitkeep', full_name = 'splitkeep', scope = { 'global' }, short_desc = N_('determines scroll behavior for split windows'), @@ -8326,6 +8376,7 @@ return { uselast If included, jump to the previously used window when jumping to errors with |quickfix| commands. ]=], + expand_cb = 'expand_set_switchbuf', full_name = 'switchbuf', list = 'onecomma', scope = { 'global' }, @@ -8580,6 +8631,7 @@ return { match Match case smart Ignore case unless an upper case letter is used ]=], + expand_cb = 'expand_set_tagcase', full_name = 'tagcase', scope = { 'global', 'buffer' }, short_desc = N_('how to handle case when searching in tags files'), @@ -9275,6 +9327,7 @@ return { slash |deprecated| Always enabled. Uses "/" in filenames. unix |deprecated| Always enabled. Uses "\n" line endings. ]=], + expand_cb = 'expand_set_sessionoptions', full_name = 'viewoptions', list = 'onecomma', scope = { 'global' }, @@ -9331,6 +9384,7 @@ return { not get a warning for it. When combined with other words, "none" is ignored. ]=], + expand_cb = 'expand_set_virtualedit', full_name = 'virtualedit', list = 'onecomma', redraw = { 'curswant' }, @@ -9395,6 +9449,7 @@ return { line (not an empty line) then it will not move to the next line. This makes "dl", "cl", "yl" etc. work normally. ]=], + expand_cb = 'expand_set_whichwrap', full_name = 'whichwrap', list = 'flagscomma', scope = { 'global' }, @@ -9578,8 +9633,9 @@ return { < Complete longest common string, then list alternatives. More info here: |cmdline-completion|. ]=], + expand_cb = 'expand_set_wildmode', full_name = 'wildmode', - list = 'onecomma', + list = 'onecommacolon', scope = { 'global' }, short_desc = N_("mode for 'wildchar' command-line expansion"), type = 'string', @@ -9609,6 +9665,7 @@ return { d #define f function ]=], + expand_cb = 'expand_set_wildoptions', full_name = 'wildoptions', list = 'onecomma', scope = { 'global' }, @@ -9637,6 +9694,7 @@ return { key is never used for the menu. This option is not used for <F10>; on Win32. ]=], + expand_cb = 'expand_set_winaltkeys', full_name = 'winaltkeys', scope = { 'global' }, short_desc = N_('when the windows system handles ALT keys'), diff --git a/src/nvim/optionstr.c b/src/nvim/optionstr.c index 750941da07..f820a9fc49 100644 --- a/src/nvim/optionstr.c +++ b/src/nvim/optionstr.c @@ -11,6 +11,7 @@ #include "nvim/autocmd.h" #include "nvim/buffer_defs.h" #include "nvim/charset.h" +#include "nvim/cmdexpand.h" #include "nvim/cursor.h" #include "nvim/cursor_shape.h" #include "nvim/diff.h" @@ -41,6 +42,7 @@ #include "nvim/optionstr.h" #include "nvim/os/os.h" #include "nvim/pos.h" +#include "nvim/regexp.h" #include "nvim/runtime.h" #include "nvim/spell.h" #include "nvim/spellfile.h" @@ -72,12 +74,25 @@ static char *(p_bkc_values[]) = { "yes", "auto", "no", "breaksymlink", "breakhar static char *(p_bo_values[]) = { "all", "backspace", "cursor", "complete", "copy", "ctrlg", "error", "esc", "ex", "hangul", "lang", "mess", "showmatch", "operator", "register", "shell", "spell", "wildmode", NULL }; +// Note: Keep this in sync with briopt_check() +static char *(p_briopt_values[]) = { "shift:", "min:", "sbr", "list:", "column:", NULL }; +// Note: Keep this in sync with diffopt_changed() +static char *(p_dip_values[]) = { "filler", "context:", "iblank", "icase", + "iwhite", "iwhiteall", "iwhiteeol", "horizontal", "vertical", + "closeoff", "hiddenoff", "foldcolumn:", "followwrap", "internal", + "indent-heuristic", "linematch:", "algorithm:", NULL }; +static char *(p_dip_algorithm_values[]) = { "myers", "minimal", "patience", "histogram", NULL }; static char *(p_nf_values[]) = { "bin", "octal", "hex", "alpha", "unsigned", NULL }; static char *(p_ff_values[]) = { FF_UNIX, FF_DOS, FF_MAC, NULL }; +static char *(p_cb_values[]) = { "unnamed", "unnamedplus", NULL }; static char *(p_cmp_values[]) = { "internal", "keepascii", NULL }; +// Note: Keep this in sync with fill_culopt_flags() +static char *(p_culopt_values[]) = { "line", "screenline", "number", "both", NULL }; static char *(p_dy_values[]) = { "lastline", "truncate", "uhex", "msgsep", NULL }; static char *(p_fdo_values[]) = { "all", "block", "hor", "mark", "percent", "quickfix", "search", "tag", "insert", "undo", "jump", NULL }; +// Note: Keep this in sync with spell_check_sps() +static char *(p_sps_values[]) = { "best", "fast", "double", "expr:", "file:", "timeout:", NULL }; /// Also used for 'viewoptions'! Keep in sync with SSOP_ flags. static char *(p_ssop_values[]) = { "buffers", "winpos", "resize", "winsize", "localoptions", "options", "help", "blank", "globals", "slash", "unix", "sesdir", @@ -89,7 +104,9 @@ static char *(p_swb_values[]) = { "useopen", "usetab", "split", "newtab", "vspli static char *(p_spk_values[]) = { "cursor", "screen", "topline", NULL }; static char *(p_tc_values[]) = { "followic", "ignore", "match", "followscs", "smart", NULL }; static char *(p_ve_values[]) = { "block", "insert", "all", "onemore", "none", "NONE", NULL }; -static char *(p_wop_values[]) = { "tagfile", "pum", "fuzzy", NULL }; +// Note: Keep this in sync with check_opt_wim() +static char *(p_wim_values[]) = { "full", "longest", "list", "lastused", NULL }; +static char *(p_wop_values[]) = { "fuzzy", "tagfile", "pum", NULL }; static char *(p_wak_values[]) = { "yes", "menu", "no", NULL }; static char *(p_mousem_values[]) = { "extend", "popup", "popup_setpos", "mac", NULL }; static char *(p_sel_values[]) = { "inclusive", "exclusive", "old", NULL }; @@ -118,7 +135,6 @@ static char *(p_scl_values[]) = { "yes", "no", "auto", "auto:1", "auto:2", "auto static char *(p_fdc_values[]) = { "auto", "auto:1", "auto:2", "auto:3", "auto:4", "auto:5", "auto:6", "auto:7", "auto:8", "auto:9", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", NULL }; -static char *(p_cb_values[]) = { "unnamed", "unnamedplus", NULL }; static char *(p_spo_values[]) = { "camel", "noplainbuffer", NULL }; static char *(p_icm_values[]) = { "nosplit", "split", NULL }; static char *(p_jop_values[]) = { "stack", "view", NULL }; @@ -659,6 +675,116 @@ static const char *did_set_option_listflag(char *val, char *flags, char *errbuf, return NULL; } +/// Expand an option that accepts a list of string values. +int expand_set_opt_string(optexpand_T *args, char **values, size_t numValues, int *numMatches, + char ***matches) +{ + regmatch_T *regmatch = args->oe_regmatch; + bool include_orig_val = args->oe_include_orig_val; + char *option_val = args->oe_opt_value; + + // Assume numValues is small since they are fixed enums, so just allocate + // upfront instead of needing two passes to calculate output size. + *matches = xmalloc(sizeof(char *) * (numValues + 1)); + + int count = 0; + + if (include_orig_val && *option_val != NUL) { + (*matches)[count++] = xstrdup(option_val); + } + + for (char **val = values; *val != NULL; val++) { + if (include_orig_val && *option_val != NUL) { + if (strcmp(*val, option_val) == 0) { + continue; + } + } + if (vim_regexec(regmatch, *val, (colnr_T)0)) { + (*matches)[count++] = xstrdup(*val); + } + } + if (count == 0) { + XFREE_CLEAR(*matches); + return FAIL; + } + *numMatches = count; + return OK; +} + +static char *set_opt_callback_orig_option = NULL; +static char *((*set_opt_callback_func)(expand_T *, int)); + +/// Callback used by expand_set_opt_generic to also include the original value. +static char *expand_set_opt_callback(expand_T *xp, int idx) +{ + if (idx == 0) { + if (set_opt_callback_orig_option != NULL) { + return set_opt_callback_orig_option; + } else { + return ""; // empty strings are ignored + } + } + return set_opt_callback_func(xp, idx - 1); +} + +/// Expand an option with a callback that iterates through a list of possible names. +int expand_set_opt_generic(optexpand_T *args, CompleteListItemGetter func, int *numMatches, + char ***matches) +{ + set_opt_callback_orig_option = args->oe_include_orig_val ? args->oe_opt_value : NULL; + set_opt_callback_func = func; + + // not using fuzzy as currently EXPAND_STRING_SETTING doesn't use it + ExpandGeneric("", args->oe_xp, args->oe_regmatch, matches, numMatches, + expand_set_opt_callback, false); + + set_opt_callback_orig_option = NULL; + set_opt_callback_func = NULL; + return OK; +} + +/// Expand an option which is a list of flags. +int expand_set_opt_listflag(optexpand_T *args, char *flags, int *numMatches, char ***matches) +{ + char *option_val = args->oe_opt_value; + char *cmdline_val = args->oe_set_arg; + bool append = args->oe_append; + bool include_orig_val = args->oe_include_orig_val && (*option_val != NUL); + + size_t num_flags = strlen(flags); + + // Assume we only have small number of flags, so just allocate max size. + *matches = xmalloc(sizeof(char *) * (num_flags + 1)); + + int count = 0; + + if (include_orig_val) { + (*matches)[count++] = xstrdup(option_val); + } + + for (char *flag = flags; *flag != NUL; flag++) { + if (append && vim_strchr(option_val, *flag) != NULL) { + continue; + } + + if (vim_strchr(cmdline_val, *flag) == NULL) { + if (include_orig_val && option_val[1] == NUL && *flag == option_val[0]) { + // This value is already used as the first choice as it's the + // existing flag. Just skip it to avoid duplicate. + continue; + } + (*matches)[count++] = xstrnsave(flag, 1); + } + } + + if (count == 0) { + XFREE_CLEAR(*matches); + return FAIL; + } + *numMatches = count; + return OK; +} + /// The 'ambiwidth' option is changed. const char *did_set_ambiwidth(optset_T *args FUNC_ATTR_UNUSED) { @@ -668,6 +794,15 @@ const char *did_set_ambiwidth(optset_T *args FUNC_ATTR_UNUSED) return check_chars_options(); } +int expand_set_ambiwidth(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_string(args, + p_ambw_values, + ARRAY_SIZE(p_ambw_values) - 1, + numMatches, + matches); +} + /// The 'background' option is changed. const char *did_set_background(optset_T *args FUNC_ATTR_UNUSED) { @@ -692,6 +827,15 @@ const char *did_set_background(optset_T *args FUNC_ATTR_UNUSED) return NULL; } +int expand_set_background(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_string(args, + p_bg_values, + ARRAY_SIZE(p_bg_values) - 1, + numMatches, + matches); +} + /// The 'backspace' option is changed. const char *did_set_backspace(optset_T *args FUNC_ATTR_UNUSED) { @@ -705,6 +849,15 @@ const char *did_set_backspace(optset_T *args FUNC_ATTR_UNUSED) return NULL; } +int expand_set_backspace(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_string(args, + p_bs_values, + ARRAY_SIZE(p_bs_values) - 1, + numMatches, + matches); +} + /// The 'backupcopy' option is changed. const char *did_set_backupcopy(optset_T *args) { @@ -739,6 +892,15 @@ const char *did_set_backupcopy(optset_T *args) return NULL; } +int expand_set_backupcopy(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_string(args, + p_bkc_values, + ARRAY_SIZE(p_bkc_values) - 1, + numMatches, + matches); +} + /// The 'backupext' or the 'patchmode' option is changed. const char *did_set_backupext_or_patchmode(optset_T *args FUNC_ATTR_UNUSED) { @@ -756,6 +918,15 @@ const char *did_set_belloff(optset_T *args FUNC_ATTR_UNUSED) return did_set_opt_flags(p_bo, p_bo_values, &bo_flags, true); } +int expand_set_belloff(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_string(args, + p_bo_values, + ARRAY_SIZE(p_bo_values) - 1, + numMatches, + matches); +} + /// The 'breakindentopt' option is changed. const char *did_set_breakindentopt(optset_T *args) { @@ -771,6 +942,15 @@ const char *did_set_breakindentopt(optset_T *args) return NULL; } +int expand_set_breakindentopt(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_string(args, + p_briopt_values, + ARRAY_SIZE(p_briopt_values) - 1, + numMatches, + matches); +} + /// The 'bufhidden' option is changed. const char *did_set_bufhidden(optset_T *args) { @@ -778,6 +958,15 @@ const char *did_set_bufhidden(optset_T *args) return did_set_opt_strings(buf->b_p_bh, p_bufhidden_values, false); } +int expand_set_bufhidden(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_string(args, + p_bufhidden_values, + ARRAY_SIZE(p_bufhidden_values) - 1, + numMatches, + matches); +} + /// The 'buftype' option is changed. const char *did_set_buftype(optset_T *args) { @@ -798,12 +987,30 @@ const char *did_set_buftype(optset_T *args) return NULL; } +int expand_set_buftype(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_string(args, + p_buftype_values, + ARRAY_SIZE(p_buftype_values) - 1, + numMatches, + matches); +} + /// The 'casemap' option is changed. const char *did_set_casemap(optset_T *args FUNC_ATTR_UNUSED) { return did_set_opt_flags(p_cmp, p_cmp_values, &cmp_flags, true); } +int expand_set_casemap(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_string(args, + p_cmp_values, + ARRAY_SIZE(p_cmp_values) - 1, + numMatches, + matches); +} + /// The global 'listchars' or 'fillchars' option is changed. static const char *did_set_global_listfillchars(win_T *win, char *val, bool opt_lcs, int opt_flags) { @@ -867,6 +1074,17 @@ const char *did_set_chars_option(optset_T *args) return errmsg; } +/// Expand 'fillchars' or 'listchars' option value. +int expand_set_chars_option(optexpand_T *args, int *numMatches, char ***matches) +{ + char **varp = (char **)args->oe_varp; + bool is_lcs = (varp == &p_lcs || varp == &curwin->w_p_lcs); + return expand_set_opt_generic(args, + is_lcs ? get_listchars_name : get_fillchars_name, + numMatches, + matches); +} + /// The 'cinoptions' option is changed. const char *did_set_cinoptions(optset_T *args FUNC_ATTR_UNUSED) { @@ -882,6 +1100,15 @@ const char *did_set_clipboard(optset_T *args FUNC_ATTR_UNUSED) return did_set_opt_flags(p_cb, p_cb_values, &cb_flags, true); } +int expand_set_clipboard(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_string(args, + p_cb_values, + ARRAY_SIZE(p_cb_values) - 1, + numMatches, + matches); +} + /// The 'colorcolumn' option is changed. const char *did_set_colorcolumn(optset_T *args) { @@ -972,6 +1199,18 @@ const char *did_set_complete(optset_T *args) return NULL; } +int expand_set_complete(optexpand_T *args, int *numMatches, char ***matches) +{ + static char *(p_cpt_values[]) = { + ".", "w", "b", "u", "k", "kspell", "s", "i", "d", "]", "t", "U", NULL + }; + return expand_set_opt_string(args, + p_cpt_values, + ARRAY_SIZE(p_cpt_values) - 1, + numMatches, + matches); +} + /// The 'completeopt' option is changed. const char *did_set_completeopt(optset_T *args FUNC_ATTR_UNUSED) { @@ -982,6 +1221,15 @@ const char *did_set_completeopt(optset_T *args FUNC_ATTR_UNUSED) return NULL; } +int expand_set_completeopt(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_string(args, + p_cot_values, + ARRAY_SIZE(p_cot_values) - 1, + numMatches, + matches); +} + #ifdef BACKSLASH_IN_FILENAME /// The 'completeslash' option is changed. const char *did_set_completeslash(optset_T *args) @@ -993,6 +1241,15 @@ const char *did_set_completeslash(optset_T *args) } return NULL; } + +int expand_set_completeslash(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_string(args, + p_csl_values, + ARRAY_SIZE(p_csl_values) - 1, + numMatches, + matches); +} #endif /// The 'concealcursor' option is changed. @@ -1003,6 +1260,11 @@ const char *did_set_concealcursor(optset_T *args) return did_set_option_listflag(*varp, COCU_ALL, args->os_errbuf, args->os_errbuflen); } +int expand_set_concealcursor(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_listflag(args, COCU_ALL, numMatches, matches); +} + /// The 'cpoptions' option is changed. const char *did_set_cpoptions(optset_T *args) { @@ -1011,12 +1273,18 @@ const char *did_set_cpoptions(optset_T *args) return did_set_option_listflag(*varp, CPO_VI, args->os_errbuf, args->os_errbuflen); } +int expand_set_cpoptions(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_listflag(args, CPO_VI, numMatches, matches); +} + /// The 'cursorlineopt' option is changed. const char *did_set_cursorlineopt(optset_T *args) { win_T *win = (win_T *)args->os_win; char **varp = (char **)args->os_varp; + // This could be changed to use opt_strings_flags() instead. if (**varp == NUL || fill_culopt_flags(*varp, win) != OK) { return e_invarg; } @@ -1024,12 +1292,30 @@ const char *did_set_cursorlineopt(optset_T *args) return NULL; } +int expand_set_cursorlineopt(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_string(args, + p_culopt_values, + ARRAY_SIZE(p_culopt_values) - 1, + numMatches, + matches); +} + /// The 'debug' option is changed. const char *did_set_debug(optset_T *args FUNC_ATTR_UNUSED) { return did_set_opt_strings(p_debug, p_debug_values, false); } +int expand_set_debug(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_string(args, + p_debug_values, + ARRAY_SIZE(p_debug_values) - 1, + numMatches, + matches); +} + /// The 'diffopt' option is changed. const char *did_set_diffopt(optset_T *args FUNC_ATTR_UNUSED) { @@ -1039,6 +1325,31 @@ const char *did_set_diffopt(optset_T *args FUNC_ATTR_UNUSED) return NULL; } +int expand_set_diffopt(optexpand_T *args, int *numMatches, char ***matches) +{ + expand_T *xp = args->oe_xp; + + if (xp->xp_pattern > args->oe_set_arg && *(xp->xp_pattern - 1) == ':') { + // Within "algorithm:", we have a subgroup of possible options. + const size_t algo_len = strlen("algorithm:"); + if (xp->xp_pattern - args->oe_set_arg >= (int)algo_len + && strncmp(xp->xp_pattern - algo_len, "algorithm:", algo_len) == 0) { + return expand_set_opt_string(args, + p_dip_algorithm_values, + ARRAY_SIZE(p_dip_algorithm_values) - 1, + numMatches, + matches); + } + return FAIL; + } + + return expand_set_opt_string(args, + p_dip_values, + ARRAY_SIZE(p_dip_values) - 1, + numMatches, + matches); +} + /// The 'display' option is changed. const char *did_set_display(optset_T *args FUNC_ATTR_UNUSED) { @@ -1050,12 +1361,30 @@ const char *did_set_display(optset_T *args FUNC_ATTR_UNUSED) return NULL; } +int expand_set_display(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_string(args, + p_dy_values, + ARRAY_SIZE(p_dy_values) - 1, + numMatches, + matches); +} + /// The 'eadirection' option is changed. const char *did_set_eadirection(optset_T *args FUNC_ATTR_UNUSED) { return did_set_opt_strings(p_ead, p_ead_values, false); } +int expand_set_eadirection(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_string(args, + p_ead_values, + ARRAY_SIZE(p_ead_values) - 1, + numMatches, + matches); +} + /// One of the 'encoding', 'fileencoding' or 'makeencoding' /// options is changed. const char *did_set_encoding(optset_T *args) @@ -1098,6 +1427,11 @@ const char *did_set_encoding(optset_T *args) return NULL; } +int expand_set_encoding(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_generic(args, get_encoding_name, numMatches, matches); +} + /// The 'eventignore' option is changed. const char *did_set_eventignore(optset_T *args FUNC_ATTR_UNUSED) { @@ -1107,6 +1441,21 @@ const char *did_set_eventignore(optset_T *args FUNC_ATTR_UNUSED) return NULL; } +static char *get_eventignore_name(expand_T *xp, int idx) +{ + // 'eventignore' allows special keyword "all" in addition to + // all event names. + if (idx == 0) { + return "all"; + } + return get_event_name_no_group(xp, idx - 1); +} + +int expand_set_eventignore(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_generic(args, get_eventignore_name, numMatches, matches); +} + /// The 'fileformat' option is changed. const char *did_set_fileformat(optset_T *args) { @@ -1130,6 +1479,15 @@ const char *did_set_fileformat(optset_T *args) return NULL; } +int expand_set_fileformat(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_string(args, + p_ff_values, + ARRAY_SIZE(p_ff_values) - 1, + numMatches, + matches); +} + /// The 'fileformats' option is changed. const char *did_set_fileformats(optset_T *args) { @@ -1160,6 +1518,15 @@ const char *did_set_foldclose(optset_T *args FUNC_ATTR_UNUSED) return did_set_opt_strings(p_fcl, p_fcl_values, true); } +int expand_set_foldclose(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_string(args, + p_fcl_values, + ARRAY_SIZE(p_fcl_values) - 1, + numMatches, + matches); +} + /// The 'foldcolumn' option is changed. const char *did_set_foldcolumn(optset_T *args) { @@ -1229,12 +1596,30 @@ const char *did_set_foldmethod(optset_T *args) return NULL; } +int expand_set_foldmethod(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_string(args, + p_fdm_values, + ARRAY_SIZE(p_fdm_values) - 1, + numMatches, + matches); +} + /// The 'foldopen' option is changed. const char *did_set_foldopen(optset_T *args FUNC_ATTR_UNUSED) { return did_set_opt_flags(p_fdo, p_fdo_values, &fdo_flags, true); } +int expand_set_foldopen(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_string(args, + p_fdo_values, + ARRAY_SIZE(p_fdo_values) - 1, + numMatches, + matches); +} + /// The 'formatoptions' option is changed. const char *did_set_formatoptions(optset_T *args) { @@ -1243,6 +1628,11 @@ const char *did_set_formatoptions(optset_T *args) return did_set_option_listflag(*varp, FO_ALL, args->os_errbuf, args->os_errbuflen); } +int expand_set_formatoptions(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_listflag(args, FO_ALL, numMatches, matches); +} + /// The 'guicursor' option is changed. const char *did_set_guicursor(optset_T *args FUNC_ATTR_UNUSED) { @@ -1321,6 +1711,15 @@ const char *did_set_jumpoptions(optset_T *args FUNC_ATTR_UNUSED) return did_set_opt_flags(p_jop, p_jop_values, &jop_flags, true); } +int expand_set_jumpoptions(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_string(args, + p_jop_values, + ARRAY_SIZE(p_jop_values) - 1, + numMatches, + matches); +} + /// The 'keymap' option has changed. const char *did_set_keymap(optset_T *args) { @@ -1384,6 +1783,15 @@ const char *did_set_keymodel(optset_T *args FUNC_ATTR_UNUSED) return NULL; } +int expand_set_keymodel(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_string(args, + p_km_values, + ARRAY_SIZE(p_km_values) - 1, + numMatches, + matches); +} + /// The 'lispoptions' option is changed. const char *did_set_lispoptions(optset_T *args) { @@ -1395,6 +1803,16 @@ const char *did_set_lispoptions(optset_T *args) return NULL; } +int expand_set_lispoptions(optexpand_T *args, int *numMatches, char ***matches) +{ + static char *(p_lop_values[]) = { "expr:0", "expr:1", NULL }; + return expand_set_opt_string(args, + p_lop_values, + ARRAY_SIZE(p_lop_values) - 1, + numMatches, + matches); +} + /// The 'matchpairs' option is changed. const char *did_set_matchpairs(optset_T *args) { @@ -1439,12 +1857,26 @@ const char *did_set_mouse(optset_T *args) return did_set_option_listflag(*varp, MOUSE_ALL, args->os_errbuf, args->os_errbuflen); } +int expand_set_mouse(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_listflag(args, MOUSE_ALL, numMatches, matches); +} + /// The 'mousemodel' option is changed. const char *did_set_mousemodel(optset_T *args FUNC_ATTR_UNUSED) { return did_set_opt_strings(p_mousem, p_mousem_values, false); } +int expand_set_mousemodel(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_string(args, + p_mousem_values, + ARRAY_SIZE(p_mousem_values) - 1, + numMatches, + matches); +} + /// Handle setting 'mousescroll'. /// @return error message, NULL if it's OK. const char *did_set_mousescroll(optset_T *args FUNC_ATTR_UNUSED) @@ -1518,6 +1950,15 @@ const char *did_set_nrformats(optset_T *args) return did_set_opt_strings(*varp, p_nf_values, true); } +int expand_set_nrformats(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_string(args, + p_nf_values, + ARRAY_SIZE(p_nf_values) - 1, + numMatches, + matches); +} + /// One of the '*expr' options is changed:, 'diffexpr', 'foldexpr', 'foldtext', /// 'formatexpr', 'includeexpr', 'indentexpr', 'patchexpr' and 'charconvert'. const char *did_set_optexpr(optset_T *args) @@ -1553,6 +1994,16 @@ const char *did_set_rightleftcmd(optset_T *args) return NULL; } +int expand_set_rightleftcmd(optexpand_T *args, int *numMatches, char ***matches) +{ + static char *(p_rlc_values[]) = { "search", NULL }; + return expand_set_opt_string(args, + p_rlc_values, + ARRAY_SIZE(p_rlc_values) - 1, + numMatches, + matches); +} + /// The 'rulerformat' option is changed. const char *did_set_rulerformat(optset_T *args) { @@ -1565,6 +2016,15 @@ const char *did_set_scrollopt(optset_T *args FUNC_ATTR_UNUSED) return did_set_opt_strings(p_sbo, p_scbopt_values, true); } +int expand_set_scrollopt(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_string(args, + p_scbopt_values, + ARRAY_SIZE(p_scbopt_values) - 1, + numMatches, + matches); +} + /// The 'selection' option is changed. const char *did_set_selection(optset_T *args FUNC_ATTR_UNUSED) { @@ -1574,12 +2034,30 @@ const char *did_set_selection(optset_T *args FUNC_ATTR_UNUSED) return NULL; } +int expand_set_selection(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_string(args, + p_sel_values, + ARRAY_SIZE(p_sel_values) - 1, + numMatches, + matches); +} + /// The 'selectmode' option is changed. const char *did_set_selectmode(optset_T *args FUNC_ATTR_UNUSED) { return did_set_opt_strings(p_slm, p_slm_values, true); } +int expand_set_selectmode(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_string(args, + p_slm_values, + ARRAY_SIZE(p_slm_values) - 1, + numMatches, + matches); +} + /// The 'sessionoptions' option is changed. const char *did_set_sessionoptions(optset_T *args) { @@ -1595,6 +2073,15 @@ const char *did_set_sessionoptions(optset_T *args) return NULL; } +int expand_set_sessionoptions(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_string(args, + p_ssop_values, + ARRAY_SIZE(p_ssop_values) - 1, + numMatches, + matches); +} + static const char *did_set_shada(vimoption_T **opt, int *opt_idx, bool *free_oldval, char *errbuf, size_t errbuflen) { @@ -1661,6 +2148,11 @@ const char *did_set_shortmess(optset_T *args) return did_set_option_listflag(*varp, SHM_ALL, args->os_errbuf, args->os_errbuflen); } +int expand_set_shortmess(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_listflag(args, SHM_ALL, numMatches, matches); +} + /// The 'showbreak' option is changed. const char *did_set_showbreak(optset_T *args) { @@ -1681,6 +2173,15 @@ const char *did_set_showcmdloc(optset_T *args FUNC_ATTR_UNUSED) return did_set_opt_strings(p_sloc, p_sloc_values, true); } +int expand_set_showcmdloc(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_string(args, + p_sloc_values, + ARRAY_SIZE(p_sloc_values) - 1, + numMatches, + matches); +} + /// The 'signcolumn' option is changed. const char *did_set_signcolumn(optset_T *args) { @@ -1700,6 +2201,15 @@ const char *did_set_signcolumn(optset_T *args) return NULL; } +int expand_set_signcolumn(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_string(args, + p_scl_values, + ARRAY_SIZE(p_scl_values) - 1, + numMatches, + matches); +} + /// The 'spellcapcheck' option is changed. const char *did_set_spellcapcheck(optset_T *args) { @@ -1745,6 +2255,15 @@ const char *did_set_spelloptions(optset_T *args) return NULL; } +int expand_set_spelloptions(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_string(args, + p_spo_values, + ARRAY_SIZE(p_spo_values) - 1, + numMatches, + matches); +} + /// The 'spellsuggest' option is changed. const char *did_set_spellsuggest(optset_T *args FUNC_ATTR_UNUSED) { @@ -1754,12 +2273,30 @@ const char *did_set_spellsuggest(optset_T *args FUNC_ATTR_UNUSED) return NULL; } +int expand_set_spellsuggest(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_string(args, + p_sps_values, + ARRAY_SIZE(p_sps_values) - 1, + numMatches, + matches); +} + /// The 'splitkeep' option is changed. const char *did_set_splitkeep(optset_T *args FUNC_ATTR_UNUSED) { return did_set_opt_strings(p_spk, p_spk_values, false); } +int expand_set_splitkeep(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_string(args, + p_spk_values, + ARRAY_SIZE(p_spk_values) - 1, + numMatches, + matches); +} + /// The 'statuscolumn' option is changed. const char *did_set_statuscolumn(optset_T *args) { @@ -1817,6 +2354,15 @@ const char *did_set_switchbuf(optset_T *args FUNC_ATTR_UNUSED) return did_set_opt_flags(p_swb, p_swb_values, &swb_flags, true); } +int expand_set_switchbuf(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_string(args, + p_swb_values, + ARRAY_SIZE(p_swb_values) - 1, + numMatches, + matches); +} + /// The 'tabline' option is changed. const char *did_set_tabline(optset_T *args) { @@ -1850,6 +2396,15 @@ const char *did_set_tagcase(optset_T *args) return NULL; } +int expand_set_tagcase(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_string(args, + p_tc_values, + ARRAY_SIZE(p_tc_values) - 1, + numMatches, + matches); +} + /// The 'termpastefilter' option is changed. const char *did_set_termpastefilter(optset_T *args FUNC_ATTR_UNUSED) { @@ -1988,12 +2543,28 @@ const char *did_set_virtualedit(optset_T *args) return NULL; } +int expand_set_virtualedit(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_string(args, + p_ve_values, + ARRAY_SIZE(p_ve_values) - 1, + numMatches, + matches); +} + /// The 'whichwrap' option is changed. const char *did_set_whichwrap(optset_T *args) { char **varp = (char **)args->os_varp; - return did_set_option_listflag(*varp, WW_ALL, args->os_errbuf, args->os_errbuflen); + // Add ',' to the list flags because 'whichwrap' is a flag + // list that is comma-separated. + return did_set_option_listflag(*varp, WW_ALL ",", args->os_errbuf, args->os_errbuflen); +} + +int expand_set_whichwrap(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_listflag(args, WW_ALL, numMatches, matches); } /// The 'wildmode' option is changed. @@ -2005,12 +2576,30 @@ const char *did_set_wildmode(optset_T *args FUNC_ATTR_UNUSED) return NULL; } +int expand_set_wildmode(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_string(args, + p_wim_values, + ARRAY_SIZE(p_wim_values) - 1, + numMatches, + matches); +} + /// The 'wildoptions' option is changed. const char *did_set_wildoptions(optset_T *args FUNC_ATTR_UNUSED) { return did_set_opt_flags(p_wop, p_wop_values, &wop_flags, true); } +int expand_set_wildoptions(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_string(args, + p_wop_values, + ARRAY_SIZE(p_wop_values) - 1, + numMatches, + matches); +} + /// The 'winaltkeys' option is changed. const char *did_set_winaltkeys(optset_T *args FUNC_ATTR_UNUSED) { @@ -2020,6 +2609,15 @@ const char *did_set_winaltkeys(optset_T *args FUNC_ATTR_UNUSED) return NULL; } +int expand_set_winaltkeys(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_string(args, + p_wak_values, + ARRAY_SIZE(p_wak_values) - 1, + numMatches, + matches); +} + /// The 'winbar' option is changed. const char *did_set_winbar(optset_T *args) { @@ -2536,6 +3134,40 @@ static const char *set_chars_option(win_T *wp, const char *value, const bool is_ return NULL; // no error } +/// Handle the new value of 'fillchars'. +const char *set_fillchars_option(win_T *wp, char *val, bool apply) +{ + return set_chars_option(wp, val, false, apply); +} + +/// Handle the new value of 'listchars'. +const char *set_listchars_option(win_T *wp, char *val, bool apply) +{ + return set_chars_option(wp, val, true, apply); +} + +/// Function given to ExpandGeneric() to obtain possible arguments of the +/// 'fillchars' option. +char *get_fillchars_name(expand_T *xp FUNC_ATTR_UNUSED, int idx) +{ + if (idx >= (int)ARRAY_SIZE(fcs_tab)) { + return NULL; + } + + return (char *)fcs_tab[idx].name; +} + +/// Function given to ExpandGeneric() to obtain possible arguments of the +/// 'listchars' option. +char *get_listchars_name(expand_T *xp FUNC_ATTR_UNUSED, int idx) +{ + if (idx >= (int)ARRAY_SIZE(lcs_tab)) { + return NULL; + } + + return (char *)lcs_tab[idx].name; +} + /// Check all global and local values of 'listchars' and 'fillchars'. /// May set different defaults in case character widths change. /// @@ -2558,15 +3190,3 @@ const char *check_chars_options(void) } return NULL; } - -/// Handle the new value of 'fillchars'. -const char *set_fillchars_option(win_T *wp, char *val, bool apply) -{ - return set_chars_option(wp, val, false, apply); -} - -/// Handle the new value of 'listchars'. -const char *set_listchars_option(win_T *wp, char *val, bool apply) -{ - return set_chars_option(wp, val, true, apply); -} diff --git a/src/nvim/optionstr.h b/src/nvim/optionstr.h index 3520cc2061..0993b67c9a 100644 --- a/src/nvim/optionstr.h +++ b/src/nvim/optionstr.h @@ -2,6 +2,7 @@ #define NVIM_OPTIONSTR_H #include "nvim/buffer_defs.h" +#include "nvim/cmdexpand.h" #include "nvim/option_defs.h" #ifdef INCLUDE_GENERATED_DECLARATIONS diff --git a/src/nvim/spellsuggest.c b/src/nvim/spellsuggest.c index 2ee93b4934..7b92e69821 100644 --- a/src/nvim/spellsuggest.c +++ b/src/nvim/spellsuggest.c @@ -403,6 +403,7 @@ int spell_check_sps(void) if (*s != NUL && !ascii_isdigit(*s)) { f = -1; } + // Note: Keep this in sync with p_sps_values. } else if (strcmp(buf, "best") == 0) { f = SPS_BEST; } else if (strcmp(buf, "fast") == 0) { diff --git a/src/nvim/vim.h b/src/nvim/vim.h index fc1f15b285..da88202525 100644 --- a/src/nvim/vim.h +++ b/src/nvim/vim.h @@ -164,6 +164,8 @@ enum { EXPAND_BREAKPOINT, EXPAND_SCRIPTNAMES, EXPAND_RUNTIME, + EXPAND_STRING_SETTING, + EXPAND_SETTING_SUBTRACT, EXPAND_CHECKHEALTH, EXPAND_LUA, }; |