Commit: patch 9.2.0579: :mksession, :mkview and :mkvimrc emit legacy Vim script

1 view
Skip to first unread message

Christian Brabandt

unread,
May 31, 2026, 5:15:13 PM (11 hours ago) May 31
to vim...@googlegroups.com
patch 9.2.0579: :mksession, :mkview and :mkvimrc emit legacy Vim script

Commit: https://github.com/vim/vim/commit/d69cf0dbcfdd721d94189ffa1558660c96129627
Author: Miguel Barro <miguel...@live.com>
Date: Sun May 31 21:03:12 2026 +0000

patch 9.2.0579: :mksession, :mkview and :mkvimrc emit legacy Vim script

Problem: :mksession, :mkview and :mkvimrc emit legacy Vim script
Solution: Generate vim9 script for those commands (Miguel Barro).

fixes: #16549
fixes: #16688
fixes: #19005
closes: #20152

Co-authored-by: Christian Brabandt <c...@256bit.org>
Signed-off-by: Miguel Barro <miguel...@live.com>
Signed-off-by: Christian Brabandt <c...@256bit.org>

diff --git a/runtime/doc/version9.txt b/runtime/doc/version9.txt
index 29882b4d5..2496d1b69 100644
--- a/runtime/doc/version9.txt
+++ b/runtime/doc/version9.txt
@@ -1,4 +1,4 @@
-*version9.txt* For Vim version 9.2. Last change: 2026 May 25
+*version9.txt* For Vim version 9.2. Last change: 2026 May 31


VIM REFERENCE MANUAL by Bram Moolenaar
@@ -52644,6 +52644,8 @@ Other ~
attribute to handle completion of single arguments with spaces as expected.
- Support %0{} in 'statusline' to insert the expression result verbatim and
not drop leading spaces |stl-%0{|.
+- Generated Session and View files are written in Vim9 script, see |:mksession|,
+ |:mkview| and |:mkvimrc|

Platform specific ~
-----------------
diff --git a/src/fold.c b/src/fold.c
index b9b54d8fc..19c444c14 100644
--- a/src/fold.c
+++ b/src/fold.c
@@ -3548,7 +3548,7 @@ put_folds(FILE *fd, win_T *wp)
{
if (put_line(fd, "silent! normal! zE") == FAIL
|| put_folds_recurse(fd, &wp->w_folds, (linenr_T)0) == FAIL
- || put_line(fd, "let &fdl = &fdl") == FAIL)
+ || put_line(fd, "&fdl = &fdl") == FAIL)
return FAIL;
}

