commit 7852993f4927c2d004d627bbe244dbbe09edb94f
parent 34b3bd1ac5758813743836b1bd559102191d57a6
Author: zeertzjq <zeertzjq@outlook.com>
Date: Thu, 26 Feb 2026 21:30:44 +0800
vim-patch:9.2.0061: Not possible to know when a session will be loaded (#38071)
Problem: Not possible to know when a session will be loaded.
Solution: Add the SessionLoadPre autocommand (Colin Kennedy).
fixes: vim/vim#19084
closes: vim/vim#19306
https://github.com/vim/vim/commit/1c0d468d72e0220d4cb25936043ac35439a981b5
Co-authored-by: Colin Kennedy <colinvfx@gmail.com>
Diffstat:
9 files changed, 78 insertions(+), 2 deletions(-)
diff --git a/runtime/doc/autocmd.txt b/runtime/doc/autocmd.txt
@@ -961,6 +961,9 @@ SafeState When nothing is pending, going to wait for the
check more with `state()`, e.g. whether the
screen was scrolled for messages.
+ *SessionLoadPre*
+SessionLoadPre Before loading the session file created using
+ the |:mksession| command.
*SessionLoadPost*
SessionLoadPost After loading the session file created using
the |:mksession| command.
diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt
@@ -240,6 +240,7 @@ EVENTS
• Creating or updating a progress message with |nvim_echo()| triggers a |Progress| event.
• |MarkSet| is triggered after a |mark| is set by the user (currently doesn't
support implicit marks like |'[| or |'<|, …).
+• |SessionLoadPre| is triggered before loading a |Session| file.
• |TabClosedPre| is triggered before closing a |tabpage|.
• New `terminator` parameter for |TermRequest| event.
diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt
@@ -2553,6 +2553,7 @@ A jump table for the options with a short description can be found at |Q_op|.
|RemoteReply|,
|SafeState|,
|SessionLoadPost|,
+ |SessionLoadPre|,
|SessionWritePost|,
|ShellCmdPost|,
|Signal|,
diff --git a/runtime/doc/starting.txt b/runtime/doc/starting.txt
@@ -852,8 +852,8 @@ This saves the current Session, and starts off the command to load another.
A session includes all tab pages, unless "tabpages" was removed from
'sessionoptions'. |tab-page|
-The |SessionLoadPost| autocmd event is triggered after a session file is
-loaded/sourced.
+The |SessionLoadPre| autocmd event is triggered before a session file is
+loaded/sourced and |SessionLoadPost| autocmd event is triggered after.
*SessionLoad-variable*
While the session file is loading, the SessionLoad global variable is set to
1. Plugins can use this to postpone some work until the SessionLoadPost event
diff --git a/runtime/lua/vim/_meta/api_keysets.lua b/runtime/lua/vim/_meta/api_keysets.lua
@@ -176,6 +176,7 @@ error('Cannot require a meta file')
--- |'SafeState'
--- |'SearchWrapped'
--- |'SessionLoadPost'
+--- |'SessionLoadPre'
--- |'SessionWritePost'
--- |'ShellCmdPost'
--- |'ShellFilterPost'
diff --git a/runtime/lua/vim/_meta/options.lua b/runtime/lua/vim/_meta/options.lua
@@ -2231,6 +2231,7 @@ vim.go.ei = vim.go.eventignore
--- `RemoteReply`,
--- `SafeState`,
--- `SessionLoadPost`,
+--- `SessionLoadPre`,
--- `SessionWritePost`,
--- `ShellCmdPost`,
--- `Signal`,
diff --git a/src/nvim/auevents.lua b/src/nvim/auevents.lua
@@ -97,6 +97,7 @@ return {
SafeState = false, -- going to wait for a character
SearchWrapped = true, -- after the search wrapped around
SessionLoadPost = false, -- after loading a session file
+ SessionLoadPre = false, -- before loading a session file
SessionWritePost = false, -- after writing a session file
ShellCmdPost = false, -- after ":!cmd"
ShellFilterPost = true, -- after ":1,2!cmd", ":w !cmd", ":r !cmd".
diff --git a/src/nvim/ex_session.c b/src/nvim/ex_session.c
@@ -600,6 +600,9 @@ static int makeopens(FILE *fd, char *dirnow)
// Begin by setting v:this_session, and then other sessionable variables.
PUTLINE_FAIL("let v:this_session=expand(\"<sfile>:p\")");
+
+ PUTLINE_FAIL("doautoall SessionLoadPre");
+
if (ssop_flags & kOptSsopFlagGlobals) {
if (store_session_globals(fd) == FAIL) {
return FAIL;
diff --git a/test/old/testdir/test_autocmd.vim b/test/old/testdir/test_autocmd.vim
@@ -1135,6 +1135,71 @@ func Test_BufEnter()
only
endfunc
+func Test_autocmd_SessLoadPre()
+ tabnew
+ set noswapfile
+ mksession! Session.vim
+
+ call assert_false(exists('g:session_loaded_var'))
+
+ let content =<< trim [CODE]
+ set nocp noswapfile
+
+ func! Assert(cond, msg)
+ if !a:cond
+ echomsg "ASSERT_FAIL: " .. a:msg
+ else
+ echomsg "ASSERT_OK: " .. a:msg
+ endif
+ endfunc
+
+ func! OnSessionLoadPre()
+ call Assert(!exists('g:session_loaded_var'),
+ \ 'SessionLoadPre: var NOT set')
+ endfunc
+ au SessionLoadPre * call OnSessionLoadPre()
+
+ func! OnSessionLoadPost()
+ call Assert(exists('g:session_loaded_var'),
+ \ 'SessionLoadPost: var IS set')
+ echomsg "SessionLoadPost DONE"
+ endfunc
+ au SessionLoadPost * call OnSessionLoadPost()
+
+ func! WriteErrors()
+ call writefile([execute("messages")], "XerrorsPost")
+ endfunc
+ au VimLeave * call WriteErrors()
+ [CODE]
+
+ call writefile(content, 'Xvimrc', 'D')
+
+ call writefile(
+ \ ['let g:session_loaded_var = 1'],
+ \ 'Sessionx.vim',
+ \ 'b'
+ \ )
+
+ " --- Run child Vim ---
+ call system(
+ \ GetVimCommand('Xvimrc')
+ \ .. ' --headless --noplugins -S Session.vim -c cq'
+ \ )
+
+ call WaitForAssert({-> assert_true(filereadable('XerrorsPost'))})
+
+ let errors = join(readfile('XerrorsPost'), "\n")
+ call assert_notmatch('ASSERT_FAIL', errors)
+ call assert_match('ASSERT_OK: SessionLoadPre: var NOT set', errors)
+ call assert_match('ASSERT_OK: SessionLoadPost: var IS set', errors)
+ call assert_match('SessionLoadPost DONE', errors)
+
+ set swapfile
+ for file in ['Session.vim', 'Sessionx.vim', 'XerrorsPost']
+ call delete(file)
+ endfor
+endfunc
+
" Closing a window might cause an endless loop
" E814 for older Vims
func Test_autocmd_bufwipe_in_SessLoadPost()