changeset 31902:1c5ef864fe4c v9.0.1283

patch 9.0.1283: the code for setting options is too complicated Commit: https://github.com/vim/vim/commit/c72078b631832a1f3d7d8ee90560d54f7d7d3ffe Author: Yegappan Lakshmanan <yegappan@yahoo.com> Date: Sun Feb 5 16:02:35 2023 +0000 patch 9.0.1283: the code for setting options is too complicated Problem: The code for setting options is too complicated. Solution: Refactor the do_set() function. (Yegappan Lakshmanan, Lewis Russell, closes #11945)
author Bram Moolenaar <Bram@vim.org>
date Sun, 05 Feb 2023 17:15:04 +0100
parents e18577c21e54
children 1cedb94012da
files src/option.c src/version.c
diffstat 2 files changed, 464 insertions(+), 304 deletions(-) [+]
line wrap: on
line diff
--- a/src/option.c
+++ b/src/option.c
@@ -1211,6 +1211,9 @@ ex_set(exarg_T *eap)
     }
 }
 
+/*
+ * :set operator types
+ */
 typedef enum {
     OP_NONE = 0,
     OP_ADDING,		// "opt+=arg"
@@ -1218,6 +1221,181 @@ typedef enum {
     OP_REMOVING,	// "opt-=arg"
 } set_op_T;
 
+typedef enum {
+    PREFIX_NO = 0,	// "no" prefix
+    PREFIX_NONE,	// no prefix
+    PREFIX_INV,		// "inv" prefix
+} set_prefix_T;
+
+/*
+ * Return the prefix type for the option name in *argp.
+ */
+    static set_prefix_T
+get_option_prefix(char_u **argp)
+{
+    int		prefix = PREFIX_NONE;
+    char_u	*arg = *argp;
+
+    if (STRNCMP(arg, "no", 2) == 0 && STRNCMP(arg, "novice", 6) != 0)
+    {
+	prefix = PREFIX_NO;
+	arg += 2;
+    }
+    else if (STRNCMP(arg, "inv", 3) == 0)
+    {
+	prefix = PREFIX_INV;
+	arg += 3;
+    }
+
+    *argp = arg;
+    return prefix;
+}
+
+/*
+ * Parse the option name in "arg" and return the option index in "*opt_idxp",
+ * and the option name length in "*lenp".  For a <t_xx> option, return the key
+ * number in "*keyp".
+ *
+ * Returns FAIL if an option starting with "<" doesn't end with a ">",
+ * otherwise returns OK.
+ */
+    static int
+parse_option_name(char_u *arg, int *opt_idxp, int *lenp, int *keyp)
+{
+    int	key = 0;
+    int	len;
+    int	opt_idx;
+
+    if (*arg == '<')
+    {
+	opt_idx = -1;
+	// look out for <t_>;>
+	if (arg[1] == 't' && arg[2] == '_' && arg[3] && arg[4])
+	    len = 5;
+	else
+	{
+	    len = 1;
+	    while (arg[len] != NUL && arg[len] != '>')
+		++len;
+	}
+	if (arg[len] != '>')
+	    return FAIL;
+
+	arg[len] = NUL;			    // put NUL after name
+	if (arg[1] == 't' && arg[2] == '_') // could be term code
+	    opt_idx = findoption(arg + 1);
+	arg[len++] = '>';		    // restore '>'
+	if (opt_idx == -1)
+	    key = find_key_option(arg + 1, TRUE);
+    }
+    else
+    {
+	int	nextchar;   // next non-white char after option name
+
+	len = 0;
+	/*
+	 * The two characters after "t_" may not be alphanumeric.
+	 */
+	if (arg[0] == 't' && arg[1] == '_' && arg[2] && arg[3])
+	    len = 4;
+	else
+	    while (ASCII_ISALNUM(arg[len]) || arg[len] == '_')
+		++len;
+	nextchar = arg[len];
+	arg[len] = NUL;			    // put NUL after name
+	opt_idx = findoption(arg);
+	arg[len] = nextchar;		    // restore nextchar
+	if (opt_idx == -1)
+	    key = find_key_option(arg, FALSE);
+    }
+
+    *keyp = key;
+    *lenp = len;
+    *opt_idxp = opt_idx;
+
+    return OK;
+}
+
+/*
+ * Get the option operator (+=, ^=, -=).
+ */
+    static set_op_T
+get_opt_op(char_u *arg)
+{
+    set_op_T op = OP_NONE;
+
+    if (*arg != NUL && *(arg + 1) == '=')
+    {
+	if (*arg == '+')
+	    op = OP_ADDING;		// "+="
+	else if (*arg == '^')
+	    op = OP_PREPENDING;		// "^="
+	else if (*arg == '-')
+	    op = OP_REMOVING;		// "-="
+    }
+
+    return op;
+}
+
+/*
+ * Validate whether the value of the option in "opt_idx" can be changed.
+ * Returns FAIL if the option can be skipped or cannot be changed. Returns OK
+ * if it can be changed.
+ */
+    static int
+validate_opt_idx(int opt_idx, int opt_flags, long_u flags, char **errmsg)
+{
+    // Skip all options that are not window-local (used when showing
+    // an already loaded buffer in a window).
+    if ((opt_flags & OPT_WINONLY)
+	    && (opt_idx < 0 || options[opt_idx].var != VAR_WIN))
+	return FAIL;
+
+    // Skip all options that are window-local (used for :vimgrep).
+    if ((opt_flags & OPT_NOWIN) && opt_idx >= 0
+	    && options[opt_idx].var == VAR_WIN)
+	return FAIL;
+
+    // Disallow changing some options from modelines.
+    if (opt_flags & OPT_MODELINE)
+    {
+	if (flags & (P_SECURE | P_NO_ML))
+	{
+	    *errmsg = e_not_allowed_in_modeline;
+	    return FAIL;
+	}
+	if ((flags & P_MLE) && !p_mle)
+	{
+	    *errmsg = e_not_allowed_in_modeline_when_modelineexpr_is_off;
+	    return FAIL;
+	}
+#ifdef FEAT_DIFF
+	// In diff mode some options are overruled.  This avoids that
+	// 'foldmethod' becomes "marker" instead of "diff" and that
+	// "wrap" gets set.
+	if (curwin->w_p_diff
+		&& opt_idx >= 0  // shut up coverity warning
+		&& (
+# ifdef FEAT_FOLDING
+		    options[opt_idx].indir == PV_FDM ||
+# endif
+		    options[opt_idx].indir == PV_WRAP))
+	    return FAIL;
+#endif
+    }
+
+#ifdef HAVE_SANDBOX
+    // Disallow changing some options in the sandbox
+    if (sandbox != 0 && (flags & P_SECURE))
+    {
+	*errmsg = e_not_allowed_in_sandbox;
+	return FAIL;
+    }
+#endif
+
+    return OK;
+}
+
 /*
  * Part of do_set() for string options.
  * Returns FAIL on failure, do not process further options.
@@ -1637,6 +1815,263 @@ do_set_string(
 }
 
 /*
+ * Set a boolean option
+ */
+    static char *
+do_set_option_bool(
+    int		opt_idx,
+    int		opt_flags,
+    set_prefix_T prefix,
+    long_u	flags,
+    char_u	*varp,
+    int		nextchar,
+    int		afterchar,
+    int		cp_val)
+
+{
+    varnumber_T	value;
+
+    if (nextchar == '=' || nextchar == ':')
+	return e_invalid_argument;
+
+    /*
+     * ":set opt!": invert
+     * ":set opt&": reset to default value
+     * ":set opt<": reset to global value
+     */
+    if (nextchar == '!')
+	value = *(int *)(varp) ^ 1;
+    else if (nextchar == '&')
+	value = (int)(long)(long_i)options[opt_idx].def_val[
+	    ((flags & P_VI_DEF) || cp_val) ? VI_DEFAULT : VIM_DEFAULT];
+    else if (nextchar == '<')
+    {
+	// For 'autoread' -1 means to use global value.
+	if ((int *)varp == &curbuf->b_p_ar && opt_flags == OPT_LOCAL)
+	    value = -1;
+	else
+	    value = *(int *)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL);
+    }
+    else
+    {
+	/*
+	 * ":set invopt": invert
+	 * ":set opt" or ":set noopt": set or reset
+	 */
+	if (nextchar != NUL && !VIM_ISWHITE(afterchar))
+	    return e_trailing_characters;
+	if (prefix == PREFIX_INV)
+	    value = *(int *)(varp) ^ 1;
+	else
+	    value = prefix == PREFIX_NO ? 0 : 1;
+    }
+
+    return set_bool_option(opt_idx, varp, (int)value, opt_flags);
+}
+
+/*
+ * Set a numeric option
+ */
+    static char *
+do_set_option_numeric(
+    int		opt_idx,
+    int		opt_flags,
+    char_u	**argp,
+    int		nextchar,
+    set_op_T	op,
+    long_u	flags,
+    int		cp_val,
+    char_u	*varp,
+    char	*errbuf,
+    size_t	errbuflen)
+{
+    char_u		*arg = *argp;
+    varnumber_T		value;
+    int			i;
+    char		*errmsg = NULL;
+
+    /*
+     * Different ways to set a number option:
+     * &	    set to default value
+     * <	    set to global value
+     * <xx>	    accept special key codes for 'wildchar'
+     * c	    accept any non-digit for 'wildchar'
+     * [-]0-9   set number
+     * other    error
+     */
+    ++arg;
+    if (nextchar == '&')
+	value = (long)(long_i)options[opt_idx].def_val[
+	    ((flags & P_VI_DEF) || cp_val) ? VI_DEFAULT : VIM_DEFAULT];
+    else if (nextchar == '<')
+    {
+	// For 'undolevels' NO_LOCAL_UNDOLEVEL means to
+	// use the global value.
+	if ((long *)varp == &curbuf->b_p_ul && opt_flags == OPT_LOCAL)
+	    value = NO_LOCAL_UNDOLEVEL;
+	else
+	    value = *(long *)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL);
+    }
+    else if (((long *)varp == &p_wc || (long *)varp == &p_wcm)
+	    && (*arg == '<'
+		|| *arg == '^'
+		|| (*arg != NUL
+		    && (!arg[1] || VIM_ISWHITE(arg[1]))
+		    && !VIM_ISDIGIT(*arg))))
+    {
+	value = string_to_key(arg, FALSE);
+	if (value == 0 && (long *)varp != &p_wcm)
+	{
+	    errmsg = e_invalid_argument;
+	    goto skip;
+	}
+    }
+    else if (*arg == '-' || VIM_ISDIGIT(*arg))
+    {
+	// Allow negative (for 'undolevels'), octal and hex numbers.
+	vim_str2nr(arg, NULL, &i, STR2NR_ALL, &value, NULL, 0, TRUE);
+	if (i == 0 || (arg[i] != NUL && !VIM_ISWHITE(arg[i])))
+	{
+	    errmsg = e_number_required_after_equal;
+	    goto skip;
+	}
+    }
+    else
+    {
+	errmsg = e_number_required_after_equal;
+	goto skip;
+    }
+
+    if (op == OP_ADDING)
+	value = *(long *)varp + value;
+    else if (op == OP_PREPENDING)
+	value = *(long *)varp * value;
+    else if (op == OP_REMOVING)
+	value = *(long *)varp - value;
+
+    errmsg = set_num_option(opt_idx, varp, value, errbuf, errbuflen,
+								opt_flags);
+
+skip:
+    *argp = arg;
+    return errmsg;
+}
+
+/*
+ * Set a key code (t_xx) option
+ */
+    static char *
+do_set_option_keycode(char_u **argp, char_u *key_name, int nextchar)
+{
+    char_u	*arg = *argp;
+    char_u	*p;
+
+    if (nextchar == '&')
+    {
+	if (add_termcap_entry(key_name, TRUE) == FAIL)
+	    return e_not_found_in_termcap;
+    }
+    else
+    {
+	++arg; // jump to after the '=' or ':'
+	for (p = arg; *p && !VIM_ISWHITE(*p); ++p)
+	    if (*p == '\\' && p[1] != NUL)
+		++p;
+	nextchar = *p;
+	*p = NUL;
+	add_termcode(key_name, arg, FALSE);
+	*p = nextchar;
+    }
+    if (full_screen)
+	ttest(FALSE);
+    redraw_all_later(UPD_CLEAR);
+
+    *argp = arg;
+    return NULL;
+}
+
+/*
+ * Set an option to a new value.
+ */
+    static char *
+do_set_option_value(
+    int		opt_idx,
+    int		opt_flags,
+    char_u	**argp,
+    set_prefix_T prefix,
+    set_op_T	op,
+    long_u	flags,
+    char_u	*varp,
+    char_u	*key_name,
+    int		nextchar,
+    int		afterchar,
+    int		cp_val,
+    int		*stopopteval,
+    char	*errbuf,
+    size_t	errbuflen)
+{
+    int		value_checked = FALSE;
+    char	*errmsg = NULL;
+    char_u	*arg = *argp;
+
+    if (flags & P_BOOL)
+    {
+	// boolean option
+	errmsg = do_set_option_bool(opt_idx, opt_flags, prefix, flags, varp,
+						nextchar, afterchar, cp_val);
+	if (errmsg != NULL)
+	    goto skip;
+    }
+    else
+    {
+	// numeric or string option
+	if (vim_strchr((char_u *)"=:&<", nextchar) == NULL
+		|| prefix != PREFIX_NONE)
+	{
+	    errmsg = e_invalid_argument;
+	    goto skip;
+	}
+
+	if (flags & P_NUM)
+	{
+	    // numeric option
+	    errmsg = do_set_option_numeric(opt_idx, opt_flags, &arg, nextchar,
+						op, flags, cp_val, varp,
+						errbuf, errbuflen);
+	    if (errmsg != NULL)
+		goto skip;
+	}
+	else if (opt_idx >= 0)
+	{
+	    // string option
+	    if (do_set_string(opt_idx, opt_flags, &arg, nextchar, op, flags,
+				    cp_val, varp, errbuf, &value_checked,
+				    &errmsg) == FAIL)
+	    {
+		if (errmsg != NULL)
+		    goto skip;
+		*stopopteval = TRUE;
+		goto skip;
+	    }
+	}
+	else
+	{
+	    // key code option
+	    errmsg = do_set_option_keycode(&arg, key_name, nextchar);
+	    if (errmsg != NULL)
+		goto skip;
+	}
+    }
+
+    if (opt_idx >= 0)
+	did_set_option(opt_idx, opt_flags, op == OP_NONE, value_checked);
+
+skip:
+    *argp = arg;
+    return errmsg;
+}
+
+/*
  * Set an option to a new value.
  * Return NULL if OK, return an untranslated error message when something is
  * wrong.  "errbuf[errbuflen]" can be used to create the error message.
@@ -1652,78 +2087,27 @@ do_set_option(
     char	*errbuf,
     size_t	errbuflen)
 {
-    char	*errmsg = NULL;
-    int		prefix;	    // 1: nothing, 0: "no", 2: "inv" in front of name
-    int		nextchar;   // next non-white char after option name
-    int		afterchar;  // character just after option name
-    char_u	*arg = *argp;
-    int		key;
     int		opt_idx;
-    int		len;
-    set_op_T	op = 0;
-    long_u	flags;		    // flags for current option
-    char_u	*varp = NULL;	    // pointer to variable for current option
+    char_u	*arg;
+    set_prefix_T prefix;	// no prefix, "no" prefix or "inv" prefix
+    set_op_T	op;
+    long_u	flags;		// flags for current option
+    char_u	*varp;		// pointer to variable for current option
     char_u	key_name[2];
-    int		cp_val = 0;
-    varnumber_T	value;
-    int		i;
-
-    prefix = 1;
-    if (STRNCMP(arg, "no", 2) == 0 && STRNCMP(arg, "novice", 6) != 0)
-    {
-	prefix = 0;
-	arg += 2;
-    }
-    else if (STRNCMP(arg, "inv", 3) == 0)
-    {
-	prefix = 2;
-	arg += 3;
-    }
+    int		nextchar;	// next non-white char after option name
+    int		afterchar;	// character just after option name
+    int		cp_val;
+    char	*errmsg = NULL;
+    int		key;
+    int		len;
+
+    prefix = get_option_prefix(argp);
+    arg = *argp;
 
     // find end of name
     key = 0;
-    if (*arg == '<')
-    {
-	opt_idx = -1;
-	// look out for <t_>;>
-	if (arg[1] == 't' && arg[2] == '_' && arg[3] && arg[4])
-	    len = 5;
-	else
-	{
-	    len = 1;
-	    while (arg[len] != NUL && arg[len] != '>')
-		++len;
-	}
-	if (arg[len] != '>')
-	{
-	    errmsg = e_invalid_argument;
-	    goto skip;
-	}
-	arg[len] = NUL;			    // put NUL after name
-	if (arg[1] == 't' && arg[2] == '_') // could be term code
-	    opt_idx = findoption(arg + 1);
-	arg[len++] = '>';		    // restore '>'
-	if (opt_idx == -1)
-	    key = find_key_option(arg + 1, TRUE);
-    }
-    else
-    {
-	len = 0;
-	/*
-	 * The two characters after "t_" may not be alphanumeric.
-	 */
-	if (arg[0] == 't' && arg[1] == '_' && arg[2] && arg[3])
-	    len = 4;
-	else
-	    while (ASCII_ISALNUM(arg[len]) || arg[len] == '_')
-		++len;
-	nextchar = arg[len];
-	arg[len] = NUL;			    // put NUL after name
-	opt_idx = findoption(arg);
-	arg[len] = nextchar;		    // restore nextchar
-	if (opt_idx == -1)
-	    key = find_key_option(arg, FALSE);
-    }
+    if (parse_option_name(arg, &opt_idx, &len, &key) == FAIL)
+	return e_invalid_argument;
 
     // remember character after option name
     afterchar = arg[len];
