From 57dc215230302f0abf0789e0e048f8d29a4e8362 Mon Sep 17 00:00:00 2001 From: Sanzhar Kuandyk <92693103+SanzharKuandyk@users.noreply.github.com> Date: Thu, 23 Jul 2026 16:57:19 +0500 Subject: [PATCH] refactor(:restart): unnecessary console allocation on Windows #40919 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problem: `:restart` on Windows always forces `AllocConsole`, even though we could reuse old server's existing console + unnecessary env. Analysis: After d5516daf121aa718e79bcd423ee24c24492893c0 changed the restart lifecycle and 789741bb83d1f31b7162cd8944bc2d846fa2fa51 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`. --- src/nvim/channel.c | 16 +++++----------- src/nvim/ex_docmd.c | 3 --- src/nvim/os/os.h | 1 - 3 files changed, 5 insertions(+), 15 deletions(-) diff --git a/src/nvim/channel.c b/src/nvim/channel.c index dcd422aa13..203bdbe680 100644 --- a/src/nvim/channel.c +++ b/src/nvim/channel.c @@ -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); } diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 6cb539deb7..7dd4a108f5 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -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 diff --git a/src/nvim/os/os.h b/src/nvim/os/os.h index d9677cb58b..af8872a8a5 100644 --- a/src/nvim/os/os.h +++ b/src/nvim/os/os.h @@ -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"