refactor(:restart): unnecessary console allocation on Windows #40919

Problem:
`:restart` on Windows always forces `AllocConsole`, even though we could
reuse old server's existing console + unnecessary env.

Analysis:
After d5516daf12 changed the restart
lifecycle and 789741bb83 separated the
Windows console paths, we no longer need to force `AllocConsole()` for
`:restart`. The `:detach` path now allocates its own console when it
actually runs.

Solution:
Try to attach the replacement server to its parent’s console and fall
back to `AllocConsole()` if attachment fails. Drop the restart-specific
env that forced allocation.

As documented by Microsoft, [“A console is closed when the last process
attached to it terminates or calls
`FreeConsole`.”](https://learn.microsoft.com/en-us/windows/console/attachconsole)

Btw, this way we can preserve `io.stdout:write()`, though if for some
reason `AttachConsole` would fail then it won't work; but now at least
it's not always not rendering on `:restart`.
This commit is contained in:
Sanzhar Kuandyk
2026-07-23 16:57:19 +05:00
committed by GitHub
parent fcb43e8a11
commit 57dc215230
3 changed files with 5 additions and 15 deletions

View File

@@ -563,19 +563,13 @@ uint64_t channel_from_stdio(bool rpc, CallbackReader on_output, const char **err
if (embedded_mode && os_has_conpty_working()) {
stdin_dup_fd = os_dup_cloexec(STDIN_FILENO);
stdout_dup_fd = os_dup_cloexec(STDOUT_FILENO);
// :restart spawns a replacement server that must not borrow the parent
// Nvim process console, because that parent process will soon exit.
const bool restart_alloc_console = os_env_exists(ENV_RESTART_ALLOC_CONSOLE, true);
if (restart_alloc_console) {
os_unsetenv(ENV_RESTART_ALLOC_CONSOLE);
}
if (!GetConsoleWindow()) {
// Borrow the parent's console so CONOUT$ resolves to the real terminal,
// preserving io.stdout rendering (e.g. SIXEL/Kitty images). Only fall
// back to a hidden AllocConsole when there is no parent console (e.g.
// launched from a non-console parent), or for the replacement server
// spawned by :restart, because the parent Nvim process will soon exit.
if (restart_alloc_console || !AttachConsole(ATTACH_PARENT_PROCESS)) {
// preserving io.stdout rendering (e.g. SIXEL/Kitty images). A replacement
// server started by :restart can reuse the current server's console.
// Only fall back to a hidden console when the parent has no console.
if (!AttachConsole(ATTACH_PARENT_PROCESS)) {
ILOG("parent console attach failed: %lu; allocating hidden console", GetLastError());
AllocConsole();
ShowWindow(GetConsoleWindow(), SW_HIDE);
}

View File

@@ -5079,9 +5079,6 @@ static void ex_restart(exarg_T *eap)
dict_T *env = create_environment(NULL, false, false, false, NULL);
tv_dict_add_str(env, S_LEN(ENV_STARTREASON), startreason);
#ifdef MSWIN
tv_dict_add_str(env, S_LEN(ENV_RESTART_ALLOC_CONSOLE), "1");
#endif
CallbackReader on_err = CALLBACK_READER_INIT;
#ifdef MSWIN

View File

@@ -27,6 +27,5 @@ extern char *default_lib_dir;
#define ENV_LOGFILE "NVIM_LOG_FILE"
#define ENV_LOGFILE_WANT "__NVIM_LOG_FILE_WANT"
#define ENV_NVIM "NVIM"
#define ENV_RESTART_ALLOC_CONSOLE "__NVIM_RESTART_ALLOC_CONSOLE"
#define ENV_STARTREASON "__NVIM_STARTREASON"
#define ENV_TEST_LOG "__NVIM_TEST_LOG"