@@ -1748,25 +2132,10 @@ do_set_option(
 	while (VIM_ISWHITE(arg[len]))
 	    ++len;
 
-    op = OP_NONE;
-    if (arg[len] != NUL && arg[len + 1] == '=')
-    {
-	if (arg[len] == '+')
-	{
-	    op = OP_ADDING;		// "+="
-	    ++len;
-	}
-	else if (arg[len] == '^')
-	{
-	    op = OP_PREPENDING;		// "^="
-	    ++len;
-	}
-	else if (arg[len] == '-')
-	{
-	    op = OP_REMOVING;		// "-="
-	    ++len;
-	}
-    }
+    op = get_opt_op(arg + len);
+    if (op != OP_NONE)
+	len++;
+
     nextchar = arg[len];
 
     if (opt_idx == -1 && key == 0)	// found a mismatch: skip
@@ -1810,54 +2179,10 @@ do_set_option(
 	}
     }
 
-    // Skip all options that are not window-local (used when showing
-    // an already loaded buffer in a window).
-    if ((opt_flags & OPT_WINONLY)
-	    && (opt_idx < 0 || options[opt_idx].var != VAR_WIN))
-	goto skip;
-
-    // Skip all options that are window-local (used for :vimgrep).
-    if ((opt_flags & OPT_NOWIN) && opt_idx >= 0
-	    && options[opt_idx].var == VAR_WIN)
+    // Make sure the option value can be changed.
+    if (validate_opt_idx(opt_idx, opt_flags, flags, &errmsg) == FAIL)
 	goto skip;
 