@@ -3576,7 +3576,7 @@ put_folds_recurse(FILE *fd, garray_T *gap, linenr_T off)
// Do nested folds first, they will be created closed.
if (put_folds_recurse(fd, &fp->fd_nested, off + fp->fd_top) == FAIL)
return FAIL;
- if (fprintf(fd, "sil! %ld,%ldfold", fp->fd_top + off,
+ if (fprintf(fd, "sil! :%ld,%ldfold", fp->fd_top + off,
fp->fd_top + off + fp->fd_len - 1) < 0
|| put_eol(fd) == FAIL)
return FAIL;
@@ -3610,7 +3610,7 @@ put_foldopen_recurse(
{
// open nested folds while this fold is open
// ignore errors
- if (fprintf(fd, "%ld", fp->fd_top + off) < 0
+ if (fprintf(fd, ":%ld", fp->fd_top + off) < 0
|| put_eol(fd) == FAIL
|| put_line(fd, "sil! normal! zo") == FAIL)
return FAIL;
@@ -3651,7 +3651,7 @@ put_foldopen_recurse(
static int
put_fold_open_close(FILE *fd, fold_T *fp, linenr_T off)
{
- if (fprintf(fd, "%ld", fp->fd_top + off) < 0
+ if (fprintf(fd, ":%ld", fp->fd_top + off) < 0
|| put_eol(fd) == FAIL
|| fprintf(fd, "sil! normal! z%c",
fp->fd_flags == FD_CLOSED ? 'c' : 'o') < 0
diff --git a/src/map.c b/src/map.c
index 1c0d0ac7f..34e465ec3 100644
--- a/src/map.c
+++ b/src/map.c
@@ -2087,13 +2087,19 @@ makemap(
did_cpo = TRUE;
if (did_cpo)
{
- if (fprintf(fd, "let s:cpo_save=&cpo") < 0
+ if (fprintf(fd, "cpo_save = &cpo") < 0
|| put_eol(fd) < 0
|| fprintf(fd, "set cpo&vim") < 0
|| put_eol(fd) < 0)
return FAIL;
}
}
+#ifdef FEAT_EVAL
+ // If it is not vim9 use legacy
+ if (mp->m_expr && mp->m_script_ctx.sc_version < SCRIPT_VERSION_VIM9
+ && fputs("legacy ", fd) < 0)
+ return FAIL;
+#endif
if (c1 && putc(c1, fd) < 0)
return FAIL;
if (mp->m_noremap != REMAP_YES && fprintf(fd, "nore") < 0)
@@ -2128,9 +2134,7 @@ makemap(
}

if (did_cpo)
- if (fprintf(fd, "let &cpo=s:cpo_save") < 0
- || put_eol(fd) < 0
- || fprintf(fd, "unlet s:cpo_save") < 0
+ if (fprintf(fd, "&cpo = cpo_save") < 0
|| put_eol(fd) < 0)
return FAIL;
return OK;
diff --git a/src/session.c b/src/session.c
index eed03269a..090448937 100644
--- a/src/session.c
+++ b/src/session.c
@@ -105,7 +105,7 @@ ses_arglist(

if (fputs(cmd, fd) < 0 || put_eol(fd) == FAIL)
return FAIL;
- if (put_line(fd, "%argdel") == FAIL)
+ if (put_line(fd, ":%argdel") == FAIL)
return FAIL;
for (i = 0; i < gap->ga_len; ++i)
{
@@ -122,7 +122,7 @@ ses_arglist(
s = buf;
}
}
- if (fputs("$argadd ", fd) < 0
+ if (fputs(":$argadd ", fd) < 0
|| ses_put_fname(fd, s, flagp) == FAIL
|| put_eol(fd) == FAIL)
{
@@ -220,7 +220,7 @@ ses_win_rec(FILE *fd, frame_T *fr)

// Go back to the first window.
if (count > 0 && (fprintf(fd, fr->fr_layout == FR_COL
- ? "%dwincmd k" : "%dwincmd h", count) < 0
+ ? ":%dwincmd k" : ":%dwincmd h", count) < 0
|| put_eol(fd) == FAIL))
return FAIL;

@@ -259,14 +259,14 @@ ses_winsizes(
// restore height when not full height
if (wp->w_height + wp->w_status_height < topframe->fr_height
&& (fprintf(fd,
- "exe '%dresize ' . ((&lines * %ld + %ld) / %ld)",
+ "exe ':%dresize ' .. ((&lines * %ld + %ld) / %ld)",
n, (long)wp->w_height, Rows / 2, Rows) < 0
|| put_eol(fd) == FAIL))
return FAIL;

// restore width when not full width
if (wp->w_width < Columns && (fprintf(fd,
- "exe 'vert %dresize ' . ((&columns * %ld + %ld) / %ld)",
+ "exe 'vert :%dresize ' .. ((&columns * %ld + %ld) / %ld)",
n, (long)wp->w_width, Columns / 2, Columns) < 0
|| put_eol(fd) == FAIL))
return FAIL;
@@ -339,7 +339,7 @@ put_view(
if (wp->w_arg_idx != current_arg_idx && wp->w_arg_idx < WARGCOUNT(wp)
&& flagp == &ssop_flags)
{
- if (fprintf(fd, "%ldargu", (long)wp->w_arg_idx + 1) < 0
+ if (fprintf(fd, ":%ldargu", (long)wp->w_arg_idx + 1) < 0
|| put_eol(fd) == FAIL)
return FAIL;
did_next = TRUE;
@@ -469,29 +469,32 @@ put_view(

// Restore the cursor line in the file and relatively in the
// window. Don't use "G", it changes the jumplist.
+ if (put_line(fd, "{") == FAIL)
+ return FAIL;
+
if (wp->w_height <= 0)
{
- if (fprintf(fd, "let s:l = %ld", (long)wp->w_cursor.lnum) < 0)
+ if (fprintf(fd, " var l: number = %ld", (long)wp->w_cursor.lnum) < 0)
return FAIL;
}
else if (fprintf(fd,
- "let s:l = %ld - ((%ld * winheight(0) + %ld) / %ld)",
+ " var l: number = %ld - ((%ld * winheight(0) + %ld) / %ld)",
(long)wp->w_cursor.lnum,
(long)(wp->w_cursor.lnum - wp->w_topline),
(long)wp->w_height / 2, (long)wp->w_height) < 0)
return FAIL;

if (put_eol(fd) == FAIL
- || put_line(fd, "if s:l < 1 | let s:l = 1 | endif") == FAIL
- || put_line(fd, "keepjumps exe s:l") == FAIL
- || put_line(fd, "normal! zt") == FAIL
- || fprintf(fd, "keepjumps %ld", (long)wp->w_cursor.lnum) < 0
+ || put_line(fd, " if l < 1 | l = 1 | endif") == FAIL
+ || put_line(fd, " keepjumps exe \":\" .. l") == FAIL
+ || put_line(fd, " normal! zt") == FAIL
+ || fprintf(fd, " keepjumps :%ld", (long)wp->w_cursor.lnum) < 0
|| put_eol(fd) == FAIL)
return FAIL;
// Restore the cursor column and left offset when not wrapping.
if (wp->w_cursor.col == 0)
{
- if (put_line(fd, "normal! 0") == FAIL)
+ if (put_line(fd, " normal! 0") == FAIL)
return FAIL;
}
else
@@ -499,24 +502,27 @@ put_view(
if (!wp->w_p_wrap && wp->w_leftcol > 0 && wp->w_width > 0)
{
if (fprintf(fd,
- "let s:c = %ld - ((%ld * winwidth(0) + %ld) / %ld)",
+ " var c: number = %ld - ((%ld * winwidth(0) + %ld) / %ld)",
(long)wp->w_virtcol + 1,
(long)(wp->w_virtcol - wp->w_leftcol),
(long)wp->w_width / 2, (long)wp->w_width) < 0
|| put_eol(fd) == FAIL
- || put_line(fd, "if s:c > 0") == FAIL
+ || put_line(fd, " if c > 0") == FAIL
|| fprintf(fd,
- " exe 'normal! ' . s:c . '|zs' . %ld . '|'",
+ " exe 'normal! ' .. c .. '|zs' .. %ld .. '|'",
(long)wp->w_virtcol + 1) < 0
|| put_eol(fd) == FAIL
- || put_line(fd, "else") == FAIL
- || put_view_curpos(fd, wp, " ") == FAIL
- || put_line(fd, "endif") == FAIL)
+ || put_line(fd, " else") == FAIL
+ || put_view_curpos(fd, wp, " ") == FAIL
+ || put_line(fd, " endif") == FAIL)
return FAIL;
}
- else if (put_view_curpos(fd, wp, "") == FAIL)
+ else if (put_view_curpos(fd, wp, " ") == FAIL)
return FAIL;
}
+
+ if (put_line(fd, "}") == FAIL)
+ return FAIL;
}

// Local directory, if the current flag is not view options or the "curdir"
@@ -566,7 +572,7 @@ store_session_globals(FILE *fd)
*t = 'n';
else if (*t == '
')
*t = 'r';
- if ((fprintf(fd, "let %s = %c%s%c",
+ if ((fprintf(fd, "g:%s = %c%s%c",
this_var->di_key,
(this_var->di_tv.v_type == VAR_STRING) ? '"'
: ' ',
@@ -591,7 +597,7 @@ store_session_globals(FILE *fd)
f = -f;
sign = '-';
}
- if ((fprintf(fd, "let %s = %c%f",
+ if ((fprintf(fd, "g:%s = %c%f",
this_var->di_key, sign, f) < 0)
|| put_eol(fd) == FAIL)
return FAIL;
@@ -638,12 +644,21 @@ makeopens(
// Begin by setting the this_session variable, and then other
// sessionable variables.
# ifdef FEAT_EVAL
- if (put_line(fd, "let v:this_session=expand(\"<sfile>:p\")") == FAIL)
+
+ if (put_line(fd, "v:this_session = expand(\"<sfile>:p\")") == FAIL)
goto fail;

if (put_line(fd, "doautoall SessionLoadPre") == FAIL)
goto fail;

+ if (put_line(fd, "var save_splitbelow: bool") == FAIL
+ || put_line(fd, "var save_splitright: bool") == FAIL
+ || put_line(fd, "var save_winminheight: number") == FAIL
+ || put_line(fd, "var save_winminwidth: number") == FAIL
+ || put_line(fd, "var wipebuf: number = -1") == FAIL
+ || put_line(fd, "var shortmess_save: string") == FAIL)
+ goto fail;
+
if (ssop_flags & SSOP_GLOBALS)
if (store_session_globals(fd) == FAIL)
goto fail;
@@ -659,7 +674,7 @@ makeopens(
// Now a :cd command to the session directory or the current directory
if (ssop_flags & SSOP_SESDIR)
{
- if (put_line(fd, "exe \"cd \" . escape(expand(\"<sfile>:p:h\"), ' ')")
+ if (put_line(fd, "exe \"cd \" .. escape(expand(\"<sfile>:p:h\"), ' ')")
== FAIL)
goto fail;
}
@@ -681,14 +696,14 @@ makeopens(
// Remember the buffer number.
if (put_line(fd, "if expand('%') == '' && !&modified && line('$') <= 1 && getline(1) == ''") == FAIL)
goto fail;
- if (put_line(fd, " let s:wipebuf = bufnr('%')") == FAIL)
+ if (put_line(fd, " wipebuf = bufnr('%')") == FAIL)
goto fail;
if (put_line(fd, "endif") == FAIL)
goto fail;

// Save 'shortmess' if not storing options.
if ((ssop_flags & SSOP_OPTIONS) == 0
- && put_line(fd, "let s:shortmess_save = &shortmess") == FAIL)
+ && put_line(fd, "shortmess_save = &shortmess") == FAIL)
goto fail;

// Set 'shortmess' for the following.
@@ -834,16 +849,16 @@ makeopens(
if (tab_topframe->fr_layout != FR_LEAF)
{
// Save current window layout.
- if (put_line(fd, "let s:save_splitbelow = &splitbelow") == FAIL
- || put_line(fd, "let s:save_splitright = &splitright")
+ if (put_line(fd, "save_splitbelow = &splitbelow") == FAIL
+ || put_line(fd, "save_splitright = &splitright")
== FAIL)
goto fail;
if (put_line(fd, "set splitbelow splitright") == FAIL)
goto fail;
if (ses_win_rec(fd, tab_topframe) == FAIL)
goto fail;
- if (put_line(fd, "let &splitbelow = s:save_splitbelow") == FAIL
- || put_line(fd, "let &splitright = s:save_splitright")
+ if (put_line(fd, "&splitbelow = save_splitbelow") == FAIL
+ || put_line(fd, "&splitright = save_splitright")
== FAIL)
goto fail;
}
@@ -874,8 +889,8 @@ makeopens(
// cursor can be set. This is done again below.
// winminheight and winminwidth need to be set to avoid an error if
// the user has set winheight or winwidth.
- if (put_line(fd, "let s:save_winminheight = &winminheight") == FAIL
- || put_line(fd, "let s:save_winminwidth = &winminwidth")
+ if (put_line(fd, "save_winminheight = &winminheight") == FAIL
+ || put_line(fd, "save_winminwidth = &winminwidth")
== FAIL)
goto fail;
if (put_line(fd, "set winminheight=0") == FAIL
@@ -925,7 +940,7 @@ makeopens(
cur_arg_idx = next_arg_idx;

// Restore cursor to the current window if it's not the first one.
- if (cnr > 1 && (fprintf(fd, "%dwincmd w", cnr) < 0
+ if (cnr > 1 && (fprintf(fd, ":%dwincmd w", cnr) < 0
|| put_eol(fd) == FAIL))
goto fail;

@@ -950,15 +965,13 @@ makeopens(
goto fail;

// Wipe out an empty unnamed buffer we started in.
- if (put_line(fd, "if exists('s:wipebuf') && len(win_findbuf(s:wipebuf)) == 0")
+ if (put_line(fd, "if wipebuf != -1 && len(win_findbuf(wipebuf)) == 0")
== FAIL)
goto fail;
- if (put_line(fd, " silent exe 'bwipe ' . s:wipebuf") == FAIL)
+ if (put_line(fd, " silent exe 'bwipe ' .. wipebuf") == FAIL)
goto fail;
if (put_line(fd, "endif") == FAIL)
goto fail;
- if (put_line(fd, "unlet! s:wipebuf") == FAIL)
- goto fail;

// Re-apply 'winheight' and 'winwidth'.
if (fprintf(fd, "set winheight=%ld winwidth=%ld",
@@ -973,22 +986,22 @@ makeopens(
}
else
{
- if (put_line(fd, "let &shortmess = s:shortmess_save") == FAIL)
+ if (put_line(fd, "&shortmess = shortmess_save") == FAIL)
goto fail;
}

if (restore_height_width)
{
// Restore 'winminheight' and 'winminwidth'.
- if (put_line(fd, "let &winminheight = s:save_winminheight") == FAIL
- || put_line(fd, "let &winminwidth = s:save_winminwidth") == FAIL)
+ if (put_line(fd, "&winminheight = save_winminheight") == FAIL
+ || put_line(fd, "&winminwidth = save_winminwidth") == FAIL)
goto fail;
}

// Lastly, execute the x.vim file if it exists.
- if (put_line(fd, "let s:sx = expand(\"<sfile>:p:r\").\"x.vim\"") == FAIL
- || put_line(fd, "if filereadable(s:sx)") == FAIL
- || put_line(fd, " exe \"source \" . fnameescape(s:sx)") == FAIL
+ if (put_line(fd, "var sx: string = expand(\"<sfile>:p:r\") .. \"x.vim\"") == FAIL
+ || put_line(fd, "if filereadable(sx)") == FAIL
+ || put_line(fd, " exe \"source \" .. fnameescape(sx)") == FAIL
|| put_line(fd, "endif") == FAIL)
goto fail;

@@ -1137,9 +1150,9 @@ write_session_file(char_u *filename)
fd = open_exfile(filename, TRUE, APPENDBIN);

failed = (fd == NULL
- || put_line(fd, "let v:this_session = Save_VV_this_session")
+ || put_line(fd, "v:this_session = g:Save_VV_this_session")
== FAIL
- || put_line(fd, "unlet Save_VV_this_session") == FAIL);
+ || put_line(fd, "unlet g:Save_VV_this_session") == FAIL);

if (fd != NULL && fclose(fd) != 0)
failed = TRUE;
@@ -1177,6 +1190,10 @@ ex_mkrc(exarg_T *eap)
char_u *viewFile = NULL;
unsigned *flagp;
#endif
+#if defined(FEAT_EVAL)
+ int sid;
+ scriptitem_T *si = NULL;
+#endif

if (eap->cmdidx == CMD_mksession || eap->cmdidx == CMD_mkview)
{
@@ -1258,6 +1275,10 @@ ex_mkrc(exarg_T *eap)
mksession_nl = TRUE;
#endif

+ // Enforce vim9script
+ if (put_line(fd, "vim9script") == FAIL)
+ failed = TRUE;
+
// Write the version command for :mkvimrc
if (eap->cmdidx == CMD_mkvimrc)
(void)put_line(fd, "version 6.0");
@@ -1265,7 +1286,7 @@ ex_mkrc(exarg_T *eap)
#ifdef FEAT_SESSION
if (eap->cmdidx == CMD_mksession)
{
- if (put_line(fd, "let SessionLoad = 1") == FAIL)
+ if (put_line(fd, "g:SessionLoad = 1") == FAIL)
failed = TRUE;
}

@@ -1283,23 +1304,47 @@ ex_mkrc(exarg_T *eap)
#ifdef FEAT_SESSION
if (!view_session
|| (eap->cmdidx == CMD_mksession
- && (*flagp & SSOP_OPTIONS)))
+ && (*flagp & (SSOP_OPTIONS | SSOP_LOCALOPTIONS))))
#endif
{
+ bool do_mappings = true;
int flags = OPT_GLOBAL;

#ifdef FEAT_SESSION
- if (eap->cmdidx == CMD_mksession && (*flagp & SSOP_SKIP_RTP))
- flags |= OPT_SKIPRTP;
+ failed |= put_line(fd, "var cpo_save: string") == FAIL;
+
+ if (eap->cmdidx == CMD_mksession)
+ {
+ if (*flagp & SSOP_SKIP_RTP)
+ flags |= OPT_SKIPRTP;
+
+ // SSOP_LOCALOPTIONS requires only local mappings
+ do_mappings = *flagp & SSOP_OPTIONS;
+ }
#endif
- failed |= (makemap(fd, NULL) == FAIL
+
+ if (do_mappings)
+ failed |= (makemap(fd, NULL) == FAIL
|| makeset(fd, flags, FALSE) == FAIL);
+
+#if defined(FEAT_EVAL)
+ // Save delay load import modules.
+ // Either SSOP_LOCALOPTIONS or SSOP_OPTIONS require them
+ for (sid = 1; sid <= script_items.ga_len; ++sid)
+ {
+ si = SCRIPT_ITEM(sid);
+ if (si->sn_autoload_prefix &&
+ (fprintf(fd, "import autoload '%s'", si->sn_name) < 0 ||
+ put_eol(fd) == FAIL))
+ failed = TRUE;
+ }
+#endif
}

#ifdef FEAT_SESSION
if (!failed && view_session)
{
- if (put_line(fd, "let s:so_save = &g:so | let s:siso_save = &g:siso | setg so=0 siso=0 | setl so=-1 siso=-1") == FAIL)
+ if (put_line(fd, "const so_save: number = &g:so | const siso_save: number = &g:siso | setg so=0 siso=0 | setl so=-1 siso=-1") == FAIL)
failed = TRUE;
if (eap->cmdidx == CMD_mksession)
{
@@ -1341,11 +1386,11 @@ ex_mkrc(exarg_T *eap)
}
else
{
+ failed |= put_line(fd, "var cpo_save: string") == FAIL;
failed |= (put_view(fd, curwin, curtab, !using_vdir, flagp, -1,
NULL) == FAIL);
}
- if (put_line(fd, "let &g:so = s:so_save | let &g:siso = s:siso_save")
- == FAIL)
+ if (put_line(fd, "&g:so = so_save | &g:siso = siso_save") == FAIL)
failed = TRUE;
# ifdef FEAT_SEARCH_EXTRA
if (no_hlsearch && put_line(fd, "nohlsearch") == FAIL)
@@ -1355,12 +1400,13 @@ ex_mkrc(exarg_T *eap)
failed = TRUE;
if (eap->cmdidx == CMD_mksession)
{
- if (put_line(fd, "unlet SessionLoad") == FAIL)
+ if (put_line(fd, "unlet g:SessionLoad") == FAIL)
failed = TRUE;
}
}
#endif
- if (put_line(fd, "\" vim: set ft=vim :") == FAIL)
+
+ if (put_line(fd, "# vim: set ft=vim :") == FAIL)
failed = TRUE;

failed |= fclose(fd);
diff --git a/src/testdir/test_autocmd.vim b/src/testdir/test_autocmd.vim
index ecd5791c9..a1f282e06 100644
--- a/src/testdir/test_autocmd.vim
+++ b/src/testdir/test_autocmd.vim
@@ -1199,10 +1199,10 @@ endfunc
" Closing a window might cause an endless loop
" E814 for older Vims
func Test_autocmd_bufwipe_in_SessLoadPost()
+ set noswapfile
edit Xtest
tabnew
file Xsomething
- set noswapfile
mksession!

let content =<< trim [CODE]
diff --git a/src/testdir/test_mksession.vim b/src/testdir/test_mksession.vim
index 9dbd0094e..9bf1b47ff 100644
--- a/src/testdir/test_mksession.vim
+++ b/src/testdir/test_mksession.vim
@@ -75,9 +75,9 @@ func Test_mksession()
\ ' four leadinG spaces',
\ 'two consecutive tabs',
\ 'two tabs in one line',
- \ 'one ä multibyteCharacter',
- \ 'aä Ä two multiByte characters',
- \ 'Aäöü three mulTibyte characters',
+ \ 'one ä multibyteCharacter',
+ \ 'aä Ä two multiByte characters',
+ \ 'Aäöü three mulTibyte characters',
\ 'short line',
\ ])
let tmpfile = 'Xtemp'
@@ -126,33 +126,33 @@ func Test_mksession()
mksession! Xtest_mks.out
let li = filter(readfile('Xtest_mks.out'), 'v:val =~# "\(^ *normal! [0$]\|^ *exe ''normal!\)"')
let expected = [
- \ 'normal! 016|',
- \ 'normal! 016|',
- \ 'normal! 016|',
- \ 'normal! 08|',
- \ 'normal! 08|',
- \ 'normal! 016|',
- \ 'normal! 016|',
- \ 'normal! 016|',
- \ 'normal! $',
- \ " exe 'normal! ' . s:c . '|zs' . 16 . '|'",
- \ " normal! 016|",
- \ " exe 'normal! ' . s:c . '|zs' . 16 . '|'",
- \ " normal! 016|",
- \ " exe 'normal! ' . s:c . '|zs' . 16 . '|'",
- \ " normal! 016|",
- \ " exe 'normal! ' . s:c . '|zs' . 8 . '|'",
- \ " normal! 08|",
- \ " exe 'normal! ' . s:c . '|zs' . 8 . '|'",
- \ " normal! 08|",
- \ " exe 'normal! ' . s:c . '|zs' . 16 . '|'",
- \ " normal! 016|",
- \ " exe 'normal! ' . s:c . '|zs' . 16 . '|'",
- \ " normal! 016|",
- \ " exe 'normal! ' . s:c . '|zs' . 16 . '|'",
- \ " normal! 016|",
- \ " exe 'normal! ' . s:c . '|zs' . 16 . '|'",
- \ " normal! 016|"
+ \ ' normal! 016|',
+ \ ' normal! 016|',
+ \ ' normal! 016|',
+ \ ' normal! 08|',
+ \ ' normal! 08|',
+ \ ' normal! 016|',
+ \ ' normal! 016|',
+ \ ' normal! 016|',
+ \ ' normal! $',
+ \ " exe 'normal! ' .. c .. '|zs' .. 16 .. '|'",
+ \ " normal! 016|",
+ \ " exe 'normal! ' .. c .. '|zs' .. 16 .. '|'",
+ \ " normal! 016|",
+ \ " exe 'normal! ' .. c .. '|zs' .. 16 .. '|'",
+ \ " normal! 016|",
+ \ " exe 'normal! ' .. c .. '|zs' .. 8 .. '|'",
+ \ " normal! 08|",
+ \ " exe 'normal! ' .. c .. '|zs' .. 8 .. '|'",
+ \ " normal! 08|",
+ \ " exe 'normal! ' .. c .. '|zs' .. 16 .. '|'",
+ \ " normal! 016|",
+ \ " exe 'normal! ' .. c .. '|zs' .. 16 .. '|'",
+ \ " normal! 016|",
+ \ " exe 'normal! ' .. c .. '|zs' .. 16 .. '|'",
+ \ " normal! 016|",
+ \ " exe 'normal! ' .. c .. '|zs' .. 16 .. '|'",
+ \ " normal! 016|"
\ ]
call assert_equal(expected, li)
tabclose!
@@ -1066,7 +1066,7 @@ func Test_mksession_winminheight()
let found_restore = 0
let lines = readfile('Xtest_mks.out')
for line in lines
- if line =~ '= s:save_winmin\(width\|height\)'
+ if line =~ '= save_winmin\(width\|height\)'
let found_restore += 1
endif
endfor
@@ -1088,11 +1088,11 @@ func Test_mksession_shortmess()
for line in lines
let line = trim(line)

- if line ==# 'let s:shortmess_save = &shortmess'
+ if line ==# 'shortmess_save = &shortmess'
let found_save += 1
endif

- if found_save !=# 0 && line ==# 'let &shortmess = s:shortmess_save'
+ if found_save !=# 0 && line ==# '&shortmess = shortmess_save'
let found_restore += 1
endif
endfor
@@ -1109,7 +1109,7 @@ func Test_mksession_shortmess()
let found_restore = 0
let lines = readfile('Xtest_mks.out')
for line in lines
- if line =~# 's:shortmess_save'
+ if line =~# '\(var \)\@<!shortmess_save'
let found_restore += 1
endif
endfor
@@ -1331,4 +1331,268 @@ func Test_mkview_default_home()
endif
endfunc

+" Test vim9 expression mappings
+func Test_mksession_vim9_expr_mappings()
+
+ CheckFeature packages
+
+ " Create a dummy vim9 plugin
+ const base = getcwd() . '/rtdir'
+ const root = base . '/pack/test/opt/dummy9'
+ call mkdir(root . '/plugin', 'p')
+ let plugin_sources =<< trim END
+ vim9script
+ import autoload 'dummy9.vim'
+ nnoremap <expr> dummy-test dummy9.Test() .. "<CR>"
+ END
+ call writefile(plugin_sources, root . '/plugin/dummy9.vim')
+
+ call mkdir(root . '/autoload', 'p')
+ let auto_sources =<< trim END
+ vim9script
+ const ref_txt = 'Hello from vim9 dummy plugin!'
+ export def Test(): string
+ writefile([ref_txt], 'XDummyOutput')
+ return has("gui_running") ? '' : $':echomsg "{ref_txt}"'
+ enddef
+ END
+ call writefile(auto_sources, root . '/autoload/dummy9.vim')
+
+ " clean up later
+ defer delete(base, 'rf')
+
+ " Load and check the plugin
+ const ref_txt = 'Hello from vim9 dummy plugin!'
+ let &packpath .= ',' . base
+ packadd dummy9
+ messages clear
+ normal dummy-test
+
+ if !has('gui_running')
+ call assert_match(ref_txt, execute('messages'), 'No vim9 plugin dummy.Test() execution')
+ endif
+ call assert_true(filereadable('XDummyOutput'), 'Output file was not created by Vim9 plugin')
+ call assert_equal([ref_txt], readfile('XDummyOutput'))
+ call delete('XDummyOutput')
+
+ " Create a session file
+ mksession! XDummySession.vim
+ defer delete('XDummySession.vim')
+ call assert_true(filereadable('XDummySession.vim'), 'Session file was not created')
+
+ " Check the session file mappings are operational
+ let test_sources =<< trim END
+ " load session
+ source XDummySession.vim
+ " execute vim9 expression mapping
+ normal dummy-test
+ " on my way
+ cq
+ END
+ call writefile(test_sources, 'XTest.vim', 'D')
+ " spawn a new Vim instance to load the session and execute the mapping
+ call system(GetVimCommand('XTest.vim'))
+ defer delete('XDummyOutput')
+ call assert_true(filereadable('XDummyOutput'),
+ \ 'Expected output file was not created by Vim9 plugin')
+ call assert_equal([ref_txt], readfile('XDummyOutput'))
+
+endfunc
+
+" Test legacy vimscript expression mappings
+func Test_mksession_legacy_expr_mappings()
+
+ CheckFeature packages
+
+ " Create a dummy vim9 plugin
+ const base = getcwd() . '/rtdir'
+ const root = base . '/pack/test/opt/dummy'
+ call mkdir(root . '/plugin', 'p')
+
+ " clean up later
+ defer delete(base, 'rf')
+
+ let plugin_sources =<< trim END
+ nnoremap <expr> dummy-test dummy#Test() . "<CR>"
+ END
+ call writefile(plugin_sources, root . '/plugin/dummy.vim')
+
+ call mkdir(root . '/autoload', 'p')
+ let auto_sources =<< trim END
+ const s:ref_txt = 'Hello from good old dummy plugin!'
+ func dummy#Test()
+ call writefile([s:ref_txt], 'XDummyOutput')
+ return has("gui_running") ? '' : $':echomsg "{s:ref_txt}"'
+ endfunc
+ END
+ call writefile(auto_sources, root . '/autoload/dummy.vim')
+
+ " Load and check the plugin
+ const ref_txt = 'Hello from good old dummy plugin!'
+ let &packpath .= ',' . base
+ packadd dummy
+ messages clear
+ normal dummy-test
+
+ if !has("gui_running")
+ call assert_match(ref_txt, execute('messages'), 'No vim9 plugin dummy.Test() execution')
+ endif
+ call assert_true(filereadable('XDummyOutput'), 'Output file was not created by legacy plugin')
+ call assert_equal([ref_txt], readfile('XDummyOutput'))
+ call delete('XDummyOutput')
+
+ " Create a session file
+ mksession! XDummySession.vim
+ defer delete('XDummySession.vim')
+ call assert_true(filereadable('XDummySession.vim'), 'Session file was not created')
+
+ " Check the session file mappings are operational
+ let test_sources =<< trim END
+ " load session
+ source XDummySession.vim
+ " execute legacy vimscript expression mapping
+ normal dummy-test
+ " on my way
+ cq
+ END
+ call writefile(test_sources, 'XTest.vim', 'D')
+ " spawn a new Vim instance to load the session and execute the mapping
+ call system(GetVimCommand('XTest.vim'))
+ defer delete('XDummyOutput')
+ call assert_true(filereadable('XDummyOutput'),
+ \ 'Expected output file was not created by legacy vim plugin')
+ call assert_equal([ref_txt], readfile('XDummyOutput'))
+
+endfunc
+
+" Test sessions cursor position management
+func Test_mksession_cursor_position()
+
+ " Set windows test scenario
+ let files = []
+ for i in range(10)
+ let file = $'Xfile{i}'
+ exe $"{i ? 'split' : 'edit'} {file}"
+ call append(0, $"Session file cursor position testing {i}")
+ " Force cursor position restoring commands
+ setlocal nowrap
+ normal dd29zl
+ " Check expected position
+ call assert_equal([0, 1, 30, 0], getpos('.'), $"Fail to set cursor position for {file}")
+ write!
+ let files += [file]
+ endfor
+
+ " Save session
+ mksession! Xtest_curpos
+
+ " Test restoring session
+ %bwipe!
+ try
+ source Xtest_curpos
+ catch
+ call assert_report("Failure sourcing session file")
+ endtry
+
+ " Check cursor position
+ for file in files
+ exe $"drop {file}"
+ call assert_equal([0, 1, 30, 0], getpos('.'), $"Cursor position not restored correctly for {file}")
+ endfor
+
+ " Clean up
+ call delete('Xtest_curpos')
+ for file in files
+ call delete(file)
+ endfor
+endfunc
+
+" Test sessions global and local mappings
+func Test_mksession_localmappings()
+
+ " Create sessions. Mapping execution is tested running a file
+ let valid_sessions = [] " keep map info
+ let invalid_sessions = [] " do not keep map info
+ " localoptions requires a buffer
+ setlocal noswapfile
+ silent write XDummy
+ defer delete('XDummy')
+
+ for option in ["&", "=options", "=localoptions"]
+ for global in [0, 1]
+
+ " select options
+ exe "set sessionoptions" .. option
+
+ " mapping
+ exe "nnoremap" . (global ? " " : " <buffer> ")
+ \ . "dummy-test <Cmd>silent write XDummyOutput<CR>"
+ let case = $"mapping_{global ? "global" : "local"}_{option}"
+
+ " test mapping
+ normal dummy-test
+ call assert_true(filereadable("XDummyOutput"), $"Output file was not created by {case}")
+ call delete("XDummyOutput")
+
+ " session
+ let sessionfile = "XSession_" . case
+ exe $"mksession {sessionfile}"
+
+ if global && option =~ "localoptions"
+ let invalid_sessions += [sessionfile]
+ else
+ let valid_sessions += [sessionfile]
+ endif
+
+ " clear mappings
+ nmapclear
+ nmapclear <buffer>
+
+ endfor
+ endfor
+
+ " Check the session files are operational
+ for session in valid_sessions
+
+ let test_sources =<< trim eval END
+ " load session
+ silent source {session}
+ " execute legacy vimscript expression mapping
+ normal dummy-test
+ " on my way
+ cq
+ END
+
+ call writefile(test_sources, 'XTest.vim')
+ call system(GetVimCommand('XTest.vim'))
+ call assert_true(filereadable('XDummyOutput'),
+ \ $"Expected map not defined in session file {session}")
+ call delete('XDummyOutput')
+ call delete(session)
+
+ endfor
+
+ for session in invalid_sessions
+
+ let test_sources =<< trim eval END
+ " load session
+ silent source {session}
+ " execute legacy vimscript expression mapping
+ normal dummy-test
+ " on my way
+ cq
+ END
+
+ call writefile(test_sources, 'XTest.vim')
+ call system(GetVimCommand('XTest.vim'))
+ call assert_false(filereadable('XDummyOutput'),
+ \ $"Unexpected map defined in session file {session}")
+ if filereadable('XDummyOutput')
+ call delete('XDummyOutput')
+ endif
+ call delete(session)
+ endfor
+
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/testdir/test_mksession_utf8.vim b/src/testdir/test_mksession_utf8.vim
index d467dc20c..90695f0c8 100644
--- a/src/testdir/test_mksession_utf8.vim
+++ b/src/testdir/test_mksession_utf8.vim
@@ -64,34 +64,38 @@ func Test_mksession_utf8()
mksession! test_mks.out
let li = filter(readfile('test_mks.out'), 'v:val =~# "\(^ *normal! 0\|^ *exe ''normal!\)"')
let expected =<< trim [DATA]
- normal! 016|
- normal! 016|
- normal! 016|
- normal! 08|
- normal! 08|
- normal! 016|
- normal! 016|
- normal! 016|
- exe 'normal! ' . s:c . '|zs' . 16 . '|'
+ |
normal! 016|
- exe 'normal! ' . s:c . '|zs' . 16 . '|'
normal! 016|
- exe 'normal! ' . s:c . '|zs' . 16 . '|'
normal! 016|
- exe 'normal! ' . s:c . '|zs' . 8 . '|'
normal! 08|
- exe 'normal! ' . s:c . '|zs' . 8 . '|'
normal! 08|
- exe 'normal! ' . s:c . '|zs' . 16 . '|'
normal! 016|
- exe 'normal! ' . s:c . '|zs' . 16 . '|'
normal! 016|
- exe 'normal! ' . s:c . '|zs' . 16 . '|'
- normal! 016|
- exe 'normal! ' . s:c . '|zs' . 16 . '|'
normal! 016|
+ exe 'normal! ' .. c .. '|zs' .. 16 .. '|'
+ normal! 016|
+ exe 'normal! ' .. c .. '|zs' .. 16 .. '|'
+ normal! 016|
+ exe 'normal! ' .. c .. '|zs' .. 16 .. '|'
+ normal! 016|
+ exe 'normal! ' .. c .. '|zs' .. 8 .. '|'
+ normal! 08|
+ exe 'normal! ' .. c .. '|zs' .. 8 .. '|'
+ normal! 08|
+ exe 'normal! ' .. c .. '|zs' .. 16 .. '|'
+ normal! 016|
+ exe 'normal! ' .. c .. '|zs' .. 16 .. '|'
+ normal! 016|
+ exe 'normal! ' .. c .. '|zs' .. 16 .. '|'
+ normal! 016|
+ exe 'normal! ' .. c .. '|zs' .. 16 .. '|'
+ normal! 016|
[DATA]

+ " remove indent marker
+ call remove(expected, 0)
+
call assert_equal(expected, li)
tabclose!

diff --git a/src/version.c b/src/version.c
index 80020f59f..95107bb28 100644
--- a/src/version.c
+++ b/src/version.c
@@ -729,6 +729,8 @@ static char *(features[]) =

static int included_patches[] =
{ /* Add new patch number below this line */
+/**/
+ 579,
/**/
578,
/**/
Reply all
Reply to author
Forward
0 new messages