From: Miguel Barro Date: Sun, 31 May 2026 21:03:12 +0000 (+0000) Subject: patch 9.2.0579: :mksession, :mkview and :mkvimrc emit legacy Vim script X-Git-Tag: v9.2.0579^0 X-Git-Url: http://git.ipfire.org/gitweb/index.cgi?a=commitdiff_plain;h=d69cf0dbcfdd721d94189ffa1558660c96129627;p=thirdparty%2Fvim.git 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 Signed-off-by: Miguel Barro Signed-off-by: Christian Brabandt --- diff --git a/runtime/doc/version9.txt b/runtime/doc/version9.txt index 29882b4d55..2496d1b695 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 b9b54d8fc9..19c444c14c 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 1c0d0ac7fd..34e465ec3f 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 eed03269a0..090448937f 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 == '\r') *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(\":p\")") == FAIL) + + if (put_line(fd, "v:this_session = expand(\":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(\":p:h\"), ' ')") + if (put_line(fd, "exe \"cd \" .. escape(expand(\":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(\":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(\":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 ecd5791c90..a1f282e06d 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 9dbd0094e2..9bf1b47ff5 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 \)\@ dummy-test dummy9.Test() .. "" + 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 dummy-test dummy#Test() . "" + 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 ? " " : " ") + \ . "dummy-test silent write XDummyOutput" + 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 + + 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 d467dc20c6..90695f0c8d 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 80020f59fd..95107bb286 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, /**/