feat(ui): :connect command #34586

Add the `:connect <address>` command which connects the currently
running TUI to the server at the given address.
This commit is contained in:
Siddhant Agarwal
2025-08-15 04:28:09 +05:30
committed by GitHub
parent bd45e2be63
commit 649bb372f6
9 changed files with 218 additions and 3 deletions

View File

@@ -319,6 +319,20 @@ bool remote_ui_restart(uint64_t channel_id, Error *err)
return true;
}
// Send a connect UI event to the UI on the given channel
void remote_ui_connect(uint64_t channel_id, char *server_addr, Error *err)
{
RemoteUI *ui = get_ui_or_err(channel_id, err);
if (!ui) {
return;
}
MAXSIZE_TEMP_ARRAY(args, 1);
ADD_C(args, CSTR_AS_OBJ(server_addr));
push_call(ui, "connect", args);
}
// TODO(bfredl): use me to detach a specific ui from the server
void remote_ui_stop(RemoteUI *ui)
{

View File

@@ -27,6 +27,8 @@ void visual_bell(void)
FUNC_API_SINCE(3);
void flush(void)
FUNC_API_SINCE(3) FUNC_API_REMOTE_IMPL;
void connect(Array args)
FUNC_API_SINCE(14) FUNC_API_REMOTE_ONLY FUNC_API_REMOTE_IMPL FUNC_API_CLIENT_IMPL;
void restart(String progpath, Array argv)
FUNC_API_SINCE(14) FUNC_API_REMOTE_ONLY FUNC_API_REMOTE_IMPL FUNC_API_CLIENT_IMPL;
void suspend(void)

View File

@@ -146,7 +146,7 @@ void stream_close_handle(Stream *stream, bool rstream)
static void rstream_close_cb(uv_handle_t *handle)
{
RStream *stream = handle->data;
if (stream->buffer) {
if (stream && stream->buffer) {
free_block(stream->buffer);
}
close_cb(handle);
@@ -155,10 +155,10 @@ static void rstream_close_cb(uv_handle_t *handle)
static void close_cb(uv_handle_t *handle)
{
Stream *stream = handle->data;
if (stream->close_cb) {
if (stream && stream->close_cb) {
stream->close_cb(stream, stream->close_cb_data);
}
if (stream->internal_close_cb) {
if (stream && stream->internal_close_cb) {
stream->internal_close_cb(stream, stream->internal_data);
}
}

View File

@@ -582,6 +582,12 @@ M.cmds = {
addr_type = 'ADDR_OTHER',
func = 'ex_menu',
},
{
command = 'connect',
flags = bit.bor(BANG, WORD1, NOTRLCOM, NEEDARG),
addr_type = 'ADDR_NONE',
func = 'ex_connect',
},
{
command = 'copy',
flags = bit.bor(RANGE, WHOLEFOLD, EXTRA, TRLBAR, CMDWIN, LOCK_OK, MODIFY),

View File

@@ -5649,6 +5649,32 @@ static void ex_detach(exarg_T *eap)
}
}
/// ":connect"
///
/// Connects the current UI to a different server
///
/// ":connect <address>" detaches the current UI and connects to the given server.
/// ":connect! <address>" stops the current server if no other UIs are attached, then connects to the given server.
static void ex_connect(exarg_T *eap)
{
bool stop_server = eap->forceit ? (ui_active() == 1) : false;
Error err = ERROR_INIT;
remote_ui_connect(current_ui, eap->arg, &err);
if (ERROR_SET(&err)) {
emsg(err.msg);
api_clear_error(&err);
return;
}
ex_detach(NULL);
if (stop_server) {
exiting = true;
getout(0);
}
}
/// ":mode":
/// If no argument given, get the screen size and redraw.
static void ex_mode(exarg_T *eap)

View File

@@ -281,6 +281,38 @@ void ui_client_event_raw_line(GridLineEvent *g)
(const schar_T *)grid_line_buf_char, grid_line_buf_attr);
}
void ui_client_event_connect(Array args)
{
if (args.size < 1 || args.items[0].type != kObjectTypeString) {
ELOG("Error handling UI event 'connect'");
return;
}
char *server_addr = args.items[0].data.string.data;
multiqueue_put(main_loop.fast_events, channel_connect_event, server_addr);
}
static void channel_connect_event(void **argv)
{
char *server_addr = argv[0];
const char *err = "";
bool is_tcp = !!strrchr(server_addr, ':');
CallbackReader on_data = CALLBACK_READER_INIT;
uint64_t chan = channel_connect(is_tcp, server_addr, true, on_data, 50, &err);
if (!strequal(err, "")) {
ELOG("Error handling UI event 'connect': %s", err);
return;
}
ui_client_channel_id = chan;
ui_client_is_remote = true;
ui_client_attach(tui_width, tui_height, tui_term, tui_rgb);
ELOG("Connected to channel: %" PRId64, chan);
}
/// When a "restart" UI event is received, its arguments are saved here when
/// waiting for the server to exit.
static Array restart_args = ARRAY_DICT_INIT;