-    // Disallow changing some options from modelines.
-    if (opt_flags & OPT_MODELINE)
-    {
-	if (flags & (P_SECURE | P_NO_ML))
-	{
-	    errmsg = e_not_allowed_in_modeline;
-	    goto skip;
-	}
-	if ((flags & P_MLE) && !p_mle)
-	{
-	    errmsg = e_not_allowed_in_modeline_when_modelineexpr_is_off;
-	    goto skip;
-	}
-#ifdef FEAT_DIFF
-	// In diff mode some options are overruled.  This avoids that
-	// 'foldmethod' becomes "marker" instead of "diff" and that
-	// "wrap" gets set.
-	if (curwin->w_p_diff
-		&& opt_idx >= 0  // shut up coverity warning
-		&& (
-#ifdef FEAT_FOLDING
-		    options[opt_idx].indir == PV_FDM ||
-#endif
-		    options[opt_idx].indir == PV_WRAP))
-	    goto skip;
-#endif
-    }
-
-#ifdef HAVE_SANDBOX
-    // Disallow changing some options in the sandbox
-    if (sandbox != 0 && (flags & P_SECURE))
-    {
-	errmsg = e_not_allowed_in_sandbox;
-	goto skip;
-    }
-#endif
-
     if (vim_strchr((char_u *)"?=:!&<", nextchar) != NULL)
     {
 	arg += len;
@@ -1888,7 +2213,7 @@ do_set_option(
      * allows only one '=' character per "set" command line. grrr. (jw)
      */
     if (nextchar == '?'
-	    || (prefix == 1
+	    || (prefix == PREFIX_NONE
 		&& vim_strchr((char_u *)"=:&<", nextchar) == NULL
 		&& !(flags & P_BOOL)))
     {
@@ -1939,177 +2264,10 @@ do_set_option(
     }
     else
     {
-	int value_checked = FALSE;
-
-	if (flags & P_BOOL)		    // boolean
-	{
-	    if (nextchar == '=' || nextchar == ':')
-	    {
-		errmsg = e_invalid_argument;
-		goto skip;
-	    }
-
-	    /*
-	     * ":set opt!": invert
-	     * ":set opt&": reset to default value
-	     * ":set opt<": reset to global value
-	     */
-	    if (nextchar == '!')
-		value = *(int *)(varp) ^ 1;
-	    else if (nextchar == '&')
-		value = (int)(long)(long_i)options[opt_idx].def_val[
-		    ((flags & P_VI_DEF) || cp_val)
-			?  VI_DEFAULT : VIM_DEFAULT];
-	    else if (nextchar == '<')
-	    {
-		// For 'autoread' -1 means to use global value.
-		if ((int *)varp == &curbuf->b_p_ar
-			&& opt_flags == OPT_LOCAL)
-		    value = -1;
-		else
-		    value = *(int *)get_varp_scope(&(options[opt_idx]),
-			    OPT_GLOBAL);
-	    }
-	    else
-	    {
-		/*
-		 * ":set invopt": invert
-		 * ":set opt" or ":set noopt": set or reset
-		 */
-		if (nextchar != NUL && !VIM_ISWHITE(afterchar))
-		{
-		    errmsg = e_trailing_characters;
-		    goto skip;
-		}
-		if (prefix == 2)	// inv
-		    value = *(int *)(varp) ^ 1;
-		else
-		    value = prefix;
-	    }
-
-	    errmsg = set_bool_option(opt_idx, varp, (int)value,
-		    opt_flags);
-	}
-	else				    // numeric or string
-	{
-	    if (vim_strchr((char_u *)"=:&<", nextchar) == NULL
-		    || prefix != 1)
-	    {
-		errmsg = e_invalid_argument;
-		goto skip;
-	    }
-
-	    if (flags & P_NUM)		    // numeric
-	    {
-		/*
-		 * Different ways to set a number option:
-		 * &	    set to default value
-		 * <	    set to global value
-		 * <xx>	    accept special key codes for 'wildchar'
-		 * c	    accept any non-digit for 'wildchar'
-		 * [-]0-9   set number
-		 * other    error
-		 */
-		++arg;
-		if (nextchar == '&')
-		    value = (long)(long_i)options[opt_idx].def_val[
-			((flags & P_VI_DEF) || cp_val)
-			    ?  VI_DEFAULT : VIM_DEFAULT];
-		else if (nextchar == '<')
-		{
-		    // For 'undolevels' NO_LOCAL_UNDOLEVEL means to
-		    // use the global value.
-		    if ((long *)varp == &curbuf->b_p_ul
-			    && opt_flags == OPT_LOCAL)
-			value = NO_LOCAL_UNDOLEVEL;
-		    else
-			value = *(long *)get_varp_scope(
-				&(options[opt_idx]), OPT_GLOBAL);
-		}
-		else if (((long *)varp == &p_wc
-			    || (long *)varp == &p_wcm)
-			&& (*arg == '<'
-			    || *arg == '^'
-			    || (*arg != NUL
-				&& (!arg[1] || VIM_ISWHITE(arg[1]))
-				&& !VIM_ISDIGIT(*arg))))
-		{
-		    value = string_to_key(arg, FALSE);
-		    if (value == 0 && (long *)varp != &p_wcm)
-		    {
-			errmsg = e_invalid_argument;
-			goto skip;
-		    }
-		}
-		else if (*arg == '-' || VIM_ISDIGIT(*arg))
-		{
-		    // Allow negative (for 'undolevels'), octal and
-		    // hex numbers.
-		    vim_str2nr(arg, NULL, &i, STR2NR_ALL,
-			    &value, NULL, 0, TRUE);
-		    if (i == 0 || (arg[i] != NUL
-				&& !VIM_ISWHITE(arg[i])))
-		    {
-			errmsg = e_number_required_after_equal;
-			goto skip;
-		    }
-		}
-		else
-		{
-		    errmsg = e_number_required_after_equal;
-		    goto skip;
-		}
-
-		if (op == OP_ADDING)
-		    value = *(long *)varp + value;
-		else if (op == OP_PREPENDING)
-		    value = *(long *)varp * value;
-		else if (op == OP_REMOVING)
-		    value = *(long *)varp - value;
-		errmsg = set_num_option(opt_idx, varp, value,
-			errbuf, errbuflen, opt_flags);
-	    }
-	    else if (opt_idx >= 0)		    // string
-	    {
-		if (do_set_string(opt_idx, opt_flags, &arg, nextchar,
-			    op, flags, cp_val, varp, errbuf,
-			    &value_checked, &errmsg) == FAIL)
-		{
-		    if (errmsg != NULL)
-			goto skip;
-		    *stopopteval = TRUE;
-		    goto skip;
-		}
-	    }
-	    else	    // key code option
-	    {
-		char_u	    *p;
-
-		if (nextchar == '&')
-		{
-		    if (add_termcap_entry(key_name, TRUE) == FAIL)
-			errmsg = e_not_found_in_termcap;
-		}
-		else
-		{
-		    ++arg; // jump to after the '=' or ':'
-		    for (p = arg; *p && !VIM_ISWHITE(*p); ++p)
-			if (*p == '\\' && p[1] != NUL)
-			    ++p;
-		    nextchar = *p;
-		    *p = NUL;
-		    add_termcode(key_name, arg, FALSE);
-		    *p = nextchar;
-		}
-		if (full_screen)
-		    ttest(FALSE);
-		redraw_all_later(UPD_CLEAR);
-	    }
-	}
-
-	if (opt_idx >= 0)
-	    did_set_option(
-		    opt_idx, opt_flags, op == OP_NONE, value_checked);
+	errmsg = do_set_option_value(opt_idx, opt_flags, &arg, prefix, op,
+					flags, varp, key_name, nextchar,
+					afterchar, cp_val, stopopteval, errbuf,
+					errbuflen);
     }
 
 skip:
--- a/src/version.c
+++ b/src/version.c
@@ -696,6 +696,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    1283,
+/**/
     1282,
 /**/
     1281,