static void showoptions(int all, int opt_flags);
static int optval_default(struct vimoption *, char_u *varp, int compatible);
static void showoneopt(struct vimoption *, int opt_flags);
-static int put_setstring(FILE *fd, char *cmd, char *name, char_u **valuep, long_u flags);
+static int put_setstring(FILE *fd, bool legacy, char *cmd, char *name, char_u **valuep, long_u flags);
static int put_setnum(FILE *fd, char *cmd, char *name, long *valuep);
static int put_setbool(FILE *fd, char *cmd, char *name, int value);
static int istermoption(struct vimoption *p);
}
}
+/*
+ * Returns true if an option value will be evaluated as a Vim9 script.
+ * Checks stored script context for the option. For options that have values in
+ * multiple contexts, opt_flags selects the desired context with OPT_GLOBAL, or
+ * OPT_LOCAL, selecting global or buffer/window context, respectively.
+ */
+ bool
+is_option_value_vim9(int opt_idx, int opt_flags)
+{
+ int indir = (int)options[opt_idx].indir;
+ sctx_T *sctx = NULL;
+
+ if ((opt_flags & OPT_GLOBAL) || (indir & (PV_BUF|PV_WIN)) == 0)
+ sctx = &options[opt_idx].script_ctx;
+
+ if ((opt_flags & OPT_LOCAL) || (indir & (PV_BUF|PV_WIN)))
+ {
+ if (indir & PV_BUF)
+ sctx = &curbuf->b_p_script_ctx[indir & PV_MASK];
+ else if (indir & PV_WIN)
+ sctx = &curwin->w_p_script_ctx[indir & PV_MASK];
+ }
+
+ // If "sc_sid" is not set, it means the option value was not modified from
+ // the default yet. As we move towards Vim9 all the default values should
+ // be valid Vim9.
+ return !SCRIPT_ID_VALID(sctx->sc_sid) ||
+ sctx->sc_version >= SCRIPT_VERSION_VIM9;
+}
+
/*
* Get the script context of global option "name".
*
else // P_STRING
{
int do_endif = FALSE;
+ bool legacy;
+
+#ifdef FEAT_EVAL
+ legacy = !is_option_value_vim9(p - &options[0],
+ round == 1 ? opt_flags | OPT_GLOBAL : OPT_LOCAL);
+#else
+ // I think none of the options that require expression
+ // evaluation will be present if expression evaluation is
+ // disabled. For example, 'includeexpr' is set to NULL if
+ // FEAT_EVAL is not present. So it should be safe to use
+ // normal "set" and "setlocal" in the session file.
+ legacy = false;
+#endif
// Don't set 'syntax' and 'filetype' again if the value is
// already right, avoids reloading the syntax file.
return FAIL;
do_endif = TRUE;
}
- if (put_setstring(fd, cmd, p->fullname, (char_u **)varp,
- p->flags) == FAIL)
+ if (put_setstring(fd, legacy, cmd, p->fullname,
+ (char_u **)varp, p->flags) == FAIL)
return FAIL;
if (do_endif)
{
int
makefoldset(FILE *fd)
{
- if (put_setstring(fd, "setlocal", "fdm", &curwin->w_p_fdm, 0) == FAIL
# ifdef FEAT_EVAL
- || put_setstring(fd, "setlocal", "fde", &curwin->w_p_fde, 0)
+ sctx_T *fde_script_ctx = &curwin->w_p_script_ctx[WV_FDE];
+ // Similarly to the check in is_option_value_vim9(), if "sc_id" is not set
+ // we have a default value, so it is safe to avoid the ":legacy" prefix.
+ bool fde_is_legacy = SCRIPT_ID_VALID(fde_script_ctx->sc_sid)
+ && fde_script_ctx->sc_version < SCRIPT_VERSION_VIM9;
+# endif
+
+ if (put_setstring(fd, false, "setlocal", "fdm", &curwin->w_p_fdm, 0)
== FAIL
+# ifdef FEAT_EVAL
+ || put_setstring(fd, fde_is_legacy, "setlocal", "fde",
+ &curwin->w_p_fde, 0) == FAIL
# endif
- || put_setstring(fd, "setlocal", "fmr", &curwin->w_p_fmr, 0)
+ || put_setstring(fd, false, "setlocal", "fmr", &curwin->w_p_fmr, 0)
== FAIL
- || put_setstring(fd, "setlocal", "fdi", &curwin->w_p_fdi, 0)
+ || put_setstring(fd, false, "setlocal", "fdi", &curwin->w_p_fdi, 0)
== FAIL
|| put_setnum(fd, "setlocal", "fdl", &curwin->w_p_fdl) == FAIL
|| put_setnum(fd, "setlocal", "fml", &curwin->w_p_fml) == FAIL
static int
put_setstring(
FILE *fd,
+ bool legacy,
char *cmd,
char *name,
char_u **valuep,
char_u *part = NULL;
char_u *p;
+ if (legacy && fprintf(fd, "legacy ") < 0)
+ return FAIL;
if (fprintf(fd, "%s %s=", cmd, name) < 0)
return FAIL;
if (*valuep != NULL)
p = buf;
while (*p != NUL)
{
+ if (legacy && fprintf(fd, "legacy ") < 0)
+ return FAIL;
// for each comma separated option part, append value to
// the option, :set rtp+=value
if (fprintf(fd, "%s %s+=", cmd, name) < 0)
" determine expected value
let expected=split(&rtp, ',')
- let expected = map(expected, '"set runtimepath+=".v:val')
- let expected = ['set runtimepath='] + expected
+ let expected = map(expected, '"legacy set runtimepath+=".v:val')
+ let expected = ['legacy set runtimepath='] + expected
let expected = map(expected, {v,w -> substitute(w, $HOME, "~", "g")})
mksession! Xtest_mks.out
set wildcharm=<F7>
call assert_fails('mkvimrc Xtestvimrc', 'E189: "Xtestvimrc" exists')
mkvimrc! Xtestvimrc
- call assert_notequal(-1, index(readfile('Xtestvimrc'), 'set pastetoggle=<F5>'))
- call assert_notequal(-1, index(readfile('Xtestvimrc'), 'set wildchar=<F6>'))
- call assert_notequal(-1, index(readfile('Xtestvimrc'), 'set wildcharm=<F7>'))
+ let content = readfile('Xtestvimrc')
+ call assert_notequal(-1, index(content, 'legacy set pastetoggle=<F5>'))
+ call assert_notequal(-1, index(content, 'set wildchar=<F6>'))
+ call assert_notequal(-1, index(content, 'set wildcharm=<F7>'))
set pastetoggle& wildchar& wildcharm&
call delete('Xtestvimrc')
endfunc
+" Make sure options are marked as legacy in the session file when they are set
+" by the user from the command execution interface. Only applies to string
+" options.
+func Test_mksession_preserve_option_script_version_set_manually()
+
+ let orig_includeexpr = &includeexpr
+ defer execute('let &includeexpr = orig_includeexpr')
+
+ " Using "execute()" to simulate input from the user, rather than the option
+ " being set directly by the test script itself.
+ execute('set includeexpr=FooDefault(v:fname)')
+
+ " Try a few options, to make sure they all work correctly.
+ new
+ execute('legacy set includeexpr=FooLegacy(v:fname)')
+
+ new
+ execute('vim9 set includeexpr=FooVim9(v:fname)')
+
+ mksession! XDummySession.vim
+ defer delete('XDummySession.vim')
+
+ let session_content = readfile('XDummySession.vim')
+ " We expect that without an explicit script version specification the command
+ " line input is treated as legacy input.
+ call assert_notequal(-1, index(session_content,
+ \ 'legacy setlocal includeexpr=FooDefault(v:fname)'))
+ call assert_notequal(-1, index(session_content,
+ \ 'legacy setlocal includeexpr=FooLegacy(v:fname)'))
+ call assert_notequal(-1, index(session_content,
+ \ 'setlocal includeexpr=FooVim9(v:fname)'))
+endfunc
+
+" Make sure options preserve legacy/Vim9 script version when written into the
+" session file based on the original script that set those options. Only
+" applies to string options.
+func Test_mksession_preserve_option_script_version_set_from_script()
+
+ CheckFeature packages
+
+ const base = getcwd() . '/rtdir'
+ " clean up later
+ defer delete(base, 'rf')
+ let orig_packpath = &packpath
+ let &packpath .= ',' . base
+ defer execute('let &packpath = orig_packpath')
+
+ " Is used to disable file type plugins.
+ let g:Global_run_ftplugins = 1
+ defer execute('unlet g:Global_run_ftplugins')
+
+ " We are going to create two simple file type plugins - one Vim9 and one
+ " legacy. Each will just set the "includeexpr" locally in the buffer to some
+ " value.
+ const root = base . '/pack/test/opt/test_option_script_version'
+ call mkdir(root . '/ftplugin', 'p')
+
+ let vim9_ftplugin_sources =<< trim END
+ vim9script
+
+ if !get(g:, 'Global_run_ftplugins')
+ finish
+ endif
+
+ &l:include = 'vim9include'
+ &l:includeexpr = 'vim9fn(v:fname, 1)'
+ END
+ call writefile(vim9_ftplugin_sources, root . '/ftplugin/vim9test_lang.vim')
+
+ let legacy_ftplugin_sources =<< trim END
+ if !get(g:, 'Global_run_ftplugins')
+ finish
+ endif
+
+ setlocal include=legacyimport
+ setlocal includeexpr=legacyfn(v:fname,\".\")
+ END
+ call writefile(legacy_ftplugin_sources, root . '/ftplugin/legacytest_lang.vim')
+
+ let orig_runtimepath = &runtimepath
+ packadd test_option_script_version
+ defer execute('let &runtimepath = orig_runtimepath')
+
+ " Next we will create two buffers, and set "filetype" in each to match our two
+ " file type plugin names. The expectation is that "includeexpr" will be set
+ " to the correct value and a ":legacy" prefix will be used only when the
+ " original value was set by a legacy script.
+ filetype plugin on
+ set filetype=vim9test_lang
+
+ new
+ set filetype=legacytest_lang
+
+ mksession! XDummySession1.vim
+ defer delete('XDummySession1.vim')
+
+ " Check that the session file indeed contains correctly set "includeexpr"
+ " values.
+ let session1_content = readfile('XDummySession1.vim')
+ call assert_notequal(-1, index(session1_content,
+ \ 'setlocal include=vim9include'))
+ call assert_notequal(-1, index(session1_content,
+ \ 'setlocal includeexpr=vim9fn(v:fname,\ 1)'))
+ call assert_notequal(-1, index(session1_content,
+ \ 'legacy setlocal include=legacyimport'))
+ call assert_notequal(-1, index(session1_content,
+ \ 'legacy setlocal includeexpr=legacyfn(v:fname,\".\")'))
+
+ " Now disable our filetype plugins, restore the session file, save session
+ " again, and make sure that the options are still stored with correct Vim
+ " script version prefixes.
+
+ let g:Global_run_ftplugins = 0
+ %bwipe!
+
+ source XDummySession1.vim
+
+ mksession! XDummySession2.vim
+ defer delete('XDummySession2.vim')
+
+ " Check that the session file indeed contains correctly set "includeexpr"
+ " values.
+ let session2_content = readfile('XDummySession2.vim')
+ call assert_notequal(-1, index(session2_content,
+ \ 'setlocal include=vim9include'))
+ call assert_notequal(-1, index(session2_content,
+ \ 'setlocal includeexpr=vim9fn(v:fname,\ 1)'))
+ call assert_notequal(-1, index(session2_content,
+ \ 'legacy setlocal include=legacyimport'))
+ call assert_notequal(-1, index(session2_content,
+ \ 'legacy setlocal includeexpr=legacyfn(v:fname,\".\")'))
+endfunc
+
" 'winminwidth' restore must not fail when the session's saved 'winwidth' is
" smaller than the sourcing context's 'winminwidth'.
func Test_mksession_winminwidth()