aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/option.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim/option.c')
-rw-r--r--src/nvim/option.c1777
1 files changed, 737 insertions, 1040 deletions
diff --git a/src/nvim/option.c b/src/nvim/option.c
index 5c487a7c62..208112561a 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -30,6 +30,7 @@
#include "nvim/arglist.h"
#include "nvim/ascii.h"
#include "nvim/buffer.h"
+#include "nvim/change.h"
#include "nvim/charset.h"
#include "nvim/cursor_shape.h"
#include "nvim/decoration_provider.h"
@@ -38,7 +39,6 @@
#include "nvim/edit.h"
#include "nvim/eval.h"
#include "nvim/eval/typval.h"
-#include "nvim/ex_cmds2.h"
#include "nvim/ex_docmd.h"
#include "nvim/ex_getln.h"
#include "nvim/ex_session.h"
@@ -52,6 +52,7 @@
#include "nvim/indent.h"
#include "nvim/indent_c.h"
#include "nvim/keycodes.h"
+#include "nvim/locale.h"
#include "nvim/macros.h"
#include "nvim/mapping.h"
#include "nvim/mbyte.h"
@@ -71,6 +72,7 @@
#include "nvim/popupmenu.h"
#include "nvim/regexp.h"
#include "nvim/screen.h"
+#include "nvim/search.h"
#include "nvim/spell.h"
#include "nvim/spellfile.h"
#include "nvim/spellsuggest.h"
@@ -81,7 +83,7 @@
#include "nvim/undo.h"
#include "nvim/vim.h"
#include "nvim/window.h"
-#ifdef WIN32
+#ifdef MSWIN
# include "nvim/os/pty_conpty_win.h"
#endif
#include "nvim/api/extmark.h"
@@ -91,33 +93,18 @@
#include "nvim/os/input.h"
#include "nvim/os/lang.h"
-/*
- * The options that are local to a window or buffer have "indir" set to one of
- * these values. Special values:
- * PV_NONE: global option.
- * PV_WIN is added: window-local option
- * PV_BUF is added: buffer-local option
- * PV_BOTH is added: global option which also has a local value.
- */
-#define PV_BOTH 0x1000
-#define PV_WIN 0x2000
-#define PV_BUF 0x4000
-#define PV_MASK 0x0fff
-#define OPT_WIN(x) (idopt_T)(PV_WIN + (int)(x))
-#define OPT_BUF(x) (idopt_T)(PV_BUF + (int)(x))
-#define OPT_BOTH(x) (idopt_T)(PV_BOTH + (int)(x))
-
-// WV_ and BV_ values get typecasted to this for the "indir" field
-typedef enum {
- PV_NONE = 0,
- PV_MAXVAL = 0xffff, // to avoid warnings for value out of range
-} idopt_T;
-
-/*
- * Options local to a window have a value local to a buffer and global to all
- * buffers. Indicate this by setting "var" to VAR_WIN.
- */
-#define VAR_WIN ((char_u *)-1)
+static char e_unknown_option[]
+ = N_("E518: Unknown option");
+static char e_not_allowed_in_modeline[]
+ = N_("E520: Not allowed in a modeline");
+static char e_not_allowed_in_modeline_when_modelineexpr_is_off[]
+ = N_("E992: Not allowed in a modeline when 'modelineexpr' is off");
+static char e_key_code_not_set[]
+ = N_("E846: Key code not set");
+static char e_number_required_after_equal[]
+ = N_("E521: Number required after =");
+static char e_preview_window_already_exists[]
+ = N_("E590: A preview window already exists");
static char *p_term = NULL;
static char *p_ttytype = NULL;
@@ -134,29 +121,14 @@ static int p_et_nopaste;
static long p_sts_nopaste;
static long p_tw_nopaste;
static long p_wm_nopaste;
-static char_u *p_vsts_nopaste;
-
-typedef struct vimoption {
- char *fullname; // full option name
- char *shortname; // permissible abbreviation
- uint32_t flags; // see below
- char_u *var; // global option: pointer to variable;
- // window-local option: VAR_WIN;
- // buffer-local option: global value
- idopt_T indir; // global option: PV_NONE;
- // local option: indirect option index
- char_u *def_val; // default values for variable (neovim!!)
- LastSet last_set; // script in which the option was last set
-} vimoption_T;
-
-/*
- * options[] is initialized here.
- * The order of the options MUST be alphabetic for ":set all" and findoption().
- * All option names MUST start with a lowercase letter (for findoption()).
- * Exception: "t_" options are at the end.
- * The options with a NULL variable are 'hidden': a set command for them is
- * ignored and they are not printed.
- */
+static char *p_vsts_nopaste;
+
+// options[] is initialized here.
+// The order of the options MUST be alphabetic for ":set all" and findoption().
+// All option names MUST start with a lowercase letter (for findoption()).
+// Exception: "t_" options are at the end.
+// The options with a NULL variable are 'hidden': a set command for them is
+// ignored and they are not printed.
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "options.generated.h"
@@ -164,10 +136,24 @@ typedef struct vimoption {
#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
+void set_init_tablocal(void)
+{
+ // susy baka: cmdheight calls itself OPT_GLOBAL but is really tablocal!
+ int ch_idx = findoption("cmdheight");
+ p_ch = (long)options[ch_idx].def_val;
+}
+
/// Initialize the options, first part.
///
/// Called only once from main(), just after creating the first buffer.
@@ -182,10 +168,8 @@ void set_init_1(bool clean_arg)
langmap_init();
- /*
- * Find default value for 'shell' option.
- * Don't use it if it is empty.
- */
+ // Find default value for 'shell' option.
+ // Don't use it if it is empty.
{
const char *shell = os_getenv("SHELL");
if (shell != NULL) {
@@ -200,10 +184,8 @@ void set_init_1(bool clean_arg)
}
}
- /*
- * Set the default for 'backupskip' to include environment variables for
- * temp files.
- */
+ // Set the default for 'backupskip' to include environment variables for
+ // temp files.
{
#ifdef UNIX
static char *(names[4]) = { "", "TMPDIR", "TEMP", "TMP" };
@@ -285,7 +267,7 @@ void set_init_1(bool clean_arg)
buf[j] = NUL;
opt_idx = findoption("cdpath");
if (opt_idx >= 0) {
- options[opt_idx].def_val = buf;
+ options[opt_idx].def_val = (char *)buf;
options[opt_idx].flags |= P_DEF_ALLOCED;
} else {
xfree(buf); // cannot happen
@@ -339,10 +321,8 @@ void set_init_1(bool clean_arg)
rtp = NULL; // ownership taken
}
- /*
- * Set all the options (except the terminal options) to their default
- * value. Also set the global value for local options.
- */
+ // Set all the options (except the terminal options) to their default
+ // value. Also set the global value for local options.
set_options_default(0);
curbuf->b_p_initialized = true;
@@ -365,15 +345,13 @@ void set_init_1(bool clean_arg)
// didset_options() because it only depends on 'encoding'.
init_spell_chartab();
- /*
- * Expand environment variables and things like "~" for the defaults.
- * If option_expand() returns non-NULL the variable is expanded. This can
- * only happen for non-indirect options.
- * Also set the default to the expanded value, so ":set" does not list
- * them.
- * Don't set the P_ALLOCED flag, because we don't want to free the
- * default.
- */
+ // Expand environment variables and things like "~" for the defaults.
+ // If option_expand() returns non-NULL the variable is expanded. This can
+ // only happen for non-indirect options.
+ // Also set the default to the expanded value, so ":set" does not list
+ // them.
+ // Don't set the P_ALLOCED flag, because we don't want to free the
+ // default.
for (opt_idx = 0; options[opt_idx].fullname; opt_idx++) {
if (options[opt_idx].flags & P_NO_DEF_EXP) {
continue;
@@ -383,7 +361,7 @@ void set_init_1(bool clean_arg)
&& options[opt_idx].var != NULL) {
p = _(*(char **)options[opt_idx].var);
} else {
- p = (char *)option_expand(opt_idx, NULL);
+ p = option_expand(opt_idx, NULL);
}
if (p != NULL) {
p = xstrdup(p);
@@ -391,7 +369,7 @@ void set_init_1(bool clean_arg)
if (options[opt_idx].flags & P_DEF_ALLOCED) {
xfree(options[opt_idx].def_val);
}
- options[opt_idx].def_val = (char_u *)p;
+ options[opt_idx].def_val = p;
options[opt_idx].flags |= P_DEF_ALLOCED;
}
}
@@ -404,7 +382,7 @@ void set_init_1(bool clean_arg)
// NOTE: mlterm's author is being asked to 'set' a variable
// instead of an environment variable due to inheritance.
if (os_env_exists("MLTERM")) {
- set_option_value("tbidi", 1L, NULL, 0);
+ set_option_value_give_err("tbidi", 1L, NULL, 0);
}
didset_options2();
@@ -419,12 +397,12 @@ void set_init_1(bool clean_arg)
// use utf-8 as 'default' if locale encoding can't be detected.
p = (char_u *)xmemdupz(S_LEN("utf-8"));
}
- fenc_default = p;
+ fenc_default = (char *)p;
#ifdef HAVE_WORKING_LIBINTL
// GNU gettext 0.10.37 supports this feature: set the codeset used for
// translated messages independently from the current locale.
- (void)bind_textdomain_codeset(PROJECT_NAME, (char *)p_enc);
+ (void)bind_textdomain_codeset(PROJECT_NAME, p_enc);
#endif
// Set the default for 'helplang'.
@@ -440,7 +418,7 @@ static void set_option_default(int opt_idx, int opt_flags)
char_u *varp; // pointer to variable for current option
int both = (opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0;
- varp = get_varp_scope(&(options[opt_idx]), both ? OPT_LOCAL : opt_flags);
+ varp = (char_u *)get_varp_scope(&(options[opt_idx]), both ? OPT_LOCAL : opt_flags);
uint32_t flags = options[opt_idx].flags;
if (varp != NULL) { // skip hidden option, nothing to do for it
if (flags & P_STRING) {
@@ -448,12 +426,12 @@ static void set_option_default(int opt_idx, int opt_flags)
// freeing and allocating the value.
if (options[opt_idx].indir != PV_NONE) {
set_string_option_direct(NULL, opt_idx,
- (char *)options[opt_idx].def_val, opt_flags, 0);
+ options[opt_idx].def_val, opt_flags, 0);
} else {
if ((opt_flags & OPT_FREE) && (flags & P_ALLOCED)) {
- free_string_option(*(char_u **)(varp));
+ free_string_option(*(char **)(varp));
}
- *(char_u **)varp = options[opt_idx].def_val;
+ *(char **)varp = options[opt_idx].def_val;
options[opt_idx].flags &= ~P_ALLOCED;
}
} else if (flags & P_NUM) {
@@ -532,9 +510,7 @@ static void set_string_default(const char *name, char *val, bool allocated)
xfree(options[opt_idx].def_val);
}
- options[opt_idx].def_val = allocated
- ? (char_u *)val
- : (char_u *)xstrdup(val);
+ options[opt_idx].def_val = allocated ? val : xstrdup(val);
options[opt_idx].flags |= P_DEF_ALLOCED;
}
}
@@ -578,7 +554,7 @@ void set_number_default(char *name, long val)
opt_idx = findoption(name);
if (opt_idx >= 0) {
- options[opt_idx].def_val = (char_u *)(intptr_t)val;
+ options[opt_idx].def_val = (char *)(intptr_t)val;
}
}
@@ -590,14 +566,14 @@ void free_all_options(void)
if (options[i].indir == PV_NONE) {
// global option: free value and default value.
if ((options[i].flags & P_ALLOCED) && options[i].var != NULL) {
- free_string_option(*(char_u **)options[i].var);
+ free_string_option(*(char **)options[i].var);
}
if (options[i].flags & P_DEF_ALLOCED) {
free_string_option(options[i].def_val);
}
} else if (options[i].var != VAR_WIN && (options[i].flags & P_STRING)) {
// buffer-local option: free global value
- clear_string_option((char_u **)options[i].var);
+ clear_string_option((char **)options[i].var);
}
}
free_operatorfunc_option();
@@ -620,10 +596,8 @@ void set_init_2(bool headless)
}
comp_col();
- /*
- * 'window' is only for backwards compatibility with Vi.
- * Default is Rows - 1.
- */
+ // 'window' is only for backwards compatibility with Vi.
+ // Default is Rows - 1.
if (!option_was_set("window")) {
p_window = Rows - 1;
}
@@ -649,41 +623,41 @@ void set_init_3(void)
: !(options[idx_sp].flags & P_WAS_SET);
size_t len = 0;
- char_u *p = (char_u *)invocation_path_tail(p_sh, &len);
- p = vim_strnsave(p, len);
+ char *p = (char *)invocation_path_tail((char_u *)p_sh, &len);
+ p = xstrnsave(p, len);
{
//
// Default for p_sp is "| tee", for p_srr is ">".
// For known shells it is changed here to include stderr.
//
- if (FNAMECMP(p, "csh") == 0
- || FNAMECMP(p, "tcsh") == 0) {
+ if (path_fnamecmp(p, "csh") == 0
+ || path_fnamecmp(p, "tcsh") == 0) {
if (do_sp) {
- p_sp = (char_u *)"|& tee";
+ p_sp = "|& tee";
options[idx_sp].def_val = p_sp;
}
if (do_srr) {
- p_srr = (char_u *)">&";
+ p_srr = ">&";
options[idx_srr].def_val = p_srr;
}
- } else if (FNAMECMP(p, "sh") == 0
- || FNAMECMP(p, "ksh") == 0
- || FNAMECMP(p, "mksh") == 0
- || FNAMECMP(p, "pdksh") == 0
- || FNAMECMP(p, "zsh") == 0
- || FNAMECMP(p, "zsh-beta") == 0
- || FNAMECMP(p, "bash") == 0
- || FNAMECMP(p, "fish") == 0
- || FNAMECMP(p, "ash") == 0
- || FNAMECMP(p, "dash") == 0) {
+ } else if (path_fnamecmp(p, "sh") == 0
+ || path_fnamecmp(p, "ksh") == 0
+ || path_fnamecmp(p, "mksh") == 0
+ || path_fnamecmp(p, "pdksh") == 0
+ || path_fnamecmp(p, "zsh") == 0
+ || path_fnamecmp(p, "zsh-beta") == 0
+ || path_fnamecmp(p, "bash") == 0
+ || path_fnamecmp(p, "fish") == 0
+ || path_fnamecmp(p, "ash") == 0
+ || path_fnamecmp(p, "dash") == 0) {
// Always use POSIX shell style redirection if we reach this
if (do_sp) {
- p_sp = (char_u *)"2>&1| tee";
+ p_sp = "2>&1| tee";
options[idx_sp].def_val = p_sp;
}
if (do_srr) {
- p_srr = (char_u *)">%s 2>&1";
+ p_srr = ">%s 2>&1";
options[idx_srr].def_val = p_srr;
}
}
@@ -717,7 +691,7 @@ void set_helplang_default(const char *lang)
int idx = findoption("hlg");
if (idx >= 0 && !(options[idx].flags & P_WAS_SET)) {
if (options[idx].flags & P_ALLOCED) {
- free_string_option(p_hlg);
+ free_string_option((char *)p_hlg);
}
p_hlg = (char_u *)xmemdupz(lang, lang_len);
// zh_CN becomes "cn", zh_TW becomes "tw".
@@ -743,19 +717,17 @@ void set_title_defaults(void)
{
int idx1;
- /*
- * If GUI is (going to be) used, we can always set the window title and
- * icon name. Saves a bit of time, because the X11 display server does
- * not need to be contacted.
- */
+ // If GUI is (going to be) used, we can always set the window title and
+ // icon name. Saves a bit of time, because the X11 display server does
+ // not need to be contacted.
idx1 = findoption("title");
if (idx1 >= 0 && !(options[idx1].flags & P_WAS_SET)) {
- options[idx1].def_val = (char_u *)(intptr_t)0;
+ options[idx1].def_val = 0;
p_title = 0;
}
idx1 = findoption("icon");
if (idx1 >= 0 && !(options[idx1].flags & P_WAS_SET)) {
- options[idx1].def_val = (char_u *)(intptr_t)0;
+ options[idx1].def_val = 0;
p_icon = 0;
}
}
@@ -775,6 +747,349 @@ void ex_set(exarg_T *eap)
(void)do_set(eap->arg, flags);
}
+/// Part of do_set() for string options.
+/// @return FAIL on failure, do not process further options.
+static int do_set_string(int opt_idx, int opt_flags, char **argp, int nextchar, set_op_T op_arg,
+ uint32_t flags, char *varp_arg, char *errbuf, size_t errbuflen,
+ int *value_checked, char **errmsg)
+{
+ char *arg = *argp;
+ set_op_T op = op_arg;
+ char *varp = varp_arg;
+ char *save_arg = NULL;
+ char *s = NULL;
+ char_u *oldval = NULL; // previous value if *varp
+ char *newval;
+ char_u *origval = NULL;
+ char_u *origval_l = NULL;
+ char_u *origval_g = NULL;
+ char *saved_origval = NULL;
+ char *saved_origval_l = NULL;
+ char *saved_origval_g = NULL;
+ char *saved_newval = NULL;
+ unsigned newlen;
+ int comma;
+ char whichwrap[80];
+
+ // When using ":set opt=val" for a global option
+ // with a local value the local value will be
+ // reset, use the global value here.
+ if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0
+ && ((int)options[opt_idx].indir & PV_BOTH)) {
+ varp = (char *)options[opt_idx].var;
+ }
+
+ // The old value is kept until we are sure that the new value is valid.
+ oldval = *(char_u **)varp;
+
+ if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0) {
+ origval_l = *(char_u **)get_varp_scope(&(options[opt_idx]), OPT_LOCAL);
+ origval_g = *(char_u **)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL);
+
+ // A global-local string option might have an empty option as value to
+ // indicate that the global value should be used.
+ if (((int)options[opt_idx].indir & PV_BOTH) && origval_l == (char_u *)empty_option) {
+ origval_l = origval_g;
+ }
+ }
+
+ // When setting the local value of a global option, the old value may be
+ // the global value.
+ if (((int)options[opt_idx].indir & PV_BOTH) && (opt_flags & OPT_LOCAL)) {
+ origval = *(char_u **)get_varp(&options[opt_idx]);
+ } else {
+ origval = oldval;
+ }
+
+ if (nextchar == '&') { // set to default val
+ newval = options[opt_idx].def_val;
+ // expand environment variables and ~ since the default value was
+ // already expanded, only required when an environment variable was set
+ // later
+ if (newval == NULL) {
+ newval = empty_option;
+ } else if (!(options[opt_idx].flags & P_NO_DEF_EXP)) {
+ s = option_expand(opt_idx, newval);
+ if (s == NULL) {
+ s = newval;
+ }
+ newval = xstrdup(s);
+ } else {
+ newval = xstrdup(newval);
+ }
+ } else if (nextchar == '<') { // set to global val
+ newval = xstrdup(*(char **)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL));
+ } else {
+ arg++; // jump to after the '=' or ':'
+
+ // Set 'keywordprg' to ":help" if an empty
+ // value was passed to :set by the user.
+ if (varp == (char *)&p_kp && (*arg == NUL || *arg == ' ')) {
+ save_arg = arg;
+ arg = ":help";
+ } else if (varp == (char *)&p_bs && ascii_isdigit(**(char_u **)varp)) {
+ // Convert 'backspace' number to string, for
+ // adding, prepending and removing string.
+ int i = getdigits_int((char **)varp, true, 0);
+ switch (i) {
+ case 0:
+ *(char **)varp = empty_option;
+ break;
+ case 1:
+ *(char_u **)varp = (char_u *)xstrdup("indent,eol");
+ break;
+ case 2:
+ *(char_u **)varp = (char_u *)xstrdup("indent,eol,start");
+ break;
+ case 3:
+ *(char_u **)varp = (char_u *)xstrdup("indent,eol,nostop");
+ break;
+ }
+ xfree(oldval);
+ if (origval == oldval) {
+ origval = *(char_u **)varp;
+ }
+ if (origval_l == oldval) {
+ origval_l = *(char_u **)varp;
+ }
+ if (origval_g == oldval) {
+ origval_g = *(char_u **)varp;
+ }
+ oldval = *(char_u **)varp;
+ } else if (varp == (char *)&p_ww && ascii_isdigit(*arg)) {
+ // Convert 'whichwrap' number to string, for backwards compatibility
+ // with Vim 3.0.
+ *whichwrap = NUL;
+ int i = getdigits_int(&arg, true, 0);
+ if (i & 1) {
+ xstrlcat(whichwrap, "b,", sizeof(whichwrap));
+ }
+ if (i & 2) {
+ xstrlcat(whichwrap, "s,", sizeof(whichwrap));
+ }
+ if (i & 4) {
+ xstrlcat(whichwrap, "h,l,", sizeof(whichwrap));
+ }
+ if (i & 8) {
+ xstrlcat(whichwrap, "<,>,", sizeof(whichwrap));
+ }
+ if (i & 16) {
+ xstrlcat(whichwrap, "[,],", sizeof(whichwrap));
+ }
+ if (*whichwrap != NUL) { // remove trailing ,
+ whichwrap[strlen(whichwrap) - 1] = NUL;
+ }
+ save_arg = arg;
+ arg = whichwrap;
+ } else if (*arg == '>' && (varp == (char *)&p_dir || varp == (char *)&p_bdir)) {
+ // Remove '>' before 'dir' and 'bdir', for backwards compatibility with
+ // version 3.0
+ arg++;
+ }
+
+ // Copy the new string into allocated memory.
+ // Can't use set_string_option_direct(), because we need to remove the
+ // backslashes.
+
+ // get a bit too much
+ newlen = (unsigned)strlen(arg) + 1;
+ if (op != OP_NONE) {
+ newlen += (unsigned)STRLEN(origval) + 1;
+ }
+ newval = xmalloc(newlen);
+ s = newval;
+
+ // Copy the string, skip over escaped chars.
+ // 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().
+ while (*arg != NUL && !ascii_iswhite(*arg)) {
+ if (*arg == '\\' && arg[1] != NUL
+#ifdef BACKSLASH_IN_FILENAME
+ && !((flags & P_EXPAND)
+ && vim_isfilec(arg[1])
+ && !ascii_iswhite(arg[1])
+ && (arg[1] != '\\'
+ || (s == newval && arg[2] != '\\')))
+#endif
+ ) {
+ arg++; // remove backslash
+ }
+ int i = utfc_ptr2len(arg);
+ if (i > 1) {
+ // copy multibyte char
+ memmove(s, arg, (size_t)i);
+ arg += i;
+ s += i;
+ } else {
+ *s++ = *arg++;
+ }
+ }
+ *s = NUL;
+
+ // Expand environment variables and ~.
+ // Don't do it when adding without inserting a comma.
+ if (op == OP_NONE || (flags & P_COMMA)) {
+ s = option_expand(opt_idx, newval);
+ if (s != NULL) {
+ xfree(newval);
+ newlen = (unsigned)strlen(s) + 1;
+ if (op != OP_NONE) {
+ newlen += (unsigned)STRLEN(origval) + 1;
+ }
+ newval = xmalloc(newlen);
+ STRCPY(newval, s);
+ }
+ }
+
+ // locate newval[] in origval[] when removing it
+ // and when adding to avoid duplicates
+ int len = 0;
+ if (op == OP_REMOVING || (flags & P_NODUP)) {
+ len = (int)STRLEN(newval);
+ s = (char *)find_dup_item(origval, (char_u *)newval, flags);
+
+ // do not add if already there
+ if ((op == OP_ADDING || op == OP_PREPENDING) && s != NULL) {
+ op = OP_NONE;
+ STRCPY(newval, origval);
+ }
+
+ // if no duplicate, move pointer to end of original value
+ if (s == NULL) {
+ s = (char *)origval + (int)STRLEN(origval);
+ }
+ }
+
+ // concatenate the two strings; add a ',' if needed
+ if (op == OP_ADDING || op == OP_PREPENDING) {
+ comma = ((flags & P_COMMA) && *origval != NUL && *newval != NUL);
+ if (op == OP_ADDING) {
+ len = (int)STRLEN(origval);
+ // Strip a trailing comma, would get 2.
+ if (comma && len > 1
+ && (flags & P_ONECOMMA) == P_ONECOMMA
+ && origval[len - 1] == ','
+ && origval[len - 2] != '\\') {
+ len--;
+ }
+ memmove(newval + len + comma, newval, strlen(newval) + 1);
+ memmove(newval, origval, (size_t)len);
+ } else {
+ len = (int)strlen(newval);
+ STRMOVE(newval + len + comma, origval);
+ }
+ if (comma) {
+ newval[len] = ',';
+ }
+ }
+
+ // Remove newval[] from origval[]. (Note: "len" has been set above and
+ // is used here).
+ if (op == OP_REMOVING) {
+ STRCPY(newval, origval);
+ if (*s) {
+ // may need to remove a comma
+ if (flags & P_COMMA) {
+ if (s == (char *)origval) {
+ // include comma after string
+ if (s[len] == ',') {
+ len++;
+ }
+ } else {
+ // include comma before string
+ s--;
+ len++;
+ }
+ }
+ STRMOVE(newval + (s - (char *)origval), s + len);
+ }
+ }
+
+ if (flags & P_FLAGLIST) {
+ // Remove flags that appear twice.
+ for (s = newval; *s;) {
+ // if options have P_FLAGLIST and P_ONECOMMA such as
+ // 'whichwrap'
+ if (flags & P_ONECOMMA) {
+ if (*s != ',' && *(s + 1) == ','
+ && vim_strchr(s + 2, *s) != NULL) {
+ // Remove the duplicated value and the next comma.
+ STRMOVE(s, s + 2);
+ continue;
+ }
+ } else {
+ if ((!(flags & P_COMMA) || *s != ',')
+ && vim_strchr(s + 1, *s) != NULL) {
+ STRMOVE(s, s + 1);
+ continue;
+ }
+ }
+ s++;
+ }
+ }
+
+ if (save_arg != NULL) {
+ arg = save_arg; // arg was temporarily changed, restore it
+ }
+ }
+
+ // Set the new value.
+ *(char_u **)(varp) = (char_u *)newval;
+
+ // origval may be freed by did_set_string_option(), make a copy.
+ saved_origval = (origval != NULL) ? xstrdup((char *)origval) : NULL;
+ saved_origval_l = (origval_l != NULL) ? xstrdup((char *)origval_l) : NULL;
+ saved_origval_g = (origval_g != NULL) ? xstrdup((char *)origval_g) : NULL;
+
+ // newval (and varp) may become invalid if the buffer is closed by
+ // autocommands.
+ saved_newval = (newval != NULL) ? xstrdup(newval) : NULL;
+
+ {
+ uint32_t *p = insecure_flag(curwin, opt_idx, opt_flags);
+ const int secure_saved = secure;
+
+ // When an option is set in the sandbox, from a modeline or in secure
+ // mode, then deal with side effects in secure mode. Also when the
+ // value was set with the P_INSECURE flag and is not completely
+ // replaced.
+ if ((opt_flags & OPT_MODELINE)
+ || sandbox != 0
+ || (op != OP_NONE && (*p & P_INSECURE))) {
+ secure = 1;
+ }
+
+ // Handle side effects, and set the global value for ":set" on local
+ // options. Note: when setting 'syntax' or 'filetype' autocommands may
+ // be triggered that can cause havoc.
+ *errmsg = did_set_string_option(opt_idx, (char **)varp, (char *)oldval,
+ errbuf, errbuflen,
+ opt_flags, value_checked);
+
+ secure = secure_saved;
+ }
+
+ if (*errmsg == NULL) {
+ if (!starting) {
+ trigger_optionsset_string(opt_idx, opt_flags, saved_origval, saved_origval_l,
+ saved_origval_g, saved_newval);
+ }
+ if (options[opt_idx].flags & P_UI_OPTION) {
+ ui_call_option_set(cstr_as_string(options[opt_idx].fullname),
+ STRING_OBJ(cstr_as_string(saved_newval)));
+ }
+ }
+ xfree(saved_origval);
+ xfree(saved_origval_l);
+ xfree(saved_origval_g);
+ xfree(saved_newval);
+
+ *argp = arg;
+ return *errmsg == NULL ? OK : FAIL;
+}
+
/// Parse 'arg' for option settings.
///
/// 'arg' may be IObuff, but only when no errors can be present and option
@@ -795,7 +1110,7 @@ int do_set(char *arg, int opt_flags)
int opt_idx;
char *errmsg;
char errbuf[80];
- char_u *startarg;
+ char *startarg;
int prefix; // 1: nothing, 0: "no", 2: "inv" in front of name
char_u nextchar; // next non-white char after option name
int afterchar; // character just after option name
@@ -806,9 +1121,7 @@ int do_set(char *arg, int opt_flags)
uint32_t flags; // flags for current option
char *varp = NULL; // pointer to variable for current option
int did_show = false; // already showed one value
- int adding; // "opt+=arg"
- int prepending; // "opt^=arg"
- int removing; // "opt-=arg"
+ set_op_T op = 0;
if (*arg == NUL) {
showoptions(0, opt_flags);
@@ -818,14 +1131,12 @@ int do_set(char *arg, int opt_flags)
while (*arg != NUL) { // loop to process all options
errmsg = NULL;
- startarg = (char_u *)arg; // remember for error message
+ startarg = arg; // remember for error message
if (STRNCMP(arg, "all", 3) == 0 && !isalpha(arg[3])
&& !(opt_flags & OPT_MODELINE)) {
- /*
- * ":set all" show all options.
- * ":set all&" set all options to their default value.
- */
+ // ":set all" show all options.
+ // ":set all&" set all options to their default value.
arg += 3;
if (*arg == '&') {
arg++;
@@ -834,7 +1145,7 @@ int do_set(char *arg, int opt_flags)
didset_options();
didset_options2();
ui_refresh_options();
- redraw_all_later(CLEAR);
+ redraw_all_later(UPD_CLEAR);
} else {
showoptions(1, opt_flags);
did_show = true;
@@ -897,25 +1208,23 @@ int do_set(char *arg, int opt_flags)
len++;
}
- adding = false;
- prepending = false;
- removing = false;
+ op = OP_NONE;
if (arg[len] != NUL && arg[len + 1] == '=') {
if (arg[len] == '+') {
- adding = true; // "+="
+ op = OP_ADDING; // "+="
len++;
} else if (arg[len] == '^') {
- prepending = true; // "^="
+ op = OP_PREPENDING; // "^="
len++;
} else if (arg[len] == '-') {
- removing = true; // "-="
+ op = OP_REMOVING; // "-="
len++;
}
}
nextchar = (uint8_t)arg[len];
if (opt_idx == -1 && key == 0) { // found a mismatch: skip
- errmsg = N_("E518: Unknown option");
+ errmsg = e_unknown_option;
goto skip;
}
@@ -926,13 +1235,13 @@ int do_set(char *arg, int opt_flags)
if (vim_strchr("=:!&<", nextchar) == NULL
&& (!(options[opt_idx].flags & P_BOOL)
|| nextchar == '?')) {
- errmsg = _(e_unsupportedoption);
+ errmsg = e_unsupportedoption;
}
goto skip;
}
flags = options[opt_idx].flags;
- varp = (char *)get_varp_scope(&(options[opt_idx]), opt_flags);
+ varp = get_varp_scope(&(options[opt_idx]), opt_flags);
} else {
flags = P_STRING;
}
@@ -953,11 +1262,11 @@ int do_set(char *arg, int opt_flags)
// Disallow changing some options from modelines.
if (opt_flags & OPT_MODELINE) {
if (flags & (P_SECURE | P_NO_ML)) {
- errmsg = N_("E520: Not allowed in a modeline");
+ errmsg = e_not_allowed_in_modeline;
goto skip;
}
if ((flags & P_MLE) && !p_mle) {
- errmsg = N_("E992: Not allowed in a modeline when 'modelineexpr' is off");
+ errmsg = e_not_allowed_in_modeline_when_modelineexpr_is_off;
goto skip;
}
// In diff mode some options are overruled. This avoids that
@@ -1001,9 +1310,7 @@ int do_set(char *arg, int opt_flags)
|| (prefix == 1
&& vim_strchr("=:&<", nextchar) == NULL
&& !(flags & P_BOOL))) {
- /*
- * print value
- */
+ // print value
if (did_show) {
msg_putchar('\n'); // cursor below last one
} else {
@@ -1025,7 +1332,7 @@ int do_set(char *arg, int opt_flags)
}
}
} else {
- errmsg = N_("E846: Key code not set");
+ errmsg = e_key_code_not_set;
goto skip;
}
if (nextchar != '?'
@@ -1033,7 +1340,6 @@ int do_set(char *arg, int opt_flags)
errmsg = e_trailing;
}
} else {
- int value_is_replaced = !prepending && !adding && !removing;
int value_checked = false;
if (flags & P_BOOL) { // boolean
@@ -1042,11 +1348,9 @@ int do_set(char *arg, int opt_flags)
goto skip;
}
- /*
- * ":set opt!": invert
- * ":set opt&": reset to default value
- * ":set opt<": reset to global value
- */
+ // ":set opt!": invert
+ // ":set opt&": reset to default value
+ // ":set opt<": reset to global value
if (nextchar == '!') {
value = *(int *)(varp) ^ 1;
} else if (nextchar == '&') {
@@ -1061,10 +1365,8 @@ int do_set(char *arg, int opt_flags)
OPT_GLOBAL);
}
} else {
- /*
- * ":set invopt": invert
- * ":set opt" or ":set noopt": set or reset
- */
+ // ":set invopt": invert
+ // ":set opt" or ":set noopt": set or reset
if (nextchar != NUL && !ascii_iswhite(afterchar)) {
errmsg = e_trailing;
goto skip;
@@ -1116,383 +1418,36 @@ int do_set(char *arg, int opt_flags)
}
} else if (*arg == '-' || ascii_isdigit(*arg)) {
// Allow negative, octal and hex numbers.
- vim_str2nr((char_u *)arg, NULL, &i, STR2NR_ALL, &value, NULL, 0, true);
+ vim_str2nr(arg, NULL, &i, STR2NR_ALL, &value, NULL, 0, true);
if (i == 0 || (arg[i] != NUL && !ascii_iswhite(arg[i]))) {
- errmsg = N_("E521: Number required after =");
+ errmsg = e_number_required_after_equal;
goto skip;
}
} else {
- errmsg = N_("E521: Number required after =");
+ errmsg = e_number_required_after_equal;
goto skip;
}
- if (adding) {
+ if (op == OP_ADDING) {
value = *(long *)varp + value;
}
- if (prepending) {
+ if (op == OP_PREPENDING) {
value = *(long *)varp * value;
}
- if (removing) {
+ if (op == OP_REMOVING) {
value = *(long *)varp - value;
}
errmsg = set_num_option(opt_idx, (char_u *)varp, (long)value,
errbuf, sizeof(errbuf),
opt_flags);
} else if (opt_idx >= 0) { // String.
- char_u *save_arg = NULL;
- char_u *s = NULL;
- char_u *oldval = NULL; // previous value if *varp
- char_u *newval;
- char_u *origval = NULL;
- char_u *origval_l = NULL;
- char_u *origval_g = NULL;
- char *saved_origval = NULL;
- char *saved_origval_l = NULL;
- char *saved_origval_g = NULL;
- char *saved_newval = NULL;
- unsigned newlen;
- int comma;
-
- // When using ":set opt=val" for a global option
- // with a local value the local value will be
- // reset, use the global value here.
- if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0
- && ((int)options[opt_idx].indir & PV_BOTH)) {
- varp = (char *)options[opt_idx].var;
- }
-
- // The old value is kept until we are sure that the
- // new value is valid.
- oldval = *(char_u **)varp;
-
- if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0) {
- origval_l = *(char_u **)get_varp_scope(&(options[opt_idx]), OPT_LOCAL);
- origval_g = *(char_u **)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL);
-
- // A global-local string option might have an empty
- // option as value to indicate that the global
- // value should be used.
- if (((int)options[opt_idx].indir & PV_BOTH) && origval_l == empty_option) {
- origval_l = origval_g;
- }
- }
-
- // When setting the local value of a global
- // option, the old value may be the global value.
- if (((int)options[opt_idx].indir & PV_BOTH) && (opt_flags & OPT_LOCAL)) {
- origval = *(char_u **)get_varp(&options[opt_idx]);
- } else {
- origval = oldval;
- }
-
- if (nextchar == '&') { // set to default v