changeset 33399:95db67c7b754 v9.0.1958

patch 9.0.1958: cannot complete option values Commit: https://github.com/vim/vim/commit/900894b09a95398dfc75599e9f0aa2ea25723384 Author: Yee Cheng Chin <ychin.git@gmail.com> Date: Fri Sep 29 20:42:32 2023 +0200 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: #13182 Signed-off-by: Christian Brabandt <cb@256bit.org> Co-authored-by: Yee Cheng Chin <ychin.git@gmail.com>
author Christian Brabandt <cb@256bit.org>
date Fri, 29 Sep 2023 20:45:04 +0200
parents a877b968e471
children cbf1ccce432c
files runtime/doc/cmdline.txt runtime/doc/options.txt runtime/doc/tags src/autocmd.c src/clipboard.c src/cmdexpand.c src/diff.c src/ex_getln.c src/highlight.c src/indent.c src/mbyte.c src/option.c src/option.h src/optiondefs.h src/optionstr.c src/popupwin.c src/proto/autocmd.pro src/proto/cmdexpand.pro src/proto/mbyte.pro src/proto/option.pro src/proto/optionstr.pro src/proto/screen.pro src/screen.c src/spellsuggest.c src/structs.h src/term.c src/testdir/test_options.vim src/version.c src/vim.h
diffstat 29 files changed, 2598 insertions(+), 739 deletions(-) [+]
line wrap: on
line diff
--- a/runtime/doc/cmdline.txt
+++ b/runtime/doc/cmdline.txt
@@ -517,16 +517,26 @@ example, to match only files that end in
 	:e *.c$
 This will not match a file ending in ".cpp".  Without the "$" it does match.
 
-The old value of an option can be obtained by hitting 'wildchar' just after
-the '='.  For example, typing 'wildchar' after ":set dir=" will insert the
-current value of 'dir'.  This overrules file name completion for the options
-that take a file name.
-
 If you would like using <S-Tab> for CTRL-P in an xterm, put this command in
 your .cshrc: >
 	xmodmap -e "keysym Tab = Tab Find"
 And this in your .vimrc: >
 	:cmap <Esc>[1~ <C-P>
+<						*complete-set-option*
+When setting an option using |:set=|, the old value of an option can be
+obtained by hitting 'wildchar' just after the '='.  For example, typing
+'wildchar' after ":set dir=" will insert the current value of 'dir'.  This
+overrules file name completion for the options that take a file name.
+
+When using |:set=|, |:set+=|, or |:set^=|, string options that have
+pre-defined names or syntax (e.g. 'diffopt', 'listchars') or are a list of
+single-character flags (e.g. 'shortmess') will also present a list of possible
+values for completion when using 'wildchar'.
+
+When using |:set-=|, comma-separated options like 'diffopt' or 'backupdir'
+will show each item separately.  Flag list options like 'shortmess' will show
+both the entire old value and the individual flags.  Otherwise completion will
+just fill in with the entire old value.
 
 ==============================================================================
 3. Ex command-lines					*cmdline-lines*
--- a/runtime/doc/options.txt
+++ b/runtime/doc/options.txt
@@ -71,7 +71,7 @@ 1. Setting options					*set-option* *E76
 			  'ttytype'
 			Warning: This may have a lot of side effects.
 
-						*:set-args* *E487* *E521*
+						*:set-args* *:set=* *E487* *E521*
 :se[t] {option}={value}		or
 :se[t] {option}:{value}
 			Set string or number option to {value}.
@@ -79,7 +79,9 @@ 1. Setting options					*set-option* *E76
 			hex (preceded with 0x) or octal (preceded with '0').
 			The old value can be inserted by typing 'wildchar' (by
 			default this is a <Tab> or CTRL-E if 'compatible' is
-			set).  See |cmdline-completion|.
+			set). Many string options with fixed syntax and names
+			also support completing known values.  See
+			|cmdline-completion| and |complete-set-option|.
 			White space between {option} and '=' is allowed and
 			will be ignored.  White space between '=' and {value}
 			is not allowed.
@@ -113,6 +115,9 @@ 1. Setting options					*set-option* *E76
 			When the option is a list of flags, {value} must be
 			exactly as they appear in the option.  Remove flags
 			one by one to avoid problems.
+			The individual values from a comma separated list or
+			list of flags can be inserted by typing 'wildchar'.
+			See |complete-set-option|.
 			Also see |:set-args| above.
 
 The {option} arguments to ":set" may be repeated.  For example: >
--- a/runtime/doc/tags
+++ b/runtime/doc/tags
@@ -3208,6 +3208,7 @@ 90.5	usr_90.txt	/*90.5*
 :set-inv	options.txt	/*:set-inv*
 :set-termcap	options.txt	/*:set-termcap*
 :set-verbose	options.txt	/*:set-verbose*
+:set=	options.txt	/*:set=*
 :set^=	options.txt	/*:set^=*
 :set_env	options.txt	/*:set_env*
 :setf	options.txt	/*:setf*
@@ -6484,6 +6485,7 @@ complete-items	insert.txt	/*complete-ite
 complete-popup	insert.txt	/*complete-popup*
 complete-popuphidden	insert.txt	/*complete-popuphidden*
 complete-script-local-functions	cmdline.txt	/*complete-script-local-functions*
+complete-set-option	cmdline.txt	/*complete-set-option*
 complete_CTRL-E	insert.txt	/*complete_CTRL-E*
 complete_CTRL-Y	insert.txt	/*complete_CTRL-Y*
 complete_add()	builtin.txt	/*complete_add()*
--- a/src/autocmd.c
+++ b/src/autocmd.c
@@ -2737,6 +2737,16 @@ get_event_name(expand_T *xp UNUSED, int 
     return (char_u *)event_names[idx - augroups.ga_len].name;
 }
 
+/*
+ * Function given to ExpandGeneric() to obtain the list of event names. Don't
+ * include groups.
+ */
+    char_u *
+get_event_name_no_group(expand_T *xp UNUSED, int idx)
+{
+    return (char_u *)event_names[idx].name;
+}
+
 
 #if defined(FEAT_EVAL) || defined(PROTO)
 /*
--- a/src/clipboard.c
+++ b/src/clipboard.c
@@ -1266,6 +1266,7 @@ did_set_clipboard(optset_T *args UNUSED)
 
     for (p = p_cb; *p != NUL; )
     {
+	// Note: Keep this in sync with p_cb_values.
 	if (STRNCMP(p, "unnamed", 7) == 0 && (p[7] == ',' || p[7] == NUL))
 	{
 	    new_unnamed |= CLIP_UNNAMED;
--- a/src/cmdexpand.c
+++ b/src/cmdexpand.c
@@ -15,9 +15,6 @@
 
 static int	cmd_showtail;	// Only show path tail in lists ?
 
-static int      ExpandGeneric(char_u *pat, expand_T *xp, regmatch_T *regmatch,
-			      char_u ***matches, int *numMatches,
-			      char_u *((*func)(expand_T *, int)), int escaped);
 static int	ExpandFromContext(expand_T *xp, char_u *, char_u ***, int *, int);
 static char_u	*showmatches_gettail(char_u *s);
 static int	expand_showtail(expand_T *xp);
@@ -54,6 +51,8 @@ cmdline_fuzzy_completion_supported(expan
 	    && xp->xp_context != EXPAND_FILETYPE
 	    && xp->xp_context != EXPAND_HELP
 	    && 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
@@ -3093,6 +3092,10 @@ ExpandFromContext(
     if (xp->xp_context == EXPAND_SETTINGS
 	    || xp->xp_context == EXPAND_BOOL_SETTINGS)
 	ret = ExpandSettings(xp, &regmatch, pat, numMatches, matches, fuzzy);
+    else if (xp->xp_context == EXPAND_STRING_SETTING)
+	ret = ExpandStringSetting(xp, &regmatch, numMatches, matches);
+    else if (xp->xp_context == EXPAND_SETTING_SUBTRACT)
+	ret = ExpandSettingSubtract(xp, &regmatch, numMatches, matches);
     else if (xp->xp_context == EXPAND_MAPPINGS)
 	ret = ExpandMappings(pat, &regmatch, numMatches, matches);
 #if defined(FEAT_EVAL)
@@ -3121,7 +3124,7 @@ ExpandFromContext(
  *
  * Returns OK when no problems encountered, FAIL for error (out of memory).
  */
-    static int
+    int
 ExpandGeneric(
     char_u	*pat,
     expand_T	*xp,
@@ -3226,6 +3229,7 @@ ExpandGeneric(
     // applies to the completion context. Menus and scriptnames should be kept
     // in the specified order.
     if (!fuzzy && xp->xp_context != EXPAND_MENUNAMES
+					&& xp->xp_context != EXPAND_STRING_SETTING
 					&& xp->xp_context != EXPAND_MENUS
 					&& xp->xp_context != EXPAND_SCRIPTNAMES)
 	sort_matches = TRUE;
--- a/src/diff.c
+++ b/src/diff.c
@@ -2266,6 +2266,7 @@ diffopt_changed(void)
     p = p_dip;
     while (*p != NUL)
     {
+	// Note: Keep this in sync with p_dip_values
 	if (STRNCMP(p, "filler", 6) == 0)
 	{
 	    p += 6;
@@ -2343,6 +2344,7 @@ diffopt_changed(void)
 	}
 	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)
 	    {
--- a/src/ex_getln.c
+++ b/src/ex_getln.c
@@ -2650,6 +2650,7 @@ check_opt_wim(void)
 
     for (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] != ':')
--- a/src/highlight.c
+++ b/src/highlight.c
@@ -3814,6 +3814,7 @@ highlight_changed(void)
 		if (attr > HL_ALL)  // Combination with ':' is not allowed.
 		    return FAIL;
 
+		// Note: Keep this in sync with expand_set_highlight().
 		switch (*p)
 		{
 		    case 'b':	attr |= HL_BOLD;
--- a/src/indent.c
+++ b/src/indent.c
@@ -871,6 +871,7 @@ briopt_check(win_T *wp)
     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] == '-' && VIM_ISDIGIT(p[7])) || VIM_ISDIGIT(p[6])))
 	{
--- a/src/mbyte.c
+++ b/src/mbyte.c
@@ -5782,3 +5782,16 @@ f_charclass(typval_T *argvars, typval_T 
     rettv->vval.v_number = mb_get_class(argvars[0].vval.v_string);
 }
 #endif
+
+/*
+ * Function given to ExpandGeneric() to obtain the possible arguments of the
+ * encoding options.
+ */
+    char_u *
+get_encoding_name(expand_T *xp UNUSED, int idx)
+{
+    if (idx >= (int)(sizeof(enc_canon_table) / sizeof(enc_canon_table[0])))
+	return NULL;
+
+    return (char_u*)enc_canon_table[idx].name;
+}
--- a/src/option.c
+++ b/src/option.c
@@ -1632,7 +1632,7 @@ stropt_copy_value(
     // For MS-DOS and WIN32 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 && !VIM_ISWHITE(*arg))
     {
 	int i;
@@ -1837,7 +1837,7 @@ stropt_get_newval(
 					     &(options[opt_idx]), OPT_GLOBAL));
     else
     {
-	++arg;	// jump to after the '=' or ':'
+	++arg;	// joption_value2stringump to after the '=' or ':'
 
 	// Set 'keywordprg' to ":help" if an empty
 	// value was passed to :set by the user.
@@ -7232,8 +7232,10 @@ set_imsearch_global(void)
 }
 
 static int expand_option_idx = -1;
+static int expand_option_start_col = 0;
 static char_u expand_option_name[5] = {'t', '_', NUL, NUL, NUL};
 static int expand_option_flags = 0;
+static int expand_option_append = FALSE;
 
     void
 set_context_in_set_cmd(
@@ -7348,8 +7350,14 @@ set_context_in_set_cmd(
 	}
     }
     // handle "-=" and "+="
+    expand_option_append = FALSE;
+    int 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 = '=';
     }
@@ -7359,22 +7367,20 @@ set_context_in_set_cmd(
 	xp->xp_context = EXPAND_UNSUCCESSFUL;
 	return;
     }
-    if (xp->xp_context != EXPAND_BOOL_SETTINGS && 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.
 #ifdef FEAT_SYN_HL
     if (options[opt_idx].var == (char_u *)&p_syn)
     {
@@ -7382,7 +7388,38 @@ set_context_in_set_cmd(
 	return;
     }
 #endif
-
+    if (options[opt_idx].var == (char_u *)&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;
@@ -7403,10 +7440,6 @@ set_context_in_set_cmd(
 	    else
 		xp->xp_backslash = XP_BS_ONE;
 	}
-	else if (p == (char_u *)&p_ft)
-	{
-	    xp->xp_context = EXPAND_FILETYPE;
-	}
 	else
 	{
 	    xp->xp_context = EXPAND_FILES;
@@ -7418,34 +7451,55 @@ set_context_in_set_cmd(
 	}
     }
 
-    // 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 == ',')
-	{
-	    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))
+    // 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 ',' or ':'
+	    if (*p == ' ' || *p == ',' ||
+		    (*p == ':' && (flags & P_COLON)))
 	    {
-		xp->xp_pattern = p + 1;
-		break;
+		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;
+		}
 	    }
 	}
-
+    }
+
+    // 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.
 #ifdef FEAT_SPELL
-	// for 'spellsuggest' start at "file:"
-	if (options[opt_idx].var == (char_u *)&p_sps
-					       && STRNCMP(p, "file:", 5) == 0)
-	{
-	    xp->xp_pattern = p + 5;
-	    break;
-	}
-#endif
-    }
+    if (options[opt_idx].var == (char_u *)&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;
+	}
+    }
+#endif
 }
 
 /*
@@ -7464,7 +7518,7 @@ set_context_in_set_cmd(
  * 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 int
+    int
 match_str(
 	char_u		*str,
 	regmatch_T	*regmatch,
@@ -7711,6 +7765,37 @@ ExpandSettings(
     return OK;
 }
 
+// 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_u*
+escape_option_str_cmdline(char_u *var)
+{
+    char_u *buf;
+
+    // A backslash is required before some characters.  This is the reverse of
+    // what happens in do_set().
+    buf = vim_strsave_escaped(var, escape_chars);
+    if (buf == NULL)
+	return NULL;
+
+#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(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_u ***matches)
 {
@@ -7718,7 +7803,7 @@ ExpandOldSetting(int *numMatches, char_u
     char_u  *buf;
 
     *numMatches = 0;
-    *matches = ALLOC_ONE(char_u *);
+    *matches = ALLOC_MULT(char_u *, 1);
     if (*matches == NULL)
 	return FAIL;
 
@@ -7739,34 +7824,211 @@ ExpandOldSetting(int *numMatches, char_u
     else if (var == NULL)
 	var = (char_u *)"";
 
-    // A backslash is required before some characters.  This is the reverse of
-    // what happens in do_set().
-    buf = vim_strsave_escaped(var, escape_chars);
-
+    buf = escape_option_str_cmdline(var);
     if (buf == NULL)
     {
 	VIM_CLEAR(*matches);
 	return FAIL;
     }
 
-#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(var[2])
-		&& (var[2] != '\\' || (var == buf && var[4] != '\\')))
-	    STRMOVE(var, var + 1);
-#endif
-
-    *matches[0] = buf;
+    (*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_u	***matches)
+{
+    char_u  *var = NULL;	// init for GCC
+    char_u  *buf;
+
+    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;
+    args.oe_varp = get_varp_scope(&options[expand_option_idx], expand_option_flags);
+    args.oe_append = expand_option_append;
+    args.oe_regmatch = regmatch;
+    args.oe_xp = xp;
+    args.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);
+    var = NameBuff;
+    buf = escape_option_str_cmdline(var);
+    if (buf == NULL)
+	return FAIL;
+
+    args.oe_opt_value = buf;
+
+    int num_ret = options[expand_option_idx].opt_expand_cb(&args, numMatches, matches);
+
+    vim_free(buf);
+    return num_ret;
+}
+
+/*
+ * Expansion handler for :set-=
+ */
+    int
+ExpandSettingSubtract(
+    expand_T	*xp,
+    regmatch_T	*regmatch,
+    int		*numMatches,
+    char_u	***matches)
+{
+    if (expand_option_idx < 0)
+	// term option
+	return ExpandOldSetting(numMatches, matches);
+
+    char_u *option_val = *(char_u**)get_option_varp_scope(
+	expand_option_idx, expand_option_flags);
+
+    long_u 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.
+	garray_T    ga;
+
+	char_u	    *item;
+	char_u	    *option_copy;
+	char_u	    *next_val;
+	char_u	    *comma;
+
+	if (*option_val == NUL)
+	    return FAIL;
+
+	// Make a copy as we need to inject null characters destructively.
+	option_copy = vim_strsave(option_val);
+	if (option_copy == NULL)
+	    return FAIL;
+	next_val = option_copy;
+
+	ga_init2(&ga, sizeof(char_u *), 10);
+
+	do
+	{
+	    item = next_val;
+	    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_u *buf = escape_option_str_cmdline(item);
+	    if (buf == NULL)
+	    {
+		vim_free(option_copy);
+		ga_clear_strings(&ga);
+		return FAIL;
+	    }
+	    if (ga_add_string(&ga, buf) != OK)
+	    {
+		vim_free(buf);
+		break;
+	    }
+	} while (next_val != NULL);
+
+	vim_free(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;
+	}
+
+	int num_flags = STRLEN(option_val);
+	if (num_flags == 0)
+	    return FAIL;
+
+	*matches = ALLOC_MULT(char_u *, num_flags + 1);
+	if (*matches == NULL)
+	    return FAIL;
+
+	int count = 0;
+	char_u *p;
+
+	p = vim_strsave(option_val);
+	if (p == NULL)
+	{
+	    VIM_CLEAR(*matches);
+	    return FAIL;
+	}
+	(*matches)[count++] = p;
+
+	if (num_flags > 1)
+	{
+	    // If more than one flags, split the flags up and expose each
+	    // character as individual choice.
+	    for (char_u *flag = option_val; *flag != NUL; flag++)
+	    {
+		char_u *p = vim_strnsave(flag, 1);
+		if (p == NULL)
+		    break;
+		(*matches)[count++] = p;
+	    }
+	}
+
+	*numMatches = count;
+	return OK;
+    }
+
+    return ExpandOldSetting(numMatches, matches);
+}
+
+/*
  * Get the value for the numeric or string option *opp in a nice format into
  * NameBuff[].  Must not be called with a hidden option!
  */
