diff --git a/runtime/doc/gui.txt b/runtime/doc/gui.txt index b21f072e33..c24c21d133 100644 --- a/runtime/doc/gui.txt +++ b/runtime/doc/gui.txt @@ -81,7 +81,8 @@ Restart Nvim 1. Stops Nvim using `:qall` (or |+cmd|, if given). 2. Starts a new Nvim server using the same |v:argv| (except - `-- [file…]` files). + `-- [file…]` files). Sets |v:startreason| to "restart" on the + new server. 3. Attaches all UIs to the new Nvim server and runs `[command]` on it. diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt index 1ab7346ff0..573691de4f 100644 --- a/runtime/doc/news.txt +++ b/runtime/doc/news.txt @@ -453,6 +453,7 @@ VIMSCRIPT • |v:vim_did_init| is set after sourcing |init.vim| but before |load-plugins|. • |prompt_appendbuf()| appends text to prompt-buffer. • |v:exitreason| is set before |QuitPre|. +• |v:startreason| differentiates between restart and normal start. • |v:starttime| is the process start time (nanoseconds since UNIX epoch). ============================================================================== diff --git a/runtime/doc/starting.txt b/runtime/doc/starting.txt index ce79b9416d..85a4de3eae 100644 --- a/runtime/doc/starting.txt +++ b/runtime/doc/starting.txt @@ -434,7 +434,7 @@ Initialization *initialization* *startup* At startup, Nvim checks environment variables and files and sets values accordingly, proceeding as follows: -1. Set |v:starttime|. +1. Set |v:starttime| and |v:startreason|. 2. Set the 'shell' option. *SHELL* *COMSPEC* The environment variable SHELL, if it exists, is used to set the 'shell' diff --git a/runtime/doc/vvars.txt b/runtime/doc/vvars.txt index e61bc78624..6e829f8cb7 100644 --- a/runtime/doc/vvars.txt +++ b/runtime/doc/vvars.txt @@ -633,6 +633,14 @@ v:stacktrace stack trace. See also |v:exception|, |v:throwpoint|, and |throw-variables|. + *v:startreason* *startreason-variable* +v:startreason + The reason Nvim started. Possible values: + - "normal" normal startup. + - "restart" started by |:restart| or |ZR|. + + Read-only. + *v:starttime* *starttime-variable* v:starttime Timestamp (nanoseconds since UNIX epoch) when the Nvim process diff --git a/runtime/lua/vim/_meta/vvars.gen.lua b/runtime/lua/vim/_meta/vvars.gen.lua index 543d4169c1..2b97a1f8f8 100644 --- a/runtime/lua/vim/_meta/vvars.gen.lua +++ b/runtime/lua/vim/_meta/vvars.gen.lua @@ -667,6 +667,14 @@ vim.v.shell_error = ... --- @type table[] vim.v.stacktrace = ... +--- The reason Nvim started. Possible values: +--- - "normal" normal startup. +--- - "restart" started by `:restart` or `ZR`. +--- +--- Read-only. +--- @type string +vim.v.startreason = ... + --- Timestamp (nanoseconds since UNIX epoch) when the Nvim process --- started. --- diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c index 803b121133..4e467b9576 100644 --- a/src/nvim/eval/vars.c +++ b/src/nvim/eval/vars.c @@ -217,6 +217,7 @@ static struct vimvar { VV(VV_VIRTNUM, "virtnum", VAR_NUMBER, VV_RO), VV(VV_STARTTIME, "starttime", VAR_NUMBER, VV_RO), VV(VV_EXITREASON, "exitreason", VAR_STRING, VV_RO), + VV(VV_STARTREASON, "startreason", VAR_STRING, VV_RO), }; #undef VV @@ -316,6 +317,7 @@ void evalvars_init(void) set_vim_var_nr(VV_SEARCHFORWARD, 1); set_vim_var_nr(VV_HLSEARCH, 1); set_vim_var_nr(VV_COUNT1, 1); + set_vim_var_string(VV_STARTREASON, S_LEN("normal")); set_vim_var_special(VV_EXITING, kSpecialVarNull); set_vim_var_nr(VV_TYPE_NUMBER, VAR_TYPE_NUMBER); @@ -345,6 +347,15 @@ void evalvars_init(void) set_vim_var_partial(VV_LUA, vvlua_partial); set_reg_var(0); // default for v:register is not 0 but '"' + + // Set v:startreason via environment variable + const char *startreason = os_getenv_noalloc(ENV_STARTREASON); + if (strequal(startreason, "normal") || strequal(startreason, "restart")) { + set_vim_var_string(VV_STARTREASON, startreason, -1); + } + if (os_env_exists(ENV_STARTREASON, false)) { + os_unsetenv(ENV_STARTREASON); + } } #if defined(EXITFREE) diff --git a/src/nvim/eval_defs.h b/src/nvim/eval_defs.h index 7cec3d6167..f45cb35213 100644 --- a/src/nvim/eval_defs.h +++ b/src/nvim/eval_defs.h @@ -137,4 +137,5 @@ typedef enum { VV_VIRTNUM, VV_STARTTIME, VV_EXITREASON, + VV_STARTREASON, } VimVarIndex; diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 9531fc34c7..0aae15cb68 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -5057,6 +5057,10 @@ static void ex_restart(exarg_T *eap) restart_alloc_console_env = true; } #endif + bool startreason_env = false; + if (os_setenv(ENV_STARTREASON, "restart", 1) == 0) { + startreason_env = true; + } CallbackReader on_err = CALLBACK_READER_INIT; #ifdef MSWIN @@ -5073,6 +5077,9 @@ static void ex_restart(exarg_T *eap) CALLBACK_READER_INIT, on_err, CALLBACK_NONE, false, true, true, detach, kChannelStdinPipe, NULL, 0, 0, NULL, &exit_status); + if (startreason_env) { + os_unsetenv(ENV_STARTREASON); + } #ifdef MSWIN if (restart_alloc_console_env) { os_unsetenv("__NVIM_RESTART_ALLOC_CONSOLE"); diff --git a/src/nvim/os/os.h b/src/nvim/os/os.h index cd7db3bf80..9ac0ab7e24 100644 --- a/src/nvim/os/os.h +++ b/src/nvim/os/os.h @@ -26,3 +26,4 @@ extern char *default_lib_dir; #define ENV_LOGFILE "NVIM_LOG_FILE" #define ENV_NVIM "NVIM" +#define ENV_STARTREASON "__NVIM_STARTREASON" diff --git a/src/nvim/vvars.lua b/src/nvim/vvars.lua index db87bcda28..2ec77e3dc0 100644 --- a/src/nvim/vvars.lua +++ b/src/nvim/vvars.lua @@ -765,6 +765,16 @@ M.vars = { Read-only. ]=], }, + startreason = { + type = 'string', + desc = [=[ + The reason Nvim started. Possible values: + - "normal" normal startup. + - "restart" started by |:restart| or |ZR|. + + Read-only. + ]=], + }, statusmsg = { type = 'string', desc = [=[ diff --git a/test/functional/core/server_spec.lua b/test/functional/core/server_spec.lua index cf79fe817f..8b94539e16 100644 --- a/test/functional/core/server_spec.lua +++ b/test/functional/core/server_spec.lua @@ -364,7 +364,15 @@ it(':restart works in headless server (no UI)', function() n.set_session(nil) end) - fn.jobstart({ n.nvim_prog, '--clean', '--headless', '--listen', server_pipe }) + fn.jobstart({ + n.nvim_prog, + '--clean', + '--headless', + '--listen', + server_pipe, + '--cmd', + 'let g:early_startreason = v:startreason', + }) t.retry(nil, nil, function() neq(nil, vim.uv.fs_stat(server_pipe)) end) @@ -373,6 +381,8 @@ it(':restart works in headless server (no UI)', function() n.expect_exit(n.command, 'restart') n.set_session(n.connect(server_pipe)) eq(1, api.nvim_get_vvar('vim_did_enter')) + eq('restart', api.nvim_get_vvar('startreason')) + eq('restart', n.eval('g:early_startreason')) -- TODO: [command] is currently not executed without UI -- n.expect_exit(n.command, 'restart lua _G.new_server = 1') diff --git a/test/functional/vimscript/vvars_spec.lua b/test/functional/vimscript/vvars_spec.lua index 377d6296fa..8afe719299 100644 --- a/test/functional/vimscript/vvars_spec.lua +++ b/test/functional/vimscript/vvars_spec.lua @@ -48,3 +48,11 @@ describe('v:argf', function() eq({ abs1, abs2, abs3 }, n.eval('v:argf')) end) end) + +describe('v:startreason', function() + it('is read-only and starts as "normal"', function() + n.clear { args = { '--cmd', 'let g:early_startreason = v:startreason' } } + eq('normal', eval('g:early_startreason')) + t.matches('E46', t.pcall_err(command, "let v:startreason = 'restart'")) + end) +end)