diff options
| author | zeertzjq <zeertzjq@outlook.com> | 2023-10-01 21:05:12 +0800 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-10-01 21:05:12 +0800 |
| commit | b40c70f2fcaf709d2bf8b3209ae1e2f617d8e852 (patch) | |
| tree | 489e52a8c585f8f1fccfdddf814ac763ad2b7522 /src | |
| parent | 9b3045103f7d56e5ccd0574dcb93e953b72d5f50 (diff) | |
| parent | 01c51a491330bd10202c73aff92c0978984c0692 (diff) | |
| download | rneovim-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')
| -rw-r--r-- | src/nvim/autocmd.c | 7 | ||||
| -rw-r--r-- | src/nvim/cmdexpand.c | 23 | ||||
| -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/highlight_group.c | 4 | ||||
| -rw-r--r-- | src/nvim/indent.c | 1 | ||||
| -rw-r--r-- | src/nvim/mbyte.c | 11 | ||||
| -rw-r--r-- | src/nvim/option.c | 310 | ||||
| -rw-r--r-- | src/nvim/option.h | 9 | ||||
| -rw-r--r-- | src/nvim/option_defs.h | 46 | ||||
| -rw-r--r-- | src/nvim/option_vars.h | 15 | ||||
| -rw-r--r-- | src/nvim/options.lua | 75 | ||||
| -rw-r--r-- | src/nvim/optionstr.c | 826 | ||||
| -rw-r--r-- | src/nvim/optionstr.h | 1 | ||||
| -rw-r--r-- | src/nvim/spellsuggest.c | 1 | ||||
| -rw-r--r-- | src/nvim/vim.h | 2 |
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, ®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/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 the ways mentioned for the items above are possible. ]=], + expand_cb = 'expand_set_backspace', |