@@ -8097,6 +8359,7 @@ fill_culopt_flags(char_u *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;
--- a/src/option.h
+++ b/src/option.h
@@ -60,6 +60,7 @@
 #define P_RWINONLY   0x10000000L // only redraw current window
 #define P_MLE	     0x20000000L // under control of 'modelineexpr'
 #define P_FUNC	     0x40000000L // accept a function reference or a lambda
+#define P_COLON	     0x80000000L // values use colons to create sublists
 
 // Returned by get_option_value().
 typedef enum {
@@ -230,7 +231,7 @@ typedef enum {
 #define CPO_ALL		"aAbBcCdDeEfFgHiIjJkKlLmMnoOpPqrRsStuvwWxXyZ$!%*-+<>#{|&/\\.;"
 
 // 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
--- a/src/optiondefs.h
+++ b/src/optiondefs.h
@@ -272,6 +272,11 @@ struct vimoption
     // 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;
+
     char_u	*def_val[2];	// default values for variable (vi and vim)
 #ifdef FEAT_EVAL
     sctx_T	script_ctx;	// script context where the option was last set
@@ -324,7 +329,7 @@ static struct vimoption options[] =
 #else
 			    (char_u *)NULL, PV_NONE,
 #endif
-			    NULL,
+			    NULL, NULL,
 			    {
 #if defined(MSWIN) && !defined(FEAT_GUI_MSWIN)
 			    (char_u *)128L,
@@ -333,72 +338,72 @@ static struct vimoption options[] =
 #endif
 					    (char_u *)0L} SCTX_INIT},
     {"antialias",   "anti", P_BOOL|P_VI_DEF|P_VIM|P_RCLR,
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)FALSE, (char_u *)FALSE}
 			    SCTX_INIT},
     {"arabic",	    "arab", P_BOOL|P_VI_DEF|P_VIM|P_CURSWANT,
 #ifdef FEAT_ARABIC
-			    (char_u *)VAR_WIN, PV_ARAB, did_set_arabic,
+			    (char_u *)VAR_WIN, PV_ARAB, did_set_arabic, NULL,
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 #endif
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"arabicshape", "arshape", P_BOOL|P_VI_DEF|P_VIM|P_RCLR,
 #ifdef FEAT_ARABIC
-			    (char_u *)&p_arshape, PV_NONE, NULL,
+			    (char_u *)&p_arshape, PV_NONE, NULL, NULL,
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 #endif
 			    {(char_u *)TRUE, (char_u *)0L} SCTX_INIT},
     {"allowrevins", "ari",  P_BOOL|P_VI_DEF|P_VIM,
 #ifdef FEAT_RIGHTLEFT
-			    (char_u *)&p_ari, PV_NONE, NULL,
+			    (char_u *)&p_ari, PV_NONE, NULL, NULL,
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 #endif
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"altkeymap",   "akm",  P_BOOL|P_VI_DEF,
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"ambiwidth",  "ambw",  P_STRING|P_VI_DEF|P_RCLR,
-			    (char_u *)&p_ambw, PV_NONE, did_set_ambiwidth,
+			    (char_u *)&p_ambw, PV_NONE, did_set_ambiwidth, expand_set_ambiwidth,
 			    {(char_u *)"single", (char_u *)0L}
 			    SCTX_INIT},
     {"autochdir",  "acd",   P_BOOL|P_VI_DEF,
 #ifdef FEAT_AUTOCHDIR
-			    (char_u *)&p_acd, PV_NONE, did_set_autochdir,
+			    (char_u *)&p_acd, PV_NONE, did_set_autochdir, NULL,
 			    {(char_u *)FALSE, (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)0L, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"autoshelldir",  "asd",   P_BOOL|P_VI_DEF,
 #ifdef FEAT_AUTOSHELLDIR
-			    (char_u *)&p_asd, PV_NONE, NULL,
+			    (char_u *)&p_asd, PV_NONE, NULL, NULL,
 			    {(char_u *)FALSE, (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)0L, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"autoindent",  "ai",   P_BOOL|P_VI_DEF,
-			    (char_u *)&p_ai, PV_AI, NULL,
+			    (char_u *)&p_ai, PV_AI, NULL, NULL,
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"autoprint",   "ap",   P_BOOL|P_VI_DEF,
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"autoread",    "ar",   P_BOOL|P_VI_DEF,
-			    (char_u *)&p_ar, PV_AR, NULL,
+			    (char_u *)&p_ar, PV_AR, NULL, NULL,
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"autowrite",   "aw",   P_BOOL|P_VI_DEF,
-			    (char_u *)&p_aw, PV_NONE, NULL,
+			    (char_u *)&p_aw, PV_NONE, NULL, NULL,
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"autowriteall","awa",  P_BOOL|P_VI_DEF,
-			    (char_u *)&p_awa, PV_NONE, NULL,
+			    (char_u *)&p_awa, PV_NONE, NULL, NULL,
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"background",  "bg",   P_STRING|P_VI_DEF|P_RCLR,
-			    (char_u *)&p_bg, PV_NONE, did_set_background,
+			    (char_u *)&p_bg, PV_NONE, did_set_background, expand_set_background,
 			    {
 #if (defined(MSWIN)) && !defined(FEAT_GUI)
 			    (char_u *)"dark",
@@ -407,13 +412,13 @@ static struct vimoption options[] =
 #endif
 					    (char_u *)0L} SCTX_INIT},
     {"backspace",   "bs",   P_STRING|P_VI_DEF|P_VIM|P_ONECOMMA|P_NODUP,
-			    (char_u *)&p_bs, PV_NONE, did_set_backspace,
+			    (char_u *)&p_bs, PV_NONE, did_set_backspace, expand_set_backspace,
 			    {(char_u *)"", (char_u *)0L} SCTX_INIT},
     {"backup",	    "bk",   P_BOOL|P_VI_DEF|P_VIM,
-			    (char_u *)&p_bk, PV_NONE, NULL,
+			    (char_u *)&p_bk, PV_NONE, NULL, NULL,
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"backupcopy",  "bkc",  P_STRING|P_VIM|P_ONECOMMA|P_NODUP,
-			    (char_u *)&p_bkc, PV_BKC, did_set_backupcopy,
+			    (char_u *)&p_bkc, PV_BKC, did_set_backupcopy, expand_set_backupcopy,
 #ifdef UNIX
 			    {(char_u *)"yes", (char_u *)"auto"}
 #else
@@ -422,11 +427,11 @@ static struct vimoption options[] =
 			    SCTX_INIT},
     {"backupdir",   "bdir", P_STRING|P_EXPAND|P_VI_DEF|P_ONECOMMA
 							    |P_NODUP|P_SECURE,
-			    (char_u *)&p_bdir, PV_NONE, NULL,
+			    (char_u *)&p_bdir, PV_NONE, NULL, NULL,
 			    {(char_u *)DFLT_BDIR, (char_u *)0L} SCTX_INIT},
     {"backupext",   "bex",  P_STRING|P_VI_DEF|P_NFNAME,
 			    (char_u *)&p_bex, PV_NONE,
-			    did_set_backupext_or_patchmode,
+			    did_set_backupext_or_patchmode, NULL,
 			    {
 #ifdef VMS
 			    (char_u *)"_",
@@ -435,77 +440,77 @@ static struct vimoption options[] =
 #endif
 					    (char_u *)0L} SCTX_INIT},
     {"backupskip",  "bsk",  P_STRING|P_VI_DEF|P_ONECOMMA|P_NODUP,
-			    (char_u *)&p_bsk, PV_NONE, NULL,
+			    (char_u *)&p_bsk, PV_NONE, NULL, NULL,
 			    {(char_u *)"", (char_u *)0L}
 			    SCTX_INIT},
     {"balloondelay","bdlay",P_NUM|P_VI_DEF,
 #ifdef FEAT_BEVAL
-			    (char_u *)&p_bdlay, PV_NONE, NULL,
+			    (char_u *)&p_bdlay, PV_NONE, NULL, NULL,
 			    {(char_u *)600L, (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)0L, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"ballooneval", "beval",P_BOOL|P_VI_DEF|P_NO_MKRC,
 #ifdef FEAT_BEVAL_GUI
-			    (char_u *)&p_beval, PV_NONE, did_set_ballooneval,
+			    (char_u *)&p_beval, PV_NONE, did_set_ballooneval, NULL,
 			    {(char_u *)FALSE, (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)0L, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"balloonevalterm", "bevalterm",P_BOOL|P_VI_DEF|P_NO_MKRC,
 #ifdef FEAT_BEVAL_TERM
 			    (char_u *)&p_bevalterm, PV_NONE,
-			    did_set_balloonevalterm,
+			    did_set_balloonevalterm, NULL,
 			    {(char_u *)FALSE, (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)0L, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"balloonexpr", "bexpr", P_STRING|P_ALLOCED|P_VI_DEF|P_VIM|P_MLE,
 #if defined(FEAT_BEVAL) && defined(FEAT_EVAL)
-			    (char_u *)&p_bexpr, PV_BEXPR, did_set_optexpr,
+			    (char_u *)&p_bexpr, PV_BEXPR, did_set_optexpr, NULL,
 			    {(char_u *)"", (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)0L, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"beautify",    "bf",   P_BOOL|P_VI_DEF,
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"belloff",      "bo",  P_STRING|P_VI_DEF|P_COMMA|P_NODUP,
-			    (char_u *)&p_bo, PV_NONE, did_set_belloff,
+			    (char_u *)&p_bo, PV_NONE, did_set_belloff, expand_set_belloff,
 			    {(char_u *)"", (char_u *)0L} SCTX_INIT},
     {"binary",	    "bin",  P_BOOL|P_VI_DEF|P_RSTAT,
-			    (char_u *)&p_bin, PV_BIN, did_set_binary,
+			    (char_u *)&p_bin, PV_BIN, did_set_binary, NULL,
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"bioskey",	    "biosk",P_BOOL|P_VI_DEF,
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)TRUE, (char_u *)0L} SCTX_INIT},
     {"bomb",	    NULL,   P_BOOL|P_NO_MKRC|P_VI_DEF|P_RSTAT,
 			    (char_u *)&p_bomb, PV_BOMB,
-			    did_set_eof_eol_fixeol_bomb,
+			    did_set_eof_eol_fixeol_bomb, NULL,
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"breakat",	    "brk",  P_STRING|P_VI_DEF|P_RALL|P_FLAGLIST,
 #ifdef FEAT_LINEBREAK
-			    (char_u *)&p_breakat, PV_NONE, did_set_breakat,
+			    (char_u *)&p_breakat, PV_NONE, did_set_breakat, NULL,
 			    {(char_u *)" \t!@*-+;:,./?", (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)0L, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"breakindent",   "bri",  P_BOOL|P_VI_DEF|P_VIM|P_RWIN,
 #ifdef FEAT_LINEBREAK
-			    (char_u *)VAR_WIN, PV_BRI, NULL,
+			    (char_u *)VAR_WIN, PV_BRI, NULL, NULL,
 			    {(char_u *)FALSE, (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)0L, (char_u *)0L}
 #endif
 			    SCTX_INIT},
@@ -513,80 +518,80 @@ static struct vimoption options[] =
 						  |P_ONECOMMA|P_NODUP,
 #ifdef FEAT_LINEBREAK
 			    (char_u *)VAR_WIN, PV_BRIOPT,
-			    did_set_breakindentopt,
+			    did_set_breakindentopt, expand_set_breakindentopt,
 			    {(char_u *)"", (char_u *)NULL}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)"", (char_u *)NULL}
 #endif
 			    SCTX_INIT},
     {"browsedir",   "bsdir",P_STRING|P_VI_DEF,
 #ifdef FEAT_BROWSE
-			    (char_u *)&p_bsdir, PV_NONE, did_set_browsedir,
+			    (char_u *)&p_bsdir, PV_NONE, did_set_browsedir, expand_set_browsedir,
 			    {(char_u *)"last", (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)0L, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"bufhidden",   "bh",   P_STRING|P_ALLOCED|P_VI_DEF|P_NOGLOB,
-			    (char_u *)&p_bh, PV_BH, did_set_bufhidden,
+			    (char_u *)&p_bh, PV_BH, did_set_bufhidden, expand_set_bufhidden,
 			    {(char_u *)"", (char_u *)0L}
 			    SCTX_INIT},
     {"buflisted",   "bl",   P_BOOL|P_VI_DEF|P_NOGLOB,
-			    (char_u *)&p_bl, PV_BL, did_set_buflisted,
+			    (char_u *)&p_bl, PV_BL, did_set_buflisted, NULL,
 			    {(char_u *)1L, (char_u *)0L}
 			    SCTX_INIT},
     {"buftype",	    "bt",   P_STRING|P_ALLOCED|P_VI_DEF|P_NOGLOB,
-			    (char_u *)&p_bt, PV_BT, did_set_buftype,
+			    (char_u *)&p_bt, PV_BT, did_set_buftype, expand_set_buftype,
 			    {(char_u *)"", (char_u *)0L}
 			    SCTX_INIT},
     {"casemap",	    "cmp",   P_STRING|P_VI_DEF|P_ONECOMMA|P_NODUP,
-			    (char_u *)&p_cmp, PV_NONE, did_set_casemap,
+			    (char_u *)&p_cmp, PV_NONE, did_set_casemap, expand_set_casemap,
 			    {(char_u *)"internal,keepascii", (char_u *)0L}
 			    SCTX_INIT},
     {"cdhome",	    "cdh",  P_BOOL|P_VI_DEF|P_VIM|P_SECURE,
-			    (char_u *)&p_cdh, PV_NONE, NULL,
+			    (char_u *)&p_cdh, PV_NONE, NULL, NULL,
 			    {(char_u *)FALSE, (char_u *)0L}
 			    SCTX_INIT},
     {"cdpath",	    "cd",   P_STRING|P_EXPAND|P_VI_DEF|P_SECURE|P_COMMA|P_NODUP,
-			    (char_u *)&p_cdpath, PV_NONE, NULL,
+			    (char_u *)&p_cdpath, PV_NONE, NULL, NULL,
 			    {(char_u *)",,", (char_u *)0L}
 			    SCTX_INIT},
     {"cedit",	    NULL,   P_STRING,
-			    (char_u *)&p_cedit, PV_NONE, did_set_cedit,
+			    (char_u *)&p_cedit, PV_NONE, did_set_cedit, NULL,
 			    {(char_u *)"", (char_u *)CTRL_F_STR}
 			    SCTX_INIT},
     {"charconvert",  "ccv", P_STRING|P_VI_DEF|P_SECURE,
 #if defined(FEAT_EVAL)
-			    (char_u *)&p_ccv, PV_NONE, did_set_optexpr,
+			    (char_u *)&p_ccv, PV_NONE, did_set_optexpr, NULL,
 			    {(char_u *)"", (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)0L, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"cindent",	    "cin",  P_BOOL|P_VI_DEF|P_VIM,
-			    (char_u *)&p_cin, PV_CIN, NULL,
+			    (char_u *)&p_cin, PV_CIN, NULL, NULL,
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"cinkeys",	    "cink", P_STRING|P_ALLOCED|P_VI_DEF|P_ONECOMMA|P_NODUP,
-			    (char_u *)&p_cink, PV_CINK, NULL,
+			    (char_u *)&p_cink, PV_CINK, NULL, NULL,
 			    {INDENTKEYS_DEFAULT, (char_u *)0L} SCTX_INIT},
     {"cinoptions",  "cino", P_STRING|P_ALLOCED|P_VI_DEF|P_ONECOMMA|P_NODUP,
-			    (char_u *)&p_cino, PV_CINO, did_set_cinoptions,
+			    (char_u *)&p_cino, PV_CINO, did_set_cinoptions, NULL,
 			    {(char_u *)"", (char_u *)0L} SCTX_INIT},
     {"cinscopedecls", "cinsd", P_STRING|P_ALLOCED|P_VI_DEF|P_ONECOMMA|P_NODUP,
-			    (char_u *)&p_cinsd, PV_CINSD, NULL,
+			    (char_u *)&p_cinsd, PV_CINSD, NULL, NULL,
 			    {(char_u *)"public,protected,private", (char_u *)0L}
 			    SCTX_INIT},
     {"cinwords",    "cinw", P_STRING|P_ALLOCED|P_VI_DEF|P_ONECOMMA|P_NODUP,
-			    (char_u *)&p_cinw, PV_CINW, NULL,
+			    (char_u *)&p_cinw, PV_CINW, NULL, NULL,
 			    {(char_u *)"if,else,while,do,for,switch",
 				(char_u *)0L}
 			    SCTX_INIT},
     {"clipboard",   "cb",   P_STRING|P_VI_DEF|P_ONECOMMA|P_NODUP,
 #ifdef FEAT_CLIPBOARD
-			    (char_u *)&p_cb, PV_NONE, did_set_clipboard,
+			    (char_u *)&p_cb, PV_NONE, did_set_clipboard, expand_set_clipboard,
 # ifdef FEAT_XCLIPBOARD
 			    {(char_u *)"autoselect,exclude:cons\\|linux",
 							       (char_u *)0L}
@@ -594,343 +599,343 @@ static struct vimoption options[] =
 			    {(char_u *)"", (char_u *)0L}
 # endif
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)"", (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"cmdheight",   "ch",   P_NUM|P_VI_DEF|P_RALL,
-			    (char_u *)&p_ch, PV_NONE, did_set_cmdheight,
+			    (char_u *)&p_ch, PV_NONE, did_set_cmdheight, NULL,
 			    {(char_u *)1L, (char_u *)0L} SCTX_INIT},
     {"cmdwinheight", "cwh", P_NUM|P_VI_DEF,
-			    (char_u *)&p_cwh, PV_NONE, NULL,
+			    (char_u *)&p_cwh, PV_NONE, NULL, NULL,
 			    {(char_u *)7L, (char_u *)0L} SCTX_INIT},
     {"colorcolumn", "cc",   P_STRING|P_VI_DEF|P_ONECOMMA|P_NODUP|P_RWIN,
 #ifdef FEAT_SYN_HL
-			    (char_u *)VAR_WIN, PV_CC, did_set_colorcolumn,
+			    (char_u *)VAR_WIN, PV_CC, did_set_colorcolumn, NULL,
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 #endif
 			    {(char_u *)"", (char_u *)0L} SCTX_INIT},
     {"columns",	    "co",   P_NUM|P_NODEFAULT|P_NO_MKRC|P_VI_DEF|P_RCLR,
-			    (char_u *)&Columns, PV_NONE, NULL,
+			    (char_u *)&Columns, PV_NONE, NULL, NULL,
 			    {(char_u *)80L, (char_u *)0L} SCTX_INIT},
     {"comments",    "com",  P_STRING|P_ALLOCED|P_VI_DEF|P_ONECOMMA
 							  |P_NODUP|P_CURSWANT,
-			    (char_u *)&p_com, PV_COM, did_set_comments,
+			    (char_u *)&p_com, PV_COM, did_set_comments, NULL,
 			    {(char_u *)"s1:/*,mb:*,ex:*/,://,b:#,:%,:XCOMM,n:>,fb:-",
 				(char_u *)0L}
 			    SCTX_INIT},
     {"commentstring", "cms", P_STRING|P_ALLOCED|P_VI_DEF|P_CURSWANT,
 #ifdef FEAT_FOLDING
-			    (char_u *)&p_cms, PV_CMS, did_set_commentstring,
+			    (char_u *)&p_cms, PV_CMS, did_set_commentstring, NULL,
 			    {(char_u *)"/*%s*/", (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)0L, (char_u *)0L}
 #endif
 			    SCTX_INIT},
 			    // P_PRI_MKRC isn't needed here, optval_default()
 			    // always returns TRUE for 'compatible'
     {"compatible",  "cp",   P_BOOL|P_RALL,
-			    (char_u *)&p_cp, PV_NONE, did_set_compatible,
+			    (char_u *)&p_cp, PV_NONE, did_set_compatible, NULL,
 			    {(char_u *)TRUE, (char_u *)FALSE} SCTX_INIT},
     {"complete",    "cpt",  P_STRING|P_ALLOCED|P_VI_DEF|P_ONECOMMA|P_NODUP,
-			    (char_u *)&p_cpt, PV_CPT, did_set_complete,
+			    (char_u *)&p_cpt, PV_CPT, did_set_complete, expand_set_complete,
 			    {(char_u *)".,w,b,u,t,i", (char_u *)0L}
 			    SCTX_INIT},
-    {"concealcursor","cocu", P_STRING|P_ALLOCED|P_RWIN|P_VI_DEF,
+    {"concealcursor","cocu", P_STRING|P_ALLOCED|P_RWIN|P_VI_DEF|P_FLAGLIST,
 #ifdef FEAT_CONCEAL
-			    (char_u *)VAR_WIN, PV_COCU, did_set_concealcursor,
+			    (char_u *)VAR_WIN, PV_COCU, did_set_concealcursor, expand_set_concealcursor,
 			    {(char_u *)"", (char_u *)NULL}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)NULL, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"conceallevel","cole", P_NUM|P_RWIN|P_VI_DEF,
 #ifdef FEAT_CONCEAL
-			    (char_u *)VAR_WIN, PV_COLE, did_set_conceallevel,
+			    (char_u *)VAR_WIN, PV_COLE, did_set_conceallevel, NULL,
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 #endif
 			    {(char_u *)0L, (char_u *)0L}
 			    SCTX_INIT},
     {"completefunc", "cfu", P_STRING|P_ALLOCED|P_VI_DEF|P_SECURE|P_FUNC,
 #ifdef FEAT_COMPL_FUNC
-			    (char_u *)&p_cfu, PV_CFU, did_set_completefunc,
+			    (char_u *)&p_cfu, PV_CFU, did_set_completefunc, NULL,
 			    {(char_u *)"", (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)0L, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"completeopt",   "cot",  P_STRING|P_VI_DEF|P_ONECOMMA|P_NODUP,
-			    (char_u *)&p_cot, PV_NONE, did_set_completeopt,
+			    (char_u *)&p_cot, PV_NONE, did_set_completeopt, expand_set_completeopt,
 			    {(char_u *)"menu,preview", (char_u *)0L}
 			    SCTX_INIT},
-    {"completepopup", "cpp", P_STRING|P_VI_DEF|P_COMMA|P_NODUP,
+    {"completepopup", "cpp", P_STRING|P_VI_DEF|P_COMMA|P_NODUP|P_COLON,
 #if defined(FEAT_PROP_POPUP) && defined(FEAT_QUICKFIX)
-			    (char_u *)&p_cpp, PV_NONE, did_set_completepopup,
+			    (char_u *)&p_cpp, PV_NONE, did_set_completepopup, expand_set_popupoption,
 			    {(char_u *)"", (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)NULL, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"completeslash",   "csl",  P_STRING|P_VI_DEF|P_VIM,
 #if defined(BACKSLASH_IN_FILENAME)
-			    (char_u *)&p_csl, PV_CSL, did_set_completeslash,
+			    (char_u *)&p_csl, PV_CSL, did_set_completeslash, expand_set_completeslash,
 			    {(char_u *)"", (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)0L, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"confirm",     "cf",   P_BOOL|P_VI_DEF,
 #if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)
-			    (char_u *)&p_confirm, PV_NONE, NULL,
+			    (char_u *)&p_confirm, PV_NONE, NULL, NULL,
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 #endif
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"conskey",	    "consk",P_BOOL|P_VI_DEF,
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"copyindent",  "ci",   P_BOOL|P_VI_DEF|P_VIM,
-			    (char_u *)&p_ci, PV_CI, NULL,
+			    (char_u *)&p_ci, PV_CI, NULL, NULL,
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"cpoptions",   "cpo",  P_STRING|P_VIM|P_RALL|P_FLAGLIST,
-			    (char_u *)&p_cpo, PV_NONE, did_set_cpoptions,
+			    (char_u *)&p_cpo, PV_NONE, did_set_cpoptions, expand_set_cpoptions,
 			    {(char_u *)CPO_VI, (char_u *)CPO_VIM}
 			    SCTX_INIT},
     {"cryptmethod", "cm",   P_STRING|P_ALLOCED|P_VI_DEF,
 #ifdef FEAT_CRYPT
-			    (char_u *)&p_cm, PV_CM, did_set_cryptmethod,
+			    (char_u *)&p_cm, PV_CM, did_set_cryptmethod, expand_set_cryptmethod,
 			    {(char_u *)"blowfish2", (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)0L, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"cscopepathcomp", "cspc", P_NUM|P_VI_DEF|P_VIM,
 #ifdef FEAT_CSCOPE
-			    (char_u *)&p_cspc, PV_NONE, NULL,
+			    (char_u *)&p_cspc, PV_NONE, NULL, NULL,
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 #endif
 			    {(char_u *)0L, (char_u *)0L} SCTX_INIT},
     {"cscopeprg",   "csprg", P_STRING|P_EXPAND|P_VI_DEF|P_SECURE,
 #ifdef FEAT_CSCOPE
-			    (char_u *)&p_csprg, PV_NONE, NULL,
+			    (char_u *)&p_csprg, PV_NONE, NULL, NULL,
 			    {(char_u *)"cscope", (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)0L, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"cscopequickfix", "csqf", P_STRING|P_VI_DEF|P_ONECOMMA|P_NODUP,
 #if defined(FEAT_CSCOPE) && defined(FEAT_QUICKFIX)
 			    (char_u *)&p_csqf, PV_NONE,
-			    did_set_cscopequickfix,
+			    did_set_cscopequickfix, NULL,
 			    {(char_u *)"", (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)0L, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"cscoperelative", "csre", P_BOOL|P_VI_DEF|P_VIM,
 #ifdef FEAT_CSCOPE
-			    (char_u *)&p_csre, PV_NONE, NULL,
+			    (char_u *)&p_csre, PV_NONE, NULL, NULL,
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 #endif
 			    {(char_u *)0L, (char_u *)0L} SCTX_INIT},
     {"cscopetag",   "cst",  P_BOOL|P_VI_DEF|P_VIM,
 #ifdef FEAT_CSCOPE
-			    (char_u *)&p_cst, PV_NONE, NULL,
+			    (char_u *)&p_cst, PV_NONE, NULL, NULL,
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 #endif
 			    {(char_u *)0L, (char_u *)0L} SCTX_INIT},
     {"cscopetagorder", "csto", P_NUM|P_VI_DEF|P_VIM,
 #ifdef FEAT_CSCOPE
-			    (char_u *)&p_csto, PV_NONE, NULL,
+			    (char_u *)&p_csto, PV_NONE, NULL, NULL,
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 #endif
 			    {(char_u *)0L, (char_u *)0L} SCTX_INIT},
     {"cscopeverbose", "csverb", P_BOOL|P_VI_DEF|P_VIM,
 #ifdef FEAT_CSCOPE
-			    (char_u *)&p_csverbose, PV_NONE, NULL,
+			    (char_u *)&p_csverbose, PV_NONE, NULL, NULL,
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 #endif
 			    {(char_u *)0L, (char_u *)0L} SCTX_INIT},
     {"cursorbind",  "crb",  P_BOOL|P_VI_DEF,
-			    (char_u *)VAR_WIN, PV_CRBIND, NULL,
+			    (char_u *)VAR_WIN, PV_CRBIND, NULL, NULL,
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"cursorcolumn", "cuc", P_BOOL|P_VI_DEF|P_RWINONLY,
 #ifdef FEAT_SYN_HL
-			    (char_u *)VAR_WIN, PV_CUC, NULL,
+			    (char_u *)VAR_WIN, PV_CUC, NULL, NULL,
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 #endif
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"cursorline",   "cul", P_BOOL|P_VI_DEF|P_RWINONLY,
 #ifdef FEAT_SYN_HL
-			    (char_u *)VAR_WIN, PV_CUL, NULL,
+			    (char_u *)VAR_WIN, PV_CUL, NULL, NULL,
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 #endif
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"cursorlineopt", "culopt", P_STRING|P_VI_DEF|P_RWIN|P_ONECOMMA|P_NODUP,
 #ifdef FEAT_SYN_HL
 			    (char_u *)VAR_WIN, PV_CULOPT,
-			    did_set_cursorlineopt,
+			    did_set_cursorlineopt, expand_set_cursorlineopt,
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 #endif
 			    {(char_u *)"both", (char_u *)0L} SCTX_INIT},
     {"debug",	    NULL,   P_STRING|P_VI_DEF,
-			    (char_u *)&p_debug, PV_NONE, did_set_debug,
+			    (char_u *)&p_debug, PV_NONE, did_set_debug, expand_set_debug,
 			    {(char_u *)"", (char_u *)0L} SCTX_INIT},
     {"define",	    "def",  P_STRING|P_ALLOCED|P_VI_DEF|P_CURSWANT,
 #ifdef FEAT_FIND_ID
-			    (char_u *)&p_def, PV_DEF, NULL,
+			    (char_u *)&p_def, PV_DEF, NULL, NULL,
 			    {(char_u *)"^\\s*#\\s*define", (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)NULL, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"delcombine", "deco",  P_BOOL|P_VI_DEF|P_VIM,
-			    (char_u *)&p_deco, PV_NONE, NULL,
+			    (char_u *)&p_deco, PV_NONE, NULL, NULL,
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"dictionary",  "dict", P_STRING|P_EXPAND|P_VI_DEF|P_ONECOMMA|P_NODUP|P_NDNAME,
-			    (char_u *)&p_dict, PV_DICT, NULL,
+			    (char_u *)&p_dict, PV_DICT, NULL, NULL,
 			    {(char_u *)"", (char_u *)0L} SCTX_INIT},
     {"diff",	    NULL,   P_BOOL|P_VI_DEF|P_RWIN|P_NOGLOB,
 #ifdef FEAT_DIFF
-			    (char_u *)VAR_WIN, PV_DIFF, did_set_diff,
+			    (char_u *)VAR_WIN, PV_DIFF, did_set_diff, NULL,
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 #endif
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"diffexpr",    "dex",  P_STRING|P_VI_DEF|P_SECURE|P_CURSWANT,
 #if defined(FEAT_DIFF) && defined(FEAT_EVAL)
-			    (char_u *)&p_dex, PV_NONE, did_set_optexpr,
+			    (char_u *)&p_dex, PV_NONE, did_set_optexpr, NULL,
 			    {(char_u *)"", (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)0L, (char_u *)0L}
 #endif
 			    SCTX_INIT},
-    {"diffopt",	    "dip",  P_STRING|P_ALLOCED|P_VI_DEF|P_RWIN|P_ONECOMMA
+    {"diffopt",	    "dip",  P_STRING|P_ALLOCED|P_VI_DEF|P_RWIN|P_ONECOMMA|P_COLON
 								     |P_NODUP,
 #ifdef FEAT_DIFF
-			    (char_u *)&p_dip, PV_NONE, did_set_diffopt,
+			    (char_u *)&p_dip, PV_NONE, did_set_diffopt, expand_set_diffopt,
 			    {(char_u *)"internal,filler,closeoff",
 								(char_u *)NULL}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)"", (char_u *)NULL}
 #endif
 			    SCTX_INIT},
     {"digraph",	    "dg",   P_BOOL|P_VI_DEF|P_VIM,
 #ifdef FEAT_DIGRAPHS
-			    (char_u *)&p_dg, PV_NONE, NULL,
+			    (char_u *)&p_dg, PV_NONE, NULL, NULL,
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 #endif
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"directory",   "dir",  P_STRING|P_EXPAND|P_VI_DEF|P_ONECOMMA
 							    |P_NODUP|P_SECURE,
-			    (char_u *)&p_dir, PV_NONE, NULL,
+			    (char_u *)&p_dir, PV_NONE, NULL, NULL,
 			    {(char_u *)DFLT_DIR, (char_u *)0L} SCTX_INIT},
     {"display",	    "dy",   P_STRING|P_VI_DEF|P_ONECOMMA|P_RALL|P_NODUP,
-			    (char_u *)&p_dy, PV_NONE, did_set_display,
+			    (char_u *)&p_dy, PV_NONE, did_set_display, expand_set_display,
 			    {(char_u *)"", (char_u *)0L} SCTX_INIT},
     {"eadirection", "ead",  P_STRING|P_VI_DEF,
-			    (char_u *)&p_ead, PV_NONE, did_set_eadirection,
+			    (char_u *)&p_ead, PV_NONE, did_set_eadirection, expand_set_eadirection,
 			    {(char_u *)"both", (char_u *)0L}
 			    SCTX_INIT},
     {"edcompatible","ed",   P_BOOL|P_VI_DEF,
-			    (char_u *)&p_ed, PV_NONE, NULL,
+			    (char_u *)&p_ed, PV_NONE, NULL, NULL,
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"emoji",      "emo",   P_BOOL|P_VI_DEF|P_RCLR,
-			    (char_u *)&p_emoji, PV_NONE, did_set_ambiwidth,
+			    (char_u *)&p_emoji, PV_NONE, did_set_ambiwidth, NULL,
 			    {(char_u *)TRUE, (char_u *)0L}
 			    SCTX_INIT},
     {"encoding",    "enc",  P_STRING|P_VI_DEF|P_RCLR|P_NO_ML,
-			    (char_u *)&p_enc, PV_NONE, did_set_encoding,
+			    (char_u *)&p_enc, PV_NONE, did_set_encoding, expand_set_encoding,
 			    {(char_u *)ENC_DFLT, (char_u *)0L}
 			    SCTX_INIT},
     {"endoffile",   "eof",  P_BOOL|P_NO_MKRC|P_VI_DEF|P_RSTAT,
 			    (char_u *)&p_eof, PV_EOF,
-			    did_set_eof_eol_fixeol_bomb,
+			    did_set_eof_eol_fixeol_bomb, NULL,
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"endofline",   "eol",  P_BOOL|P_NO_MKRC|P_VI_DEF|P_RSTAT,
 			    (char_u *)&p_eol, PV_EOL,
-			    did_set_eof_eol_fixeol_bomb,
+			    did_set_eof_eol_fixeol_bomb, NULL,
 			    {(char_u *)TRUE, (char_u *)0L} SCTX_INIT},
     {"equalalways", "ea",   P_BOOL|P_VI_DEF|P_RALL,
-			    (char_u *)&p_ea, PV_NONE, did_set_equalalways,
+			    (char_u *)&p_ea, PV_NONE, did_set_equalalways, NULL,
 			    {(char_u *)TRUE, (char_u *)0L} SCTX_INIT},
     {"equalprg",    "ep",   P_STRING|P_EXPAND|P_VI_DEF|P_SECURE,
-			    (char_u *)&p_ep, PV_EP, NULL,
+			    (char_u *)&p_ep, PV_EP, NULL, NULL,
 			    {(char_u *)"", (char_u *)0L} SCTX_INIT},
     {"errorbells",  "eb",   P_BOOL|P_VI_DEF,
-			    (char_u *)&p_eb, PV_NONE, NULL,
+			    (char_u *)&p_eb, PV_NONE, NULL, NULL,
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"errorfile",   "ef",   P_STRING|P_EXPAND|P_VI_DEF|P_SECURE,
 #ifdef FEAT_QUICKFIX
-			    (char_u *)&p_ef, PV_NONE, NULL,
+			    (char_u *)&p_ef, PV_NONE, NULL, NULL,
 			    {(char_u *)DFLT_ERRORFILE, (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)NULL, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"errorformat", "efm",  P_STRING|P_VI_DEF|P_ONECOMMA|P_NODUP,
 #ifdef FEAT_QUICKFIX
-			    (char_u *)&p_efm, PV_EFM, NULL,
+			    (char_u *)&p_efm, PV_EFM, NULL, NULL,
 			    {(char_u *)DFLT_EFM, (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)NULL, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"esckeys",	    "ek",   P_BOOL|P_VIM,
-			    (char_u *)&p_ek, PV_NONE, NULL,
+			    (char_u *)&p_ek, PV_NONE, NULL, NULL,
 			    {(char_u *)FALSE, (char_u *)TRUE} SCTX_INIT},
     {"eventignore", "ei",   P_STRING|P_VI_DEF|P_ONECOMMA|P_NODUP,
-			    (char_u *)&p_ei, PV_NONE, did_set_eventignore,
+			    (char_u *)&p_ei, PV_NONE, did_set_eventignore, expand_set_eventignore,
 			    {(char_u *)"", (char_u *)0L} SCTX_INIT},
     {"expandtab",   "et",   P_BOOL|P_VI_DEF|P_VIM,
-			    (char_u *)&p_et, PV_ET, NULL,
+			    (char_u *)&p_et, PV_ET, NULL, NULL,
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"exrc",	    "ex",   P_BOOL|P_VI_DEF|P_SECURE,
-			    (char_u *)&p_exrc, PV_NONE, NULL,
+			    (char_u *)&p_exrc, PV_NONE, NULL, NULL,
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"fileencoding","fenc", P_STRING|P_ALLOCED|P_VI_DEF|P_RSTAT|P_RBUF
 								   |P_NO_MKRC,
-			    (char_u *)&p_fenc, PV_FENC, did_set_encoding,
+			    (char_u *)&p_fenc, PV_FENC, did_set_encoding, expand_set_encoding,
 			    {(char_u *)"", (char_u *)0L}
 			    SCTX_INIT},
     {"fileencodings","fencs", P_STRING|P_VI_DEF|P_ONECOMMA,
-			    (char_u *)&p_fencs, PV_NONE, NULL,
+			    (char_u *)&p_fencs, PV_NONE, NULL, expand_set_encoding,
 			    {(char_u *)"ucs-bom", (char_u *)0L}
 			    SCTX_INIT},
     {"fileformat",  "ff",   P_STRING|P_ALLOCED|P_VI_DEF|P_RSTAT|P_NO_MKRC
 								  |P_CURSWANT,
-			    (char_u *)&p_ff, PV_FF, did_set_fileformat,
+			    (char_u *)&p_ff, PV_FF, did_set_fileformat, expand_set_fileformat,
 			    {(char_u *)DFLT_FF, (char_u *)0L} SCTX_INIT},
     {"fileformats", "ffs",  P_STRING|P_VIM|P_ONECOMMA|P_NODUP,
-			    (char_u *)&p_ffs, PV_NONE, did_set_fileformats,
+			    (char_u *)&p_ffs, PV_NONE, did_set_fileformats, expand_set_fileformat,
 			    {(char_u *)DFLT_FFS_VI, (char_u *)DFLT_FFS_VIM}
 			    SCTX_INIT},
     {"fileignorecase", "fic", P_BOOL|P_VI_DEF,
-			    (char_u *)&p_fic, PV_NONE, NULL,
+			    (char_u *)&p_fic, PV_NONE, NULL, NULL,
 			    {
 #ifdef CASE_INSENSITIVE_FILENAME
 				    (char_u *)TRUE,
@@ -940,190 +945,190 @@ static struct vimoption options[] =
 					(char_u *)0L} SCTX_INIT},
     {"filetype",    "ft",   P_STRING|P_EXPAND|P_ALLOCED|P_VI_DEF|P_NOGLOB|P_NFNAME,
 			    (char_u *)&p_ft, PV_FT,
-			    did_set_filetype_or_syntax,
+			    did_set_filetype_or_syntax, NULL,
 			    {(char_u *)"", (char_u *)0L}
 			    SCTX_INIT},
     {"fillchars",   "fcs",  P_STRING|P_VI_DEF|P_RALL|P_ONECOMMA|P_NODUP,
-			    (char_u *)&p_fcs, PV_FCS, did_set_chars_option,
+			    (char_u *)&p_fcs, PV_FCS, did_set_chars_option, expand_set_chars_option,
 			    {(char_u *)"vert:|,fold:-,eob:~,lastline:@",
 								  (char_u *)0L}
 			    SCTX_INIT},
     {"fixendofline",  "fixeol", P_BOOL|P_VI_DEF|P_RSTAT,
 			    (char_u *)&p_fixeol, PV_FIXEOL,
-			    did_set_eof_eol_fixeol_bomb,
+			    did_set_eof_eol_fixeol_bomb, NULL,
 			    {(char_u *)TRUE, (char_u *)0L} SCTX_INIT},
     {"fkmap",	    "fk",   P_BOOL|P_VI_DEF,
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"flash",	    "fl",   P_BOOL|P_VI_DEF,
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"foldclose",   "fcl",  P_STRING|P_VI_DEF|P_ONECOMMA|P_NODUP|P_RWIN,
 #ifdef FEAT_FOLDING
-			    (char_u *)&p_fcl, PV_NONE, did_set_foldclose,
+			    (char_u *)&p_fcl, PV_NONE, did_set_foldclose, expand_set_foldclose,
 			    {(char_u *)"", (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)NULL, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"foldcolumn",  "fdc",  P_NUM|P_VI_DEF|P_RWIN,
 #ifdef FEAT_FOLDING
-			    (char_u *)VAR_WIN, PV_FDC, did_set_foldcolumn,
+			    (char_u *)VAR_WIN, PV_FDC, did_set_foldcolumn, NULL,
 			    {(char_u *)FALSE, (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)NULL, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"foldenable",  "fen",  P_BOOL|P_VI_DEF|P_RWIN,
 #ifdef FEAT_FOLDING
-			    (char_u *)VAR_WIN, PV_FEN, NULL,
+			    (char_u *)VAR_WIN, PV_FEN, NULL, NULL,
 			    {(char_u *)TRUE, (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)NULL, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"foldexpr",    "fde",  P_STRING|P_ALLOCED|P_VIM|P_VI_DEF|P_RWIN|P_MLE,
 #if defined(FEAT_FOLDING) && defined(FEAT_EVAL)
-			    (char_u *)VAR_WIN, PV_FDE, did_set_foldexpr,
+			    (char_u *)VAR_WIN, PV_FDE, did_set_foldexpr, NULL,
 			    {(char_u *)"0", (char_u *)NULL}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)NULL, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"foldignore",  "fdi",  P_STRING|P_ALLOCED|P_VIM|P_VI_DEF|P_RWIN,
 #ifdef FEAT_FOLDING
-			    (char_u *)VAR_WIN, PV_FDI, did_set_foldignore,
+			    (char_u *)VAR_WIN, PV_FDI, did_set_foldignore, NULL,
 			    {(char_u *)"#", (char_u *)NULL}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)NULL, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"foldlevel",   "fdl",  P_NUM|P_VI_DEF|P_RWIN,
 #ifdef FEAT_FOLDING
-			    (char_u *)VAR_WIN, PV_FDL, did_set_foldlevel,
+			    (char_u *)VAR_WIN, PV_FDL, did_set_foldlevel, NULL,
 			    {(char_u *)0L, (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)NULL, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"foldlevelstart","fdls", P_NUM|P_VI_DEF|P_CURSWANT,
 #ifdef FEAT_FOLDING
-			    (char_u *)&p_fdls, PV_NONE, NULL,
+			    (char_u *)&p_fdls, PV_NONE, NULL, NULL,
 			    {(char_u *)-1L, (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)NULL, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"foldmarker",  "fmr",  P_STRING|P_ALLOCED|P_VIM|P_VI_DEF|
 						    P_RWIN|P_ONECOMMA|P_NODUP,
 #ifdef FEAT_FOLDING
-			    (char_u *)VAR_WIN, PV_FMR, did_set_foldmarker,
+			    (char_u *)VAR_WIN, PV_FMR, did_set_foldmarker, NULL,
 			    {(char_u *)"{{{,}}}", (char_u *)NULL}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)NULL, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"foldmethod",  "fdm",  P_STRING|P_ALLOCED|P_VIM|P_VI_DEF|P_RWIN,
 #ifdef FEAT_FOLDING
-			    (char_u *)VAR_WIN, PV_FDM, did_set_foldmethod,
+			    (char_u *)VAR_WIN, PV_FDM, did_set_foldmethod, expand_set_foldmethod,
 			    {(char_u *)"manual", (char_u *)NULL}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)NULL, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"foldminlines","fml",  P_NUM|P_VI_DEF|P_RWIN,
 #ifdef FEAT_FOLDING
-			    (char_u *)VAR_WIN, PV_FML, did_set_foldminlines,
+			    (char_u *)VAR_WIN, PV_FML, did_set_foldminlines, NULL,
 			    {(char_u *)1L, (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)NULL, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"foldnestmax", "fdn",  P_NUM|P_VI_DEF|P_RWIN,
 #ifdef FEAT_FOLDING
-			    (char_u *)VAR_WIN, PV_FDN, did_set_foldnestmax,
+			    (char_u *)VAR_WIN, PV_FDN, did_set_foldnestmax, NULL,
 			    {(char_u *)20L, (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)NULL, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"foldopen",    "fdo",  P_STRING|P_VI_DEF|P_ONECOMMA|P_NODUP|P_CURSWANT,
 #ifdef FEAT_FOLDING
-			    (char_u *)&p_fdo, PV_NONE, did_set_foldopen,
+			    (char_u *)&p_fdo, PV_NONE, did_set_foldopen, expand_set_foldopen,
 		 {(char_u *)"block,hor,mark,percent,quickfix,search,tag,undo",
 						 (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)NULL, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"foldtext",    "fdt",  P_STRING|P_ALLOCED|P_VIM|P_VI_DEF|P_RWIN|P_MLE,
 #if defined(FEAT_FOLDING) && defined(FEAT_EVAL)
-			    (char_u *)VAR_WIN, PV_FDT, did_set_optexpr,
+			    (char_u *)VAR_WIN, PV_FDT, did_set_optexpr, NULL,
 			    {(char_u *)"foldtext()", (char_u *)NULL}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)NULL, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"formatexpr", "fex",   P_STRING|P_ALLOCED|P_VI_DEF|P_VIM|P_MLE,
 #ifdef FEAT_EVAL
-			    (char_u *)&p_fex, PV_FEX, did_set_optexpr,
+			    (char_u *)&p_fex, PV_FEX, did_set_optexpr, NULL,
 			    {(char_u *)"", (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)0L, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"formatoptions","fo",  P_STRING|P_ALLOCED|P_VIM|P_FLAGLIST,
-			    (char_u *)&p_fo, PV_FO, did_set_formatoptions,
+			    (char_u *)&p_fo, PV_FO, did_set_formatoptions, expand_set_formatoptions,
 			    {(char_u *)DFLT_FO_VI, (char_u *)DFLT_FO_VIM}
 			    SCTX_INIT},
     {"formatlistpat","flp", P_STRING|P_ALLOCED|P_VI_DEF,
-			    (char_u *)&p_flp, PV_FLP, NULL,
+			    (char_u *)&p_flp, PV_FLP, NULL, NULL,
 			    {(char_u *)"^\\s*\\d\\+[\\]:.)}\\t ]\\s*",
 						 (char_u *)0L} SCTX_INIT},
     {"formatprg",   "fp",   P_STRING|P_EXPAND|P_VI_DEF|P_SECURE,
-			    (char_u *)&p_fp, PV_FP, NULL,
+			    (char_u *)&p_fp, PV_FP, NULL, NULL,
 			    {(char_u *)"", (char_u *)0L} SCTX_INIT},
     {"fsync",       "fs",   P_BOOL|P_SECURE|P_VI_DEF,
 #ifdef HAVE_FSYNC
-			    (char_u *)&p_fs, PV_NONE, NULL,
+			    (char_u *)&p_fs, PV_NONE, NULL, NULL,
 			    {(char_u *)TRUE, (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)FALSE, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"gdefault",    "gd",   P_BOOL|P_VI_DEF|P_VIM,
-			    (char_u *)&p_gd, PV_NONE, NULL,
+			    (char_u *)&p_gd, PV_NONE, NULL, NULL,
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"graphic",	    "gr",   P_BOOL|P_VI_DEF,
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"grepformat",  "gfm",  P_STRING|P_VI_DEF|P_ONECOMMA|P_NODUP,
 #ifdef FEAT_QUICKFIX
-			    (char_u *)&p_gefm, PV_NONE, NULL,
+			    (char_u *)&p_gefm, PV_NONE, NULL, NULL,
 			    {(char_u *)DFLT_GREPFORMAT, (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)NULL, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"grepprg",	    "gp",   P_STRING|P_EXPAND|P_VI_DEF|P_SECURE,
 #ifdef FEAT_QUICKFIX
-			    (char_u *)&p_gp, PV_GP, NULL,
+			    (char_u *)&p_gp, PV_GP, NULL, NULL,
 			    {
 # if defined(MSWIN)
 			    // may be changed to "grep -n" in os_win32.c
@@ -1139,14 +1144,14 @@ static struct vimoption options[] =
 # endif
 			    (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)NULL, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"guicursor",    "gcr", P_STRING|P_VI_DEF|P_ONECOMMA|P_NODUP,
 #ifdef CURSOR_SHAPE
 			    (char_u *)&p_guicursor, PV_NONE,
-			    did_set_guicursor,
+			    did_set_guicursor, NULL,
 			    {
 # ifdef FEAT_GUI
 				(char_u *)"n-v-c:block-Cursor/lCursor,ve:ver35-Cursor,o:hor50-Cursor,i-ci:ver25-Cursor/lCursor,r-cr:hor20-Cursor/lCursor,sm:block-Cursor-blinkwait175-blinkoff150-blinkon175",
@@ -1155,36 +1160,36 @@ static struct vimoption options[] =
 # endif
 				    (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)NULL, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"guifont",	    "gfn",  P_STRING|P_VI_DEF|P_RCLR|P_ONECOMMA|P_NODUP,
 #ifdef FEAT_GUI
-			    (char_u *)&p_guifont, PV_NONE, did_set_guifont,
+			    (char_u *)&p_guifont, PV_NONE, did_set_guifont, NULL,
 			    {(char_u *)"", (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)NULL, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"guifontset",  "gfs",  P_STRING|P_VI_DEF|P_RCLR|P_ONECOMMA,
 #if defined(FEAT_GUI) && defined(FEAT_XFONTSET)
 			    (char_u *)&p_guifontset, PV_NONE,
-			    did_set_guifontset,
+			    did_set_guifontset, NULL,
 			    {(char_u *)"", (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)NULL, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"guifontwide", "gfw",  P_STRING|P_VI_DEF|P_RCLR|P_ONECOMMA|P_NODUP,
 #if defined(FEAT_GUI)
 			    (char_u *)&p_guifontwide, PV_NONE,
-			    did_set_guifontwide,
+			    did_set_guifontwide, NULL,
 			    {(char_u *)"", (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)NULL, (char_u *)0L}
 #endif
 			    SCTX_INIT},
@@ -1193,10 +1198,10 @@ static struct vimoption options[] =
     {"guiligatures", "gli", P_STRING|P_VI_DEF|P_RCLR|P_ONECOMMA|P_NODUP,
 #if defined(FEAT_GUI_GTK)
 			    (char_u *)&p_guiligatures, PV_NONE,
-			    did_set_guiligatures,
+			    did_set_guiligatures, NULL,
 			    {(char_u *)"", (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)NULL, (char_u *)0L}
 #endif
 			    SCTX_INIT},
@@ -1204,215 +1209,215 @@ static struct vimoption options[] =
 
     {"guiheadroom", "ghr",  P_NUM|P_VI_DEF,
 #if defined(FEAT_GUI_GTK) || defined(FEAT_GUI_X11)
-			    (char_u *)&p_ghr, PV_NONE, NULL,
+			    (char_u *)&p_ghr, PV_NONE, NULL, NULL,
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 #endif
 			    {(char_u *)50L, (char_u *)0L} SCTX_INIT},
     {"guioptions",  "go",   P_STRING|P_VI_DEF|P_RALL|P_FLAGLIST,
 #if defined(FEAT_GUI)
-			    (char_u *)&p_go, PV_NONE, did_set_guioptions,
+			    (char_u *)&p_go, PV_NONE, did_set_guioptions, expand_set_guioptions,
 # if defined(UNIX)
 			    {(char_u *)"aegimrLtT", (char_u *)0L}
 # else
 			    {(char_u *)"egmrLtT", (char_u *)0L}
 # endif
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)NULL, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"guipty",	    NULL,   P_BOOL|P_VI_DEF,
 #if defined(FEAT_GUI)
-			    (char_u *)&p_guipty, PV_NONE, NULL,
+			    (char_u *)&p_guipty, PV_NONE, NULL, NULL,
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 #endif
 			    {(char_u *)TRUE, (char_u *)0L} SCTX_INIT},
     {"guitablabel",  "gtl", P_STRING|P_VI_DEF|P_RWIN|P_MLE,
 #if defined(FEAT_GUI_TABLINE)
-			    (char_u *)&p_gtl, PV_NONE, did_set_guitablabel,
+			    (char_u *)&p_gtl, PV_NONE, did_set_guitablabel, NULL,
 			    {(char_u *)"", (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)NULL, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"guitabtooltip",  "gtt", P_STRING|P_VI_DEF|P_RWIN,
 #if defined(FEAT_GUI_TABLINE)
-			    (char_u *)&p_gtt, PV_NONE, NULL,
+			    (char_u *)&p_gtt, PV_NONE, NULL, NULL,
 			    {(char_u *)"", (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)NULL, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"hardtabs",    "ht",   P_NUM|P_VI_DEF,
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)0L, (char_u *)0L} SCTX_INIT},
     {"helpfile",    "hf",   P_STRING|P_EXPAND|P_VI_DEF|P_SECURE,
-			    (char_u *)&p_hf, PV_NONE, did_set_helpfile,
+			    (char_u *)&p_hf, PV_NONE, did_set_helpfile, NULL,
 			    {(char_u *)DFLT_HELPFILE, (char_u *)0L}
 			    SCTX_INIT},
     {"helpheight",  "hh",   P_NUM|P_VI_DEF,
 			    (char_u *)&p_hh, PV_NONE,
-			    did_set_winheight_helpheight,
+			    did_set_winheight_helpheight, NULL,
 			    {(char_u *)20L, (char_u *)0L} SCTX_INIT},
     {"helplang",    "hlg",  P_STRING|P_VI_DEF|P_ONECOMMA,
 #ifdef FEAT_MULTI_LANG
-			    (char_u *)&p_hlg, PV_NONE, did_set_helplang,
+			    (char_u *)&p_hlg, PV_NONE, did_set_helplang, NULL,
 			    {(char_u *)"", (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)0L, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"hidden",	    "hid",  P_BOOL|P_VI_DEF,
-			    (char_u *)&p_hid, PV_NONE, NULL,
+			    (char_u *)&p_hid, PV_NONE, NULL, NULL,
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
-    {"highlight",   "hl",   P_STRING|P_VI_DEF|P_RCLR|P_ONECOMMA|P_NODUP,
-			    (char_u *)&p_hl, PV_NONE, did_set_highlight,
+    {"highlight",   "hl",   P_STRING|P_VI_DEF|P_RCLR|P_ONECOMMA|P_NODUP|P_COLON,
+			    (char_u *)&p_hl, PV_NONE, did_set_highlight, expand_set_highlight,
 			    {(char_u *)HIGHLIGHT_INIT, (char_u *)0L}
 			    SCTX_INIT},
     {"history",	    "hi",   P_NUM|P_VIM,
-			    (char_u *)&p_hi, PV_NONE, NULL,
+			    (char_u *)&p_hi, PV_NONE, NULL, NULL,
 			    {(char_u *)0L, (char_u *)50L} SCTX_INIT},
     {"hkmap",	    "hk",   P_BOOL|P_VI_DEF|P_VIM,
 #ifdef FEAT_RIGHTLEFT
-			    (char_u *)&p_hkmap, PV_NONE, NULL,
+			    (char_u *)&p_hkmap, PV_NONE, NULL, NULL,
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 #endif
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"hkmapp",	    "hkp",  P_BOOL|P_VI_DEF|P_VIM,
 #ifdef FEAT_RIGHTLEFT
-			    (char_u *)&p_hkmapp, PV_NONE, NULL,
+			    (char_u *)&p_hkmapp, PV_NONE, NULL, NULL,
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 #endif
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"hlsearch",    "hls",  P_BOOL|P_VI_DEF|P_VIM|P_RALL,
 #if defined(FEAT_SEARCH_EXTRA)
-			    (char_u *)&p_hls, PV_NONE, did_set_hlsearch,
+			    (char_u *)&p_hls, PV_NONE, did_set_hlsearch, NULL,
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 #endif
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"icon",	    NULL,   P_BOOL|P_VI_DEF,
-			    (char_u *)&p_icon, PV_NONE, did_set_title_icon,
+			    (char_u *)&p_icon, PV_NONE, did_set_title_icon, NULL,
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"iconstring",  NULL,   P_STRING|P_VI_DEF|P_MLE,
 			    (char_u *)&p_iconstring, PV_NONE,
-			    did_set_iconstring,
+			    did_set_iconstring, NULL,
 			    {(char_u *)"", (char_u *)0L} SCTX_INIT},
     {"ignorecase",  "ic",   P_BOOL|P_VI_DEF,
-			    (char_u *)&p_ic, PV_NONE, did_set_ignorecase,
+			    (char_u *)&p_ic, PV_NONE, did_set_ignorecase, NULL,
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"imactivatefunc","imaf",P_STRING|P_VI_DEF|P_SECURE|P_FUNC,
 #if defined(FEAT_EVAL) && \
 	(defined(FEAT_XIM) || defined(IME_WITHOUT_XIM) || defined(VIMDLL))
 			    (char_u *)&p_imaf, PV_NONE,
-			    did_set_imactivatefunc,
+			    did_set_imactivatefunc, NULL,
 			    {(char_u *)"", (char_u *)NULL}
 # else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)NULL, (char_u *)0L}
 # endif
 			    SCTX_INIT},
     {"imactivatekey","imak",P_STRING|P_VI_DEF,
 #if defined(FEAT_XIM) && defined(FEAT_GUI_GTK)
 			    (char_u *)&p_imak, PV_NONE,
-			    did_set_imactivatekey,
+			    did_set_imactivatekey, NULL,
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 #endif
 			    {(char_u *)"", (char_u *)0L} SCTX_INIT},
     {"imcmdline",   "imc",  P_BOOL|P_VI_DEF,
-			    (char_u *)&p_imcmdline, PV_NONE, NULL,
+			    (char_u *)&p_imcmdline, PV_NONE, NULL, NULL,
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"imdisable",   "imd",  P_BOOL|P_VI_DEF,
 #if defined(HAVE_INPUT_METHOD)
 			    (char_u *)&p_imdisable, PV_NONE,
-			    did_set_imdisable,
+			    did_set_imdisable, NULL,
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 #endif
 			    {(char_u *)FALSE, (char_u *)0L}
 			    SCTX_INIT},
     {"iminsert",    "imi",  P_NUM|P_VI_DEF,
-			    (char_u *)&p_iminsert, PV_IMI, did_set_iminsert,
+			    (char_u *)&p_iminsert, PV_IMI, did_set_iminsert, NULL,
 			    {(char_u *)B_IMODE_NONE, (char_u *)0L}
 			    SCTX_INIT},
     {"imsearch",    "ims",  P_NUM|P_VI_DEF,
-			    (char_u *)&p_imsearch, PV_IMS, did_set_imsearch,
+			    (char_u *)&p_imsearch, PV_IMS, did_set_imsearch, NULL,
 			    {(char_u *)B_IMODE_USE_INSERT, (char_u *)0L}
 			    SCTX_INIT},
     {"imstatusfunc","imsf",P_STRING|P_VI_DEF|P_SECURE|P_FUNC,
 #if defined(FEAT_EVAL) && \
 	(defined(FEAT_XIM) || defined(IME_WITHOUT_XIM) || defined(VIMDLL))
-			    (char_u *)&p_imsf, PV_NONE, did_set_imstatusfunc,
+			    (char_u *)&p_imsf, PV_NONE, did_set_imstatusfunc, NULL,
 			    {(char_u *)"", (char_u *)NULL}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)NULL, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"imstyle",	    "imst", P_NUM|P_VI_DEF|P_SECURE,
 #if defined(FEAT_XIM) && defined(FEAT_GUI_GTK)
-			    (char_u *)&p_imst, PV_NONE, did_set_imstyle,
+			    (char_u *)&p_imst, PV_NONE, did_set_imstyle, NULL,
 			    {(char_u *)IM_OVER_THE_SPOT, (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)0L, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"include",	    "inc",  P_STRING|P_ALLOCED|P_VI_DEF,
 #ifdef FEAT_FIND_ID
-			    (char_u *)&p_inc, PV_INC, NULL,
+			    (char_u *)&p_inc, PV_INC, NULL, NULL,
 			    {(char_u *)"^\\s*#\\s*include", (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)0L, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"includeexpr", "inex", P_STRING|P_ALLOCED|P_VI_DEF|P_MLE,
 #if defined(FEAT_FIND_ID) && defined(FEAT_EVAL)
-			    (char_u *)&p_inex, PV_INEX, did_set_optexpr,
+			    (char_u *)&p_inex, PV_INEX, did_set_optexpr, NULL,
 			    {(char_u *)"", (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)0L, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"incsearch",   "is",   P_BOOL|P_VI_DEF|P_VIM,
-			    (char_u *)&p_is, PV_NONE, NULL,
+			    (char_u *)&p_is, PV_NONE, NULL, NULL,
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"indentexpr", "inde",  P_STRING|P_ALLOCED|P_VI_DEF|P_VIM|P_MLE,
 #if defined(FEAT_EVAL)
-			    (char_u *)&p_inde, PV_INDE, did_set_optexpr,
+			    (char_u *)&p_inde, PV_INDE, did_set_optexpr, NULL,
 			    {(char_u *)"", (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)0L, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"indentkeys", "indk",  P_STRING|P_ALLOCED|P_VI_DEF|P_ONECOMMA|P_NODUP,
 #if defined(FEAT_EVAL)
-			    (char_u *)&p_indk, PV_INDK, NULL,
+			    (char_u *)&p_indk, PV_INDK, NULL, NULL,
 			    {INDENTKEYS_DEFAULT, (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)0L, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"infercase",   "inf",  P_BOOL|P_VI_DEF,
-			    (char_u *)&p_inf, PV_INF, NULL,
+			    (char_u *)&p_inf, PV_INF, NULL, NULL,
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"insertmode",  "im",   P_BOOL|P_VI_DEF|P_VIM,
-			    (char_u *)&p_im, PV_NONE, did_set_insertmode,
+			    (char_u *)&p_im, PV_NONE, did_set_insertmode, NULL,
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"isfname",	    "isf",  P_STRING|P_VI_DEF|P_COMMA|P_NODUP,
-			    (char_u *)&p_isf, PV_NONE, did_set_isopt,
+			    (char_u *)&p_isf, PV_NONE, did_set_isopt, NULL,
 			    {
 #ifdef BACKSLASH_IN_FILENAME
 				// Excluded are: & and ^ are special in cmd.exe
@@ -1429,7 +1434,7 @@ static struct vimoption options[] =
 #endif
 				(char_u *)0L} SCTX_INIT},
     {"isident",	    "isi",  P_STRING|P_VI_DEF|P_COMMA|P_NODUP,
-			    (char_u *)&p_isi, PV_NONE, did_set_isopt,
+			    (char_u *)&p_isi, PV_NONE, did_set_isopt, NULL,
 			    {
 #if defined(MSWIN)
 			    (char_u *)"@,48-57,_,128-167,224-235",
@@ -1438,7 +1443,7 @@ static struct vimoption options[] =
 #endif
 				(char_u *)0L} SCTX_INIT},
     {"iskeyword",   "isk",  P_STRING|P_ALLOCED|P_VIM|P_COMMA|P_NODUP,
-			    (char_u *)&p_isk, PV_ISK, did_set_isopt,
+			    (char_u *)&p_isk, PV_ISK, did_set_isopt, NULL,
 			    {
 				(char_u *)"@,48-57,_",
 #if defined(MSWIN)
@@ -1448,7 +1453,7 @@ static struct vimoption options[] =
 #endif
 			    } SCTX_INIT},
     {"isprint",	    "isp",  P_STRING|P_VI_DEF|P_RALL|P_COMMA|P_NODUP,
-			    (char_u *)&p_isp, PV_NONE, did_set_isopt,
+			    (char_u *)&p_isp, PV_NONE, did_set_isopt, NULL,
 			    {
 #if defined(MSWIN) || defined(VMS)
 			    (char_u *)"@,~-255",
@@ -1457,38 +1462,38 @@ static struct vimoption options[] =
 #endif
 				(char_u *)0L} SCTX_INIT},
     {"joinspaces",  "js",   P_BOOL|P_VI_DEF|P_VIM,
-			    (char_u *)&p_js, PV_NONE, NULL,
+			    (char_u *)&p_js, PV_NONE, NULL, NULL,
 			    {(char_u *)TRUE, (char_u *)0L} SCTX_INIT},
     {"jumpoptions", "jop",  P_STRING|P_VI_DEF|P_VIM|P_ONECOMMA|P_NODUP,
-			    (char_u *)&p_jop, PV_NONE, did_set_jumpoptions,
+			    (char_u *)&p_jop, PV_NONE, did_set_jumpoptions, expand_set_jumpoptions,
 			    {(char_u *)"", (char_u *)0L} SCTX_INIT},
     {"key",	    NULL,   P_STRING|P_ALLOCED|P_VI_DEF|P_NO_MKRC,
 #ifdef FEAT_CRYPT
-			    (char_u *)&p_key, PV_KEY, did_set_cryptkey,
+			    (char_u *)&p_key, PV_KEY, did_set_cryptkey, NULL,
 			    {(char_u *)"", (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)0L, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"keymap",	    "kmp",  P_STRING|P_ALLOCED|P_VI_DEF|P_RBUF|P_RSTAT|P_NFNAME|P_PRI_MKRC,
 #ifdef FEAT_KEYMAP
-			    (char_u *)&p_keymap, PV_KMAP, did_set_keymap,
+			    (char_u *)&p_keymap, PV_KMAP, did_set_keymap, NULL,
 			    {(char_u *)"", (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)"", (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"keymodel",    "km",   P_STRING|P_VI_DEF|P_ONECOMMA|P_NODUP,
-			    (char_u *)&p_km, PV_NONE, did_set_keymodel,
+			    (char_u *)&p_km, PV_NONE, did_set_keymodel, expand_set_keymodel,
 			    {(char_u *)"", (char_u *)0L} SCTX_INIT},
-    {"keyprotocol", "kpc",  P_STRING|P_VI_DEF|P_ONECOMMA|P_NODUP,
-			    (char_u *)&p_kpc, PV_NONE, did_set_keyprotocol,
+    {"keyprotocol", "kpc",  P_STRING|P_VI_DEF|P_ONECOMMA|P_NODUP|P_COLON,
+			    (char_u *)&p_kpc, PV_NONE, did_set_keyprotocol, expand_set_keyprotocol,
 			    {(char_u *)"kitty:kitty,foot:kitty,wezterm:kitty,xterm:mok2", (char_u *)0L}
 			    SCTX_INIT},
     {"keywordprg",  "kp",   P_STRING|P_EXPAND|P_VI_DEF|P_SECURE,
-			    (char_u *)&p_kp, PV_KP, NULL,
+			    (char_u *)&p_kp, PV_KP, NULL, NULL,
 			    {
 #if defined(MSWIN)
 			    (char_u *)":help",
@@ -1502,49 +1507,49 @@ static struct vimoption options[] =
 				(char_u *)0L} SCTX_INIT},
     {"langmap",     "lmap", P_STRING|P_VI_DEF|P_ONECOMMA|P_NODUP|P_SECURE,
 #ifdef FEAT_LANGMAP
-			    (char_u *)&p_langmap, PV_NONE, did_set_langmap,
+			    (char_u *)&p_langmap, PV_NONE, did_set_langmap, NULL,
 			    {(char_u *)"", (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)NULL, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"langmenu",    "lm",   P_STRING|P_VI_DEF|P_NFNAME,
 #if defined(FEAT_MENU) && defined(FEAT_MULTI_LANG)
-			    (char_u *)&p_lm, PV_NONE, NULL,
+			    (char_u *)&p_lm, PV_NONE, NULL, NULL,
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 #endif
 			    {(char_u *)"", (char_u *)0L} SCTX_INIT},
     {"langnoremap",  "lnr",   P_BOOL|P_VI_DEF,
 #ifdef FEAT_LANGMAP
-			    (char_u *)&p_lnr, PV_NONE, did_set_langnoremap,
+			    (char_u *)&p_lnr, PV_NONE, did_set_langnoremap, NULL,
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 #endif
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"langremap",  "lrm",   P_BOOL|P_VI_DEF,
 #ifdef FEAT_LANGMAP
-			    (char_u *)&p_lrm, PV_NONE, did_set_langremap,
+			    (char_u *)&p_lrm, PV_NONE, did_set_langremap, NULL,
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 #endif
 			    {(char_u *)TRUE, (char_u *)0L} SCTX_INIT},
     {"laststatus",  "ls",   P_NUM|P_VI_DEF|P_RALL,
-			    (char_u *)&p_ls, PV_NONE, did_set_laststatus,
+			    (char_u *)&p_ls, PV_NONE, did_set_laststatus, NULL,
 			    {(char_u *)1L, (char_u *)0L} SCTX_INIT},
     {"lazyredraw",  "lz",   P_BOOL|P_VI_DEF,
-			    (char_u *)&p_lz, PV_NONE, NULL,
+			    (char_u *)&p_lz, PV_NONE, NULL, NULL,
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"linebreak",   "lbr",  P_BOOL|P_VI_DEF|P_RWIN,
 #ifdef FEAT_LINEBREAK
-			    (char_u *)VAR_WIN, PV_LBR, NULL,
+			    (char_u *)VAR_WIN, PV_LBR, NULL, NULL,
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 #endif
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"lines",	    NULL,   P_NUM|P_NODEFAULT|P_NO_MKRC|P_VI_DEF|P_RCLR,
-			    (char_u *)&Rows, PV_NONE, NULL,
+			    (char_u *)&Rows, PV_NONE, NULL, NULL,
 			    {
 #if defined(MSWIN)
 			    (char_u *)25L,
@@ -1555,9 +1560,9 @@ static struct vimoption options[] =
     {"linespace",   "lsp",  P_NUM|P_VI_DEF|P_RCLR,
 #ifdef FEAT_GUI
 			    (char_u *)&p_linespace, PV_NONE,
-			    did_set_linespace,
+			    did_set_linespace, NULL,
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 #endif
 #ifdef FEAT_GUI_MSWIN
 			    {(char_u *)1L, (char_u *)0L}
@@ -1566,135 +1571,135 @@ static struct vimoption options[] =
 #endif
 			    SCTX_INIT},
     {"lisp",	    NULL,   P_BOOL|P_VI_DEF,
-			    (char_u *)&p_lisp, PV_LISP, did_set_lisp,
+			    (char_u *)&p_lisp, PV_LISP, did_set_lisp, NULL,
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"lispoptions", "lop",  P_STRING|P_ALLOCED|P_VI_DEF|P_ONECOMMA|P_NODUP,
-			    (char_u *)&p_lop, PV_LOP, did_set_lispoptions,
+			    (char_u *)&p_lop, PV_LOP, did_set_lispoptions, expand_set_lispoptions,
 			    {(char_u *)"", (char_u *)0L} SCTX_INIT},
     {"lispwords",   "lw",   P_STRING|P_VI_DEF|P_ONECOMMA|P_NODUP,
-			    (char_u *)&p_lispwords, PV_LW, NULL,
+			    (char_u *)&p_lispwords, PV_LW, NULL, NULL,
 			    {(char_u *)LISPWORD_VALUE, (char_u *)0L} SCTX_INIT},
     {"list",	    NULL,   P_BOOL|P_VI_DEF|P_RWIN,
-			    (char_u *)VAR_WIN, PV_LIST, NULL,
+			    (char_u *)VAR_WIN, PV_LIST, NULL, NULL,
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"listchars",   "lcs",  P_STRING|P_VI_DEF|P_RALL|P_ONECOMMA|P_NODUP,
-			    (char_u *)&p_lcs, PV_LCS, did_set_chars_option,
+			    (char_u *)&p_lcs, PV_LCS, did_set_chars_option, expand_set_chars_option,
 			    {(char_u *)"eol:$", (char_u *)0L} SCTX_INIT},
     {"loadplugins", "lpl",  P_BOOL|P_VI_DEF,
-			    (char_u *)&p_lpl, PV_NONE, NULL,
+			    (char_u *)&p_lpl, PV_NONE, NULL, NULL,
 			    {(char_u *)TRUE, (char_u *)0L} SCTX_INIT},
     {"luadll",      NULL,   P_STRING|P_EXPAND|P_VI_DEF|P_SECURE,
 #if defined(DYNAMIC_LUA)
-			    (char_u *)&p_luadll, PV_NONE, NULL,
+			    (char_u *)&p_luadll, PV_NONE, NULL, NULL,
 			    {(char_u *)DYNAMIC_LUA_DLL, (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)"", (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"macatsui",    NULL,   P_BOOL|P_VI_DEF|P_RCLR,
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)"", (char_u *)0L}
 			    SCTX_INIT},
     {"magic",	    NULL,   P_BOOL|P_VI_DEF,
-			    (char_u *)&p_magic, PV_NONE, NULL,
+			    (char_u *)&p_magic, PV_NONE, NULL, NULL,
 			    {(char_u *)TRUE, (char_u *)0L} SCTX_INIT},
     {"makeef",	    "mef",  P_STRING|P_EXPAND|P_VI_DEF|P_SECURE,
 #ifdef FEAT_QUICKFIX
-			    (char_u *)&p_mef, PV_NONE, NULL,
+			    (char_u *)&p_mef, PV_NONE, NULL, NULL,
 			    {(char_u *)"", (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)NULL, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"makeencoding","menc", P_STRING|P_VI_DEF,
-			    (char_u *)&p_menc, PV_MENC, did_set_encoding,
+			    (char_u *)&p_menc, PV_MENC, did_set_encoding, expand_set_encoding,
 			    {(char_u *)"", (char_u *)0L}
 			    SCTX_INIT},
     {"makeprg",	    "mp",   P_STRING|P_EXPAND|P_VI_DEF|P_SECURE,
 #ifdef FEAT_QUICKFIX
-			    (char_u *)&p_mp, PV_MP, NULL,
+			    (char_u *)&p_mp, PV_MP, NULL, NULL,
 # ifdef VMS
 			    {(char_u *)"MMS", (char_u *)0L}
 # else
 			    {(char_u *)"make", (char_u *)0L}
 # endif
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)NULL, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"matchpairs",  "mps",  P_STRING|P_ALLOCED|P_VI_DEF|P_ONECOMMA|P_NODUP,
-			    (char_u *)&p_mps, PV_MPS, did_set_matchpairs,
+			    (char_u *)&p_mps, PV_MPS, did_set_matchpairs, NULL,
 			    {(char_u *)"(:),{:},[:]", (char_u *)0L}
 			    SCTX_INIT},
     {"matchtime",   "mat",  P_NUM|P_VI_DEF,
-			    (char_u *)&p_mat, PV_NONE, NULL,
+			    (char_u *)&p_mat, PV_NONE, NULL, NULL,
 			    {(char_u *)5L, (char_u *)0L} SCTX_INIT},
     {"maxcombine",  "mco",  P_NUM|P_VI_DEF|P_CURSWANT,
-			    (char_u *)&p_mco, PV_NONE, did_set_maxcombine,
+			    (char_u *)&p_mco, PV_NONE, did_set_maxcombine, NULL,
 			    {(char_u *)2, (char_u *)0L} SCTX_INIT},
     {"maxfuncdepth", "mfd", P_NUM|P_VI_DEF,
 #ifdef FEAT_EVAL
-			    (char_u *)&p_mfd, PV_NONE, NULL,
+			    (char_u *)&p_mfd, PV_NONE, NULL, NULL,
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 #endif
 			    {(char_u *)100L, (char_u *)0L} SCTX_INIT},
     {"maxmapdepth", "mmd",  P_NUM|P_VI_DEF,
-			    (char_u *)&p_mmd, PV_NONE, NULL,
+			    (char_u *)&p_mmd, PV_NONE, NULL, NULL,
 			    {(char_u *)1000L, (char_u *)0L} SCTX_INIT},
     {"maxmem",	    "mm",   P_NUM|P_VI_DEF,
-			    (char_u *)&p_mm, PV_NONE, NULL,
+			    (char_u *)&p_mm, PV_NONE, NULL, NULL,
 			    {(char_u *)DFLT_MAXMEM, (char_u *)0L}
 			    SCTX_INIT},
     {"maxmempattern","mmp", P_NUM|P_VI_DEF,
-			    (char_u *)&p_mmp, PV_NONE, NULL,
+			    (char_u *)&p_mmp, PV_NONE, NULL, NULL,
 			    {(char_u *)1000L, (char_u *)0L} SCTX_INIT},
     {"maxmemtot",   "mmt",  P_NUM|P_VI_DEF,
-			    (char_u *)&p_mmt, PV_NONE, NULL,
+			    (char_u *)&p_mmt, PV_NONE, NULL, NULL,
 			    {(char_u *)DFLT_MAXMEMTOT, (char_u *)0L}
 			    SCTX_INIT},
     {"menuitems",   "mis",  P_NUM|P_VI_DEF,
 #ifdef FEAT_MENU
-			    (char_u *)&p_mis, PV_NONE, NULL,
+			    (char_u *)&p_mis, PV_NONE, NULL, NULL,
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 #endif
 			    {(char_u *)25L, (char_u *)0L} SCTX_INIT},
     {"mesg",	    NULL,   P_BOOL|P_VI_DEF,
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"mkspellmem",  "msm",  P_STRING|P_VI_DEF|P_EXPAND|P_SECURE,
 #ifdef FEAT_SPELL
-			    (char_u *)&p_msm, PV_NONE, did_set_mkspellmem,
+			    (char_u *)&p_msm, PV_NONE, did_set_mkspellmem, NULL,
 			    {(char_u *)"460000,2000,500", (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)0L, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"modeline",    "ml",   P_BOOL|P_VIM,
-			    (char_u *)&p_ml, PV_ML, NULL,
+			    (char_u *)&p_ml, PV_ML, NULL, NULL,
 			    {(char_u *)FALSE, (char_u *)TRUE} SCTX_INIT},
     {"modelineexpr", "mle",  P_BOOL|P_VI_DEF|P_SECURE,
-			    (char_u *)&p_mle, PV_NONE, NULL,
+			    (char_u *)&p_mle, PV_NONE, NULL, NULL,
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"modelines",   "mls",  P_NUM|P_VI_DEF,
-			    (char_u *)&p_mls, PV_NONE, NULL,
+			    (char_u *)&p_mls, PV_NONE, NULL, NULL,
 			    {(char_u *)5L, (char_u *)0L} SCTX_INIT},
     {"modifiable",  "ma",   P_BOOL|P_VI_DEF|P_NOGLOB,
-			    (char_u *)&p_ma, PV_MA, did_set_modifiable,
+			    (char_u *)&p_ma, PV_MA, did_set_modifiable, NULL,
 			    {(char_u *)TRUE, (char_u *)0L} SCTX_INIT},
     {"modified",    "mod",  P_BOOL|P_NO_MKRC|P_VI_DEF|P_RSTAT,
-			    (char_u *)&p_mod, PV_MOD, did_set_modified,
+			    (char_u *)&p_mod, PV_MOD, did_set_modified, NULL,
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"more",	    NULL,   P_BOOL|P_VIM,
-			    (char_u *)&p_more, PV_NONE, NULL,
+			    (char_u *)&p_more, PV_NONE, NULL, NULL,
 			    {(char_u *)FALSE, (char_u *)TRUE} SCTX_INIT},
     {"mouse",	    NULL,   P_STRING|P_VI_DEF|P_FLAGLIST,
-			    (char_u *)&p_mouse, PV_NONE, did_set_mouse,
+			    (char_u *)&p_mouse, PV_NONE, did_set_mouse, expand_set_mouse,
 			    {
 #if defined(MSWIN)
 				(char_u *)"a",
@@ -1704,20 +1709,20 @@ static struct vimoption options[] =
 				(char_u *)0L} SCTX_INIT},
     {"mousefocus",   "mousef", P_BOOL|P_VI_DEF,
 #ifdef FEAT_GUI
-			    (char_u *)&p_mousef, PV_NONE, NULL,
+			    (char_u *)&p_mousef, PV_NONE, NULL, NULL,
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 #endif
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"mousehide",   "mh",   P_BOOL|P_VI_DEF,
 #ifdef FEAT_GUI
-			    (char_u *)&p_mh, PV_NONE, did_set_mousehide,
+			    (char_u *)&p_mh, PV_NONE, did_set_mousehide, NULL,
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 #endif
 			    {(char_u *)TRUE, (char_u *)0L} SCTX_INIT},
     {"mousemodel",  "mousem", P_STRING|P_VI_DEF,
-			    (char_u *)&p_mousem, PV_NONE, did_set_mousemodel,
+			    (char_u *)&p_mousem, PV_NONE, did_set_mousemodel, expand_set_mousemodel,
 			    {
 #if defined(MSWIN)
 				(char_u *)"popup",
@@ -1731,127 +1736,127 @@ static struct vimoption options[] =
 				(char_u *)0L} SCTX_INIT},
     {"mousemoveevent",   "mousemev",   P_BOOL|P_VI_DEF,
 #ifdef FEAT_GUI
-			    (char_u *)&p_mousemev, PV_NONE, NULL,
+			    (char_u *)&p_mousemev, PV_NONE, NULL, NULL,
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 #endif
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"mouseshape",  "mouses",  P_STRING|P_VI_DEF|P_ONECOMMA|P_NODUP,
 #ifdef FEAT_MOUSESHAPE
 			    (char_u *)&p_mouseshape, PV_NONE,
-			    did_set_mouseshape,
+			    did_set_mouseshape, NULL,
 			    {(char_u *)"i-r:beam,s:updown,sd:udsizing,vs:leftright,vd:lrsizing,m:no,ml:up-arrow,v:rightup-arrow", (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)NULL, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"mousetime",   "mouset",	P_NUM|P_VI_DEF,
-			    (char_u *)&p_mouset, PV_NONE, NULL,
+			    (char_u *)&p_mouset, PV_NONE, NULL, NULL,
 			    {(char_u *)500L, (char_u *)0L} SCTX_INIT},
     {"mzschemedll", NULL,   P_STRING|P_EXPAND|P_VI_DEF|P_SECURE,
 #if defined(DYNAMIC_MZSCHEME)
-			    (char_u *)&p_mzschemedll, PV_NONE, NULL,
+			    (char_u *)&p_mzschemedll, PV_NONE, NULL, NULL,
 			    {(char_u *)DYNAMIC_MZSCH_DLL, (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)"", (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"mzschemegcdll", NULL, P_STRING|P_EXPAND|P_VI_DEF|P_SECURE,
 #if defined(DYNAMIC_MZSCHEME)
-			    (char_u *)&p_mzschemegcdll, PV_NONE, NULL,
+			    (char_u *)&p_mzschemegcdll, PV_NONE, NULL, NULL,
 			    {(char_u *)DYNAMIC_MZGC_DLL, (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)"", (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"mzquantum",  "mzq",   P_NUM,
 #ifdef FEAT_MZSCHEME
-			    (char_u *)&p_mzq, PV_NONE, did_set_mzquantum,
+			    (char_u *)&p_mzq, PV_NONE, did_set_mzquantum, NULL,
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 #endif
 			    {(char_u *)100L, (char_u *)100L} SCTX_INIT},
     {"novice",	    NULL,   P_BOOL|P_VI_DEF,
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"nrformats",   "nf",   P_STRING|P_ALLOCED|P_VI_DEF|P_ONECOMMA|P_NODUP,
-			    (char_u *)&p_nf, PV_NF, did_set_nrformats,
+			    (char_u *)&p_nf, PV_NF, did_set_nrformats, expand_set_nrformats,
 			    {(char_u *)"bin,octal,hex", (char_u *)0L}
 			    SCTX_INIT},
     {"number",	    "nu",   P_BOOL|P_VI_DEF|P_RWIN,
 			    (char_u *)VAR_WIN, PV_NU,
-			    did_set_number_relativenumber,
+			    did_set_number_relativenumber, NULL,
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"numberwidth", "nuw",  P_NUM|P_RWIN|P_VIM,
 #ifdef FEAT_LINEBREAK
-			    (char_u *)VAR_WIN, PV_NUW, did_set_numberwidth,
+			    (char_u *)VAR_WIN, PV_NUW, did_set_numberwidth, NULL,
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 #endif
 			    {(char_u *)8L, (char_u *)4L} SCTX_INIT},
     {"omnifunc",    "ofu",  P_STRING|P_ALLOCED|P_VI_DEF|P_SECURE|P_FUNC,
 #ifdef FEAT_COMPL_FUNC
-			    (char_u *)&p_ofu, PV_OFU, did_set_omnifunc,
+			    (char_u *)&p_ofu, PV_OFU, did_set_omnifunc, NULL,
 			    {(char_u *)"", (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)0L, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"open",	    NULL,   P_BOOL|P_VI_DEF,
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"opendevice",  "odev", P_BOOL|P_VI_DEF,
 #if defined(MSWIN)
-			    (char_u *)&p_odev, PV_NONE, NULL,
+			    (char_u *)&p_odev, PV_NONE, NULL, NULL,
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 #endif
 			    {(char_u *)FALSE, (char_u *)FALSE}
 			    SCTX_INIT},
     {"operatorfunc", "opfunc", P_STRING|P_VI_DEF|P_SECURE|P_FUNC,
 			    (char_u *)&p_opfunc, PV_NONE,
-			    did_set_operatorfunc,
+			    did_set_operatorfunc, NULL,
 			    {(char_u *)"", (char_u *)0L} SCTX_INIT},
     {"optimize",    "opt",  P_BOOL|P_VI_DEF,
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"osfiletype",  "oft",  P_STRING|P_ALLOCED|P_VI_DEF,
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)0L, (char_u *)0L} SCTX_INIT},
     {"packpath",    "pp",   P_STRING|P_VI_DEF|P_EXPAND|P_ONECOMMA|P_NODUP
 								    |P_SECURE,
-			    (char_u *)&p_pp, PV_NONE, NULL,
+			    (char_u *)&p_pp, PV_NONE, NULL, NULL,
 			    {(char_u *)DFLT_RUNTIMEPATH, (char_u *)0L}
 			    SCTX_INIT},
     {"paragraphs",  "para", P_STRING|P_VI_DEF,
-			    (char_u *)&p_para, PV_NONE, NULL,
+			    (char_u *)&p_para, PV_NONE, NULL, NULL,
 			    {(char_u *)"IPLPPPQPP TPHPLIPpLpItpplpipbp",
 				(char_u *)0L} SCTX_INIT},
     {"paste",	    NULL,   P_BOOL|P_VI_DEF|P_PRI_MKRC,
-			    (char_u *)&p_paste, PV_NONE, did_set_paste,
+			    (char_u *)&p_paste, PV_NONE, did_set_paste, NULL,
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"pastetoggle", "pt",   P_STRING|P_VI_DEF,
-			    (char_u *)&p_pt, PV_NONE, did_set_pastetoggle,
+			    (char_u *)&p_pt, PV_NONE, did_set_pastetoggle, NULL,
 			    {(char_u *)"", (char_u *)0L} SCTX_INIT},
     {"patchexpr",   "pex",  P_STRING|P_VI_DEF|P_SECURE,
 #if defined(FEAT_DIFF) && defined(FEAT_EVAL)
-			    (char_u *)&p_pex, PV_NONE, did_set_optexpr,
+			    (char_u *)&p_pex, PV_NONE, did_set_optexpr, NULL,
 			    {(char_u *)"", (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)0L, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"patchmode",   "pm",   P_STRING|P_VI_DEF|P_NFNAME,
 			    (char_u *)&p_pm, PV_NONE,
-			    did_set_backupext_or_patchmode,
+			    did_set_backupext_or_patchmode, NULL,
 			    {(char_u *)"", (char_u *)0L} SCTX_INIT},
     {"path",	    "pa",   P_STRING|P_EXPAND|P_VI_DEF|P_COMMA|P_NODUP,
-			    (char_u *)&p_path, PV_PATH, NULL,
+			    (char_u *)&p_path, PV_PATH, NULL, NULL,
 			    {
 #if defined(AMIGA) || defined(MSWIN)
 			    (char_u *)".,,",
@@ -1861,70 +1866,70 @@ static struct vimoption options[] =
 				(char_u *)0L} SCTX_INIT},
     {"perldll",     NULL,   P_STRING|P_EXPAND|P_VI_DEF|P_SECURE,
 #if defined(DYNAMIC_PERL)
-			    (char_u *)&p_perldll, PV_NONE, NULL,
+			    (char_u *)&p_perldll, PV_NONE, NULL, NULL,
 			    {(char_u *)DYNAMIC_PERL_DLL, (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)0L, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"preserveindent", "pi", P_BOOL|P_VI_DEF|P_VIM,
-			    (char_u *)&p_pi, PV_PI, NULL,
+			    (char_u *)&p_pi, PV_PI, NULL, NULL,
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"previewheight", "pvh", P_NUM|P_VI_DEF,
 #if defined(FEAT_QUICKFIX)
-			    (char_u *)&p_pvh, PV_NONE, NULL,
+			    (char_u *)&p_pvh, PV_NONE, NULL, NULL,
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 #endif
 			    {(char_u *)12L, (char_u *)0L} SCTX_INIT},
-    {"previewpopup", "pvp", P_STRING|P_VI_DEF|P_COMMA|P_NODUP,
+    {"previewpopup", "pvp", P_STRING|P_VI_DEF|P_COMMA|P_NODUP|P_COLON,
 #ifdef FEAT_PROP_POPUP
-			    (char_u *)&p_pvp, PV_NONE, did_set_previewpopup,
+			    (char_u *)&p_pvp, PV_NONE, did_set_previewpopup, expand_set_popupoption,
 			    {(char_u *)"", (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)NULL, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"previewwindow", "pvw", P_BOOL|P_VI_DEF|P_RSTAT|P_NOGLOB,
 #if defined(FEAT_QUICKFIX)
-			    (char_u *)VAR_WIN, PV_PVW, did_set_previewwindow,
+			    (char_u *)VAR_WIN, PV_PVW, did_set_previewwindow, NULL,
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 #endif
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"printdevice", "pdev", P_STRING|P_VI_DEF|P_SECURE,
 #ifdef FEAT_PRINTER
-			    (char_u *)&p_pdev, PV_NONE, NULL,
+			    (char_u *)&p_pdev, PV_NONE, NULL, NULL,
 			    {(char_u *)"", (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)NULL, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"printencoding", "penc", P_STRING|P_VI_DEF,
 #ifdef FEAT_POSTSCRIPT
 			    (char_u *)&p_penc, PV_NONE,
-			    did_set_printencoding,
+			    did_set_printencoding, expand_set_encoding,
 			    {(char_u *)"", (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)NULL, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"printexpr", "pexpr",  P_STRING|P_VI_DEF|P_SECURE,
 #ifdef FEAT_POSTSCRIPT
-			    (char_u *)&p_pexpr, PV_NONE, did_set_optexpr,
+			    (char_u *)&p_pexpr, PV_NONE, did_set_optexpr, NULL,
 			    {(char_u *)"", (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)NULL, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"printfont", "pfn",    P_STRING|P_VI_DEF,
 #ifdef FEAT_PRINTER
-			    (char_u *)&p_pfn, PV_NONE, NULL,
+			    (char_u *)&p_pfn, PV_NONE, NULL, NULL,
 			    {
 # ifdef MSWIN
 				(char_u *)"Courier_New:h10",
@@ -1933,253 +1938,253 @@ static struct vimoption options[] =
 # endif
 				(char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)NULL, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"printheader", "pheader",  P_STRING|P_VI_DEF|P_GETTEXT,
 #ifdef FEAT_PRINTER
-			    (char_u *)&p_header, PV_NONE, NULL,
+			    (char_u *)&p_header, PV_NONE, NULL, NULL,
 			    // untranslated to avoid problems when 'encoding'
 			    // is changed
 			    {(char_u *)"%<%f%h%m%=Page %N", (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)NULL, (char_u *)0L}
 #endif
 			    SCTX_INIT},
-   {"printmbcharset", "pmbcs",  P_STRING|P_VI_DEF,
+    {"printmbcharset", "pmbcs",  P_STRING|P_VI_DEF,
 #if defined(FEAT_POSTSCRIPT)
-			    (char_u *)&p_pmcs, PV_NONE, NULL,
+			    (char_u *)&p_pmcs, PV_NONE, NULL, NULL,
 			    {(char_u *)"", (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)NULL, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"printmbfont", "pmbfn",  P_STRING|P_VI_DEF,
 #if defined(FEAT_POSTSCRIPT)
-			    (char_u *)&p_pmfn, PV_NONE, parse_printmbfont,
+			    (char_u *)&p_pmfn, PV_NONE, parse_printmbfont, NULL,
 			    {(char_u *)"", (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)NULL, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"printoptions", "popt", P_STRING|P_VI_DEF|P_ONECOMMA|P_NODUP,
 #ifdef FEAT_PRINTER
-			    (char_u *)&p_popt, PV_NONE, parse_printoptions,
+			    (char_u *)&p_popt, PV_NONE, parse_printoptions, expand_set_printoptions,
 			    {(char_u *)"", (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)NULL, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"prompt",	    NULL,   P_BOOL|P_VI_DEF,
-			    (char_u *)&p_prompt, PV_NONE, NULL,
+			    (char_u *)&p_prompt, PV_NONE, NULL, NULL,
 			    {(char_u *)TRUE, (char_u *)0L} SCTX_INIT},
     {"pumheight",   "ph",   P_NUM|P_VI_DEF,
-			    (char_u *)&p_ph, PV_NONE, NULL,
+			    (char_u *)&p_ph, PV_NONE, NULL, NULL,
 			    {(char_u *)0L, (char_u *)0L} SCTX_INIT},
     {"pumwidth",    "pw",   P_NUM|P_VI_DEF,
-			    (char_u *)&p_pw, PV_NONE, NULL,
+			    (char_u *)&p_pw, PV_NONE, NULL, NULL,
 			    {(char_u *)15L, (char_u *)15L} SCTX_INIT},
     {"pythonthreedll",  NULL,   P_STRING|P_EXPAND|P_VI_DEF|P_SECURE,
 #if defined(DYNAMIC_PYTHON3)
-			    (char_u *)&p_py3dll, PV_NONE, NULL,
+			    (char_u *)&p_py3dll, PV_NONE, NULL, NULL,
 			    {(char_u *)DYNAMIC_PYTHON3_DLL, (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)NULL, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"pythonthreehome", NULL,   P_STRING|P_EXPAND|P_VI_DEF|P_SECURE,
 #if defined(FEAT_PYTHON3)
-			    (char_u *)&p_py3home, PV_NONE, NULL,
+			    (char_u *)&p_py3home, PV_NONE, NULL, NULL,
 			    {(char_u *)"", (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)NULL, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"pythondll",   NULL,   P_STRING|P_EXPAND|P_VI_DEF|P_SECURE,
 #if defined(DYNAMIC_PYTHON)
-			    (char_u *)&p_pydll, PV_NONE, NULL,
+			    (char_u *)&p_pydll, PV_NONE, NULL, NULL,
 			    {(char_u *)DYNAMIC_PYTHON_DLL, (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)NULL, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"pythonhome",  NULL,   P_STRING|P_EXPAND|P_VI_DEF|P_SECURE,
 #if defined(FEAT_PYTHON)
-			    (char_u *)&p_pyhome, PV_NONE, NULL,
+			    (char_u *)&p_pyhome, PV_NONE, NULL, NULL,
 			    {(char_u *)"", (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)NULL, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"pyxversion", "pyx",   P_NUM|P_VI_DEF|P_SECURE,
 #if defined(FEAT_PYTHON) || defined(FEAT_PYTHON3)
-			    (char_u *)&p_pyx, PV_NONE, did_set_pyxversion,
+			    (char_u *)&p_pyx, PV_NONE, did_set_pyxversion, NULL,
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 #endif
 			    {(char_u *)DEFAULT_PYTHON_VER, (char_u *)0L}
 			    SCTX_INIT},
     {"quickfixtextfunc", "qftf", P_STRING|P_ALLOCED|P_VI_DEF|P_VIM|P_SECURE|P_FUNC,
 #if defined(FEAT_QUICKFIX) && defined(FEAT_EVAL)
 			    (char_u *)&p_qftf, PV_NONE,
-			    did_set_quickfixtextfunc,
+			    did_set_quickfixtextfunc, NULL,
 			    {(char_u *)"", (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)NULL, (char_u *)NULL}
 #endif
 			    SCTX_INIT},
     {"quoteescape", "qe",   P_STRING|P_ALLOCED|P_VI_DEF,
-			    (char_u *)&p_qe, PV_QE, NULL,
+			    (char_u *)&p_qe, PV_QE, NULL, NULL,
 			    {(char_u *)"\\", (char_u *)0L} SCTX_INIT},
     {"readonly",    "ro",   P_BOOL|P_VI_DEF|P_RSTAT|P_NOGLOB,
-			    (char_u *)&p_ro, PV_RO, did_set_readonly,
+			    (char_u *)&p_ro, PV_RO, did_set_readonly, NULL,
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"redraw",	    NULL,   P_BOOL|P_VI_DEF,
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"redrawtime",  "rdt",  P_NUM|P_VI_DEF,
 #ifdef FEAT_RELTIME
-			    (char_u *)&p_rdt, PV_NONE, NULL,
+			    (char_u *)&p_rdt, PV_NONE, NULL, NULL,
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 #endif
 			    {(char_u *)2000L, (char_u *)0L} SCTX_INIT},
     {"regexpengine", "re",  P_NUM|P_VI_DEF,
-			    (char_u *)&p_re, PV_NONE, NULL,
+			    (char_u *)&p_re, PV_NONE, NULL, NULL,
 			    {(char_u *)0L, (char_u *)0L} SCTX_INIT},
     {"relativenumber", "rnu", P_BOOL|P_VI_DEF|P_RWIN,
 			    (char_u *)VAR_WIN, PV_RNU,
-			    did_set_number_relativenumber,
+			    did_set_number_relativenumber, NULL,
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"remap",	    NULL,   P_BOOL|P_VI_DEF,
-			    (char_u *)&p_remap, PV_NONE, NULL,
+			    (char_u *)&p_remap, PV_NONE, NULL, NULL,
 			    {(char_u *)TRUE, (char_u *)0L} SCTX_INIT},
     {"renderoptions", "rop", P_STRING|P_ONECOMMA|P_RCLR|P_VI_DEF,
 #ifdef FEAT_RENDER_OPTIONS
-			    (char_u *)&p_rop, PV_NONE, did_set_renderoptions,
+			    (char_u *)&p_rop, PV_NONE, did_set_renderoptions, NULL,
 			    {(char_u *)"", (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)NULL, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"report",	    NULL,   P_NUM|P_VI_DEF,
-			    (char_u *)&p_report, PV_NONE, NULL,
+			    (char_u *)&p_report, PV_NONE, NULL, NULL,
 			    {(char_u *)2L, (char_u *)0L} SCTX_INIT},
     {"restorescreen", "rs", P_BOOL|P_VI_DEF,
 #ifdef MSWIN
-			    (char_u *)&p_rs, PV_NONE, NULL,
+			    (char_u *)&p_rs, PV_NONE, NULL, NULL,
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 #endif
 			    {(char_u *)TRUE, (char_u *)0L} SCTX_INIT},
     {"revins",	    "ri",   P_BOOL|P_VI_DEF|P_VIM,
 #ifdef FEAT_RIGHTLEFT
-			    (char_u *)&p_ri, PV_NONE, NULL,
+			    (char_u *)&p_ri, PV_NONE, NULL, NULL,
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 #endif
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"rightleft",   "rl",   P_BOOL|P_VI_DEF|P_RWIN,
 #ifdef FEAT_RIGHTLEFT
-			    (char_u *)VAR_WIN, PV_RL, NULL,
+			    (char_u *)VAR_WIN, PV_RL, NULL, NULL,
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 #endif
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"rightleftcmd", "rlc", P_STRING|P_ALLOCED|P_VI_DEF|P_RWIN,
 #ifdef FEAT_RIGHTLEFT
-			    (char_u *)VAR_WIN, PV_RLC, did_set_rightleftcmd,
+			    (char_u *)VAR_WIN, PV_RLC, did_set_rightleftcmd, expand_set_rightleftcmd,
 			    {(char_u *)"search", (char_u *)NULL}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)NULL, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"rubydll",     NULL,   P_STRING|P_EXPAND|P_VI_DEF|P_SECURE,
 #if defined(DYNAMIC_RUBY)
-			    (char_u *)&p_rubydll, PV_NONE, NULL,
+			    (char_u *)&p_rubydll, PV_NONE, NULL, NULL,
 			    {(char_u *)DYNAMIC_RUBY_DLL, (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)NULL, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"ruler",	    "ru",   P_BOOL|P_VI_DEF|P_VIM|P_RSTAT,
-			    (char_u *)&p_ru, PV_NONE, NULL,
+			    (char_u *)&p_ru, PV_NONE, NULL, NULL,
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"rulerformat", "ruf",  P_STRING|P_VI_DEF|P_ALLOCED|P_RSTAT|P_MLE,
 #ifdef FEAT_STL_OPT
-			    (char_u *)&p_ruf, PV_NONE, did_set_rulerformat,
+			    (char_u *)&p_ruf, PV_NONE, did_set_rulerformat, NULL,
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 #endif
 			    {(char_u *)"", (char_u *)0L} SCTX_INIT},
     {"runtimepath", "rtp",  P_STRING|P_VI_DEF|P_EXPAND|P_ONECOMMA|P_NODUP
 								    |P_SECURE,
-			    (char_u *)&p_rtp, PV_NONE, NULL,
+			    (char_u *)&p_rtp, PV_NONE, NULL, NULL,
 			    {(char_u *)DFLT_RUNTIMEPATH, (char_u *)0L}
 			    SCTX_INIT},
     {"scroll",	    "scr",  P_NUM|P_NO_MKRC|P_VI_DEF,
-			    (char_u *)VAR_WIN, PV_SCROLL, NULL,
+			    (char_u *)VAR_WIN, PV_SCROLL, NULL, NULL,
 			    {(char_u *)0L, (char_u *)0L} SCTX_INIT},
     {"scrollbind",  "scb",  P_BOOL|P_VI_DEF,
-			    (char_u *)VAR_WIN, PV_SCBIND, did_set_scrollbind,
+			    (char_u *)VAR_WIN, PV_SCBIND, did_set_scrollbind, NULL,
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"scrollfocus", "scf",  P_BOOL|P_VI_DEF,
 #if defined(MSWIN) && defined(FEAT_GUI)
-			    (char_u *)&p_scf, PV_NONE, NULL,
+			    (char_u *)&p_scf, PV_NONE, NULL, NULL,
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 #endif
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"scrolljump",  "sj",   P_NUM|P_VI_DEF|P_VIM,
-			    (char_u *)&p_sj, PV_NONE, NULL,
+			    (char_u *)&p_sj, PV_NONE, NULL, NULL,
 			    {(char_u *)1L, (char_u *)0L} SCTX_INIT},
     {"scrolloff",   "so",   P_NUM|P_VI_DEF|P_VIM|P_RALL,
-			    (char_u *)&p_so, PV_SO, NULL,
+			    (char_u *)&p_so, PV_SO, NULL, NULL,
 			    {(char_u *)0L, (char_u *)0L} SCTX_INIT},
     {"scrollopt",   "sbo",  P_STRING|P_VI_DEF|P_ONECOMMA|P_NODUP,
-			    (char_u *)&p_sbo, PV_NONE, did_set_scrollopt,
+			    (char_u *)&p_sbo, PV_NONE, did_set_scrollopt, expand_set_scrollopt,
 			    {(char_u *)"ver,jump", (char_u *)0L}
 			    SCTX_INIT},
     {"sections",    "sect", P_STRING|P_VI_DEF,
-			    (char_u *)&p_sections, PV_NONE, NULL,
+			    (char_u *)&p_sections, PV_NONE, NULL, NULL,
 			    {(char_u *)"SHNHH HUnhsh", (char_u *)0L}
 			    SCTX_INIT},
     {"secure",	    NULL,   P_BOOL|P_VI_DEF|P_SECURE,
-			    (char_u *)&p_secure, PV_NONE, NULL,
+			    (char_u *)&p_secure, PV_NONE, NULL, NULL,
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"selection",   "sel",  P_STRING|P_VI_DEF,
-			    (char_u *)&p_sel, PV_NONE, did_set_selection,
+			    (char_u *)&p_sel, PV_NONE, did_set_selection, expand_set_selection,
 			    {(char_u *)"inclusive", (char_u *)0L}
 			    SCTX_INIT},
     {"selectmode",  "slm",  P_STRING|P_VI_DEF|P_ONECOMMA|P_NODUP,
-			    (char_u *)&p_slm, PV_NONE, did_set_selectmode,
+			    (char_u *)&p_slm, PV_NONE, did_set_selectmode, expand_set_selectmode,
 			    {(char_u *)"", (char_u *)0L} SCTX_INIT},
     {"sessionoptions", "ssop", P_STRING|P_VI_DEF|P_ONECOMMA|P_NODUP,
 #ifdef FEAT_SESSION
 			    (char_u *)&p_ssop, PV_NONE,
-			    did_set_sessionoptions,
+			    did_set_sessionoptions, expand_set_sessionoptions,
 	 {(char_u *)"blank,buffers,curdir,folds,help,options,tabpages,winsize,terminal",
 							       (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)0L, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"shell",	    "sh",   P_STRING|P_EXPAND|P_VI_DEF|P_SECURE,
-			    (char_u *)&p_sh, PV_NONE, NULL,
+			    (char_u *)&p_sh, PV_NONE, NULL, NULL,
 			    {
 #ifdef VMS
 			    (char_u *)"-",
@@ -2192,7 +2197,7 @@ static struct vimoption options[] =
 #endif // VMS
 				(char_u *)0L} SCTX_INIT},
     {"shellcmdflag","shcf", P_STRING|P_VI_DEF|P_SECURE,
-			    (char_u *)&p_shcf, PV_NONE, NULL,
+			    (char_u *)&p_shcf, PV_NONE, NULL, NULL,
 			    {
 #if defined(MSWIN)
 			    (char_u *)"/c",
@@ -2202,7 +2207,7 @@ static struct vimoption options[] =
 				(char_u *)0L} SCTX_INIT},
     {"shellpipe",   "sp",   P_STRING|P_VI_DEF|P_SECURE,
 #ifdef FEAT_QUICKFIX
-			    (char_u *)&p_sp, PV_NONE, NULL,
+			    (char_u *)&p_sp, PV_NONE, NULL, NULL,
 			    {
 #if defined(UNIX)
 			    (char_u *)"| tee",
@@ -2211,35 +2216,35 @@ static struct vimoption options[] =
 #endif
 				(char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)0L, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"shellquote",  "shq",  P_STRING|P_VI_DEF|P_SECURE,
-			    (char_u *)&p_shq, PV_NONE, NULL,
+			    (char_u *)&p_shq, PV_NONE, NULL, NULL,
 			    {(char_u *)"", (char_u *)0L} SCTX_INIT},
     {"shellredir",  "srr",  P_STRING|P_VI_DEF|P_SECURE,
-			    (char_u *)&p_srr, PV_NONE, NULL,
+			    (char_u *)&p_srr, PV_NONE, NULL, NULL,
 			    {(char_u *)">", (char_u *)0L} SCTX_INIT},
     {"shellslash",  "ssl",   P_BOOL|P_VI_DEF,
 #ifdef BACKSLASH_IN_FILENAME
-			    (char_u *)&p_ssl, PV_NONE, did_set_shellslash,
+			    (char_u *)&p_ssl, PV_NONE, did_set_shellslash, NULL,
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 #endif
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"shelltemp",   "stmp", P_BOOL,
-			    (char_u *)&p_stmp, PV_NONE, NULL,
+			    (char_u *)&p_stmp, PV_NONE, NULL, NULL,
 			    {(char_u *)FALSE, (char_u *)TRUE} SCTX_INIT},
     {"shelltype",   "st",   P_NUM|P_VI_DEF,
 #ifdef AMIGA
-			    (char_u *)&p_st, PV_NONE, NULL,
+			    (char_u *)&p_st, PV_NONE, NULL, NULL,
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 #endif
 			    {(char_u *)0L, (char_u *)0L} SCTX_INIT},
     {"shellxquote", "sxq",  P_STRING|P_VI_DEF|P_SECURE,
-			    (char_u *)&p_sxq, PV_NONE, NULL,
+			    (char_u *)&p_sxq, PV_NONE, NULL, NULL,
 			    {
 #if defined(UNIX) && defined(USE_SYSTEM)
 			    (char_u *)"\"",
@@ -2248,7 +2253,7 @@ static struct vimoption options[] =
 #endif
 				(char_u *)0L} SCTX_INIT},
     {"shellxescape", "sxe", P_STRING|P_VI_DEF|P_SECURE,
-			    (char_u *)&p_sxe, PV_NONE, NULL,
+			    (char_u *)&p_sxe, PV_NONE, NULL, NULL,
 			    {
 #if defined(MSWIN)
 			    (char_u *)"\"&|<>()@^",
@@ -2257,28 +2262,28 @@ static struct vimoption options[] =
 #endif
 				(char_u *)0L} SCTX_INIT},
     {"shiftround",  "sr",   P_BOOL|P_VI_DEF|P_VIM,
-			    (char_u *)&p_sr, PV_NONE, NULL,
+			    (char_u *)&p_sr, PV_NONE, NULL, NULL,
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"shiftwidth",  "sw",   P_NUM|P_VI_DEF,
 			    (char_u *)&p_sw, PV_SW,
-			    did_set_shiftwidth_tabstop,
+			    did_set_shiftwidth_tabstop, NULL,
 			    {(char_u *)8L, (char_u *)0L} SCTX_INIT},
     {"shortmess",   "shm",  P_STRING|P_VIM|P_FLAGLIST,
-			    (char_u *)&p_shm, PV_NONE, did_set_shortmess,
+			    (char_u *)&p_shm, PV_NONE, did_set_shortmess, expand_set_shortmess,
 			    {(char_u *)"S", (char_u *)"filnxtToOS"}
 			    SCTX_INIT},
     {"shortname",   "sn",   P_BOOL|P_VI_DEF,
-			    (char_u *)&p_sn, PV_SN, NULL,
+			    (char_u *)&p_sn, PV_SN, NULL, NULL,
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"showbreak",   "sbr",  P_STRING|P_VI_DEF|P_RALL,
 #ifdef FEAT_LINEBREAK
-			    (char_u *)&p_sbr, PV_SBR, did_set_showbreak,
+			    (char_u *)&p_sbr, PV_SBR, did_set_showbreak, NULL,
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 #endif
 			    {(char_u *)"", (char_u *)0L} SCTX_INIT},
     {"showcmd",	    "sc",   P_BOOL|P_VIM,
-			    (char_u *)&p_sc, PV_NONE, NULL,
+			    (char_u *)&p_sc, PV_NONE, NULL, NULL,
 			    {(char_u *)FALSE,
 #ifdef UNIX
 				(char_u *)FALSE
@@ -2287,182 +2292,182 @@ static struct vimoption options[] =
 #endif
 				} SCTX_INIT},
     {"showcmdloc",  "sloc", P_STRING|P_RSTAT,
-			    (char_u *)&p_sloc, PV_NONE, did_set_showcmdloc,
+			    (char_u *)&p_sloc, PV_NONE, did_set_showcmdloc, expand_set_showcmdloc,
 			    {(char_u *)"last", (char_u *)"last"} SCTX_INIT},
     {"showfulltag", "sft",  P_BOOL|P_VI_DEF,
-			    (char_u *)&p_sft, PV_NONE, NULL,
+			    (char_u *)&p_sft, PV_NONE, NULL, NULL,
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"showmatch",   "sm",   P_BOOL|P_VI_DEF,
-			    (char_u *)&p_sm, PV_NONE, NULL,
+			    (char_u *)&p_sm, PV_NONE, NULL, NULL,
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"showmode",    "smd",  P_BOOL|P_VIM,
-			    (char_u *)&p_smd, PV_NONE, NULL,
+			    (char_u *)&p_smd, PV_NONE, NULL, NULL,
 			    {(char_u *)FALSE, (char_u *)TRUE} SCTX_INIT},
     {"showtabline", "stal", P_NUM|P_VI_DEF|P_RALL,
-			    (char_u *)&p_stal, PV_NONE, did_set_showtabline,
+			    (char_u *)&p_stal, PV_NONE, did_set_showtabline, NULL,
 			    {(char_u *)1L, (char_u *)0L} SCTX_INIT},
     {"sidescroll",  "ss",   P_NUM|P_VI_DEF,
-			    (char_u *)&p_ss, PV_NONE, NULL,
+			    (char_u *)&p_ss, PV_NONE, NULL, NULL,
 			    {(char_u *)0L, (char_u *)0L} SCTX_INIT},
     {"sidescrolloff", "siso", P_NUM|P_VI_DEF|P_VIM|P_RBUF,
-			    (char_u *)&p_siso, PV_SISO, NULL,
+			    (char_u *)&p_siso, PV_SISO, NULL, NULL,
 			    {(char_u *)0L, (char_u *)0L} SCTX_INIT},
     {"signcolumn",   "scl",  P_STRING|P_ALLOCED|P_VI_DEF|P_RCLR,
 #ifdef FEAT_SIGNS
-			    (char_u *)VAR_WIN, PV_SCL, did_set_signcolumn,
+			    (char_u *)VAR_WIN, PV_SCL, did_set_signcolumn, expand_set_signcolumn,
 			    {(char_u *)"auto", (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)NULL, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"slowopen",    "slow", P_BOOL|P_VI_DEF,
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"smartcase",   "scs",  P_BOOL|P_VI_DEF|P_VIM,
-			    (char_u *)&p_scs, PV_NONE, NULL,
+			    (char_u *)&p_scs, PV_NONE, NULL, NULL,
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"smartindent", "si",   P_BOOL|P_VI_DEF|P_VIM,
-			    (char_u *)&p_si, PV_SI, NULL,
+			    (char_u *)&p_si, PV_SI, NULL, NULL,
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"smarttab",    "sta",  P_BOOL|P_VI_DEF|P_VIM,
-			    (char_u *)&p_sta, PV_NONE, NULL,
+			    (char_u *)&p_sta, PV_NONE, NULL, NULL,
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"smoothscroll", "sms", P_BOOL|P_VI_DEF|P_RWIN,
-			    (char_u *)VAR_WIN, PV_SMS, did_set_smoothscroll,
+			    (char_u *)VAR_WIN, PV_SMS, did_set_smoothscroll, NULL,
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"softtabstop", "sts",  P_NUM|P_VI_DEF|P_VIM,
-			    (char_u *)&p_sts, PV_STS, NULL,
+			    (char_u *)&p_sts, PV_STS, NULL, NULL,
 			    {(char_u *)0L, (char_u *)0L} SCTX_INIT},
     {"sourceany",   NULL,   P_BOOL|P_VI_DEF,
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"spell",	    NULL,   P_BOOL|P_VI_DEF|P_RWIN,
 #ifdef FEAT_SPELL
-			    (char_u *)VAR_WIN, PV_SPELL, did_set_spell,
+			    (char_u *)VAR_WIN, PV_SPELL, did_set_spell, NULL,
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 #endif
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"spellcapcheck", "spc", P_STRING|P_ALLOCED|P_VI_DEF|P_RBUF,
 #ifdef FEAT_SPELL
-			    (char_u *)&p_spc, PV_SPC, did_set_spellcapcheck,
+			    (char_u *)&p_spc, PV_SPC, did_set_spellcapcheck, NULL,
 			    {(char_u *)"[.?!]\\_[\\])'\"	 ]\\+", (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)0L, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"spellfile",   "spf",  P_STRING|P_EXPAND|P_ALLOCED|P_VI_DEF|P_SECURE
 								  |P_ONECOMMA,
 #ifdef FEAT_SPELL
-			    (char_u *)&p_spf, PV_SPF, did_set_spellfile,
+			    (char_u *)&p_spf, PV_SPF, did_set_spellfile, NULL,
 			    {(char_u *)"", (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)0L, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"spelllang",   "spl",  P_STRING|P_ALLOCED|P_VI_DEF|P_ONECOMMA
 							     |P_RBUF|P_EXPAND,
 #ifdef FEAT_SPELL
-			    (char_u *)&p_spl, PV_SPL, did_set_spelllang,
+			    (char_u *)&p_spl, PV_SPL, did_set_spelllang, NULL,
 			    {(char_u *)"en", (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)0L, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"spelloptions", "spo",  P_STRING|P_ALLOCED|P_VI_DEF
 						    |P_ONECOMMA|P_NODUP|P_RBUF,
 #ifdef FEAT_SPELL
-			    (char_u *)&p_spo, PV_SPO, did_set_spelloptions,
+			    (char_u *)&p_spo, PV_SPO, did_set_spelloptions, expand_set_spelloptions,
 			    {(char_u *)"", (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)0L, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"spellsuggest", "sps", P_STRING|P_VI_DEF|P_EXPAND|P_SECURE|P_ONECOMMA,
 #ifdef FEAT_SPELL
-			    (char_u *)&p_sps, PV_NONE, did_set_spellsuggest,
+			    (char_u *)&p_sps, PV_NONE, did_set_spellsuggest, expand_set_spellsuggest,
 			    {(char_u *)"best", (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)0L, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"splitbelow",  "sb",   P_BOOL|P_VI_DEF,
-			    (char_u *)&p_sb, PV_NONE, NULL,
+			    (char_u *)&p_sb, PV_NONE, NULL, NULL,
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"splitkeep",   "spk",  P_STRING,
-			    (char_u *)&p_spk, PV_NONE, did_set_splitkeep,
+			    (char_u *)&p_spk, PV_NONE, did_set_splitkeep, expand_set_splitkeep,
 			    {(char_u *)"cursor", (char_u *)"cursor"} SCTX_INIT},
     {"splitright",  "spr",  P_BOOL|P_VI_DEF,
-			    (char_u *)&p_spr, PV_NONE, NULL,
+			    (char_u *)&p_spr, PV_NONE, NULL, NULL,
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"startofline", "sol",  P_BOOL|P_VI_DEF|P_VIM,
-			    (char_u *)&p_sol, PV_NONE, NULL,
+			    (char_u *)&p_sol, PV_NONE, NULL, NULL,
 			    {(char_u *)TRUE, (char_u *)0L} SCTX_INIT},
     {"statusline"  ,"stl",  P_STRING|P_VI_DEF|P_ALLOCED|P_RSTAT|P_MLE,
 #ifdef FEAT_STL_OPT
-			    (char_u *)&p_stl, PV_STL, did_set_statusline,
+			    (char_u *)&p_stl, PV_STL, did_set_statusline, NULL,
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 #endif
 			    {(char_u *)"", (char_u *)0L} SCTX_INIT},
     {"suffixes",    "su",   P_STRING|P_VI_DEF|P_ONECOMMA|P_NODUP,
-			    (char_u *)&p_su, PV_NONE, NULL,
+			    (char_u *)&p_su, PV_NONE, NULL, NULL,
 			    {(char_u *)".bak,~,.o,.h,.info,.swp,.obj",
 				(char_u *)0L} SCTX_INIT},
     {"suffixesadd", "sua",  P_STRING|P_VI_DEF|P_ALLOCED|P_ONECOMMA|P_NODUP,
-			    (char_u *)&p_sua, PV_SUA, NULL,
+			    (char_u *)&p_sua, PV_SUA, NULL, NULL,
 			    {(char_u *)"", (char_u *)0L}
 			    SCTX_INIT},
     {"swapfile",    "swf",  P_BOOL|P_VI_DEF|P_RSTAT,
-			    (char_u *)&p_swf, PV_SWF, did_set_swapfile,
+			    (char_u *)&p_swf, PV_SWF, did_set_swapfile, NULL,
 			    {(char_u *)TRUE, (char_u *)0L} SCTX_INIT},
     {"swapsync",    "sws",  P_STRING|P_VI_DEF,
-			    (char_u *)&p_sws, PV_NONE, did_set_swapsync,
+			    (char_u *)&p_sws, PV_NONE, did_set_swapsync, expand_set_swapsync,
 			    {(char_u *)"fsync", (char_u *)0L} SCTX_INIT},
     {"switchbuf",   "swb",  P_STRING|P_VI_DEF|P_ONECOMMA|P_NODUP,
-			    (char_u *)&p_swb, PV_NONE, did_set_switchbuf,
+			    (char_u *)&p_swb, PV_NONE, did_set_switchbuf, expand_set_switchbuf,
 			    {(char_u *)"", (char_u *)0L} SCTX_INIT},
     {"synmaxcol",   "smc",  P_NUM|P_VI_DEF|P_RBUF,
 #ifdef FEAT_SYN_HL
-			    (char_u *)&p_smc, PV_SMC, NULL,
+			    (char_u *)&p_smc, PV_SMC, NULL, NULL,
 			    {(char_u *)3000L, (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)0L, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"syntax",	    "syn",  P_STRING|P_ALLOCED|P_VI_DEF|P_NOGLOB|P_NFNAME,
 #ifdef FEAT_SYN_HL
 			    (char_u *)&p_syn, PV_SYN,
-			    did_set_filetype_or_syntax,
+			    did_set_filetype_or_syntax, NULL,
 			    {(char_u *)"", (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)0L, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"tabline",	    "tal",  P_STRING|P_VI_DEF|P_RALL|P_MLE,
 #ifdef FEAT_STL_OPT
-			    (char_u *)&p_tal, PV_NONE, did_set_tabline,
+			    (char_u *)&p_tal, PV_NONE, did_set_tabline, NULL,
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 #endif
 			    {(char_u *)"", (char_u *)0L} SCTX_INIT},
     {"tabpagemax",  "tpm",  P_NUM|P_VI_DEF,
-			    (char_u *)&p_tpm, PV_NONE, NULL,
+			    (char_u *)&p_tpm, PV_NONE, NULL, NULL,
 			    {(char_u *)10L, (char_u *)0L} SCTX_INIT},
     {"tabstop",	    "ts",   P_NUM|P_VI_DEF|P_RBUF,
 			    (char_u *)&p_ts, PV_TS,
-			    did_set_shiftwidth_tabstop,
+			    did_set_shiftwidth_tabstop, NULL,
 			    {(char_u *)8L, (char_u *)0L} SCTX_INIT},
     {"tagbsearch",  "tbs",   P_BOOL|P_VI_DEF,
-			    (char_u *)&p_tbs, PV_NONE, NULL,
+			    (char_u *)&p_tbs, PV_NONE, NULL, NULL,
 #ifdef VMS	// binary searching doesn't appear to work on VMS
 			    {(char_u *)0L, (char_u *)0L}
 #else
@@ -2470,25 +2475,25 @@ static struct vimoption options[] =
 #endif
 			    SCTX_INIT},
     {"tagcase",	    "tc",   P_STRING|P_VIM,
-			    (char_u *)&p_tc, PV_TC, did_set_tagcase,
+			    (char_u *)&p_tc, PV_TC, did_set_tagcase, expand_set_tagcase,
 			    {(char_u *)"followic", (char_u *)"followic"} SCTX_INIT},
     {"tagfunc",    "tfu",   P_STRING|P_ALLOCED|P_VI_DEF|P_SECURE|P_FUNC,
 #ifdef FEAT_EVAL
-			    (char_u *)&p_tfu, PV_TFU, did_set_tagfunc,
+			    (char_u *)&p_tfu, PV_TFU, did_set_tagfunc, NULL,
 			    {(char_u *)"", (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)0L, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"taglength",   "tl",   P_NUM|P_VI_DEF,
-			    (char_u *)&p_tl, PV_NONE, NULL,
+			    (char_u *)&p_tl, PV_NONE, NULL, NULL,
 			    {(char_u *)0L, (char_u *)0L} SCTX_INIT},
     {"tagrelative", "tr",   P_BOOL|P_VIM,
-			    (char_u *)&p_tr, PV_NONE, NULL,
+			    (char_u *)&p_tr, PV_NONE, NULL, NULL,
 			    {(char_u *)FALSE, (char_u *)TRUE} SCTX_INIT},
     {"tags",	    "tag",  P_STRING|P_EXPAND|P_VI_DEF|P_ONECOMMA|P_NODUP,
-			    (char_u *)&p_tags, PV_TAGS, NULL,
+			    (char_u *)&p_tags, PV_TAGS, NULL, NULL,
 			    {
 #if defined(FEAT_EMACS_TAGS) && !defined(CASE_INSENSITIVE_FILENAME)
 			    (char_u *)"./tags,./TAGS,tags,TAGS",
@@ -2497,85 +2502,85 @@ static struct vimoption options[] =
 #endif
 				(char_u *)0L} SCTX_INIT},
     {"tagstack",    "tgst", P_BOOL|P_VI_DEF,
-			    (char_u *)&p_tgst, PV_NONE, NULL,
+			    (char_u *)&p_tgst, PV_NONE, NULL, NULL,
 			    {(char_u *)TRUE, (char_u *)0L} SCTX_INIT},
     {"tcldll",      NULL,   P_STRING|P_EXPAND|P_VI_DEF|P_SECURE,
 #if defined(DYNAMIC_TCL)
-			    (char_u *)&p_tcldll, PV_NONE, NULL,
+			    (char_u *)&p_tcldll, PV_NONE, NULL, NULL,
 			    {(char_u *)DYNAMIC_TCL_DLL, (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)0L, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"term",	    NULL,   P_STRING|P_EXPAND|P_NODEFAULT|P_NO_MKRC|P_VI_DEF|P_RALL,
-			    (char_u *)&T_NAME, PV_NONE, did_set_term,
+			    (char_u *)&T_NAME, PV_NONE, did_set_term, NULL,
 			    {(char_u *)"", (char_u *)0L} SCTX_INIT},
     {"termbidi", "tbidi",   P_BOOL|P_VI_DEF,
 #ifdef FEAT_ARABIC
-			    (char_u *)&p_tbidi, PV_NONE, NULL,
+			    (char_u *)&p_tbidi, PV_NONE, NULL, NULL,
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 #endif
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"termencoding", "tenc", P_STRING|P_VI_DEF|P_RCLR,
-			    (char_u *)&p_tenc, PV_NONE, did_set_encoding,
+			    (char_u *)&p_tenc, PV_NONE, did_set_encoding, expand_set_encoding,
 			    {(char_u *)"", (char_u *)0L}
 			    SCTX_INIT},
     {"termguicolors", "tgc", P_BOOL|P_VI_DEF|P_VIM|P_RCLR,
 #ifdef FEAT_TERMGUICOLORS
-			    (char_u *)&p_tgc, PV_NONE, did_set_termguicolors,
+			    (char_u *)&p_tgc, PV_NONE, did_set_termguicolors, NULL,
 			    {(char_u *)FALSE, (char_u *)FALSE}
 #else
-			    (char_u*)NULL, PV_NONE, NULL,
+			    (char_u*)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)FALSE, (char_u *)FALSE}
 #endif
 			    SCTX_INIT},
     {"termwinkey", "twk",   P_STRING|P_ALLOCED|P_RWIN|P_VI_DEF,
 #ifdef FEAT_TERMINAL
-			    (char_u *)VAR_WIN, PV_TWK, did_set_termwinkey,
+			    (char_u *)VAR_WIN, PV_TWK, did_set_termwinkey, NULL,
 			    {(char_u *)"", (char_u *)NULL}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)NULL, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"termwinscroll", "twsl", P_NUM|P_VI_DEF|P_VIM|P_RBUF,
 #ifdef FEAT_TERMINAL
-			    (char_u *)&p_twsl, PV_TWSL, NULL,
+			    (char_u *)&p_twsl, PV_TWSL, NULL, NULL,
 			    {(char_u *)10000L, (char_u *)10000L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)NULL, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"termwinsize", "tws",  P_STRING|P_ALLOCED|P_RWIN|P_VI_DEF,
 #ifdef FEAT_TERMINAL
-			    (char_u *)VAR_WIN, PV_TWS, did_set_termwinsize,
+			    (char_u *)VAR_WIN, PV_TWS, did_set_termwinsize, NULL,
 			    {(char_u *)"", (char_u *)NULL}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)NULL, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"termwintype", "twt",  P_STRING|P_ALLOCED|P_VI_DEF,
 #if defined(MSWIN) && defined(FEAT_TERMINAL)
-			    (char_u *)&p_twt, PV_NONE, did_set_termwintype,
+			    (char_u *)&p_twt, PV_NONE, did_set_termwintype, expand_set_termwintype,
 			    {(char_u *)"", (char_u *)NULL}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)NULL, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"terse",	    NULL,   P_BOOL|P_VI_DEF,
-			    (char_u *)&p_terse, PV_NONE, did_set_terse,
+			    (char_u *)&p_terse, PV_NONE, did_set_terse, NULL,
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"textauto",    "ta",   P_BOOL|P_VIM,
-			    (char_u *)&p_ta, PV_NONE, did_set_textauto,
+			    (char_u *)&p_ta, PV_NONE, did_set_textauto, NULL,
 			    {(char_u *)DFLT_TEXTAUTO, (char_u *)TRUE}
 			    SCTX_INIT},
     {"textmode",    "tx",   P_BOOL|P_VI_DEF|P_NO_MKRC,
-			    (char_u *)&p_tx, PV_TX, did_set_textmode,
+			    (char_u *)&p_tx, PV_TX, did_set_textmode, NULL,
 			    {
 #ifdef USE_CRNL
 			    (char_u *)TRUE,
@@ -2584,108 +2589,108 @@ static struct vimoption options[] =
 #endif
 				(char_u *)0L} SCTX_INIT},
     {"textwidth",   "tw",   P_NUM|P_VI_DEF|P_VIM|P_RBUF,
-			    (char_u *)&p_tw, PV_TW, did_set_textwidth,
+			    (char_u *)&p_tw, PV_TW, did_set_textwidth, NULL,
 			    {(char_u *)0L, (char_u *)0L} SCTX_INIT},
     {"thesaurus",   "tsr",  P_STRING|P_EXPAND|P_VI_DEF|P_ONECOMMA|P_NODUP|P_NDNAME,
-			    (char_u *)&p_tsr, PV_TSR, NULL,
+			    (char_u *)&p_tsr, PV_TSR, NULL, NULL,
 			    {(char_u *)"", (char_u *)0L} SCTX_INIT},
     {"thesaurusfunc", "tsrfu",  P_STRING|P_ALLOCED|P_VI_DEF|P_SECURE|P_FUNC,
 #ifdef FEAT_COMPL_FUNC
 			    (char_u *)&p_tsrfu, PV_TSRFU,
-			    did_set_thesaurusfunc,
+			    did_set_thesaurusfunc, NULL,
 			    {(char_u *)"", (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)0L, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"tildeop",	    "top",  P_BOOL|P_VI_DEF|P_VIM,
-			    (char_u *)&p_to, PV_NONE, NULL,
+			    (char_u *)&p_to, PV_NONE, NULL, NULL,
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"timeout",	    "to",   P_BOOL|P_VI_DEF,
-			    (char_u *)&p_timeout, PV_NONE, NULL,
+			    (char_u *)&p_timeout, PV_NONE, NULL, NULL,
 			    {(char_u *)TRUE, (char_u *)0L} SCTX_INIT},
     {"timeoutlen",  "tm",   P_NUM|P_VI_DEF,
-			    (char_u *)&p_tm, PV_NONE, NULL,
+			    (char_u *)&p_tm, PV_NONE, NULL, NULL,
 			    {(char_u *)1000L, (char_u *)0L} SCTX_INIT},
     {"title",	    NULL,   P_BOOL|P_VI_DEF,
-			    (char_u *)&p_title, PV_NONE, did_set_title_icon,
+			    (char_u *)&p_title, PV_NONE, did_set_title_icon, NULL,
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"titlelen",    NULL,   P_NUM|P_VI_DEF,
-			    (char_u *)&p_titlelen, PV_NONE, did_set_titlelen,
+			    (char_u *)&p_titlelen, PV_NONE, did_set_titlelen, NULL,
 			    {(char_u *)85L, (char_u *)0L} SCTX_INIT},
     {"titleold",    NULL,   P_STRING|P_VI_DEF|P_GETTEXT|P_SECURE|P_NO_MKRC,
-			    (char_u *)&p_titleold, PV_NONE, NULL,
+			    (char_u *)&p_titleold, PV_NONE, NULL, NULL,
 			    {(char_u *)N_("Thanks for flying Vim"),
 							       (char_u *)0L}
 			    SCTX_INIT},
     {"titlestring", NULL,   P_STRING|P_VI_DEF|P_MLE,
 			    (char_u *)&p_titlestring, PV_NONE,
-			    did_set_titlestring,
+			    did_set_titlestring, NULL,
 			    {(char_u *)"", (char_u *)0L} SCTX_INIT},
     {"toolbar",     "tb",   P_STRING|P_ONECOMMA|P_VI_DEF|P_NODUP,
 #if defined(FEAT_TOOLBAR) && !defined(FEAT_GUI_MSWIN)
-			    (char_u *)&p_toolbar, PV_NONE, did_set_toolbar,
+			    (char_u *)&p_toolbar, PV_NONE, did_set_toolbar, expand_set_toolbar,
 			    {(char_u *)"icons,tooltips", (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)0L, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"toolbariconsize",	"tbis", P_STRING|P_VI_DEF,
 #if defined(FEAT_TOOLBAR) && defined(FEAT_GUI_GTK)
 			    (char_u *)&p_tbis, PV_NONE,
-			    did_set_toolbariconsize,
+			    did_set_toolbariconsize, expand_set_toolbariconsize,
 			    {(char_u *)"small", (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)0L, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"ttimeout",    NULL,   P_BOOL|P_VI_DEF|P_VIM,
-			    (char_u *)&p_ttimeout, PV_NONE, NULL,
+			    (char_u *)&p_ttimeout, PV_NONE, NULL, NULL,
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"ttimeoutlen", "ttm",  P_NUM|P_VI_DEF,
-			    (char_u *)&p_ttm, PV_NONE, NULL,
+			    (char_u *)&p_ttm, PV_NONE, NULL, NULL,
 			    {(char_u *)-1L, (char_u *)0L} SCTX_INIT},
     {"ttybuiltin",  "tbi",  P_BOOL|P_VI_DEF,
-			    (char_u *)&p_tbi, PV_NONE, NULL,
+			    (char_u *)&p_tbi, PV_NONE, NULL, NULL,
 			    {(char_u *)TRUE, (char_u *)0L} SCTX_INIT},
     {"ttyfast",	    "tf",   P_BOOL|P_NO_MKRC|P_VI_DEF,
-			    (char_u *)&p_tf, PV_NONE, NULL,
+			    (char_u *)&p_tf, PV_NONE, NULL, NULL,
 			    {(char_u *)TRUE, (char_u *)0L} SCTX_INIT},
     {"ttymouse",    "ttym", P_STRING|P_NODEFAULT|P_NO_MKRC|P_VI_DEF,
 #if defined(UNIX) || defined(VMS)
-			    (char_u *)&p_ttym, PV_NONE, did_set_ttymouse,
+			    (char_u *)&p_ttym, PV_NONE, did_set_ttymouse, expand_set_ttymouse,
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 #endif
 			    {(char_u *)"", (char_u *)0L} SCTX_INIT},
     {"ttyscroll",   "tsl",  P_NUM|P_VI_DEF,
-			    (char_u *)&p_ttyscroll, PV_NONE, NULL,
+			    (char_u *)&p_ttyscroll, PV_NONE, NULL, NULL,
 			    {(char_u *)999L, (char_u *)0L} SCTX_INIT},
     {"ttytype",	    "tty",  P_STRING|P_EXPAND|P_NODEFAULT|P_NO_MKRC|P_VI_DEF|P_RALL,
-			    (char_u *)&T_NAME, PV_NONE, did_set_term,
+			    (char_u *)&T_NAME, PV_NONE, did_set_term, NULL,
 			    {(char_u *)"", (char_u *)0L} SCTX_INIT},
     {"undodir",     "udir", P_STRING|P_EXPAND|P_ONECOMMA|P_NODUP|P_SECURE
 								    |P_VI_DEF,
 #ifdef FEAT_PERSISTENT_UNDO
-			    (char_u *)&p_udir, PV_NONE, NULL,
+			    (char_u *)&p_udir, PV_NONE, NULL, NULL,
 			    {(char_u *)".", (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)0L, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"undofile",    "udf",  P_BOOL|P_VI_DEF|P_VIM,
 #ifdef FEAT_PERSISTENT_UNDO
-			    (char_u *)&p_udf, PV_UDF, did_set_undofile,
+			    (char_u *)&p_udf, PV_UDF, did_set_undofile, NULL,
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 #endif
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"undolevels",  "ul",   P_NUM|P_VI_DEF,
-			    (char_u *)&p_ul, PV_UL, did_set_undolevels,
+			    (char_u *)&p_ul, PV_UL, did_set_undolevels, NULL,
 			    {
 #if defined(UNIX) || defined(MSWIN) || defined(VMS)
 			    (char_u *)1000L,
@@ -2694,61 +2699,61 @@ static struct vimoption options[] =
 #endif
 				(char_u *)0L} SCTX_INIT},
     {"undoreload",  "ur",   P_NUM|P_VI_DEF,
-			    (char_u *)&p_ur, PV_NONE, NULL,
+			    (char_u *)&p_ur, PV_NONE, NULL, NULL,
 			    { (char_u *)10000L, (char_u *)0L} SCTX_INIT},
     {"updatecount", "uc",   P_NUM|P_VI_DEF,
-			    (char_u *)&p_uc, PV_NONE, did_set_updatecount,
+			    (char_u *)&p_uc, PV_NONE, did_set_updatecount, NULL,
 			    {(char_u *)200L, (char_u *)0L} SCTX_INIT},
     {"updatetime",  "ut",   P_NUM|P_VI_DEF,
-			    (char_u *)&p_ut, PV_NONE, NULL,
+			    (char_u *)&p_ut, PV_NONE, NULL, NULL,
 			    {(char_u *)4000L, (char_u *)0L} SCTX_INIT},
     {"varsofttabstop", "vsts",  P_STRING|P_VI_DEF|P_VIM|P_COMMA,
 #ifdef FEAT_VARTABS
 			    (char_u *)&p_vsts, PV_VSTS,
-			    did_set_varsofttabstop,
+			    did_set_varsofttabstop, NULL,
 			    {(char_u *)"", (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)"", (char_u *)NULL}
 #endif
 			    SCTX_INIT},
     {"vartabstop",  "vts",  P_STRING|P_VI_DEF|P_VIM|P_RBUF|P_COMMA,
 #ifdef FEAT_VARTABS
-			    (char_u *)&p_vts, PV_VTS, did_set_vartabstop,
+			    (char_u *)&p_vts, PV_VTS, did_set_vartabstop, NULL,
 			    {(char_u *)"", (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)"", (char_u *)NULL}
 #endif
 			    SCTX_INIT},
     {"verbose",	    "vbs",  P_NUM|P_VI_DEF,
-			    (char_u *)&p_verbose, PV_NONE, NULL,
+			    (char_u *)&p_verbose, PV_NONE, NULL, NULL,
 			    {(char_u *)0L, (char_u *)0L} SCTX_INIT},
     {"verbosefile", "vfile", P_STRING|P_EXPAND|P_VI_DEF|P_SECURE,
-			    (char_u *)&p_vfile, PV_NONE, did_set_verbosefile,
+			    (char_u *)&p_vfile, PV_NONE, did_set_verbosefile, NULL,
 			    {(char_u *)"", (char_u *)0L} SCTX_INIT},
     {"viewdir",     "vdir", P_STRING|P_EXPAND|P_VI_DEF|P_SECURE,
 #ifdef FEAT_SESSION
-			    (char_u *)&p_vdir, PV_NONE, NULL,
+			    (char_u *)&p_vdir, PV_NONE, NULL, NULL,
 			    {(char_u *)DFLT_VDIR, (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)0L, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"viewoptions", "vop",  P_STRING|P_VI_DEF|P_ONECOMMA|P_NODUP,
 #ifdef FEAT_SESSION
-			    (char_u *)&p_vop, PV_NONE, did_set_viewoptions,
+			    (char_u *)&p_vop, PV_NONE, did_set_viewoptions, expand_set_sessionoptions,
 			    {(char_u *)"folds,options,cursor,curdir",
 								  (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)0L, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"viminfo",	    "vi",   P_STRING|P_ONECOMMA|P_NODUP|P_SECURE,
 #ifdef FEAT_VIMINFO
-			    (char_u *)&p_viminfo, PV_NONE, did_set_viminfo,
+			    (char_u *)&p_viminfo, PV_NONE, did_set_viminfo, NULL,
 #if defined(MSWIN)
 			    {(char_u *)"", (char_u *)"'100,<50,s10,h,rA:,rB:"}
 #elif defined(AMIGA)
@@ -2758,104 +2763,104 @@ static struct vimoption options[] =
 			    {(char_u *)"", (char_u *)"'100,<50,s10,h"}
 #endif
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)0L, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"viminfofile", "vif",  P_STRING|P_EXPAND|P_ONECOMMA|P_NODUP
 							    |P_SECURE|P_VI_DEF,
 #ifdef FEAT_VIMINFO
-			    (char_u *)&p_viminfofile, PV_NONE, NULL,
+			    (char_u *)&p_viminfofile, PV_NONE, NULL, NULL,
 			    {(char_u *)"", (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)0L, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"virtualedit", "ve",   P_STRING|P_ONECOMMA|P_NODUP|P_VI_DEF
 							    |P_VIM|P_CURSWANT,
-			    (char_u *)&p_ve, PV_VE, did_set_virtualedit,
+			    (char_u *)&p_ve, PV_VE, did_set_virtualedit, expand_set_virtualedit,
 			    {(char_u *)"", (char_u *)""}
 			    SCTX_INIT},
     {"visualbell",  "vb",   P_BOOL|P_VI_DEF,
-			    (char_u *)&p_vb, PV_NONE, NULL,
+			    (char_u *)&p_vb, PV_NONE, NULL, NULL,
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"w300",	    NULL,   P_NUM|P_VI_DEF,
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)0L, (char_u *)0L} SCTX_INIT},
     {"w1200",	    NULL,   P_NUM|P_VI_DEF,
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)0L, (char_u *)0L} SCTX_INIT},
     {"w9600",	    NULL,   P_NUM|P_VI_DEF,
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)0L, (char_u *)0L} SCTX_INIT},
     {"warn",	    NULL,   P_BOOL|P_VI_DEF,
-			    (char_u *)&p_warn, PV_NONE, NULL,
+			    (char_u *)&p_warn, PV_NONE, NULL, NULL,
 			    {(char_u *)TRUE, (char_u *)0L} SCTX_INIT},
     {"weirdinvert", "wiv",  P_BOOL|P_VI_DEF|P_RCLR,
-			    (char_u *)&p_wiv, PV_NONE, did_set_weirdinvert,
+			    (char_u *)&p_wiv, PV_NONE, did_set_weirdinvert, NULL,
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"whichwrap",   "ww",   P_STRING|P_VIM|P_ONECOMMA|P_FLAGLIST,
-			    (char_u *)&p_ww, PV_NONE, did_set_whichwrap,
+			    (char_u *)&p_ww, PV_NONE, did_set_whichwrap, expand_set_whichwrap,
 			    {(char_u *)"", (char_u *)"b,s"} SCTX_INIT},
     {"wildchar",    "wc",   P_NUM|P_VIM,
-			    (char_u *)&p_wc, PV_NONE, NULL,
+			    (char_u *)&p_wc, PV_NONE, NULL, NULL,
 			    {(char_u *)(long)Ctrl_E, (char_u *)(long)TAB}
 			    SCTX_INIT},
     {"wildcharm",   "wcm",  P_NUM|P_VI_DEF,
-			    (char_u *)&p_wcm, PV_NONE, NULL,
+			    (char_u *)&p_wcm, PV_NONE, NULL, NULL,
 			    {(char_u *)0L, (char_u *)0L} SCTX_INIT},
     {"wildignore",  "wig",  P_STRING|P_VI_DEF|P_ONECOMMA|P_NODUP,
-			    (char_u *)&p_wig, PV_NONE, NULL,
+			    (char_u *)&p_wig, PV_NONE, NULL, NULL,
 			    {(char_u *)"", (char_u *)0L} SCTX_INIT},
     {"wildignorecase", "wic", P_BOOL|P_VI_DEF,
-			    (char_u *)&p_wic, PV_NONE, NULL,
+			    (char_u *)&p_wic, PV_NONE, NULL, NULL,
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"wildmenu",    "wmnu", P_BOOL|P_VI_DEF,
-			    (char_u *)&p_wmnu, PV_NONE, NULL,
+			    (char_u *)&p_wmnu, PV_NONE, NULL, NULL,
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
-    {"wildmode",    "wim",  P_STRING|P_VI_DEF|P_ONECOMMA|P_NODUP,
-			    (char_u *)&p_wim, PV_NONE, did_set_wildmode,
+    {"wildmode",    "wim",  P_STRING|P_VI_DEF|P_ONECOMMA|P_NODUP|P_COLON,
+			    (char_u *)&p_wim, PV_NONE, did_set_wildmode, expand_set_wildmode,
 			    {(char_u *)"full", (char_u *)0L} SCTX_INIT},
     {"wildoptions", "wop",  P_STRING|P_VI_DEF|P_ONECOMMA|P_NODUP,
-			    (char_u *)&p_wop, PV_NONE, did_set_wildoptions,
+			    (char_u *)&p_wop, PV_NONE, did_set_wildoptions, expand_set_wildoptions,
 			    {(char_u *)"", (char_u *)0L}
 			    SCTX_INIT},
     {"winaltkeys",  "wak",  P_STRING|P_VI_DEF,
 #ifdef FEAT_WAK
-			    (char_u *)&p_wak, PV_NONE, did_set_winaltkeys,
+			    (char_u *)&p_wak, PV_NONE, did_set_winaltkeys, expand_set_winaltkeys,
 			    {(char_u *)"menu", (char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)NULL, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"wincolor", "wcr",	    P_STRING|P_ALLOCED|P_VI_DEF|P_RWIN,
-			    (char_u *)VAR_WIN, PV_WCR, did_set_wincolor,
+			    (char_u *)VAR_WIN, PV_WCR, did_set_wincolor, expand_set_wincolor,
 			    {(char_u *)"", (char_u *)NULL}
 			    SCTX_INIT},
     {"window",	    "wi",   P_NUM|P_VI_DEF,
-			    (char_u *)&p_window, PV_NONE, did_set_window,
+			    (char_u *)&p_window, PV_NONE, did_set_window, NULL,
 			    {(char_u *)0L, (char_u *)0L} SCTX_INIT},
     {"winheight",   "wh",   P_NUM|P_VI_DEF,
 			    (char_u *)&p_wh, PV_NONE,
-			    did_set_winheight_helpheight,
+			    did_set_winheight_helpheight, NULL,
 			    {(char_u *)1L, (char_u *)0L} SCTX_INIT},
     {"winfixheight", "wfh", P_BOOL|P_VI_DEF|P_RSTAT,
-			    (char_u *)VAR_WIN, PV_WFH, NULL,
+			    (char_u *)VAR_WIN, PV_WFH, NULL, NULL,
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"winfixwidth", "wfw", P_BOOL|P_VI_DEF|P_RSTAT,
-			    (char_u *)VAR_WIN, PV_WFW, NULL,
+			    (char_u *)VAR_WIN, PV_WFW, NULL, NULL,
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"winminheight", "wmh", P_NUM|P_VI_DEF,
-			    (char_u *)&p_wmh, PV_NONE, did_set_winminheight,
+			    (char_u *)&p_wmh, PV_NONE, did_set_winminheight, NULL,
 			    {(char_u *)1L, (char_u *)0L} SCTX_INIT},
     {"winminwidth", "wmw", P_NUM|P_VI_DEF,
-			    (char_u *)&p_wmw, PV_NONE, did_set_winminwidth,
+			    (char_u *)&p_wmw, PV_NONE, did_set_winminwidth, NULL,
 			    {(char_u *)1L, (char_u *)0L} SCTX_INIT},
     {"winptydll", NULL,	    P_STRING|P_EXPAND|P_VI_DEF|P_SECURE,
 #if defined(MSWIN) && defined(FEAT_TERMINAL)
-			    (char_u *)&p_winptydll, PV_NONE, NULL, {
+			    (char_u *)&p_winptydll, PV_NONE, NULL, NULL, {
 # ifdef _WIN64
 			    (char_u *)"winpty64.dll",
 # else
@@ -2863,30 +2868,30 @@ static struct vimoption options[] =
 # endif
 				(char_u *)0L}
 #else
-			    (char_u *)NULL, PV_NONE, NULL,
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
 			    {(char_u *)0L, (char_u *)0L}
 #endif
 			    SCTX_INIT},
     {"winwidth",   "wiw",   P_NUM|P_VI_DEF,
-			    (char_u *)&p_wiw, PV_NONE, did_set_winwidth,
+			    (char_u *)&p_wiw, PV_NONE, did_set_winwidth, NULL,
 			    {(char_u *)20L, (char_u *)0L} SCTX_INIT},
     {"wrap",	    NULL,   P_BOOL|P_VI_DEF|P_RWIN,
-			    (char_u *)VAR_WIN, PV_WRAP, did_set_wrap,
+			    (char_u *)VAR_WIN, PV_WRAP, did_set_wrap, NULL,
 			    {(char_u *)TRUE, (char_u *)0L} SCTX_INIT},
     {"wrapmargin",  "wm",   P_NUM|P_VI_DEF,
-			    (char_u *)&p_wm, PV_WM, NULL,
+			    (char_u *)&p_wm, PV_WM, NULL, NULL,
 			    {(char_u *)0L, (char_u *)0L} SCTX_INIT},
     {"wrapscan",    "ws",   P_BOOL|P_VI_DEF,
-			    (char_u *)&p_ws, PV_NONE, NULL,
+			    (char_u *)&p_ws, PV_NONE, NULL, NULL,
 			    {(char_u *)TRUE, (char_u *)0L} SCTX_INIT},
     {"write",	    NULL,   P_BOOL|P_VI_DEF,
-			    (char_u *)&p_write, PV_NONE, NULL,
+			    (char_u *)&p_write, PV_NONE, NULL, NULL,
 			    {(char_u *)TRUE, (char_u *)0L} SCTX_INIT},
     {"writeany",    "wa",   P_BOOL|P_VI_DEF,
-			    (char_u *)&p_wa, PV_NONE, NULL,
+			    (char_u *)&p_wa, PV_NONE, NULL, NULL,
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
     {"writebackup", "wb",   P_BOOL|P_VI_DEF|P_VIM,
-			    (char_u *)&p_wb, PV_NONE, NULL,
+			    (char_u *)&p_wb, PV_NONE, NULL, NULL,
 			    {
 #ifdef FEAT_WRITEBACKUP
 			    (char_u *)TRUE,
@@ -2895,16 +2900,16 @@ static struct vimoption options[] =
 #endif
 				(char_u *)0L} SCTX_INIT},
     {"writedelay",  "wd",   P_NUM|P_VI_DEF,
-			    (char_u *)&p_wd, PV_NONE, NULL,
+			    (char_u *)&p_wd, PV_NONE, NULL, NULL,
 			    {(char_u *)0L, (char_u *)0L} SCTX_INIT},
     {"xtermcodes",  NULL,   P_BOOL|P_VI_DEF,
-			    (char_u *)&p_xtermcodes, PV_NONE, NULL,
+			    (char_u *)&p_xtermcodes, PV_NONE, NULL, NULL,
 			    {(char_u *)TRUE, (char_u *)0L} SCTX_INIT},
 
 // terminal output codes
 #define p_term(sss, vvv) \
 			    {sss, NULL, P_STRING|P_VI_DEF|P_RALL|P_SECURE, \
-			    (char_u *)&vvv, PV_NONE, did_set_term_option, \
+			    (char_u *)&vvv, PV_NONE, did_set_term_option, NULL, \
 			    {(char_u *)"", (char_u *)0L} SCTX_INIT},
 
     p_term("t_AB", T_CAB)
@@ -3000,7 +3005,7 @@ static struct vimoption options[] =
 // terminal key codes are not in here
 
     // end marker
-    {NULL, NULL, 0, NULL, PV_NONE, NULL, {NULL, NULL} SCTX_INIT}
+    {NULL, NULL, 0, NULL, PV_NONE, NULL, NULL, {NULL, NULL} SCTX_INIT}
 };
 
 #define OPTION_COUNT ARRAY_LENGTH(options)
--- a/src/optionstr.c
+++ b/src/optionstr.c
@@ -24,8 +24,21 @@ static char *(p_bo_values[]) = {"all", "
 				 "hangul", "insertmode", "lang", "mess",
 				 "showmatch", "operator", "register", "shell",
 				 "spell", "term", "wildmode", NULL};
+#if defined(FEAT_LINEBREAK)
+// Note: Keep this in sync with briopt_check()
+static char *(p_briopt_values[]) = {"shift:", "min:", "sbr", "list:", "column:", NULL};
+#endif
+#if defined(FEAT_DIFF)
+// 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", "algorithm:", NULL};
+static char *(p_dip_algorithm_values[]) = {"myers", "minimal", "patience", "histogram", NULL};
+#endif
 static char *(p_nf_values[]) = {"bin", "octal", "hex", "alpha", "unsigned", NULL};
 static char *(p_ff_values[]) = {FF_UNIX, FF_DOS, FF_MAC, NULL};
+#ifdef FEAT_CLIPBOARD
+// Note: Keep this in sync with did_set_clipboard()
+static char *(p_cb_values[]) = {"unnamed", "unnamedplus", "autoselect", "autoselectplus", "autoselectml", "html", "exclude:", NULL};
+#endif
 #ifdef FEAT_CRYPT
 static char *(p_cm_values[]) = {"zip", "blowfish", "blowfish2",
  # ifdef FEAT_SODIUM
@@ -34,6 +47,10 @@ static char *(p_cm_values[]) = {"zip", "
     NULL};
 #endif
 static char *(p_cmp_values[]) = {"internal", "keepascii", NULL};
+#ifdef FEAT_SYN_HL
+// Note: Keep this in sync with fill_culopt_flags()
+static char *(p_culopt_values[]) = {"line", "screenline", "number", "both", NULL};
+#endif
 static char *(p_dy_values[]) = {"lastline", "truncate", "uhex", NULL};
 static char *(p_jop_values[]) = {"stack", NULL};
 #ifdef FEAT_FOLDING
@@ -41,6 +58,18 @@ static char *(p_fdo_values[]) = {"all", 
 				 "quickfix", "search", "tag", "insert",
 				 "undo", "jump", NULL};
 #endif
+// Note: Keep this in sync with match_keyprotocol()
+static char *(p_kpc_protocol_values[]) = {"none", "mok2", "kitty", NULL};
+#ifdef FEAT_PROP_POPUP
+// Note: Keep this in sync with parse_popup_option()
+static char *(p_popup_option_values[]) = {"height:", "width:", "highlight:", "border:", "align:", NULL};
+static char *(p_popup_option_border_values[]) = {"on", "off", NULL};
+static char *(p_popup_option_align_values[]) = {"item", "menu", NULL};
+#endif
+#if defined(FEAT_SPELL)
+// Note: Keep this in sync with spell_check_sps()
+static char *(p_sps_values[]) = {"best", "fast", "double", "expr:", "file:", "timeout:", NULL};
+#endif
 #ifdef FEAT_SESSION
 // Also used for 'viewoptions'!  Keep in sync with SSOP_ flags.
 static char *(p_ssop_values[]) = {"buffers", "winpos", "resize", "winsize",
@@ -62,6 +91,8 @@ static char *(p_tbis_values[]) = {"tiny"
 static char *(p_ttym_values[]) = {"xterm", "xterm2", "dec", "netterm", "jsbterm", "pterm", "urxvt", "sgr", NULL};
 #endif
 static char *(p_ve_values[]) = {"block", "insert", "all", "onemore", "none", "NONE", 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};
 #ifdef FEAT_WAK
 static char *(p_wak_values[]) = {"yes", "menu", "no", NULL};
@@ -700,6 +731,198 @@ did_set_option_listflag(char_u *val, cha
 }
 
 /*
+ * Expand an option that accepts a list of string values.
+ */
+    int
+expand_set_opt_string(
+	optexpand_T *args,
+	char **values,
+	int numValues,
+	int *numMatches,
+	char_u ***matches)
+{
+    char_u	*p;
+    regmatch_T	*regmatch = args->oe_regmatch;
+    int		include_orig_val = args->oe_include_orig_val;
+    char_u	*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 = ALLOC_MULT(char_u *, numValues + 1);
+    if (*matches == NULL)
+	return FAIL;
+
+    int count = 0;
+
+    if (include_orig_val && *option_val != NUL)
+    {
+	p = vim_strsave(option_val);
+	if (p == NULL)
+	{
+	    VIM_CLEAR(*matches);
+	    return FAIL;
+	}
+	(*matches)[count++] = p;
+    }
+
+    for (char **val = values; *val != NULL; val++)
+    {
+	if (include_orig_val && *option_val != NUL)
+	{
+	    if (STRCMP((char_u*)*val, option_val) == 0)
+		continue;
+	}
+	if (vim_regexec(regmatch, (char_u*)(*val), (colnr_T)0))
+	{
+	    p = vim_strsave((char_u*)*val);
+	    if (p == NULL)
+	    {
+		if (count == 0)
+		{
+		    VIM_CLEAR(*matches);
+		    return FAIL;
+		}
+		else
+		    break;
+	    }
+	    (*matches)[count++] = p;
+	}
+    }
+    if (count == 0)
+    {
+	VIM_CLEAR(*matches);
+	return FAIL;
+    }
+    *numMatches = count;
+    return OK;
+}
+
+static char_u *set_opt_callback_orig_option = NULL;
+static char_u *((*set_opt_callback_func)(expand_T *, int));
+
+/*
+ * Callback used by expand_set_opt_generic to also include the original value.
+ */
+    static char_u *
+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 (char_u *)""; // 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,
+	char_u *((*func)(expand_T *, int)),
+	int *numMatches,
+	char_u ***matches)
+{
+    int ret;
+
+    set_opt_callback_orig_option = args->oe_include_orig_val ?
+	args->oe_opt_value : NULL;
+    set_opt_callback_func = func;
+
+    ret = ExpandGeneric(
+	    (char_u*)"", // not using fuzzy as currently EXPAND_STRING_SETTING doesn't use it
+	    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 ret;
+}
+
+
+/*
+ * Expand an option which is a list of flags.
+ */
+    int
+expand_set_opt_listflag(
+	optexpand_T *args,
+	char_u *flags,
+	int *numMatches,
+	char_u ***matches)
+{
+    char_u  *p;
+    char_u  *option_val = args->oe_opt_value;
+    char_u  *cmdline_val = args->oe_set_arg;
+    int	    append = args->oe_append;
+    int	    include_orig_val = args->oe_include_orig_val && (*option_val != NUL);
+
+    int num_flags = STRLEN(flags);
+
+    // Assume we only have small number of flags, so just allocate max size.
+    *matches = ALLOC_MULT(char_u *, num_flags + 1);
+    if (*matches == NULL)
+	return FAIL;
+
+    int count = 0;
+
+    if (include_orig_val)
+    {
+	p = vim_strsave(option_val);
+	if (p == NULL)
+	{
+	    VIM_CLEAR(*matches);
+	    return FAIL;
+	}
+	(*matches)[count++] = p;
+    }
+
+    for (char_u *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;
+	    }
+	    p = vim_strnsave(flag, 1);
+	    if (p == NULL)
+	    {
+		if (count == 0)
+		{
+		    VIM_CLEAR(*matches);
+		    return FAIL;
+		}
+		else
+		    break;
+	    }
+	    (*matches)[count++] = p;
+	}
+    }
+
+    if (count == 0)
+    {
+	VIM_CLEAR(*matches);
+	return FAIL;
+    }
+    *numMatches = count;
+    return OK;
+}
+
+/*
  * The 'ambiwidth' option is changed.
  */
     char *
@@ -711,6 +934,17 @@ did_set_ambiwidth(optset_T *args UNUSED)
     return check_chars_options();
 }
 
+    int
+expand_set_ambiwidth(optexpand_T *args, int *numMatches, char_u ***matches)
+{
+    return expand_set_opt_string(
+	    args,
+	    p_ambw_values,
+	    sizeof(p_ambw_values) / sizeof(p_ambw_values[0]) - 1,
+	    numMatches,
+	    matches);
+}
+
 /*
  * The 'background' option is changed.
  */
@@ -747,6 +981,17 @@ did_set_background(optset_T *args UNUSED
     return NULL;
 }
 
+    int
+expand_set_background(optexpand_T *args, int *numMatches, char_u ***matches)
+{
+    return expand_set_opt_string(
+	    args,
+	    p_bg_values,
+	    sizeof(p_bg_values) / sizeof(p_bg_values[0]) - 1,
+	    numMatches,
+	    matches);
+}
+
 /*
  * The 'backspace' option is changed.
  */
@@ -764,6 +1009,17 @@ did_set_backspace(optset_T *args UNUSED)
     return NULL;
 }
 
+    int
+expand_set_backspace(optexpand_T *args, int *numMatches, char_u ***matches)
+{
+    return expand_set_opt_string(
+	    args,
+	    p_bs_values,
+	    sizeof(p_bs_values) / sizeof(p_bs_values[0]) - 1,
+	    numMatches,
+	    matches);
+}
+
 /*
  * The 'backupcopy' option is changed.
  */
@@ -801,6 +1057,17 @@ did_set_backupcopy(optset_T *args)
     return errmsg;
 }
 
+    int
+expand_set_backupcopy(optexpand_T *args, int *numMatches, char_u ***matches)
+{
+    return expand_set_opt_string(
+	    args,
+	    p_bkc_values,
+	    sizeof(p_bkc_values) / sizeof(p_bkc_values[0]) - 1,
+	    numMatches,
+	    matches);
+}
+
 /*
  * The 'backupext' or the 'patchmode' option is changed.
  */
@@ -823,6 +1090,17 @@ did_set_belloff(optset_T *args UNUSED)
     return did_set_opt_flags(p_bo, p_bo_values, &bo_flags, TRUE);
 }
 
+    int
+expand_set_belloff(optexpand_T *args, int *numMatches, char_u ***matches)
+{
+    return expand_set_opt_string(
+	    args,
+	    p_bo_values,
+	    sizeof(p_bo_values) / sizeof(p_bo_values[0]) - 1,
+	    numMatches,
+	    matches);
+}
+
 #if defined(FEAT_LINEBREAK) || defined(PROTO)
 /*
  * The 'breakindentopt' option is changed.
@@ -840,6 +1118,17 @@ did_set_breakindentopt(optset_T *args UN
 
     return errmsg;
 }
+
+    int
+expand_set_breakindentopt(optexpand_T *args, int *numMatches, char_u ***matches)
+{
+    return expand_set_opt_string(
+	    args,
+	    p_briopt_values,
+	    sizeof(p_briopt_values) / sizeof(p_briopt_values[0]) - 1,
+	    numMatches,
+	    matches);
+}
 #endif
 
 #if defined(FEAT_BROWSE) || defined(PROTO)
@@ -855,6 +1144,17 @@ did_set_browsedir(optset_T *args UNUSED)
 
     return NULL;
 }
+
+    int
+expand_set_browsedir(optexpand_T *args, int *numMatches, char_u ***matches)
+{
+    return expand_set_opt_string(
+	    args,
+	    p_bsdir_values,
+	    sizeof(p_bsdir_values) / sizeof(p_bsdir_values[0]) - 1,
+	    numMatches,
+	    matches);
+}
 #endif
 
 /*
@@ -866,6 +1166,17 @@ did_set_bufhidden(optset_T *args UNUSED)
     return did_set_opt_strings(curbuf->b_p_bh, p_bufhidden_values, FALSE);
 }
 
+    int
+expand_set_bufhidden(optexpand_T *args, int *numMatches, char_u ***matches)
+{
+    return expand_set_opt_string(
+	    args,
+	    p_bufhidden_values,
+	    sizeof(p_bufhidden_values) / sizeof(p_bufhidden_values[0]) - 1,
+	    numMatches,
+	    matches);
+}
+
 /*
  * The 'buftype' option is changed.
  */
@@ -886,6 +1197,17 @@ did_set_buftype(optset_T *args UNUSED)
     return NULL;
 }
 
+    int
+expand_set_buftype(optexpand_T *args, int *numMatches, char_u ***matches)
+{
+    return expand_set_opt_string(
+	    args,
+	    p_buftype_values,
+	    sizeof(p_buftype_values) / sizeof(p_buftype_values[0]) - 1,
+	    numMatches,
+	    matches);
+}
+
 /*
  * The 'casemap' option is changed.
  */
@@ -895,6 +1217,30 @@ did_set_casemap(optset_T *args UNUSED)
     return did_set_opt_flags(p_cmp, p_cmp_values, &cmp_flags, TRUE);
 }
 
+    int
+expand_set_casemap(optexpand_T *args, int *numMatches, char_u ***matches)
+{
+    return expand_set_opt_string(
+	    args,
+	    p_cmp_values,
+	    sizeof(p_cmp_values) / sizeof(p_cmp_values[0]) - 1,
+	    numMatches,
+	    matches);
+}
+
+#if defined(FEAT_CLIPBOARD) || defined(PROTO)
+    int
+expand_set_clipboard(optexpand_T *args, int *numMatches, char_u ***matches)
+{
+    return expand_set_opt_string(
+	    args,
+	    p_cb_values,
+	    sizeof(p_cb_values) / sizeof(p_cb_values[0]) - 1,
+	    numMatches,
+	    matches);
+}
+#endif
+
 /*
  * The global 'listchars' or 'fillchars' option is changed.
  */
@@ -967,6 +1313,21 @@ did_set_chars_option(optset_T *args)
 }
 
 /*
+ * Expand 'fillchars' or 'listchars' option value.
+ */
+    int
+expand_set_chars_option(optexpand_T *args, int *numMatches, char_u ***matches)
+{
+    char_u **varp = (char_u **)args->oe_varp;
+    int 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.
  */
     char *
@@ -1091,6 +1452,20 @@ did_set_complete(optset_T *args)
     return NULL;
 }
 
+    int
+expand_set_complete(optexpand_T *args, int *numMatches, char_u ***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,
+	    sizeof(p_cpt_values) / sizeof(p_cpt_values[0]) - 1,
+	    numMatches,
+	    matches);
+}
+
 /*
  * The 'completeopt' option is changed.
  */
@@ -1104,6 +1479,17 @@ did_set_completeopt(optset_T *args UNUSE
     return NULL;
 }
 
+    int
+expand_set_completeopt(optexpand_T *args, int *numMatches, char_u ***matches)
+{
+    return expand_set_opt_string(
+	    args,
+	    p_cot_values,
+	    sizeof(p_cot_values) / sizeof(p_cot_values[0]) - 1,
+	    numMatches,
+	    matches);
+}
+
 #if (defined(FEAT_PROP_POPUP) && defined(FEAT_QUICKFIX)) || defined(PROTO)
 /*
  * The 'completepopup' option is changed.
@@ -1132,6 +1518,17 @@ did_set_completeslash(optset_T *args UNU
 
     return NULL;
 }
+
+    int
+expand_set_completeslash(optexpand_T *args, int *numMatches, char_u ***matches)
+{
+    return expand_set_opt_string(
+	    args,
+	    p_csl_values,
+	    sizeof(p_csl_values) / sizeof(p_csl_values[0]) - 1,
+	    numMatches,
+	    matches);
+}
 #endif
 
 #if defined(FEAT_CONCEAL) || defined(PROTO)
@@ -1145,6 +1542,12 @@ did_set_concealcursor(optset_T *args)
 
     return did_set_option_listflag(*varp, (char_u *)COCU_ALL, args->os_errbuf);
 }
+
+    int
+expand_set_concealcursor(optexpand_T *args, int *numMatches, char_u ***matches)
+{
+    return expand_set_opt_listflag(args, (char_u*)COCU_ALL, numMatches, matches);
+}
 #endif
 
 /*
@@ -1158,6 +1561,12 @@ did_set_cpoptions(optset_T *args)
     return did_set_option_listflag(*varp, (char_u *)CPO_ALL, args->os_errbuf);
 }
 
+    int
+expand_set_cpoptions(optexpand_T *args, int *numMatches, char_u ***matches)
+{
+    return expand_set_opt_listflag(args, (char_u*)CPO_ALL, numMatches, matches);
+}
+
 #if defined(FEAT_CRYPT) || defined(PROTO)
 /*
  * The 'cryptkey' option is changed.
@@ -1244,6 +1653,17 @@ did_set_cryptmethod(optset_T *args)
     }
     return NULL;
 }
+
+    int
+expand_set_cryptmethod(optexpand_T *args, int *numMatches, char_u ***matches)
+{
+    return expand_set_opt_string(
+	    args,
+	    p_cm_values,
+	    sizeof(p_cm_values) / sizeof(p_cm_values[0]) - 1,
+	    numMatches,
+	    matches);
+}
 #endif
 
 #if (defined(FEAT_CSCOPE) && defined(FEAT_QUICKFIX)) || defined(PROTO)
@@ -1285,11 +1705,23 @@ did_set_cursorlineopt(optset_T *args)
 {
     char_u	**varp = (char_u **)args->os_varp;
 
+    // This could be changed to use opt_strings_flags() instead.
     if (**varp == NUL || fill_culopt_flags(*varp, curwin) != OK)
 	return e_invalid_argument;
 
     return NULL;
 }
+
+    int
+expand_set_cursorlineopt(optexpand_T *args, int *numMatches, char_u ***matches)
+{
+    return expand_set_opt_string(
+	    args,
+	    p_culopt_values,
+	    sizeof(p_culopt_values) / sizeof(p_culopt_values[0]) - 1,
+	    numMatches,
+	    matches);
+}
 #endif
 
 /*
@@ -1301,6 +1733,17 @@ did_set_debug(optset_T *args UNUSED)
     return did_set_opt_strings(p_debug, p_debug_values, TRUE);
 }
 
+    int
+expand_set_debug(optexpand_T *args, int *numMatches, char_u ***matches)
+{
+    return expand_set_opt_string(
+	    args,
+	    p_debug_values,
+	    sizeof(p_debug_values) / sizeof(p_debug_values[0]) - 1,
+	    numMatches,
+	    matches);
+}
+
 #if defined(FEAT_DIFF) || defined(PROTO)
 /*
  * The 'diffopt' option is changed.
@@ -1313,6 +1756,36 @@ did_set_diffopt(optset_T *args UNUSED)
 
     return NULL;
 }
+
+    int
+expand_set_diffopt(optexpand_T *args, int *numMatches, char_u ***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.
+	int algo_len = STRLEN("algorithm:");
+	if (xp->xp_pattern - args->oe_set_arg >= algo_len &&
+		STRNCMP(xp->xp_pattern - algo_len, "algorithm:", algo_len) == 0)
+	{
+	    return expand_set_opt_string(
+		    args,
+		    p_dip_algorithm_values,
+		    sizeof(p_dip_algorithm_values) / sizeof(p_dip_algorithm_values[0]) - 1,
+		    numMatches,
+		    matches);
+	}
+	return FAIL;
+    }
+
+    return expand_set_opt_string(
+	    args,
+	    p_dip_values,
+	    sizeof(p_dip_values) / sizeof(p_dip_values[0]) - 1,
+	    numMatches,
+	    matches);
+}
 #endif
 
 /*
@@ -1328,6 +1801,17 @@ did_set_display(optset_T *args UNUSED)
     return NULL;
 }
 
+    int
+expand_set_display(optexpand_T *args, int *numMatches, char_u ***matches)
+{
+    return expand_set_opt_string(
+	    args,
+	    p_dy_values,
+	    sizeof(p_dy_values) / sizeof(p_dy_values[0]) - 1,
+	    numMatches,
+	    matches);
+}
+
 /*
  * The 'eadirection' option is changed.
  */
@@ -1337,6 +1821,17 @@ did_set_eadirection(optset_T *args UNUSE
     return did_set_opt_strings(p_ead, p_ead_values, FALSE);
 }
 
+    int
+expand_set_eadirection(optexpand_T *args, int *numMatches, char_u ***matches)
+{
+    return expand_set_opt_string(
+	    args,
+	    p_ead_values,
+	    sizeof(p_ead_values) / sizeof(p_ead_values[0]) - 1,
+	    numMatches,
+	    matches);
+}
+
 /*
  * One of the 'encoding', 'fileencoding', 'termencoding' or 'makeencoding'
  * options is changed.
@@ -1430,6 +1925,16 @@ did_set_encoding(optset_T *args)
     return errmsg;
 }
 
+    int
+expand_set_encoding(optexpand_T *args, int *numMatches, char_u ***matches)
+{
+    return expand_set_opt_generic(
+	    args,
+	    get_encoding_name,
+	    numMatches,
+	    matches);
+}
+
 /*
  * The 'eventignore' option is changed.
  */
@@ -1441,6 +1946,26 @@ did_set_eventignore(optset_T *args UNUSE
     return NULL;
 }
 
+    static char_u *
+get_eventignore_name(expand_T *xp, int idx)
+{
+    // 'eventignore' allows special keyword "all" in addition to
+    // all event names.
+    if (idx == 0)
+	return (char_u *)"all";
+    return get_event_name_no_group(xp, idx - 1);
+}
+
+    int
+expand_set_eventignore(optexpand_T *args, int *numMatches, char_u ***matches)
+{
+    return expand_set_opt_generic(
+	    args,
+	    get_eventignore_name,
+	    numMatches,
+	    matches);
+}
+
 /*
  * The 'fileformat' option is changed.
  */
@@ -1470,6 +1995,17 @@ did_set_fileformat(optset_T *args)
     return NULL;
 }
 
+    int
+expand_set_fileformat(optexpand_T *args, int *numMatches, char_u ***matches)
+{
+    return expand_set_opt_string(
+	    args,
+	    p_ff_values,
+	    sizeof(p_ff_values) / sizeof(p_ff_values[0]) - 1,
+	    numMatches,
+	    matches);
+}
+
 /*
  * The 'fileformats' option is changed.
  */
@@ -1517,6 +2053,17 @@ did_set_foldclose(optset_T *args UNUSED)
 {
     return did_set_opt_strings(p_fcl, p_fcl_values, TRUE);
 }
+
+    int
+expand_set_foldclose(optexpand_T *args, int *numMatches, char_u ***matches)
+{
+    return expand_set_opt_string(
+	    args,
+	    p_fcl_values,
+	    sizeof(p_fcl_values) / sizeof(p_fcl_values[0]) - 1,
+	    numMatches,
+	    matches);
+}
 #endif
 
 #if (defined(FEAT_EVAL) && defined(FEAT_FOLDING)) || defined(PROTO)
@@ -1583,6 +2130,17 @@ did_set_foldmethod(optset_T *args)
     return NULL;
 }
 
+    int
+expand_set_foldmethod(optexpand_T *args, int *numMatches, char_u ***matches)
+{
+    return expand_set_opt_string(
+	    args,
+	    p_fdm_values,
+	    sizeof(p_fdm_values) / sizeof(p_fdm_values[0]) - 1,
+	    numMatches,
+	    matches);
+}
+
 /*
  * The 'foldopen' option is changed.
  */
@@ -1591,6 +2149,17 @@ did_set_foldopen(optset_T *args UNUSED)
 {
     return did_set_opt_flags(p_fdo, p_fdo_values, &fdo_flags, TRUE);
 }
+
+    int
+expand_set_foldopen(optexpand_T *args, int *numMatches, char_u ***matches)
+{
+    return expand_set_opt_string(
+	    args,
+	    p_fdo_values,
+	    sizeof(p_fdo_values) / sizeof(p_fdo_values[0]) - 1,
+	    numMatches,
+	    matches);
+}
 #endif
 
 /*
@@ -1604,6 +2173,12 @@ did_set_formatoptions(optset_T *args)
     return did_set_option_listflag(*varp, (char_u *)FO_ALL, args->os_errbuf);
 }
 
+    int
+expand_set_formatoptions(optexpand_T *args, int *numMatches, char_u ***matches)
+{
+    return expand_set_opt_listflag(args, (char_u*)FO_ALL, numMatches, matches);
+}
+
 #if defined(CURSOR_SHAPE) || defined(PROTO)
 /*
  * The 'guicursor' option is changed.
@@ -1722,6 +2297,12 @@ did_set_guioptions(optset_T *args)
     gui_init_which_components(args->os_oldval.string);
     return NULL;
 }
+
+    int
+expand_set_guioptions(optexpand_T *args, int *numMatches, char_u ***matches)
+{
+    return expand_set_opt_listflag(args, (char_u*)GO_ALL, numMatches, matches);
+}
 #endif
 
 #if defined(FEAT_GUI_TABLINE) || defined(PROTO)
@@ -1788,6 +2369,125 @@ did_set_highlight(optset_T *args UNUSED)
 }
 
 /*
+ * Expand 'highlight' option.
+ */
+    int
+expand_set_highlight(optexpand_T *args, int *numMatches, char_u ***matches)
+{
+    char_u	    *p;
+    expand_T	    *xp = args->oe_xp;
+    static char_u   hl_flags[HLF_COUNT] = HL_FLAGS;
+    int		    i;
+    int		    count = 0;
+
+    if (xp->xp_pattern > args->oe_set_arg && *(xp->xp_pattern-1) == ':')
+    {
+	// Right after a ':', meaning we just return all highlight names.
+	return expand_set_opt_generic(
+		args,
+		get_highlight_name,
+		numMatches,
+		matches);
+    }
+
+    if (*xp->xp_pattern == NUL)
+    {
+	// At beginning of a comma-separated list. Return the specific list of
+	// supported occasions.
+	*matches = ALLOC_MULT(char_u *, HLF_COUNT + 1);
+	if (*matches == NULL)
+	    return FAIL;
+
+	// We still want to return the full option if it's requested.
+	if (args->oe_include_orig_val)
+	{
+	    p = vim_strsave(args->oe_opt_value);
+	    if (p == NULL)
+	    {
+		VIM_CLEAR(*matches);
+		return FAIL;
+	    }
+	    (*matches)[count++] = p;
+	}
+
+	for (i = 0; i < HLF_COUNT; i++)
+	{
+	    p = vim_strnsave(&hl_flags[i], 1);
+	    if (p == NULL)
+	    {
+		if (count == 0)
+		{
+		    VIM_CLEAR(*matches);
+		    return FAIL;
+		}
+		else
+		    break;
+	    }
+	    (*matches)[count++] = p;
+	}
+
+	if (count == 0)
+	{
+	    VIM_CLEAR(*matches);
+	    return FAIL;
+	}
+	*numMatches = count;
+	return OK;
+    }
+
+    // We are after the initial character (which indicates the occasion). We
+    // already made sure we are not matching after a ':' above, so now we want
+    // to match against display mode modifiers.
+    // Since the xp_pattern starts from the beginning, we need to include it in
+    // the returned match.
+
+    // Note: Keep this in sync with highlight_changed()
+    static char p_hl_mode_values[] =
+	{':', 'b', 'i', '-', 'n', 'r', 's', 'u', 'c', '2', 'd', '=', 't'};
+    int num_hl_modes = sizeof(p_hl_mode_values) / sizeof(p_hl_mode_values[0]);
+
+    *matches = ALLOC_MULT(char_u *, num_hl_modes);
+    if (*matches == NULL)
+	return FAIL;
+
+    int pattern_len = STRLEN(xp->xp_pattern);
+
+    for (i = 0; i < num_hl_modes; i++)
+    {
+	// Don't allow duplicates as these are unique flags
+	if (vim_strchr(xp->xp_pattern + 1, p_hl_mode_values[i]) != NULL)
+	    continue;
+
+	// ':' only works by itself, not with other flags.
+	if (pattern_len > 1 && p_hl_mode_values[i] == ':')
+	    continue;
+
+	p = vim_strnsave(xp->xp_pattern, pattern_len + 1);
+	if (p == NULL)
+	{
+	    if (i == 0)
+	    {
+		VIM_CLEAR(*matches);
+		return FAIL;
+	    }
+	    else
+		break;
+	}
+	p[pattern_len] = p_hl_mode_values[i];
+	p[pattern_len + 1] = NUL;
+	(*matches)[count++] = p;
+    }
+    if (count == 0)
+    {
+	VIM_CLEAR(*matches);
+	return FAIL;
+    }
+    *numMatches = count;
+
+    return OK;
+}
+
+/*
  * The 'titlestring' or the 'iconstring' option is changed.
  */
     static char *
@@ -1866,6 +2566,17 @@ did_set_jumpoptions(optset_T *args UNUSE
     return NULL;
 }
 
+    int
+expand_set_jumpoptions(optexpand_T *args, int *numMatches, char_u ***matches)
+{
+    return expand_set_opt_string(
+	    args,
+	    p_jop_values,
+	    sizeof(p_jop_values) / sizeof(p_jop_values[0]) - 1,
+	    numMatches,
+	    matches);
+}
+
 #if defined(FEAT_KEYMAP) || defined(PROTO)
 /*
  * The 'keymap' option is changed.
@@ -1939,6 +2650,17 @@ did_set_keymodel(optset_T *args UNUSED)
     return NULL;
 }
 
+    int
+expand_set_keymodel(optexpand_T *args, int *numMatches, char_u ***matches)
+{
+    return expand_set_opt_string(
+	    args,
+	    p_km_values,
+	    sizeof(p_km_values) / sizeof(p_km_values[0]) - 1,
+	    numMatches,
+	    matches);
+}
+
 /*
  * The 'keyprotocol' option is changed.
  */
@@ -1955,6 +2677,27 @@ did_set_keyprotocol(optset_T *args UNUSE
     return NULL;
 }
 
+    int
+expand_set_keyprotocol(optexpand_T *args, int *numMatches, char_u ***matches)
+{
+    expand_T *xp = args->oe_xp;
+    if (xp->xp_pattern > args->oe_set_arg && *(xp->xp_pattern-1) == ':')
+    {
+	// 'keyprotocol' only has well-defined terms for completion for the
+	// protocol part after the colon.
+	return expand_set_opt_string(
+		args,
+		p_kpc_protocol_values,
+		sizeof(p_kpc_protocol_values) / sizeof(p_kpc_protocol_values[0]) - 1,
+		numMatches,
+		matches);
+    }
+    // Use expand_set_opt_string instead of returning FAIL so that we can
+    // include the original value if args->oe_include_orig_val is set.
+    static char *(empty[]) = {NULL};
+    return expand_set_opt_string(args, empty, 0, numMatches, matches);
+}
+
 /*
  * The 'lispoptions' option is changed.
  */
@@ -1971,6 +2714,18 @@ did_set_lispoptions(optset_T *args)
     return NULL;
 }
 
+    int
+expand_set_lispoptions(optexpand_T *args, int *numMatches, char_u ***matches)
+{
+    static char *(p_lop_values[]) = {"expr:0", "expr:1", NULL};
+    return expand_set_opt_string(
+	    args,
+	    p_lop_values,
+	    sizeof(p_lop_values) / sizeof(p_lop_values[0]) - 1,
+	    numMatches,
+	    matches);
+}
+
 /*
  * The 'matchpairs' option is changed.
  */
@@ -2042,6 +2797,12 @@ did_set_mouse(optset_T *args)
 							args->os_errbuf);
 }
 
+    int
+expand_set_mouse(optexpand_T *args, int *numMatches, char_u ***matches)
+{
+    return expand_set_opt_listflag(args, (char_u*)MOUSE_ALL, numMatches, matches);
+}
+
 /*
  * The 'mousemodel' option is changed.
  */
@@ -2060,6 +2821,17 @@ did_set_mousemodel(optset_T *args UNUSED
     return NULL;
 }
 
+    int
+expand_set_mousemodel(optexpand_T *args, int *numMatches, char_u ***matches)
+{
+    return expand_set_opt_string(
+	    args,
+	    p_mousem_values,
+	    sizeof(p_mousem_values) / sizeof(p_mousem_values[0]) - 1,
+	    numMatches,
+	    matches);
+}
+
 #if defined(FEAT_MOUSESHAPE) || defined(PROTO)
     char *
 did_set_mouseshape(optset_T *args UNUSED)
@@ -2084,6 +2856,17 @@ 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_u ***matches)
+{
+    return expand_set_opt_string(
+	    args,
+	    p_nf_values,
+	    sizeof(p_nf_values) / sizeof(p_nf_values[0]) - 1,
+	    numMatches,
+	    matches);
+}
+
 #if defined(FEAT_EVAL) || defined(PROTO)
 /*
  * One of the '*expr' options is changed: 'balloonexpr', 'diffexpr',
@@ -2144,6 +2927,58 @@ did_set_previewpopup(optset_T *args UNUS
 
     return NULL;
 }
+
+    int
+expand_set_popupoption(optexpand_T *args, int *numMatches, char_u ***matches)
+{
+    expand_T *xp = args->oe_xp;
+
+    if (xp->xp_pattern > args->oe_set_arg && *(xp->xp_pattern-1) == ':')
+    {
+	// Within "highlight:"/"border:"/"align:", we have a subgroup of possible options.
+	int border_len = STRLEN("border:");
+	if (xp->xp_pattern - args->oe_set_arg >= border_len &&
+		STRNCMP(xp->xp_pattern - border_len, "border:", border_len) == 0)
+	{
+	    return expand_set_opt_string(
+		    args,
+		    p_popup_option_border_values,
+		    sizeof(p_popup_option_border_values) / sizeof(p_popup_option_border_values[0]) - 1,
+		    numMatches,
+		    matches);
+	}
+	int align_len = STRLEN("align:");
+	if (xp->xp_pattern - args->oe_set_arg >= align_len &&
+		STRNCMP(xp->xp_pattern - align_len, "align:", align_len) == 0)
+	{
+	    return expand_set_opt_string(
+		    args,
+		    p_popup_option_align_values,
+		    sizeof(p_popup_option_align_values) / sizeof(p_popup_option_align_values[0]) - 1,
+		    numMatches,
+		    matches);
+	}
+	int highlight_len = STRLEN("highlight:");
+	if (xp->xp_pattern - args->oe_set_arg >= highlight_len &&
+		STRNCMP(xp->xp_pattern - highlight_len, "highlight:", highlight_len) == 0)
+	{
+	    // Return the list of all highlight names
+	    return expand_set_opt_generic(
+		    args,
+		    get_highlight_name,
+		    numMatches,
+		    matches);
+	}
+	return FAIL;
+    }
+
+    return expand_set_opt_string(
+	    args,
+	    p_popup_option_values,
+	    sizeof(p_popup_option_values) / sizeof(p_popup_option_values[0]) - 1,
+	    numMatches,
+	    matches);
+}
 #endif
 
 #if defined(FEAT_POSTSCRIPT) || defined(PROTO)
@@ -2178,6 +3013,30 @@ did_set_printencoding(optset_T *args UNU
 }
 #endif
 
+#if defined(FEAT_PRINTER) || defined(PROTO)
+
+    static char_u *
+get_printoptions_names(expand_T *xp UNUSED, int idx)
+{
+    if (idx >= (int)(sizeof(printer_opts) / sizeof(printer_opts[0])))
+	return NULL;
+    return (char_u*)printer_opts[idx].name;
+}
+
+/*
+ * Expand 'printoptions' option
+ */
+    int
+expand_set_printoptions(optexpand_T *args, int *numMatches, char_u ***matches)
+{
+    return expand_set_opt_generic(
+	    args,
+	    get_printoptions_names,
+	    numMatches,
+	    matches);
+}
+#endif
+
 #if defined(FEAT_STL_OPT) || defined(PROTO)
 /*
  * The 'statusline' or the 'tabline' or the 'rulerformat' option is changed.
@@ -2244,6 +3103,18 @@ did_set_rightleftcmd(optset_T *args)
 
     return NULL;
 }
+
+    int
+expand_set_rightleftcmd(optexpand_T *args, int *numMatches, char_u ***matches)
+{
+    static char *(p_rlc_values[]) = {"search", NULL};
+    return expand_set_opt_string(
+	    args,
+	    p_rlc_values,
+	    sizeof(p_rlc_values) / sizeof(p_rlc_values[0]) - 1,
+	    numMatches,
+	    matches);
+}
 #endif
 
 #if defined(FEAT_STL_OPT) || defined(PROTO)
@@ -2266,6 +3137,17 @@ did_set_scrollopt(optset_T *args UNUSED)
     return did_set_opt_strings(p_sbo, p_scbopt_values, TRUE);
 }
 
+    int
+expand_set_scrollopt(optexpand_T *args, int *numMatches, char_u ***matches)
+{
+    return expand_set_opt_string(
+	    args,
+	    p_scbopt_values,
+	    sizeof(p_scbopt_values) / sizeof(p_scbopt_values[0]) - 1,
+	    numMatches,
+	    matches);
+}
+
 /*
  * The 'selection' option is changed.
  */
@@ -2278,6 +3160,17 @@ did_set_selection(optset_T *args UNUSED)
     return NULL;
 }
 
+    int
+expand_set_selection(optexpand_T *args, int *numMatches, char_u ***matches)
+{
+    return expand_set_opt_string(
+	    args,
+	    p_sel_values,
+	    sizeof(p_sel_values) / sizeof(p_sel_values[0]) - 1,
+	    numMatches,
+	    matches);
+}
+
 /*
  * The 'selectmode' option is changed.
  */
@@ -2287,6 +3180,17 @@ did_set_selectmode(optset_T *args UNUSED
     return did_set_opt_strings(p_slm, p_slm_values, TRUE);
 }
 
+    int
+expand_set_selectmode(optexpand_T *args, int *numMatches, char_u ***matches)
+{
+    return expand_set_opt_string(
+	    args,
+	    p_slm_values,
+	    sizeof(p_slm_values) / sizeof(p_slm_values[0]) - 1,
+	    numMatches,
+	    matches);
+}
+
 #if defined(FEAT_SESSION) || defined(PROTO)
 /*
  * The 'sessionoptions' option is changed.
@@ -2306,6 +3210,17 @@ did_set_sessionoptions(optset_T *args)
 
     return NULL;
 }
+
+    int
+expand_set_sessionoptions(optexpand_T *args, int *numMatches, char_u ***matches)
+{
+    return expand_set_opt_string(
+	    args,
+	    p_ssop_values,
+	    sizeof(p_ssop_values) / sizeof(p_ssop_values[0]) - 1,
+	    numMatches,
+	    matches);
+}
 #endif
 
 /*
@@ -2319,6 +3234,12 @@ did_set_shortmess(optset_T *args)
     return did_set_option_listflag(*varp, (char_u *)SHM_ALL, args->os_errbuf);
 }
 
+    int
+expand_set_shortmess(optexpand_T *args, int *numMatches, char_u ***matches)
+{
+    return expand_set_opt_listflag(args, (char_u*)SHM_ALL, numMatches, matches);
+}
+
 #if defined(FEAT_LINEBREAK) || defined(PROTO)
 /*
  * The 'showbreak' option is changed.
@@ -2349,6 +3270,17 @@ did_set_showcmdloc(optset_T *args UNUSED
     return did_set_opt_strings(p_sloc, p_sloc_values, FALSE);
 }
 
+    int
+expand_set_showcmdloc(optexpand_T *args, int *numMatches, char_u ***matches)
+{
+    return expand_set_opt_string(
+	    args,
+	    p_sloc_values,
+	    sizeof(p_sloc_values) / sizeof(p_sloc_values[0]) - 1,
+	    numMatches,
+	    matches);
+}
+
 #if defined(FEAT_SIGNS) || defined(PROTO)
 /*
  * The 'signcolumn' option is changed.
@@ -2369,6 +3301,17 @@ did_set_signcolumn(optset_T *args)
 
     return NULL;
 }
+
+    int
+expand_set_signcolumn(optexpand_T *args, int *numMatches, char_u ***matches)
+{
+    return expand_set_opt_string(
+	    args,
+	    p_scl_values,
+	    sizeof(p_scl_values) / sizeof(p_scl_values[0]) - 1,
+	    numMatches,
+	    matches);
+}
 #endif
 
 #if defined(FEAT_SPELL) || defined(PROTO)
@@ -2428,6 +3371,18 @@ did_set_spelloptions(optset_T *args)
     return NULL;
 }
 
+    int
+expand_set_spelloptions(optexpand_T *args, int *numMatches, char_u ***matches)
+{
+    static char *(p_spo_values[]) = {"camel", NULL};
+    return expand_set_opt_string(
+	    args,
+	    p_spo_values,
+	    sizeof(p_spo_values) / sizeof(p_spo_values[0]) - 1,
+	    numMatches,
+	    matches);
+}
+
 /*
  * The 'spellsuggest' option is changed.
  */
@@ -2439,6 +3394,17 @@ did_set_spellsuggest(optset_T *args UNUS
 
     return NULL;
 }
+
+    int
+expand_set_spellsuggest(optexpand_T *args, int *numMatches, char_u ***matches)
+{
+    return expand_set_opt_string(
+	    args,
+	    p_sps_values,
+	    sizeof(p_sps_values) / sizeof(p_sps_values[0]) - 1,
+	    numMatches,
+	    matches);
+}
 #endif
 
 /*
@@ -2450,6 +3416,17 @@ did_set_splitkeep(optset_T *args UNUSED)
     return did_set_opt_strings(p_spk, p_spk_values, FALSE);
 }
 
+    int
+expand_set_splitkeep(optexpand_T *args, int *numMatches, char_u ***matches)
+{
+    return expand_set_opt_string(
+	    args,
+	    p_spk_values,
+	    sizeof(p_spk_values) / sizeof(p_spk_values[0]) - 1,
+	    numMatches,
+	    matches);
+}
+
 #if defined(FEAT_STL_OPT) || defined(PROTO)
 /*
  * The 'statusline' option is changed.
@@ -2470,6 +3447,17 @@ did_set_swapsync(optset_T *args UNUSED)
     return did_set_opt_strings(p_sws, p_sws_values, FALSE);
 }
 
+    int
+expand_set_swapsync(optexpand_T *args, int *numMatches, char_u ***matches)
+{
+    return expand_set_opt_string(
+	    args,
+	    p_sws_values,
+	    sizeof(p_sws_values) / sizeof(p_sws_values[0]) - 1,
+	    numMatches,
+	    matches);
+}
+
 /*
  * The 'switchbuf' option is changed.
  */
@@ -2479,6 +3467,17 @@ did_set_switchbuf(optset_T *args UNUSED)
     return did_set_opt_flags(p_swb, p_swb_values, &swb_flags, TRUE);
 }
 
+    int
+expand_set_switchbuf(optexpand_T *args, int *numMatches, char_u ***matches)
+{
+    return expand_set_opt_string(
+	    args,
+	    p_swb_values,
+	    sizeof(p_swb_values) / sizeof(p_swb_values[0]) - 1,
+	    numMatches,
+	    matches);
+}
+
 #if defined(FEAT_STL_OPT) || defined(PROTO)
 /*
  * The 'tabline' option is changed.
@@ -2520,6 +3519,17 @@ did_set_tagcase(optset_T *args)
     return NULL;
 }
 
+    int
+expand_set_tagcase(optexpand_T *args, int *numMatches, char_u ***matches)
+{
+    return expand_set_opt_string(
+	    args,
+	    p_tc_values,
+	    sizeof(p_tc_values) / sizeof(p_tc_values[0]) - 1,
+	    numMatches,
+	    matches);
+}
+
 /*
  * The 'term' option is changed.
  */
@@ -2653,6 +3663,17 @@ did_set_termwintype(optset_T *args UNUSE
 {
     return did_set_opt_strings(p_twt, p_twt_values, FALSE);
 }
+
+    int
+expand_set_termwintype(optexpand_T *args, int *numMatches, char_u ***matches)
+{
+    return expand_set_opt_string(
+	    args,
+	    p_twt_values,
+	    sizeof(p_twt_values) / sizeof(p_twt_values[0]) - 1,
+	    numMatches,
+	    matches);
+}
 # endif
 #endif
 
@@ -2686,6 +3707,17 @@ did_set_toolbar(optset_T *args UNUSED)
 		(TOOLBAR_TEXT | TOOLBAR_ICONS)) != 0);
     return NULL;
 }
+
+    int
+expand_set_toolbar(optexpand_T *args, int *numMatches, char_u ***matches)
+{
+    return expand_set_opt_string(
+	    args,
+	    p_toolbar_values,
+	    sizeof(p_toolbar_values) / sizeof(p_toolbar_values[0]) - 1,
+	    numMatches,
+	    matches);
+}
 #endif
 
 #if (defined(FEAT_TOOLBAR) && defined(FEAT_GUI_GTK)) || defined(PROTO)
@@ -2703,6 +3735,17 @@ did_set_toolbariconsize(optset_T *args U
 		(TOOLBAR_TEXT | TOOLBAR_ICONS)) != 0);
     return NULL;
 }
+
+    int
+expand_set_toolbariconsize(optexpand_T *args, int *numMatches, char_u ***matches)
+{
+    return expand_set_opt_string(
+	    args,
+	    p_tbis_values,
+	    sizeof(p_tbis_values) / sizeof(p_tbis_values[0]) - 1,
+	    numMatches,
+	    matches);
+}
 #endif
 
 #if defined(UNIX) || defined(VMS) || defined(PROTO)
@@ -2726,6 +3769,17 @@ did_set_ttymouse(optset_T *args UNUSED)
 
     return errmsg;
 }
+
+    int
+expand_set_ttymouse(optexpand_T *args, int *numMatches, char_u ***matches)
+{
+    return expand_set_opt_string(
+	    args,
+	    p_ttym_values,
+	    sizeof(p_ttym_values) / sizeof(p_ttym_values[0]) - 1,
+	    numMatches,
+	    matches);
+}
 #endif
 
 #if defined(FEAT_VARTABS) || defined(PROTO)
@@ -2932,6 +3986,17 @@ did_set_virtualedit(optset_T *args)
     return NULL;
 }
 
+    int
+expand_set_virtualedit(optexpand_T *args, int *numMatches, char_u ***matches)
+{
+    return expand_set_opt_string(
+	    args,
+	    p_ve_values,
+	    sizeof(p_ve_values) / sizeof(p_ve_values[0]) - 1,
+	    numMatches,
+	    matches);
+}
+
 /*
  * The 'whichwrap' option is changed.
  */
@@ -2940,7 +4005,15 @@ did_set_whichwrap(optset_T *args)
 {
     char_u	**varp = (char_u **)args->os_varp;
 
-    return did_set_option_listflag(*varp, (char_u *)WW_ALL, args->os_errbuf);
+    // Add ',' to the list flags because 'whichwrap' is a flag
+    // list that is comma-separated.
+    return did_set_option_listflag(*varp, (char_u *)(WW_ALL ","), args->os_errbuf);
+}
+
+    int
+expand_set_whichwrap(optexpand_T *args, int *numMatches, char_u ***matches)
+{
+    return expand_set_opt_listflag(args, (char_u*)WW_ALL, numMatches, matches);
 }
 
 /*
@@ -2954,6 +4027,17 @@ did_set_wildmode(optset_T *args UNUSED)
     return NULL;
 }
 
+    int
+expand_set_wildmode(optexpand_T *args, int *numMatches, char_u ***matches)
+{
+    return expand_set_opt_string(
+	    args,
+	    p_wim_values,
+	    sizeof(p_wim_values) / sizeof(p_wim_values[0]) - 1,
+	    numMatches,
+	    matches);
+}
+
 /*
  * The 'wildoptions' option is changed.
  */
@@ -2963,6 +4047,17 @@ did_set_wildoptions(optset_T *args UNUSE
     return did_set_opt_strings(p_wop, p_wop_values, TRUE);
 }
 
+    int
+expand_set_wildoptions(optexpand_T *args, int *numMatches, char_u ***matches)
+{
+    return expand_set_opt_string(
+	    args,
+	    p_wop_values,
+	    sizeof(p_wop_values) / sizeof(p_wop_values[0]) - 1,
+	    numMatches,
+	    matches);
+}
+
 #if defined(FEAT_WAK) || defined(PROTO)
 /*
  * The 'winaltkeys' option is changed.
@@ -2985,6 +4080,17 @@ did_set_winaltkeys(optset_T *args UNUSED
 # endif
     return errmsg;
 }
+
+    int
+expand_set_winaltkeys(optexpand_T *args, int *numMatches, char_u ***matches)
+{
+    return expand_set_opt_string(
+	    args,
+	    p_wak_values,
+	    sizeof(p_wak_values) / sizeof(p_wak_values[0]) - 1,
+	    numMatches,
+	    matches);
+}
 #endif
 
 /*
@@ -2999,6 +4105,16 @@ did_set_wincolor(optset_T *args UNUSED)
     return NULL;
 }
 
+    int
+expand_set_wincolor(optexpand_T *args, int *numMatches, char_u ***matches)
+{
+    return expand_set_opt_generic(
+	    args,
+	    get_highlight_name,
+	    numMatches,
+	    matches);
+}
+
 #ifdef FEAT_SYN_HL
 /*
  * When the 'syntax' option is set, load the syntax of that name.
--- a/src/popupwin.c
+++ b/src/popupwin.c
@@ -1803,6 +1803,7 @@ parse_popup_option(win_T *wp, int is_pre
 	dig = e + 1;
 	x = getdigits(&dig);
 
+	// Note: Keep this in sync with p_popup_option_values.
 	if (STRNCMP(s, "height:", 7) == 0)
 	{
 	    if (dig != p)
@@ -1840,6 +1841,7 @@ parse_popup_option(win_T *wp, int is_pre
 	}
 	else if (STRNCMP(s, "border:", 7) == 0)
 	{
+	    // Note: Keep this in sync with p_popup_option_border_values.
 	    char_u	*arg = s + 7;
 	    int		on = STRNCMP(arg, "on", 2) == 0 && arg + 2 == p;
 	    int		off = STRNCMP(arg, "off", 3) == 0 && arg + 3 == p;
@@ -1858,6 +1860,7 @@ parse_popup_option(win_T *wp, int is_pre
 	}
 	else if (STRNCMP(s, "align:", 6) == 0)
 	{
+	    // Note: Keep this in sync with p_popup_option_align_values.
 	    char_u	*arg = s + 6;
 	    int		item = STRNCMP(arg, "item", 4) == 0 && arg + 4 == p;
 	    int		menu = STRNCMP(arg, "menu", 4) == 0 && arg + 4 == p;
--- a/src/proto/autocmd.pro
+++ b/src/proto/autocmd.pro
@@ -39,6 +39,7 @@ int has_autocmd(event_T event, char_u *s
 char_u *get_augroup_name(expand_T *xp, int idx);
 char_u *set_context_in_autocmd(expand_T *xp, char_u *arg, int doautocmd);
 char_u *get_event_name(expand_T *xp, int idx);
+char_u *get_event_name_no_group(expand_T *xp, int idx);
 int autocmd_supported(char_u *name);
 int au_exists(char_u *arg);
 void f_autocmd_add(typval_T *argvars, typval_T *rettv);
--- a/src/proto/cmdexpand.pro
+++ b/src/proto/cmdexpand.pro
@@ -9,6 +9,7 @@ int cmdline_compl_startcol(void);
 char_u *ExpandOne(expand_T *xp, char_u *str, char_u *orig, int options, int mode);
 void ExpandInit(expand_T *xp);
 void ExpandCleanup(expand_T *xp);
+int ExpandGeneric(char_u *pat, expand_T *xp, regmatch_T *regmatch, char_u ***matches, int *numMatches, char_u *((*func)(expand_T *, int)), int escaped);
 int showmatches(expand_T *xp, int wildmenu);
 char_u *addstar(char_u *fname, int len, int context);
 void set_expand_context(expand_T *xp);
--- a/src/proto/mbyte.pro
+++ b/src/proto/mbyte.pro
@@ -88,4 +88,5 @@ char_u *string_convert_ext(vimconv_T *vc
 void f_setcellwidths(typval_T *argvars, typval_T *rettv);
 void f_getcellwidths(typval_T *argvars, typval_T *rettv);
 void f_charclass(typval_T *argvars, typval_T *rettv);
+char_u *get_encoding_name(expand_T *xp, int idx);
 /* vim: set ft=c : */
--- a/src/proto/option.pro
+++ b/src/proto/option.pro
@@ -129,6 +129,8 @@ void set_imsearch_global(void);
 void set_context_in_set_cmd(expand_T *xp, char_u *arg, int opt_flags);
 int ExpandSettings(expand_T *xp, regmatch_T *regmatch, char_u *fuzzystr, int *numMatches, char_u ***matches, int can_fuzzy);
 int ExpandOldSetting(int *numMatches, char_u ***matches);
+int ExpandStringSetting(expand_T *xp, regmatch_T *regmatch, int *numMatches, char_u ***matches);
+int ExpandSettingSubtract(expand_T *xp, regmatch_T *regmatch, int *numMatches, char_u ***matches);
 int shortmess(int x);
 void vimrc_found(char_u *fname, char_u *envname);
 void change_compatible(int on);
--- a/src/proto/optionstr.pro
+++ b/src/proto/optionstr.pro
@@ -122,6 +122,71 @@ char *did_set_wildoptions(optset_T *args
 char *did_set_winaltkeys(optset_T *args);
 char *did_set_wincolor(optset_T *args);
 char *did_set_string_option(int opt_idx, char_u **varp, char_u *oldval, char_u *value, char *errbuf, int opt_flags, int *value_checked);
+int expand_set_ambiwidth(optexpand_T *args, int *numMatches, char_u ***matches);
+int expand_set_background(optexpand_T *args, int *numMatches, char_u ***matches);
+int expand_set_backspace(optexpand_T *args, int *numMatches, char_u ***matches);
+int expand_set_backupcopy(optexpand_T *args, int *numMatches, char_u ***matches);
+int expand_set_belloff(optexpand_T *args, int *numMatches, char_u ***matches);
+int expand_set_breakindentopt(optexpand_T *args, int *numMatches, char_u ***matches);
+int expand_set_browsedir(optexpand_T *args, int *numMatches, char_u ***matches);
+int expand_set_bufhidden(optexpand_T *args, int *numMatches, char_u ***matches);
+int expand_set_buftype(optexpand_T *args, int *numMatches, char_u ***matches);
+int expand_set_casemap(optexpand_T *args, int *numMatches, char_u ***matches);
+int expand_set_chars_option(optexpand_T *args, int *numMatches, char_u ***matches);
+int expand_set_clipboard(optexpand_T *args, int *numMatches, char_u ***matches);
+int expand_set_complete(optexpand_T *args, int *numMatches, char_u ***matches);
+int expand_set_completeopt(optexpand_T *args, int *numMatches, char_u ***matches);
+int expand_set_completeslash(optexpand_T *args, int *numMatches, char_u ***matches);
+int expand_set_concealcursor(optexpand_T *args, int *numMatches, char_u ***matches);
+int expand_set_cpoptions(optexpand_T *args, int *numMatches, char_u ***matches);
+int expand_set_cryptmethod(optexpand_T *args, int *numMatches, char_u ***matches);
+int expand_set_cursorlineopt(optexpand_T *args, int *numMatches, char_u ***matches);
+int expand_set_debug(optexpand_T *args, int *numMatches, char_u ***matches);
+int expand_set_diffopt(optexpand_T *args, int *numMatches, char_u ***matches);
+int expand_set_display(optexpand_T *args, int *numMatches, char_u ***matches);
+int expand_set_eadirection(optexpand_T *args, int *numMatches, char_u ***matches);
+int expand_set_encoding(optexpand_T *args, int *numMatches, char_u ***matches);
+int expand_set_eventignore(optexpand_T *args, int *numMatches, char_u ***matches);
+int expand_set_fileformat(optexpand_T *args, int *numMatches, char_u ***matches);
+int expand_set_foldclose(optexpand_T *args, int *numMatches, char_u ***matches);
+int expand_set_foldmethod(optexpand_T *args, int *numMatches, char_u ***matches);
+int expand_set_foldopen(optexpand_T *args, int *numMatches, char_u ***matches);
+int expand_set_formatoptions(optexpand_T *args, int *numMatches, char_u ***matches);
+int expand_set_guioptions(optexpand_T *args, int *numMatches, char_u ***matches);
+int expand_set_highlight(optexpand_T *args, int *numMatches, char_u ***matches);
+int expand_set_jumpoptions(optexpand_T *args, int *numMatches, char_u ***matches);
+int expand_set_keymodel(optexpand_T *args, int *numMatches, char_u ***matches);
+int expand_set_keyprotocol(optexpand_T *args, int *numMatches, char_u ***matches);
+int expand_set_lispoptions(optexpand_T *args, int *numMatches, char_u ***matches);
+int expand_set_mouse(optexpand_T *args, int *numMatches, char_u ***matches);
+int expand_set_mousemodel(optexpand_T *args, int *numMatches, char_u ***matches);
+int expand_set_nrformats(optexpand_T *args, int *numMatches, char_u ***matches);
+int expand_set_popupoption(optexpand_T *args, int *numMatches, char_u ***matches);
+int expand_set_printoptions(optexpand_T *args, int *numMatches, char_u ***matches);
+int expand_set_rightleftcmd(optexpand_T *args, int *numMatches, char_u ***matches);
+int expand_set_scrollopt(optexpand_T *args, int *numMatches, char_u ***matches);
+int expand_set_selection(optexpand_T *args, int *numMatches, char_u ***matches);
+int expand_set_selectmode(optexpand_T *args, int *numMatches, char_u ***matches);
+int expand_set_sessionoptions(optexpand_T *args, int *numMatches, char_u ***matches);
+int expand_set_shortmess(optexpand_T *args, int *numMatches, char_u ***matches);
+int expand_set_showcmdloc(optexpand_T *args, int *numMatches, char_u ***matches);
+int expand_set_signcolumn(optexpand_T *args, int *numMatches, char_u ***matches);
+int expand_set_spelloptions(optexpand_T *args, int *numMatches, char_u ***matches);
+int expand_set_spellsuggest(optexpand_T *args, int *numMatches, char_u ***matches);
+int expand_set_splitkeep(optexpand_T *args, int *numMatches, char_u ***matches);
+int expand_set_swapsync(optexpand_T *args, int *numMatches, char_u ***matches);
+int expand_set_switchbuf(optexpand_T *args, int *numMatches, char_u ***matches);
+int expand_set_tagcase(optexpand_T *args, int *numMatches, char_u ***matches);
+int expand_set_termwintype(optexpand_T *args, int *numMatches, char_u ***matches);
+int expand_set_toolbar(optexpand_T *args, int *numMatches, char_u ***matches);
+int expand_set_toolbariconsize(optexpand_T *args, int *numMatches, char_u ***matches);
+int expand_set_ttymouse(optexpand_T *args, int *numMatches, char_u ***matches);
+int expand_set_virtualedit(optexpand_T *args, int *numMatches, char_u ***matches);
+int expand_set_whichwrap(optexpand_T *args, int *numMatches, char_u ***matches);
+int expand_set_wildmode(optexpand_T *args, int *numMatches, char_u ***matches);
+int expand_set_wildoptions(optexpand_T *args, int *numMatches, char_u ***matches);
+int expand_set_winaltkeys(optexpand_T *args, int *numMatches, char_u ***matches);
+int expand_set_wincolor(optexpand_T *args, int *numMatches, char_u ***matches);
 int check_ff_value(char_u *p);
 void save_clear_shm_value(void);
 void restore_shm_value(void);
--- a/src/proto/screen.pro
+++ b/src/proto/screen.pro
@@ -57,5 +57,7 @@ int screen_screencol(void);
 int screen_screenrow(void);
 char *set_fillchars_option(win_T *wp, char_u *val, int apply);
 char *set_listchars_option(win_T *wp, char_u *val, int apply);
+char_u * get_fillchars_name(expand_T *xp, int idx);
+char_u * get_listchars_name(expand_T *xp, int idx);
 char *check_chars_options(void);
 /* vim: set ft=c : */
--- a/src/screen.c
+++ b/src/screen.c
@@ -4658,6 +4658,43 @@ get_encoded_char_adv(char_u **p)
     return mb_ptr2char_adv(p);
 }
 
+struct charstab
+{
+    int	    *cp;
+    char    *name;
+};
+static fill_chars_T fill_chars;
+static struct charstab filltab[] =
+{
+    {&fill_chars.stl,		"stl"},
+    {&fill_chars.stlnc,		"stlnc"},
+    {&fill_chars.vert,		"vert"},
+    {&fill_chars.fold,		"fold"},
+    {&fill_chars.foldopen,	"foldopen"},
+    {&fill_chars.foldclosed,	"foldclose"},
+    {&fill_chars.foldsep,	"foldsep"},
+    {&fill_chars.diff,		"diff"},
+    {&fill_chars.eob,		"eob"},
+    {&fill_chars.lastline,	"lastline"},
+};
+static lcs_chars_T lcs_chars;
+static struct charstab lcstab[] =
+{
+    {&lcs_chars.eol,		"eol"},
+    {&lcs_chars.ext,		"extends"},
+    {&lcs_chars.nbsp,		"nbsp"},
+    {&lcs_chars.prec,		"precedes"},
+    {&lcs_chars.space,		"space"},
+    {&lcs_chars.tab2,		"tab"},
+    {&lcs_chars.trail,		"trail"},
+    {&lcs_chars.lead,		"lead"},
+#ifdef FEAT_CONCEAL
+    {&lcs_chars.conceal,	"conceal"},
+#else
+    {NULL,			"conceal"},
+#endif
+};
+
 /*
  * Handle setting 'listchars' or 'fillchars'.
  * "value" points to either the global or the window-local value.
@@ -4677,46 +4714,8 @@ set_chars_option(win_T *wp, char_u *valu
     int	    multispace_len = 0;	      // Length of lcs-multispace string
     int	    lead_multispace_len = 0;  // Length of lcs-leadmultispace string
 
-    struct charstab
-    {
-	int	*cp;
-	char	*name;
-    };
     struct charstab *tab;
 
-    static fill_chars_T fill_chars;
-    static struct charstab filltab[] =
-    {
-	{&fill_chars.stl,	"stl"},
-	{&fill_chars.stlnc,	"stlnc"},
-	{&fill_chars.vert,	"vert"},
-	{&fill_chars.fold,	"fold"},
-	{&fill_chars.foldopen,	"foldopen"},
-	{&fill_chars.foldclosed, "foldclose"},
-	{&fill_chars.foldsep,	"foldsep"},
-	{&fill_chars.diff,	"diff"},
-	{&fill_chars.eob,	"eob"},
-	{&fill_chars.lastline,	"lastline"},
-    };
-
-    static lcs_chars_T lcs_chars;
-    struct charstab lcstab[] =
-    {
-	{&lcs_chars.eol,	"eol"},
-	{&lcs_chars.ext,	"extends"},
-	{&lcs_chars.nbsp,	"nbsp"},
-	{&lcs_chars.prec,	"precedes"},
-	{&lcs_chars.space,	"space"},
-	{&lcs_chars.tab2,	"tab"},
-	{&lcs_chars.trail,	"trail"},
-	{&lcs_chars.lead,	"lead"},
-#ifdef FEAT_CONCEAL
-	{&lcs_chars.conceal,	"conceal"},
-#else
-	{NULL,			"conceal"},
-#endif
-    };
-
     if (is_listchars)
     {
 	tab = lcstab;
@@ -4957,6 +4956,32 @@ set_listchars_option(win_T *wp, char_u *
 }
 
 /*
+ * Function given to ExpandGeneric() to obtain possible arguments of the
+ * 'fillchars' option.
+ */
+    char_u *
+get_fillchars_name(expand_T *xp UNUSED, int idx)
+{
+    if (idx >= (int)(sizeof(filltab) / sizeof(filltab[0])))
+	return NULL;
+
+    return (char_u*)filltab[idx].name;
+}
+
+/*
+ * Function given to ExpandGeneric() to obtain possible arguments of the
+ * 'listchars' option.
+ */
+    char_u *
+get_listchars_name(expand_T *xp UNUSED, int idx)
+{
+    if (idx >= (int)(sizeof(lcstab) / sizeof(lcstab[0])))
+	return NULL;
+
+    return (char_u*)lcstab[idx].name;
+}
+
+/*
  * Check all global and local values of 'listchars' and 'fillchars'.
  * Return an untranslated error messages if any of them is invalid, NULL
  * otherwise.
--- a/src/spellsuggest.c
+++ b/src/spellsuggest.c
@@ -424,6 +424,7 @@ spell_check_sps(void)
 	    if (*s != NUL && !VIM_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)
--- a/src/structs.h
+++ b/src/structs.h
@@ -4915,6 +4915,34 @@ typedef struct
 } optset_T;
 
 /*
+ * 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_u	*oe_varp;
+    // The original option value, escaped.
+    char_u	*oe_opt_value;
+
+    // TRUE if using set+= instead of set=
+    int		oe_append;
+    // TRUE if we would like to add the original option value as the first
+    // choice.
+    int		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_u	*oe_set_arg;
+} optexpand_T;
+
+/*
  * Spell checking variables passed from win_update() to win_line().
  */
 typedef struct {
--- a/src/term.c
+++ b/src/term.c
@@ -1913,6 +1913,7 @@ match_keyprotocol(char_u *term)
 	*colon = NUL;
 
 	keyprot_T prot;
+	// Note: Keep this in sync with p_kpc_protocol_values.
 	if (STRCMP(colon + 1, "none") == 0)
 	    prot = KEYPROTOCOL_NONE;
 	else if (STRCMP(colon + 1, "mok2") == 0)
--- a/src/testdir/test_options.vim
+++ b/src/testdir/test_options.vim
@@ -295,11 +295,11 @@ func Test_set_completion()
   call assert_equal('"set tabstop thesaurus thesaurusfunc ttyscroll', @:)
 
   " Expand current value
-  call feedkeys(":set fileencodings=\<C-A>\<C-B>\"\<CR>", 'tx')
-  call assert_equal('"set fileencodings=ucs-bom,utf-8,default,latin1', @:)
+  call feedkeys(":set suffixes=\<C-A>\<C-B>\"\<CR>", 'tx')
+  call assert_equal('"set suffixes=.bak,~,.o,.h,.info,.swp,.obj', @:)
 
-  call feedkeys(":set fileencodings:\<C-A>\<C-B>\"\<CR>", 'tx')
-  call assert_equal('"set fileencodings:ucs-bom,utf-8,default,latin1', @:)
+  call feedkeys(":set suffixes:\<C-A>\<C-B>\"\<CR>", 'tx')
+  call assert_equal('"set suffixes:.bak,~,.o,.h,.info,.swp,.obj', @:)
 
   " Expand key codes.
   call feedkeys(":set <H\<C-A>\<C-B>\"\<CR>", 'tx')
@@ -360,13 +360,17 @@ func Test_set_completion()
   call assert_equal("\"set invtabstop=", @:)
 
   " Expand options for 'spellsuggest'
-  call feedkeys(":set spellsuggest=best,file:xyz\<Tab>\<C-B>\"\<CR>", 'xt')
-  call assert_equal("\"set spellsuggest=best,file:xyz", @:)
+  call feedkeys(":set spellsuggest=file:test_options.v\<Tab>\<C-B>\"\<CR>", 'xt')
+  call assert_equal("\"set spellsuggest=file:test_options.vim", @:)
+  call feedkeys(":set spellsuggest=best,file:test_options.v\<Tab>\<C-B>\"\<CR>", 'xt')
+  call assert_equal("\"set spellsuggest=best,file:test_options.vim", @:)
 
   " Expand value for 'key'
   set key=abcd
   call feedkeys(":set key=\<Tab>\<C-B>\"\<CR>", 'xt')
   call assert_equal('"set key=*****', @:)
+  call feedkeys(":set key-=\<Tab>\<C-B>\"\<CR>", 'xt')
+  call assert_equal('"set key-=*****', @:)
   set key=
 
   " Expand values for 'filetype'
@@ -382,6 +386,278 @@ func Test_set_completion()
   call assert_equal('"set syntax=' .. getcompletion('a*', 'syntax')->join(), @:)
 endfunc
 
+" Test handling of expanding individual string option values
+func Test_set_completion_string_values()
+  "
+  " Test basic enum string options that have well-defined enum names
+  "
+
+  call assert_equal(getcompletion('set display=', 'cmdline'), ['lastline', 'truncate', 'uhex'])
+  call assert_equal(getcompletion('set display=t', 'cmdline'), ['truncate'])
+  call assert_equal(getcompletion('set display=*ex*', 'cmdline'), ['uhex'])
+
+  " Test that if a value is set, it will populate the results, but only if
+  " typed value is empty.
+  set display=uhex,lastline
+  call assert_equal(getcompletion('set display=', 'cmdline'), ['uhex,lastline', 'lastline', 'truncate', 'uhex'])
+  call assert_equal(getcompletion('set display=u', 'cmdline'), ['uhex'])
+  " If the set value is part of the enum list, it will show as the first
+  " result with no duplicate.
+  set display=uhex
+  call assert_equal(getcompletion('set display=', 'cmdline'), ['uhex', 'lastline', 'truncate'])
+  " If empty value, will just show the normal list without an empty item
+  set display=
+  call assert_equal(getcompletion('set display=', 'cmdline'), ['lastline', 'truncate', 'uhex'])
+  " Test escaping of the values
+  call assert_equal(getcompletion('set fillchars=', 'cmdline')[0], 'vert:\|,fold:-,eob:~,lastline:@')
+
+  " Test comma-separated lists will expand after a comma.
+  call assert_equal(getcompletion('set display=truncate,*ex*', 'cmdline'), ['uhex'])
+  " Also test the positioning of the expansion is correct
+  call feedkeys(":set display=truncate,l\<Tab>\<C-B>\"\<CR>", 'xt')
+  call assert_equal('"set display=truncate,lastline', @:)
+  set display&
+
+  " Test single-value options will not expand after a comma
+  call assert_equal(getcompletion('set ambw=single,', 'cmdline'), [])
+
+  " Test the other simple options to make sure they have basic auto-complete,
+  " but don't exhaustively validate their results.
+  call assert_equal(getcompletion('set ambw=', 'cmdline')[0], 'single')
+  call assert_match('light\|dark', getcompletion('set bg=', 'cmdline')[1])
+  call assert_equal(getcompletion('set backspace=', 'cmdline')[0], 'indent')
+  call assert_equal(getcompletion('set backupcopy=', 'cmdline')[1], 'yes')
+  call assert_equal(getcompletion('set belloff=', 'cmdline')[1], 'backspace')
+  call assert_equal(getcompletion('set briopt=', 'cmdline')[1], 'min:')
+  if exists('+browsedir')
+    call assert_equal(getcompletion('set browsedir=', 'cmdline')[1], 'current')
+  endif
+  call assert_equal(getcompletion('set bufhidden=', 'cmdline')[1], 'unload')
+  call assert_equal(getcompletion('set buftype=', 'cmdline')[1], 'nowrite')
+  call assert_equal(getcompletion('set casemap=', 'cmdline')[1], 'internal')
+  if exists('+clipboard')
+    call assert_match('unnamed', getcompletion('set clipboard=', 'cmdline')[1])
+  endif
+  call assert_equal(getcompletion('set complete=', 'cmdline')[1], '.')
+  call assert_equal(getcompletion('set completeopt=', 'cmdline')[1], 'menu')
+  if exists('+completeslash')
+    call assert_equal(getcompletion('set completeslash=', 'cmdline')[1], 'backslash')
+  endif
+  if exists('+cryptmethod')
+    call assert_equal(getcompletion('set cryptmethod=', 'cmdline')[1], 'zip')
+  endif
+  if exists('+cursorlineopt')
+    call assert_equal(getcompletion('set cursorlineopt=', 'cmdline')[1], 'line')
+  endif
+  call assert_equal(getcompletion('set debug=', 'cmdline')[1], 'throw')
+  call assert_equal(getcompletion('set eadirection=', 'cmdline')[1], 'ver')
+  call assert_equal(getcompletion('set fileformat=', 'cmdline')[2], 'mac')
+  if exists('+foldclose')
+    call assert_equal(getcompletion('set foldclose=', 'cmdline')[0], 'all')
+  endif
+  if exists('+foldmethod')
+    call assert_equal(getcompletion('set foldmethod=', 'cmdline')[1], 'expr')
+  endif
+  if exists('+foldopen')
+    call assert_equal(getcompletion('set foldopen=', 'cmdline')[1], 'all')
+  endif
+  call assert_equal(getcompletion('set jumpoptions=', 'cmdline')[0], 'stack')
+  call assert_equal(getcompletion('set keymodel=', 'cmdline')[1], 'stopsel')
+  call assert_equal(getcompletion('set lispoptions=', 'cmdline')[1], 'expr:1')
+  call assert_match('popup', getcompletion('set mousemodel=', 'cmdline')[2])
+  call assert_equal(getcompletion('set nrformats=', 'cmdline')[1], 'bin')
+  if exists('+rightleftcmd')
+    call assert_equal(getcompletion('set rightleftcmd=', 'cmdline')[0], 'search')
+  endif
+  call assert_equal(getcompletion('set scrollopt=', 'cmdline')[1], 'ver')
+  call assert_equal(getcompletion('set selection=', 'cmdline')[1], 'exclusive')
+  call assert_equal(getcompletion('set selectmode=', 'cmdline')[1], 'key')
+  if exists('+ssop')
+    call assert_equal(getcompletion('set ssop=', 'cmdline')[1], 'buffers')
+  endif
+  call assert_equal(getcompletion('set showcmdloc=', 'cmdline')[1], 'statusline')
+  if exists('+signcolumn')
+    call assert_equal(getcompletion('set signcolumn=', 'cmdline')[1], 'yes')
+  endif
+  if exists('+spelloptions')
+    call assert_equal(getcompletion('set spelloptions=', 'cmdline')[0], 'camel')
+  endif
+  if exists('+spellsuggest')
+    call assert_equal(getcompletion('set spellsuggest+=', 'cmdline')[0], 'best')
+  endif
+  call assert_equal(getcompletion('set splitkeep=', 'cmdline')[1], 'screen')
+  call assert_equal(getcompletion('set swapsync=', 'cmdline')[1], 'sync')
+  call assert_equal(getcompletion('set switchbuf=', 'cmdline')[1], 'usetab')
+  call assert_equal(getcompletion('set tagcase=', 'cmdline')[1], 'ignore')
+  if exists('+termwintype')
+    call assert_equal(getcompletion('set termwintype=', 'cmdline')[1], 'conpty')
+  endif
+  if exists('+toolbar')
+    call assert_equal(getcompletion('set toolbar=', 'cmdline')[1], 'text')
+  endif
+  if exists('+tbis')
+    call assert_equal(getcompletion('set tbis=', 'cmdline')[2], 'medium')
+  endif
+  if exists('+ttymouse')
+    set ttymouse=
+    call assert_equal(getcompletion('set ttymouse=', 'cmdline')[1], 'xterm2')
+    set ttymouse&
+  endif
+  call assert_equal(getcompletion('set virtualedit=', 'cmdline')[1], 'insert')
+  call assert_equal(getcompletion('set wildmode=', 'cmdline')[1], 'longest')
+  call assert_equal(getcompletion('set wildmode=list,longest:', 'cmdline')[0], 'full')
+  call assert_equal(getcompletion('set wildoptions=', 'cmdline')[1], 'tagfile')
+  if exists('+winaltkeys')
+    call assert_equal(getcompletion('set winaltkeys=', 'cmdline')[1], 'yes')
+  endif
+
+  " Other string options that queries the system rather than fixed enum names
+  call assert_equal(getcompletion('set eventignore=', 'cmdline')[0:1], ['all', 'BufAdd'])
+  call assert_equal(getcompletion('set fileencodings=', 'cmdline')[1], 'latin1')
+  call assert_equal(getcompletion('set printoptions=', 'cmdline')[0], 'top')
+  call assert_equal(getcompletion('set wincolor=', 'cmdline')[0], 'SpecialKey')
+
+  call assert_equal(getcompletion('set listchars+=', 'cmdline')[0], 'eol')
+  call assert_equal(getcompletion('setl listchars+=', 'cmdline')[0], 'eol')
+  call assert_equal(getcompletion('set fillchars+=', 'cmdline')[0], 'stl')
+  call assert_equal(getcompletion('setl fillchars+=', 'cmdline')[0], 'stl')
+
+  "
+  " Unique string options below
+  "
+
+  " keyprotocol: only auto-complete when after ':' with known protocol types
+  call assert_equal(getcompletion('set keyprotocol=', 'cmdline'), [&keyprotocol])
+  call feedkeys(":set keyprotocol+=someterm:m\<Tab>\<C-B>\"\<CR>", 'xt')
+  call assert_equal('"set keyprotocol+=someterm:mok2', @:)
+  set keyprotocol&
+
+  " previewpopup / completepopup
+  call assert_equal(getcompletion('set previewpopup=', 'cmdline')[0], 'height:')
+  call assert_equal(getcompletion('set previewpopup=highlight:End*Buffer', 'cmdline')[0], 'EndOfBuffer')
+  call feedkeys(":set previewpopup+=border:\<Tab>\<C-B>\"\<CR>", 'xt')
+  call assert_equal('"set previewpopup+=border:on', @:)
+  call feedkeys(":set completepopup=height:10,align:\<Tab>\<C-B>\"\<CR>", 'xt')
+  call assert_equal('"set completepopup=height:10,align:item', @:)
+  call assert_equal(getcompletion('set completepopup=bogusname:', 'cmdline'), [])
+  set previewpopup& completepopup&
+
+  " diffopt: special handling of algorithm:<alg_list>
+  call assert_equal(getcompletion('set diffopt+=', 'cmdline')[0], 'filler')
+  call assert_equal(getcompletion('set diffopt+=iblank,foldcolumn:', 'cmdline'), [])
+  call assert_equal(getcompletion('set diffopt+=iblank,algorithm:pat*', 'cmdline')[0], 'patience')
+
+  " highlight: special parsing, including auto-completing highlight groups
+  " after ':'
+  call assert_equal(getcompletion('set hl=', 'cmdline')[0:1], [&hl, '8'])
+  call assert_equal(getcompletion('set hl+=', 'cmdline')[0], '8')
+  call assert_equal(getcompletion('set hl+=8', 'cmdline')[0:2], ['8:', '8b', '8i'])
+  call assert_equal(getcompletion('set hl+=8b', 'cmdline')[0], '8bi')
+  call assert_equal(getcompletion('set hl+=8:No*ext', 'cmdline')[0], 'NonText')
+  " If all the display modes are used up we should be suggesting nothing. Make
+  " a hl typed option with all the modes which will look like '8bi-nrsuc2d=t',
+  " and make sure nothing is suggested from that.
+  let hl_display_modes = join(
+        \ filter(map(getcompletion('set hl+=8', 'cmdline'),
+        \            {idx, val -> val[1]}),
+        \        {idx, val -> val != ':'}),
+        \ '')
+  call assert_equal(getcompletion('set hl+=8'..hl_display_modes, 'cmdline'), [])
+
+  "
+  " Test flag lists
+  "
+
+  " Test set=. Show the original value if nothing is typed after '='.
+  " Otherwise, the list should avoid showing what's already typed.
+  set mouse=v
+  call assert_equal(getcompletion('set mouse=', 'cmdline'), ['v','a','n','i','c','h','r'])
+  set mouse=nvi
+  call assert_equal(getcompletion('set mouse=', 'cmdline'), ['nvi','a','n','v','i','c','h','r'])
+  call assert_equal(getcompletion('set mouse=hn', 'cmdline'), ['a','v','i','c','r'])
+
+  " Test set+=. Never show original value, and it also tries to avoid listing
+  " flags that's already in the option value.
+  call assert_equal(getcompletion('set mouse+=', 'cmdline'), ['a','c','h','r'])
+  call assert_equal(getcompletion('set mouse+=hn', 'cmdline'), ['a','c','r'])
+  call assert_equal(getcompletion('set mouse+=acrhn', 'cmdline'), [])
+
+  " Test that the position of the expansion is correct (even if there are
+  " additional values after the current cursor)
+  call feedkeys(":set mouse=hn\<Left>\<Tab>\<C-B>\"\<CR>", 'xt')
+  call assert_equal('"set mouse=han', @:)
+  set mouse&
+
+  " Test that other flag list options have auto-complete, but don't
+  " exhaustively validate their results.
+  if exists('+concealcursor')
+    call assert_equal(getcompletion('set cocu=', 'cmdline')[0], 'n')
+  endif
+  call assert_equal(getcompletion('set cpo=', 'cmdline')[1], 'a')
+  call assert_equal(getcompletion('set fo=', 'cmdline')[1], 't')
+  if exists('+guioptions')
+    call assert_equal(getcompletion('set go=', 'cmdline')[1], '!')
+  endif
+  call assert_equal(getcompletion('set shortmess=', 'cmdline')[1], 'r')
+  call assert_equal(getcompletion('set whichwrap=', 'cmdline')[1], 'b')
+
+  "
+  "Test set-=
+  "
+
+  " Normal single-value option just shows the existing value
+  set ambiwidth=double
+  call assert_equal(getcompletion('set ambw-=', 'cmdline'), ['double'])
+  set ambiwidth&
+
+  " Works on numbers and term options as well
+  call assert_equal(getcompletion('set laststatus-=', 'cmdline'), [string(&laststatus)])
+  set t_Ce=testCe
+  call assert_equal(getcompletion('set t_Ce-=', 'cmdline'), ['testCe'])
+  set t_Ce&
+
+  " Comma-separated lists should present each option
+  set diffopt=context:123,,,,,iblank,iwhiteall
+  call assert_equal(getcompletion('set diffopt-=', 'cmdline'), ['context:123', 'iblank', 'iwhiteall'])
+  call assert_equal(getcompletion('set diffopt-=*n*', 'cmdline'), ['context:123', 'iblank'])
+  call assert_equal(getcompletion('set diffopt-=i', 'cmdline'), ['iblank', 'iwhiteall'])
+  " Don't present more than one option as it doesn't make sense in set-=
+  call assert_equal(getcompletion('set diffopt-=iblank,', 'cmdline'), [])
+  " Test empty option
+  set diffopt=
+  call assert_equal(getcompletion('set diffopt-=', 'cmdline'), [])
+  set diffopt&
+
+  " Test escaping output
+  call assert_equal(getcompletion('set fillchars-=', 'cmdline')[0], 'vert:\|')
+
+  " Test files with commas in name are being parsed and escaped properly
+  set path=has\\\ space,file\\,with\\,comma,normal_file
+  if exists('+completeslash')
+    call assert_equal(getcompletion('set path-=', 'cmdline'), ['has\\\ space', 'file\,with\,comma', 'normal_file'])
+  else
+    call assert_equal(getcompletion('set path-=', 'cmdline'), ['has\\\ space', 'file\\,with\\,comma', 'normal_file'])
+  endif
+  set path&
+
+  " Flag list should present orig value, then individual flags
+  set mouse=v
+  call assert_equal(getcompletion('set mouse-=', 'cmdline'), ['v'])
+  set mouse=avn
+  call assert_equal(getcompletion('set mouse-=', 'cmdline'), ['avn','a','v','n'])
+  " Don't auto-complete when we have at least one flags already
+  call assert_equal(getcompletion('set mouse-=n', 'cmdline'), [])
+  " Test empty option
+  set mouse=
+  call assert_equal(getcompletion('set mouse-=', 'cmdline'), [])
+  set mouse&
+
+  " 'whichwrap' is an odd case where it's both flag list and comma-separated
+  set ww=b,h
+  call assert_equal(getcompletion('set ww-=', 'cmdline'), ['b','h'])
+  set ww&
+endfunc
+
 func Test_set_option_errors()
   call assert_fails('set scroll=-1', 'E49:')
   call assert_fails('set backupcopy=', 'E474:')
@@ -1447,7 +1723,7 @@ func Test_opt_cdhome()
   set cdhome&
 endfunc
 
-func Test_set_completion_2()
+func Test_set_completion_fuzzy()
   CheckOption termguicolors
 
   " Test default option completion
--- a/src/version.c
+++ b/src/version.c
@@ -700,6 +700,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    1958,
+/**/
     1957,
 /**/
     1956,
--- a/src/vim.h
+++ b/src/vim.h
@@ -822,6 +822,8 @@ extern int (*dyn_libintl_wputenv)(const 
 #define EXPAND_BREAKPOINT	51
 #define EXPAND_SCRIPTNAMES	52
 #define EXPAND_RUNTIME		53
+#define EXPAND_STRING_SETTING	54
+#define EXPAND_SETTING_SUBTRACT	55
 
 // Values for exmode_active (0 is no exmode)
 #define EXMODE_NORMAL		1
@@ -2330,6 +2332,20 @@ typedef enum {
  */
 typedef char *(*opt_did_set_cb_T)(optset_T *args);
 
+/*
+ * 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_u ***matches);
+
 // Flags for assignment functions.
 #define ASSIGN_VAR	0     // ":var" (nothing special)
 #define ASSIGN_FINAL	0x01  // ":final"