mirror of
https://github.com/neovim/neovim.git
synced 2026-07-29 11:58:02 +00:00
refactor(detach): eliminate nvim__ui_detach #40793
Problem:
`nvim__ui_detach` was added in 85e0559d46 in order to implement
`detach_others` from Lua. Using Lua in this case is doing more harm than
good.
Solution:
Extract `ui_detach_channel`, which also allows it to be used for the
"self detach" path from `ex_detach`.
Bonus: the old MSWIN path always called `os_swap_to_hidden_console()`;
now `ui_detach_channel` only does so for a stdio channel. For the common
stdio TUI this is identical; for a socket UI it's more correct (no
parent console to swap).
This commit is contained in:
@@ -3863,20 +3863,6 @@ nvim_ui_try_resize_grid({grid}, {width}, {height})
|
||||
• {width} (`integer`) The new requested width.
|
||||
• {height} (`integer`) The new requested height.
|
||||
|
||||
nvim__ui_detach({chan}) *nvim__ui_detach()*
|
||||
WARNING: This feature is experimental/unstable.
|
||||
|
||||
Detaches the UI on channel `chan`.
|
||||
|
||||
Sets the channel's detach flag (so the server doesn't exit on stdio UIs),
|
||||
sends an "error_exit" UI event, and closes the channel.
|
||||
|
||||
Attributes: ~
|
||||
Since: 0.12.0
|
||||
|
||||
Parameters: ~
|
||||
• {chan} (`integer`) UI channel to disconnect.
|
||||
|
||||
|
||||
==============================================================================
|
||||
Win_config Functions *api-win_config*
|
||||
|
||||
@@ -59,11 +59,15 @@ Use cases:
|
||||
------------------------------------------------------------------------------
|
||||
Stop or detach the current UI
|
||||
|
||||
*:detach* *E5768*
|
||||
*:detach* *E5768*
|
||||
:[range]detach
|
||||
Detaches the current UI. Other UIs (if any) remain attached.
|
||||
Fails with |E5768| if no UI is attached.
|
||||
The server (typically `nvim --embed`) continues running as
|
||||
Fails with "E5768" if no UI is attached. If [range] is "%",
|
||||
all UIs _except_ the current UI are detached: >vim
|
||||
|
||||
:%detach
|
||||
|
||||
< The server (typically `nvim --embed`) continues running as
|
||||
a background process, and you can reattach to it later.
|
||||
Before detaching, you may want to note the server address:
|
||||
>vim
|
||||
|
||||
@@ -219,24 +219,4 @@ function M.ex_session_restart(eap, extra)
|
||||
end
|
||||
end
|
||||
|
||||
--- Disconnects every UI except `keep_chan`, keeping the server running.
|
||||
---
|
||||
--- @param keep_chan integer Channel ID of the UI to preserve.
|
||||
--- @return integer # Number of UIs detached.
|
||||
function M.detach_others(keep_chan)
|
||||
local uicount = #vim.api.nvim_list_uis()
|
||||
for _, ui in ipairs(vim.api.nvim_list_uis()) do
|
||||
if ui.chan and ui.chan ~= keep_chan then
|
||||
vim.api.nvim__ui_detach(ui.chan)
|
||||
end
|
||||
end
|
||||
local n = uicount - #vim.api.nvim_list_uis()
|
||||
if n == 0 then
|
||||
vim.api.nvim_echo({ { 'No other UIs are attached' } }, true, {})
|
||||
else
|
||||
vim.api.nvim_echo({ { ('Detached %d non-current UIs'):format(n) } }, true, {})
|
||||
end
|
||||
return n
|
||||
end
|
||||
|
||||
return M
|
||||
|
||||
10
runtime/lua/vim/_meta/api.gen.lua
generated
10
runtime/lua/vim/_meta/api.gen.lua
generated
@@ -186,16 +186,6 @@ function vim.api.nvim__set_restart_on_crash(progpath, argv) end
|
||||
--- @return table<string,any> # Map of various internal stats.
|
||||
function vim.api.nvim__stats() end
|
||||
|
||||
--- WARNING: This feature is experimental/unstable.
|
||||
---
|
||||
--- Detaches the UI on channel `chan`.
|
||||
---
|
||||
--- Sets the channel's detach flag (so the server doesn't exit on stdio UIs),
|
||||
--- sends an "error_exit" UI event, and closes the channel.
|
||||
---
|
||||
--- @param chan integer UI channel to disconnect.
|
||||
function vim.api.nvim__ui_detach(chan) end
|
||||
|
||||
--- WARNING: This feature is experimental/unstable.
|
||||
---
|
||||
--- @param str string
|
||||
|
||||
@@ -113,36 +113,68 @@ void remote_ui_disconnect(uint64_t channel_id, Error *err, bool send_error_exit)
|
||||
remote_ui_destroy(ui);
|
||||
}
|
||||
|
||||
/// Detaches the UI on channel `chan`.
|
||||
///
|
||||
/// Sets the channel's detach flag (so the server doesn't exit on stdio UIs),
|
||||
/// sends an "error_exit" UI event, and closes the channel.
|
||||
/// Detaches the UI on channel `chan` (server keeps running). Sets `channel.detach`, sends an
|
||||
/// "error_exit" UI event, and closes the channel.
|
||||
///
|
||||
/// @param chan UI channel to disconnect.
|
||||
/// @param[out] err Error details, if any.
|
||||
void nvim__ui_detach(Integer chan, Error *err)
|
||||
FUNC_API_SINCE(14)
|
||||
void ui_detach_channel(uint64_t chan, Error *err)
|
||||
{
|
||||
Channel *c = find_channel((uint64_t)chan);
|
||||
Channel *c = find_channel(chan);
|
||||
VALIDATE(c != NULL, "%s", e_invchan, {
|
||||
return;
|
||||
});
|
||||
#ifdef MSWIN
|
||||
bool detach_stdio = c->streamtype == kChannelStreamStdio;
|
||||
#endif
|
||||
// Prevent server self-exit on channel-close.
|
||||
c->detach = true;
|
||||
remote_ui_disconnect((uint64_t)chan, err, true);
|
||||
// Server-side UI detach. Doesn't close the channel.
|
||||
remote_ui_disconnect(chan, err, true);
|
||||
if (ERROR_SET(err)) {
|
||||
return;
|
||||
}
|
||||
channel_close((uint64_t)chan, kChannelPartAll, NULL);
|
||||
// Server-side channel close.
|
||||
channel_close(chan, kChannelPartAll, NULL);
|
||||
#ifdef MSWIN
|
||||
if (detach_stdio) {
|
||||
// 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
|
||||
}
|
||||
|
||||
/// Detaches every UI except `keep_chan` (server keeps running).
|
||||
///
|
||||
/// @param keep_chan UI channel to preserve.
|
||||
/// @return Number of UIs detached.
|
||||
size_t ui_detach_others(uint64_t keep_chan)
|
||||
{
|
||||
// Snapshot the channel ids first: ui_detach_channel() mutates connected_uis.
|
||||
kvec_t(uint64_t) chans = KV_INITIAL_VALUE;
|
||||
uint64_t chan;
|
||||
map_foreach_key(&connected_uis, chan, {
|
||||
if (chan != keep_chan) {
|
||||
kv_push(chans, chan);
|
||||
}
|
||||
});
|
||||
|
||||
size_t detached = 0;
|
||||
for (size_t i = 0; i < kv_size(chans); i++) {
|
||||
Error err = ERROR_INIT;
|
||||
ui_detach_channel(kv_A(chans, i), &err);
|
||||
if (ERROR_SET(&err)) {
|
||||
api_clear_error(&err);
|
||||
} else {
|
||||
detached++;
|
||||
}
|
||||
}
|
||||
|
||||
kv_destroy(chans);
|
||||
return detached;
|
||||
}
|
||||
|
||||
#ifdef EXITFREE
|
||||
void remote_ui_free_all_mem(void)
|
||||
{
|
||||
|
||||
@@ -5964,69 +5964,42 @@ static void ex_tabs(exarg_T *eap)
|
||||
/// ":%detach" detaches all UIs _except_ the current UI.
|
||||
static void ex_detach(exarg_T *eap)
|
||||
{
|
||||
// come on pooky let's burn this mf down
|
||||
if (!current_ui) {
|
||||
emsg(_(e_noui));
|
||||
return;
|
||||
} else if (eap && eap->forceit) {
|
||||
emsg("bang (!) not supported yet");
|
||||
return;
|
||||
}
|
||||
|
||||
if (eap && eap->addr_count > 0) {
|
||||
// ":%detach" detaches every UI except the current one.
|
||||
if (eap->line1 != 1 || eap->line2 != curbuf->b_ml.ml_line_count) {
|
||||
emsg(_(e_invrange));
|
||||
return;
|
||||
}
|
||||
|
||||
typval_T lua_args[] = {
|
||||
{ .v_type = VAR_NUMBER, .vval.v_number = (varnumber_T)current_ui },
|
||||
{ .v_type = VAR_UNKNOWN },
|
||||
};
|
||||
typval_T rv = TV_INITIAL_VALUE;
|
||||
nlua_call_typval("vim._core.server", "detach_others", lua_args, &rv);
|
||||
ILOG("%%detach current_ui=%" PRIu64 " detached=%zu", current_ui,
|
||||
(rv.v_type == VAR_NUMBER && rv.vval.v_number > 0)
|
||||
? (size_t)rv.vval.v_number : (size_t)0);
|
||||
tv_clear(&rv);
|
||||
size_t n = ui_detach_others(current_ui);
|
||||
if (n == 0) {
|
||||
msg(_("No other UIs are attached"), 0);
|
||||
} else {
|
||||
smsg(0, _("Detached %d non-current UIs"), (int)n);
|
||||
}
|
||||
ILOG("%%detach current_ui=%" PRIu64 " detached=%zu", current_ui, n);
|
||||
} else {
|
||||
// come on pooky let's burn this mf down
|
||||
//
|
||||
// 1. Send "error_exit" UI-event (notification only).
|
||||
// 2. Perform server-side UI detach.
|
||||
// 3. Close server-side channel without self-exit.
|
||||
|
||||
Channel *chan = find_channel(current_ui);
|
||||
if (!chan) {
|
||||
emsg(e_invchan);
|
||||
Error err = ERROR_INIT;
|
||||
ui_detach_channel(current_ui, &err);
|
||||
if (ERROR_SET(&err)) {
|
||||
emsg(err.msg); // UI disappeared already?
|
||||
api_clear_error(&err);
|
||||
return;
|
||||
}
|
||||
// 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;
|
||||
remote_ui_disconnect(chan->id, &err2, true);
|
||||
if (ERROR_SET(&err2)) {
|
||||
emsg(err2.msg); // UI disappeared already?
|
||||
api_clear_error(&err2);
|
||||
return;
|
||||
}
|
||||
|
||||
// Server-side channel close.
|
||||
const char *err = NULL;
|
||||
bool rv = channel_close(chan->id, kChannelPartAll, &err);
|
||||
if (!rv && err) {
|
||||
emsg(err); // UI disappeared already?
|
||||
return;
|
||||
}
|
||||
// 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);
|
||||
ILOG("detach current_ui=%" PRIu64, current_ui);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -193,7 +193,7 @@ describe('vim._core', function()
|
||||
|
||||
-- All `vim._core.*` modules are builtin.
|
||||
t.eq(
|
||||
{ 'detach_others', 'ex_session_restart', 'rebind_after_restart', 'serverlist' },
|
||||
{ 'ex_session_restart', 'rebind_after_restart', 'serverlist' },
|
||||
n.exec_lua([[local k = vim.tbl_keys(require('vim._core.server')); table.sort(k); return k]])
|
||||
)
|
||||
local expected = {
|
||||
|
||||
Reference in New Issue
Block a user