mirror of
https://github.com/neovim/neovim.git
synced 2026-04-17 13:03:03 +00:00
refactor(channel): defer hidden console allocation to :detach
Refactor #37977: instead of allocating a hidden console at startup, borrow the parent's console via AttachConsole() and only create an isolated hidden console when :detach is called, with fd 0/1/2 re-bound to the new CONIN$/CONOUT$.
This commit is contained in:
@@ -38,6 +38,7 @@
|
||||
#include "nvim/msgpack_rpc/channel.h"
|
||||
#include "nvim/msgpack_rpc/server.h"
|
||||
#include "nvim/os/fs.h"
|
||||
#include "nvim/os/os.h"
|
||||
#include "nvim/os/os_defs.h"
|
||||
#include "nvim/os/shell.h"
|
||||
#include "nvim/terminal.h"
|
||||
@@ -549,13 +550,22 @@ uint64_t channel_from_stdio(bool rpc, CallbackReader on_output, const char **err
|
||||
os_set_cloexec(stdin_dup_fd);
|
||||
stdout_dup_fd = os_dup(STDOUT_FILENO);
|
||||
os_set_cloexec(stdout_dup_fd);
|
||||
|
||||
// The server may have no console (spawned with UV_PROCESS_DETACHED for
|
||||
// :detach support). Allocate a hidden one so CONIN$/CONOUT$ and ConPTY
|
||||
// (:terminal) work.
|
||||
const bool restart_alloc_console = os_env_exists("__NVIM_RESTART_ALLOC_CONSOLE", true);
|
||||
if (restart_alloc_console) {
|
||||
os_unsetenv("__NVIM_RESTART_ALLOC_CONSOLE");
|
||||
}
|
||||
if (!GetConsoleWindow()) {
|
||||
AllocConsole();
|
||||
ShowWindow(GetConsoleWindow(), SW_HIDE);
|
||||
if (restart_alloc_console) {
|
||||
AllocConsole();
|
||||
ShowWindow(GetConsoleWindow(), SW_HIDE);
|
||||
} else if (!AttachConsole(ATTACH_PARENT_PROCESS)) {
|
||||
// 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).
|
||||
AllocConsole();
|
||||
ShowWindow(GetConsoleWindow(), SW_HIDE);
|
||||
}
|
||||
}
|
||||
os_replace_stdin_to_conin();
|
||||
os_replace_stdout_and_stderr_to_conout();
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include "nvim/api/private/dispatch.h"
|
||||
#include "nvim/api/private/helpers.h"
|
||||
#include "nvim/api/ui.h"
|
||||
#include "nvim/api/vim.h"
|
||||
#include "nvim/api/vimscript.h"
|
||||
#include "nvim/arglist.h"
|
||||
#include "nvim/ascii_defs.h"
|
||||
@@ -89,6 +90,9 @@
|
||||
#include "nvim/os/input.h"
|
||||
#include "nvim/os/os.h"
|
||||
#include "nvim/os/os_defs.h"
|
||||
#ifdef MSWIN
|
||||
# include "nvim/os/os_win_console.h"
|
||||
#endif
|
||||
#include "nvim/os/shell.h"
|
||||
#include "nvim/path.h"
|
||||
#include "nvim/plines.h"
|
||||
@@ -5020,6 +5024,13 @@ static void ex_restart(exarg_T *eap)
|
||||
server_stopped = server_stop(listen_arg, true);
|
||||
}
|
||||
|
||||
#ifdef MSWIN
|
||||
bool restart_alloc_console_env = false;
|
||||
if (os_setenv("__NVIM_RESTART_ALLOC_CONSOLE", "1", 1) == 0) {
|
||||
restart_alloc_console_env = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
CallbackReader on_err = CALLBACK_READER_INIT;
|
||||
// This temporary bootstrap channel is closed intentionally once we obtain
|
||||
// the new server address. Don't forward child stderr to the current UI.
|
||||
@@ -5031,6 +5042,11 @@ 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);
|
||||
#ifdef MSWIN
|
||||
if (restart_alloc_console_env) {
|
||||
os_unsetenv("__NVIM_RESTART_ALLOC_CONSOLE");
|
||||
}
|
||||
#endif
|
||||
if (!channel) {
|
||||
emsg("cannot create a channel job");
|
||||
goto fail_1;
|
||||
@@ -5907,7 +5923,10 @@ static void ex_detach(exarg_T *eap)
|
||||
emsg(e_invchan);
|
||||
return;
|
||||
}
|
||||
chan->detach = true; // Prevent self-exit on channel-close.
|
||||
// Prevent self-exit on channel-close.
|
||||
Error detach_err = ERROR_INIT;
|
||||
nvim__chan_set_detach(chan->id, true, &detach_err);
|
||||
api_clear_error(&detach_err);
|
||||
|
||||
// Server-side UI detach. Doesn't close the channel.
|
||||
Error err2 = ERROR_INIT;
|
||||
@@ -5928,6 +5947,12 @@ static void ex_detach(exarg_T *eap)
|
||||
// XXX: Can't do this, channel_decref() is async...
|
||||
// assert(!find_channel(chan->id));
|
||||
|
||||
#ifdef MSWIN
|
||||
// After UI/channel detach, move this server off the parent's console so it
|
||||
// survives terminal closure and still has working CONIN$/CONOUT$.
|
||||
os_swap_to_hidden_console();
|
||||
#endif
|
||||
|
||||
ILOG("detach current_ui=%" PRId64, chan->id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,6 +56,19 @@ void os_replace_stdout_and_stderr_to_conout(void)
|
||||
assert(conerr_fd == STDERR_FILENO);
|
||||
}
|
||||
|
||||
/// Detach from the current console and switch stdio to a hidden private one.
|
||||
///
|
||||
/// Used when an embedded server must outlive its parent console, while keeping
|
||||
/// CONIN$/CONOUT$ and ConPTY functional for :terminal and stdio writes.
|
||||
void os_swap_to_hidden_console(void)
|
||||
{
|
||||
FreeConsole();
|
||||
AllocConsole();
|
||||
ShowWindow(GetConsoleWindow(), SW_HIDE);
|
||||
os_replace_stdin_to_conin();
|
||||
os_replace_stdout_and_stderr_to_conout();
|
||||
}
|
||||
|
||||
/// Resets Windows console icon if we got an original one on startup.
|
||||
void os_icon_reset(void)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user