aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJustin M. Keyes <justinkz@gmail.com>2025-02-10 10:21:47 -0800
committerGitHub <noreply@github.com>2025-02-10 10:21:47 -0800
commitc7d13f2895fa657ff3d9d45741f9abec25072b56 (patch)
tree0b61d786b8fb318a0e9b2c53eb633332af5aa05d
parentad60b3fb4806c0917010bbe97876c22fb57cabcd (diff)
parenta1906c23ddab6fa4d15bc5ceddee97df8034d8cb (diff)
downloadrneovim-c7d13f2895fa657ff3d9d45741f9abec25072b56.tar.gz
rneovim-c7d13f2895fa657ff3d9d45741f9abec25072b56.tar.bz2
rneovim-c7d13f2895fa657ff3d9d45741f9abec25072b56.zip
Merge #32385 UI :detach command
-rw-r--r--CONTRIBUTING.md2
-rw-r--r--runtime/doc/api.txt4
-rw-r--r--runtime/doc/gui.txt19
-rw-r--r--runtime/doc/news.txt2
-rw-r--r--src/nvim/api/ui.c2
-rw-r--r--src/nvim/api/vim.c7
-rw-r--r--src/nvim/channel.c1
-rw-r--r--src/nvim/channel.h3
-rw-r--r--src/nvim/event/proc.c16
-rw-r--r--src/nvim/event/stream.c3
-rw-r--r--src/nvim/ex_cmds.lua6
-rw-r--r--src/nvim/ex_docmd.c53
-rw-r--r--src/nvim/globals.h2
-rw-r--r--src/nvim/msgpack_rpc/channel.c20
-rw-r--r--src/nvim/os/input.c4
-rw-r--r--src/nvim/ui_client.c8
-rw-r--r--test/functional/core/job_spec.lua16
-rw-r--r--test/functional/editor/put_spec.lua2
-rw-r--r--test/functional/terminal/tui_spec.lua258
-rw-r--r--test/functional/testnvim.lua6
20 files changed, 300 insertions, 134 deletions
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 324cb3e13a..2cf93e74ed 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -129,7 +129,7 @@ Each pull request must pass the automated builds on [Cirrus CI] and [GitHub Acti
passes various linter checks.
- CI for FreeBSD runs on [Cirrus CI].
- To see CI results faster in your PR, you can temporarily set `TEST_FILE` in
- [test.yml](https://github.com/neovim/neovim/blob/e35b9020b16985eee26e942f9a3f6b045bc3809b/.github/workflows/test.yml#L29).
+ [test.yml](https://github.com/neovim/neovim/blob/ad8e0cfc1dfd937c2577dc032e524c799a772693/.github/workflows/test.yml#L26).
### Coverity
diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt
index 92f5a261ee..4f76e7e058 100644
--- a/runtime/doc/api.txt
+++ b/runtime/doc/api.txt
@@ -1208,8 +1208,8 @@ nvim_select_popupmenu_item({item}, {insert}, {finish}, {opts})
*nvim_set_client_info()*
nvim_set_client_info({name}, {version}, {type}, {methods}, {attributes})
- Self-identifies the client. Sets the `client` object returned by
- |nvim_get_chan_info()|.
+ Self-identifies the client, and sets optional flags on the channel.
+ Defines the `client` object returned by |nvim_get_chan_info()|.
Clients should call this just after connecting, to provide hints for
debugging and orchestration. (Note: Something is better than nothing!
diff --git a/runtime/doc/gui.txt b/runtime/doc/gui.txt
index ecb4de09bb..780712df6c 100644
--- a/runtime/doc/gui.txt
+++ b/runtime/doc/gui.txt
@@ -47,6 +47,25 @@ can connect to any Nvim instance).
Example: this sets "g:gui" to the value of the UI's "rgb" field: >
:autocmd UIEnter * let g:gui = filter(nvim_list_uis(),{k,v-> v.chan==v:event.chan})[0].rgb
<
+------------------------------------------------------------------------------
+Stop or detach the current UI
+
+ *:detach*
+:detach
+ Detaches the current UI. Other UIs (if any) remain attached.
+ 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
+ :echo v:servername
+<
+ Note: The server closes the UI RPC channel, so :detach
+ inherently "works" for all UIs. But if a UI isn't expecting
+ the channel to be closed, it may be (incorrectly) reported as
+ an error.
+
+------------------------------------------------------------------------------
+GUI commands
*:winp* *:winpos* *E188*
:winp[os]
diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt
index 1f1ad84a02..90a020bb4d 100644
--- a/runtime/doc/news.txt
+++ b/runtime/doc/news.txt
@@ -400,6 +400,8 @@ TUI
UI
+• |:detach| the current UI, let the Nvim server continue running as a background
+ process. Works with the builtin TUI, and all GUIs.
• |vim.ui.open()| (by default bound to |gx|) accepts an `opt.cmd` parameter
which controls the tool used to open the given path or URL. If you want to
globally set this, you can override vim.ui.open using the same approach
diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c
index f6f32a73ed..41a09999d0 100644
--- a/src/nvim/api/ui.c
+++ b/src/nvim/api/ui.c
@@ -189,6 +189,7 @@ void nvim_ui_attach(uint64_t channel_id, Integer width, Integer height, Dict opt
ui->wildmenu_active = false;
pmap_put(uint64_t)(&connected_uis, channel_id, ui);
+ current_ui = channel_id;
ui_attach_impl(ui, channel_id);
may_trigger_vim_suspend_resume(false);
@@ -214,6 +215,7 @@ void nvim_ui_set_focus(uint64_t channel_id, Boolean gained, Error *error)
}
if (gained) {
+ current_ui = channel_id;
may_trigger_vim_suspend_resume(false);
}
diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c
index c103a56032..02aa73d98d 100644
--- a/src/nvim/api/vim.c
+++ b/src/nvim/api/vim.c
@@ -359,11 +359,11 @@ void nvim_feedkeys(String keys, String mode, Boolean escape_ks)
/// @param keys to be typed
/// @return Number of bytes actually written (can be fewer than
/// requested if the buffer becomes full).
-Integer nvim_input(String keys)
+Integer nvim_input(uint64_t channel_id, String keys)
FUNC_API_SINCE(1) FUNC_API_FAST
{
may_trigger_vim_suspend_resume(false);
- return (Integer)input_enqueue(keys);
+ return (Integer)input_enqueue(channel_id, keys);
}
/// Send mouse event from GUI.
@@ -1485,7 +1485,8 @@ Array nvim_get_api_info(uint64_t channel_id, Arena *arena)
return rv;
}
-/// Self-identifies the client. Sets the `client` object returned by |nvim_get_chan_info()|.
+/// Self-identifies the client, and sets optional flags on the channel. Defines the `client` object
+/// returned by |nvim_get_chan_info()|.
///
/// Clients should call this just after connecting, to provide hints for debugging and
/// orchestration. (Note: Something is better than nothing! Fields are optional, but at least set
diff --git a/src/nvim/channel.c b/src/nvim/channel.c
index 912d515f84..c964748a20 100644
--- a/src/nvim/channel.c
+++ b/src/nvim/channel.c
@@ -219,6 +219,7 @@ Channel *channel_alloc(ChannelStreamType type)
chan->refcount = 1;
chan->exit_status = -1;
chan->streamtype = type;
+ chan->detach = false;
assert(chan->id <= VARNUMBER_MAX);
pmap_put(uint64_t)(&channels, chan->id, chan);
return chan;
diff --git a/src/nvim/channel.h b/src/nvim/channel.h
index 2327216826..81d67ceb66 100644
--- a/src/nvim/channel.h
+++ b/src/nvim/channel.h
@@ -31,6 +31,9 @@ struct Channel {
} stream;
bool is_rpc;
+ bool detach; ///< Prevents self-exit on channel-close. Normally, Nvim self-exits if its primary
+ ///< RPC channel is closed, unless detach=true. Note: currently, detach=false does
+ ///< not FORCE self-exit.
RpcState rpc;
Terminal *term;
diff --git a/src/nvim/event/proc.c b/src/nvim/event/proc.c
index 37cb102d11..e32bbbc29a 100644
--- a/src/nvim/event/proc.c
+++ b/src/nvim/event/proc.c
@@ -315,10 +315,8 @@ static void decref(Proc *proc)
static void proc_close(Proc *proc)
FUNC_ATTR_NONNULL_ARG(1)
{
- if (proc_is_tearing_down && (proc->detach || proc->type == kProcTypePty)
- && proc->closed) {
- // If a detached/pty process dies while tearing down it might get closed
- // twice.
+ if (proc_is_tearing_down && proc->closed && (proc->detach || proc->type == kProcTypePty)) {
+ // If a detached/pty process dies while tearing down it might get closed twice.
return;
}
assert(!proc->closed);
@@ -427,19 +425,21 @@ static void exit_event(void **argv)
}
}
-void exit_from_channel(int status)
+/// Performs self-exit because the primary RPC channel was closed.
+void exit_on_closed_chan(int status)
{
+ DLOG("self-exit triggered by closed RPC channel...");
multiqueue_put(main_loop.fast_events, exit_event, (void *)(intptr_t)status);
}
static void on_proc_exit(Proc *proc)
{
Loop *loop = proc->loop;
- ILOG("exited: pid=%d status=%d stoptime=%" PRIu64, proc->pid, proc->status,
- proc->stopped_time);
+ ILOG("child exited: pid=%d status=%d" PRIu64, proc->pid, proc->status);
+ // XXX: This assumes the TUI never spawns any other processes...?
if (ui_client_channel_id) {
- exit_from_channel(proc->status);
+ exit_on_closed_chan(proc->status);
}
// Process has terminated, but there could still be data to be read from the
diff --git a/src/nvim/event/stream.c b/src/nvim/event/stream.c
index 9c155b55ea..3e32813e1c 100644
--- a/src/nvim/event/stream.c
+++ b/src/nvim/event/stream.c
@@ -104,9 +104,10 @@ void stream_may_close(Stream *stream, bool rstream)
if (stream->closed) {
return;
}
- assert(!stream->closed);
DLOG("closing Stream: %p", (void *)stream);
stream->closed = true;
+ // TODO(justinmk): stream->close_cb is never actually invoked. Either remove it, or see if it can
+ // be used somewhere...
stream->close_cb = NULL;
stream->close_cb_data = NULL;
diff --git a/src/nvim/ex_cmds.lua b/src/nvim/ex_cmds.lua
index 0cdc397e9c..d118c808d3 100644
--- a/src/nvim/ex_cmds.lua
+++ b/src/nvim/ex_cmds.lua
@@ -733,6 +733,12 @@ M.cmds = {
func = 'ex_delfunction',
},
{
+ command = 'detach',
+ flags = bit.bor(BANG, FILES, CMDARG, ARGOPT, TRLBAR, CMDWIN, LOCK_OK),
+ addr_type = 'ADDR_NONE',
+ func = 'ex_detach',
+ },
+ {
command = 'display',
flags = bit.bor(EXTRA, NOTRLCOM, TRLBAR, SBOXOK, CMDWIN, LOCK_OK),
addr_type = 'ADDR_NONE',
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index ceb17a995d..3b78092daf 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -14,6 +14,7 @@
#include "auto/config.h"
#include "nvim/api/private/defs.h"
#include "nvim/api/private/helpers.h"
+#include "nvim/api/ui.h"
#include "nvim/arglist.h"
#include "nvim/ascii_defs.h"
#include "nvim/autocmd.h"
@@ -21,6 +22,7 @@
#include "nvim/buffer.h"
#include "nvim/buffer_defs.h"
#include "nvim/change.h"
+#include "nvim/channel.h"
#include "nvim/charset.h"
#include "nvim/cmdexpand.h"
#include "nvim/cmdexpand_defs.h"
@@ -67,6 +69,7 @@
#include "nvim/message.h"
#include "nvim/mouse.h"
#include "nvim/move.h"
+#include "nvim/msgpack_rpc/server.h"
#include "nvim/normal.h"
#include "nvim/normal_defs.h"
#include "nvim/ops.h"
@@ -5530,6 +5533,56 @@ static void ex_tabs(exarg_T *eap)
}
}
+/// ":detach"
+///
+/// Detaches the current UI.
+///
+/// ":detach!" with bang (!) detaches all UIs _except_ the current UI.
+static void ex_detach(exarg_T *eap)
+{
+ // come on pooky let's burn this mf down
+ if (eap && eap->forceit) {
+ emsg("bang (!) not supported yet");
+ } else {
+ // 1. (TODO) Send "detach" UI-event (notification only).
+ // 2. Perform server-side `nvim_ui_detach`.
+ // 3. Close server-side channel without self-exit.
+
+ if (!current_ui) {
+ emsg("UI not attached");
+ return;
+ }
+
+ Channel *chan = find_channel(current_ui);
+ if (!chan) {
+ emsg(e_invchan);
+ return;
+ }
+ chan->detach = true; // Prevent self-exit on channel-close.
+
+ // Server-side UI detach. Doesn't close the channel.
+ Error err2 = ERROR_INIT;
+ nvim_ui_detach(chan->id, &err2);
+ 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));
+
+ ILOG("detach current_ui=%" PRId64, chan->id);
+ }
+}
+
/// ":mode":
/// If no argument given, get the screen size and redraw.
static void ex_mode(exarg_T *eap)
diff --git a/src/nvim/globals.h b/src/nvim/globals.h
index a473180349..ea349352a8 100644
--- a/src/nvim/globals.h
+++ b/src/nvim/globals.h
@@ -301,6 +301,8 @@ EXTERN bool garbage_collect_at_exit INIT( = false);
EXTERN sctx_T current_sctx INIT( = { 0, 0, 0 });
// ID of the current channel making a client API call
EXTERN uint64_t current_channel_id INIT( = 0);
+/// Last channel that invoked 'nvim_input` or got FocusGained.
+EXTERN uint64_t current_ui INIT( = 0);
EXTERN bool did_source_packages INIT( = false);
diff --git a/src/nvim/msgpack_rpc/channel.c b/src/nvim/msgpack_rpc/channel.c
index e38bc1896d..fa50afd83b 100644
--- a/src/nvim/msgpack_rpc/channel.c
+++ b/src/nvim/msgpack_rpc/channel.c
@@ -224,8 +224,7 @@ static size_t receive_msgpack(RStream *stream, const char *rbuf, size_t c, void
if (eof) {
channel_close(channel->id, kChannelPartRpc, NULL);
char buf[256];
- snprintf(buf, sizeof(buf), "ch %" PRIu64 " was closed by the client",
- channel->id);
+ snprintf(buf, sizeof(buf), "ch %" PRIu64 " was closed by the peer", channel->id);
chan_close_with_error(channel, buf, LOGLVL_INF);
}
@@ -293,7 +292,7 @@ static void parse_msgpack(Channel *channel)
Object res = p->result;
if (p->result.type != kObjectTypeArray) {
- chan_close_with_error(channel, "msgpack-rpc request args has to be an array", LOGLVL_ERR);
+ chan_close_with_error(channel, "msgpack-rpc request args must be an array", LOGLVL_ERR);
return;
}
Array arg = res.data.array;
@@ -487,13 +486,16 @@ void rpc_close(Channel *channel)
channel->rpc.closed = true;
channel_decref(channel);
- if (channel->streamtype == kChannelStreamStdio
- || (channel->id == ui_client_channel_id && channel->streamtype != kChannelStreamProc)) {
- if (channel->streamtype == kChannelStreamStdio) {
- // Avoid hanging when there are no other UIs and a prompt is triggered on exit.
- remote_ui_disconnect(channel->id);
+ if (ui_client_channel_id && channel->id == ui_client_channel_id) {
+ assert(!channel->detach); // `Channel.detach` is not currently used by the UI client.
+ exit_on_closed_chan(0);
+ } else if (channel->streamtype == kChannelStreamStdio) {
+ // Avoid hanging when there are no other UIs and a prompt is triggered on exit.
+ remote_ui_disconnect(channel->id);
+
+ if (!channel->detach) {
+ exit_on_closed_chan(0);
}
- exit_from_channel(0);
}
}
diff --git a/src/nvim/os/input.c b/src/nvim/os/input.c
index 3259fb500b..f510c67753 100644
--- a/src/nvim/os/input.c
+++ b/src/nvim/os/input.c
@@ -274,8 +274,10 @@ void input_enqueue_raw(const char *data, size_t size)
input_write_pos += to_write;
}
-size_t input_enqueue(String keys)
+size_t input_enqueue(uint64_t chan_id, String keys)
{
+ current_ui = chan_id;
+
const char *ptr = keys.data;
const char *end = ptr + keys.size;
diff --git a/src/nvim/ui_client.c b/src/nvim/ui_client.c
index af946d799a..44fc645a04 100644
--- a/src/nvim/ui_client.c
+++ b/src/nvim/ui_client.c
@@ -61,9 +61,15 @@ uint64_t ui_client_start_server(int argc, char **argv)
CallbackReader on_err = CALLBACK_READER_INIT;
on_err.fwd_err = true;
+#ifdef MSWIN
+ // TODO(justinmk): detach breaks `tt.setup_child_nvim` tests on Windows?
+ bool detach = os_env_exists("__NVIM_DETACH");
+#else
+ bool detach = true;
+#endif
Channel *channel = channel_job_start(args, get_vim_var_str(VV_PROGPATH),
CALLBACK_READER_INIT, on_err, CALLBACK_NONE,
- false, true, true, false, kChannelStdinPipe,
+ false, true, true, detach, kChannelStdinPipe,
NULL, 0, 0, NULL, &exit_status);
if (!channel) {
return 0;
diff --git a/test/functional/core/job_spec.lua b/test/functional/core/job_spec.lua
index e833b5127d..75e09f3455 100644
--- a/test/functional/core/job_spec.lua
+++ b/test/functional/core/job_spec.lua
@@ -1265,12 +1265,16 @@ describe('jobs', function()
]])
feed(':q<CR>')
- screen:expect([[
- |
- [Process exited 0]^ |
- |*4
- {3:-- TERMINAL --} |
- ]])
+ if is_os('freebsd') then
+ screen:expect { any = vim.pesc('[Process exited 0]') }
+ else
+ screen:expect([[
+ |
+ [Process exited 0]^ |
+ |*4
+ {3:-- TERMINAL --} |
+ ]])
+ end
end)
end)
diff --git a/test/functional/editor/put_spec.lua b/test/functional/editor/put_spec.lua
index 79f9d97bc5..aeb9c75e42 100644
--- a/test/functional/editor/put_spec.lua
+++ b/test/functional/editor/put_spec.lua
@@ -931,6 +931,8 @@ describe('put command', function()
end)
it('should ring the bell when deleting if not appropriate', function()
+ t.skip(t.is_os('bsd'), 'crashes on freebsd')
+
command('goto 2')
feed('i<bs><esc>')
expect([[
diff --git a/test/functional/terminal/tui_spec.lua b/test/functional/terminal/tui_spec.lua
index 147a8675cb..e2adcb66df 100644
--- a/test/functional/terminal/tui_spec.lua
+++ b/test/functional/terminal/tui_spec.lua
@@ -33,6 +33,133 @@ local assert_log = t.assert_log
local testlog = 'Xtest-tui-log'
+describe('TUI :detach', function()
+ before_each(function()
+ os.remove(testlog)
+ end)
+ teardown(function()
+ os.remove(testlog)
+ end)
+
+ it('does not stop server', function()
+ local job_opts = {
+ env = {
+ NVIM_LOG_FILE = testlog,
+ },
+ }
+
+ if is_os('win') then
+ -- TODO(justinmk): on Windows,
+ -- - tt.setup_child_nvim() is broken.
+ -- - session.lua is broken after the pipe closes.
+ -- So this test currently just exercises __NVIM_DETACH + :detach, without asserting anything.
+
+ -- TODO(justinmk): temporary hack for Windows.
+ job_opts.env['__NVIM_DETACH'] = '1'
+ n.clear(job_opts)
+
+ local screen = Screen.new(50, 10)
+ n.feed('iHello, World')
+ screen:expect([[
+ Hello, World^ |
+ {1:~ }|*8
+ {5:-- INSERT --} |
+ ]])
+
+ -- local addr = api.nvim_get_vvar('servername')
+ eq(1, #n.api.nvim_list_uis())
+
+ -- TODO(justinmk): test util should not freak out when the pipe closes.
+ n.expect_exit(n.command, 'detach')
+
+ -- n.get_session():close() -- XXX: hangs
+ -- n.set_session(n.connect(addr)) -- XXX: hangs
+ -- eq(0, #n.api.nvim_list_uis()) -- XXX: hangs
+
+ -- Avoid a dangling process.
+ n.get_session():close('kill')
+ -- n.expect_exit(n.command, 'qall!')
+
+ return
+ end
+
+ local server_super = n.clear()
+ local client_super = n.new_session(true)
+ finally(function()
+ server_super:close()
+ client_super:close()
+ end)
+
+ local child_server = new_pipename()
+ local screen = tt.setup_child_nvim({
+ '--listen',
+ child_server,
+ '-u',
+ 'NONE',
+ '-i',
+ 'NONE',
+ '--cmd',
+ 'colorscheme vim',
+ '--cmd',
+ nvim_set .. ' notermguicolors laststatus=2 background=dark',
+ }, job_opts)
+
+ tt.feed_data('iHello, World')
+ screen:expect {
+ grid = [[
+ Hello, World^ |
+ {4:~ }|*3
+ {MATCH:No Name}
+ {3:-- INSERT --} |
+ {3:-- TERMINAL --} |
+ ]],
+ }
+
+ local child_session = n.connect(child_server)
+ finally(function()
+ child_session:request('nvim_command', 'qall!')
+ end)
+ local status, child_uis = child_session:request('nvim_list_uis')
+ assert(status)
+ eq(1, #child_uis)
+
+ tt.feed_data('\027\027:detach\013')
+ -- Note: "Process exited" message is misleading; tt.setup_child_nvim() sees the foreground
+ -- process (client) exited, and doesn't know the server is still running?
+ screen:expect {
+ any = [[Process exited 0]],
+ }
+
+ child_uis --[[@type any[] ]] = ({ child_session:request('nvim_list_uis') })[2]
+ eq(0, #child_uis)
+
+ -- NOTE: The tt.setup_child_nvim() screen just wraps :terminal, it's not connected to the child.
+ -- To use it again, we need to detach the old one.
+ screen:detach()
+
+ -- Edit some text on the headless server.
+ status = (child_session:request('nvim_input', 'ddiWe did it, pooky.<Esc><Esc>'))
+ assert(status)
+
+ -- Test reattach by connecting a new TUI.
+ local screen_reattached = tt.setup_child_nvim({
+ '--remote-ui',
+ '--server',
+ child_server,
+ }, job_opts)
+
+ screen_reattached:expect {
+ grid = [[
+ We did it, pooky^. |
+ {4:~ }|*3
+ {5:[No Name] [+] }|
+ |
+ {3:-- TERMINAL --} |
+ ]],
+ }
+ end)
+end)
+
if t.skip(is_os('win')) then
return
end
@@ -48,10 +175,7 @@ describe('TUI', function()
screen = tt.setup_child_nvim({
'--listen',
child_server,
- '-u',
- 'NONE',
- '-i',
- 'NONE',
+ '--clean',
'--cmd',
nvim_set .. ' notermguicolors laststatus=2 background=dark',
'--cmd',
@@ -2222,7 +2346,7 @@ describe('TUI', function()
end)
local screen = tt.setup_screen(
0,
- ('"%s" -u NONE -i NONE --cmd "set noswapfile noshowcmd noruler" --cmd "normal iabc" > /dev/null 2>&1 && cat testF && rm testF'):format(
+ ('"%s" --clean --cmd "set noswapfile noshowcmd noruler" --cmd "normal iabc" > /dev/null 2>&1 && cat testF && rm testF'):format(
nvim_prog
),
nil,
@@ -2242,10 +2366,7 @@ describe('TUI', function()
it('<C-h> #10134', function()
local screen = tt.setup_child_nvim({
- '-u',
- 'NONE',
- '-i',
- 'NONE',
+ '--clean',
'--cmd',
'colorscheme vim',
'--cmd',
@@ -2275,10 +2396,7 @@ describe('TUI', function()
it('draws line with many trailing spaces correctly #24955', function()
local screen = tt.setup_child_nvim({
- '-u',
- 'NONE',
- '-i',
- 'NONE',
+ '--clean',
'--cmd',
'set notermguicolors',
'--cmd',
@@ -2312,10 +2430,7 @@ describe('TUI', function()
it('draws screen lines with leading spaces correctly #29711', function()
local screen = tt.setup_child_nvim({
- '-u',
- 'NONE',
- '-i',
- 'NONE',
+ '--clean',
'--cmd',
'set foldcolumn=6 | call setline(1, ["", repeat("aabb", 1000)]) | echo 42',
}, { extra_rows = 10, cols = 66 })
@@ -2355,10 +2470,7 @@ describe('TUI', function()
-- Set a different bg colour and change $TERM to something dumber so the `print_spaces()`
-- codepath in `clear_region()` is hit.
local screen = tt.setup_child_nvim({
- '-u',
- 'NONE',
- '-i',
- 'NONE',
+ '--clean',
'--cmd',
'set notermguicolors | highlight Normal ctermbg=red',
'--cmd',
@@ -2399,10 +2511,7 @@ describe('TUI UIEnter/UILeave', function()
it('fires exactly once, after VimEnter', function()
clear()
local screen = tt.setup_child_nvim({
- '-u',
- 'NONE',
- '-i',
- 'NONE',
+ '--clean',
'--cmd',
'colorscheme vim',
'--cmd',
@@ -2665,10 +2774,7 @@ describe("TUI 't_Co' (terminal colors)", function()
local function assert_term_colors(term, colorterm, maxcolors)
clear({ env = { TERM = term }, args = {} })
screen = tt.setup_child_nvim({
- '-u',
- 'NONE',
- '-i',
- 'NONE',
+ '--clean',
'--cmd',
'colorscheme vim',
'--cmd',
@@ -2948,10 +3054,7 @@ describe("TUI 'term' option", function()
local function assert_term(term_envvar, term_expected)
clear()
screen = tt.setup_child_nvim({
- '-u',
- 'NONE',
- '-i',
- 'NONE',
+ '--clean',
'--cmd',
nvim_set .. ' notermguicolors',
}, {
@@ -3008,10 +3111,7 @@ describe('TUI', function()
local function nvim_tui(extra_args)
clear()
screen = tt.setup_child_nvim({
- '-u',
- 'NONE',
- '-i',
- 'NONE',
+ '--clean',
'--cmd',
'colorscheme vim',
'--cmd',
@@ -3089,12 +3189,9 @@ describe('TUI', function()
local child_server = new_pipename()
screen = tt.setup_child_nvim({
+ '--clean',
'--listen',
child_server,
- '-u',
- 'NONE',
- '-i',
- 'NONE',
}, {
env = {
VIMRUNTIME = os.getenv('VIMRUNTIME'),
@@ -3146,12 +3243,9 @@ describe('TUI', function()
local child_server = new_pipename()
screen = tt.setup_child_nvim({
+ '--clean',
'--listen',
child_server,
- '-u',
- 'NONE',
- '-i',
- 'NONE',
}, {
env = {
VIMRUNTIME = os.getenv('VIMRUNTIME'),
@@ -3220,12 +3314,9 @@ describe('TUI bg color', function()
command('set background=dark') -- set outer Nvim background
local child_server = new_pipename()
local screen = tt.setup_child_nvim({
+ '--clean',
'--listen',
child_server,
- '-u',
- 'NONE',
- '-i',
- 'NONE',
'--cmd',
'colorscheme vim',
'--cmd',
@@ -3243,12 +3334,9 @@ describe('TUI bg color', function()
command('set background=light') -- set outer Nvim background
local child_server = new_pipename()
local screen = tt.setup_child_nvim({
+ '--clean',
'--listen',
child_server,
- '-u',
- 'NONE',
- '-i',
- 'NONE',
'--cmd',
'colorscheme vim',
'--cmd',
@@ -3274,10 +3362,7 @@ describe('TUI bg color', function()
})
]])
tt.setup_child_nvim({
- '-u',
- 'NONE',
- '-i',
- 'NONE',
+ '--clean',
'--cmd',
'colorscheme vim',
'--cmd',
@@ -3290,10 +3375,7 @@ describe('TUI bg color', function()
it('triggers OptionSet from automatic background processing', function()
local screen = tt.setup_child_nvim({
- '-u',
- 'NONE',
- '-i',
- 'NONE',
+ '--clean',
'--cmd',
'colorscheme vim',
'--cmd',
@@ -3314,12 +3396,9 @@ describe('TUI bg color', function()
command('set background=dark') -- set outer Nvim background
local child_server = new_pipename()
local screen = tt.setup_child_nvim({
+ '--clean',
'--listen',
child_server,
- '-u',
- 'NONE',
- '-i',
- 'NONE',
'--cmd',
'colorscheme vim',
'--cmd',
@@ -3349,12 +3428,9 @@ describe('TUI client', function()
set_session(server_super)
local server_pipe = new_pipename()
local screen_server = tt.setup_child_nvim({
+ '--clean',
'--listen',
server_pipe,
- '-u',
- 'NONE',
- '-i',
- 'NONE',
'--cmd',
'colorscheme vim',
'--cmd',
@@ -3384,9 +3460,9 @@ describe('TUI client', function()
set_session(client_super)
local screen_client = tt.setup_child_nvim({
+ '--remote-ui',
'--server',
server_pipe,
- '--remote-ui',
})
screen_client:expect {
@@ -3428,9 +3504,9 @@ describe('TUI client', function()
set_session(client_super)
local screen_client = tt.setup_child_nvim({
+ '--remote-ui',
'--server',
server_pipe,
- '--remote-ui',
})
screen_client:expect {
@@ -3457,7 +3533,7 @@ describe('TUI client', function()
eq(0, api.nvim_get_vvar('shell_error'))
-- exits on input eof #22244
- fn.system({ nvim_prog, '--server', server_pipe, '--remote-ui' })
+ fn.system({ nvim_prog, '--remote-ui', '--server', server_pipe })
eq(1, api.nvim_get_vvar('shell_error'))
client_super:close()
@@ -3470,9 +3546,9 @@ describe('TUI client', function()
it('throws error when no server exists', function()
clear()
local screen = tt.setup_child_nvim({
+ '--remote-ui',
'--server',
'127.0.0.1:2436546',
- '--remote-ui',
}, { cols = 60 })
screen:expect([[
@@ -3485,18 +3561,18 @@ describe('TUI client', function()
end)
local function test_remote_tui_quit(status)
- local server_super = n.new_session(false)
+ local server_super = n.clear()
local client_super = n.new_session(true)
+ finally(function()
+ server_super:close()
+ client_super:close()
+ end)
- set_session(server_super)
local server_pipe = new_pipename()
local screen_server = tt.setup_child_nvim({
+ '--clean',
'--listen',
server_pipe,
- '-u',
- 'NONE',
- '-i',
- 'NONE',
'--cmd',
'colorscheme vim',
'--cmd',
@@ -3535,9 +3611,9 @@ describe('TUI client', function()
set_session(client_super)
local screen_client = tt.setup_child_nvim({
+ '--remote-ui',
'--server',
server_pipe,
- '--remote-ui',
})
screen_client:expect {
@@ -3554,26 +3630,8 @@ describe('TUI client', function()
set_session(server_super)
feed_data(status and ':' .. status .. 'cquit!\n' or ':quit!\n')
status = status and status or 0
- screen_server:expect {
- grid = [[
- |
- [Process exited ]] .. status .. [[]^ {MATCH:%s+}|
- |*4
- {3:-- TERMINAL --} |
- ]],
- }
- -- assert that client has exited
- screen_client:expect {
- grid = [[
- |
- [Process exited ]] .. status .. [[]^ {MATCH:%s+}|
- |*4
- {3:-- TERMINAL --} |
- ]],
- }
-
- server_super:close()
- client_super:close()
+ screen_server:expect({ any = 'Process exited ' .. status })
+ screen_client:expect({ any = 'Process exited ' .. status })
end
describe('exits when server quits', function()
diff --git a/test/functional/testnvim.lua b/test/functional/testnvim.lua
index 59cb593cf7..9b7ece3c21 100644
--- a/test/functional/testnvim.lua
+++ b/test/functional/testnvim.lua
@@ -908,8 +908,10 @@ function M.is_asan()
return version:match('-fsanitize=[a-z,]*address')
end
--- Returns a valid, platform-independent Nvim listen address.
--- Useful for communicating with child instances.
+--- Returns a valid, platform-independent Nvim listen address.
+--- Useful for communicating with child instances.
+---
+--- @return string
function M.new_pipename()
-- HACK: Start a server temporarily, get the name, then stop it.
local pipename = M.eval('serverstart()')