commit 221b6ddf1c97e31c38aa81d2199e4c304cc2180c
parent dc05598d0201d156e8ac435f156751e7f6b224ae
Author: bfredl <bjorn.linse@gmail.com>
Date: Thu, 12 Jun 2025 11:35:33 +0200
Merge pull request #34412 from bfredl/ui_buf
fix(msgpack): flush incomplete big UI event before packing RPC event
Diffstat:
4 files changed, 33 insertions(+), 6 deletions(-)
diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c
@@ -47,6 +47,7 @@
# include "ui_events_remote.generated.h" // IWYU pragma: export
#endif
+// TODO(bfredl): just make UI:s owned by their channels instead
static PMap(uint64_t) connected_uis = MAP_INIT;
static char *mpack_array_dyn16(char **buf)
@@ -91,10 +92,15 @@ void remote_ui_disconnect(uint64_t channel_id, Error *err, bool send_error_exit)
MAXSIZE_TEMP_ARRAY(args, 1);
ADD_C(args, INTEGER_OBJ(0));
push_call(ui, "error_exit", args);
- ui_flush_buf(ui);
+ ui_flush_buf(ui, false);
}
pmap_del(uint64_t)(&connected_uis, channel_id, NULL);
ui_detach_impl(ui, channel_id);
+ Channel *chan = find_channel(channel_id);
+ if (chan && chan->rpc.ui == ui) {
+ chan->rpc.ui = NULL;
+ }
+
remote_ui_destroy(ui);
}
@@ -192,6 +198,7 @@ void nvim_ui_attach(uint64_t channel_id, Integer width, Integer height, Dict opt
ui->nevents_pos = NULL;
ui->nevents = 0;
ui->flushed_events = false;
+ ui->incomplete_event = false;
ui->ncalls_pos = NULL;
ui->ncalls = 0;
ui->ncells_pending = 0;
@@ -208,6 +215,11 @@ void nvim_ui_attach(uint64_t channel_id, Integer width, Integer height, Dict opt
current_ui = channel_id;
ui_attach_impl(ui, channel_id);
+ Channel *chan = find_channel(channel_id);
+ if (chan) {
+ chan->rpc.ui = ui;
+ }
+
may_trigger_vim_suspend_resume(false);
}
@@ -588,7 +600,7 @@ static void prepare_call(RemoteUI *ui, const char *name)
{
if (ui->packer.startptr
&& (BUF_POS(ui) > UI_BUF_SIZE - EVENT_BUF_SIZE || ui->ncells_pending >= 500)) {
- ui_flush_buf(ui);
+ ui_flush_buf(ui, false);
}
if (ui->packer.startptr == NULL) {
@@ -630,7 +642,7 @@ static void push_call(RemoteUI *ui, const char *name, Array args)
static void ui_flush_callback(PackerBuffer *packer)
{
RemoteUI *ui = packer->anydata;
- ui_flush_buf(ui);
+ ui_flush_buf(ui, true);
ui_alloc_buf(ui);
}
@@ -856,7 +868,7 @@ void remote_ui_raw_line(RemoteUI *ui, Integer grid, Integer row, Integer startco
mpack_w2(&lenpos, nelem);
// We only ever set the wrap field on the final "grid_line" event for the line.
mpack_bool(buf, false);
- ui_flush_buf(ui);
+ ui_flush_buf(ui, false);
prepare_call(ui, "grid_line");
mpack_array(buf, 5);
@@ -929,11 +941,12 @@ void remote_ui_raw_line(RemoteUI *ui, Integer grid, Integer row, Integer startco
///
/// This might happen multiple times before the actual ui_flush, if the
/// total redraw size is large!
-static void ui_flush_buf(RemoteUI *ui)
+static void ui_flush_buf(RemoteUI *ui, bool incomplete_event)
{
if (!ui->packer.startptr || !BUF_POS(ui)) {
return;
}
+ ui->incomplete_event = incomplete_event;
flush_event(ui);
if (ui->nevents_pos != NULL) {
@@ -964,11 +977,16 @@ void remote_ui_flush(RemoteUI *ui)
remote_ui_cursor_goto(ui, ui->cursor_row, ui->cursor_col);
}
push_call(ui, "flush", (Array)ARRAY_DICT_INIT);
- ui_flush_buf(ui);
+ ui_flush_buf(ui, false);
ui->flushed_events = false;
}
}
+void remote_ui_flush_pending_data(RemoteUI *ui)
+{
+ ui_flush_buf(ui, false);
+}
+
static Array translate_contents(RemoteUI *ui, Array contents, Arena *arena)
{
Array new_contents = arena_array(arena, contents.size);
diff --git a/src/nvim/msgpack_rpc/channel.c b/src/nvim/msgpack_rpc/channel.c
@@ -608,6 +608,12 @@ void serialize_response(Channel *channel, MsgpackRpcRequestHandler handler, Mess
static void packer_buffer_init_channels(Channel **chans, size_t nchans, PackerBuffer *packer)
{
+ for (size_t i = 0; i < nchans; i++) {
+ Channel *chan = chans[i];
+ if (chan->rpc.ui && chan->rpc.ui->incomplete_event) {
+ remote_ui_flush_pending_data(chan->rpc.ui);
+ }
+ }
packer->startptr = alloc_block();
packer->ptr = packer->startptr;
packer->endptr = packer->startptr + ARENA_BLOCK_SIZE;
diff --git a/src/nvim/msgpack_rpc/channel_defs.h b/src/nvim/msgpack_rpc/channel_defs.h
@@ -5,6 +5,7 @@
#include "nvim/api/private/dispatch.h"
#include "nvim/map_defs.h"
+#include "nvim/ui_defs.h"
typedef struct Channel Channel;
typedef struct Unpacker Unpacker;
@@ -38,6 +39,7 @@ typedef struct {
typedef struct {
bool closed;
Unpacker *unpacker;
+ RemoteUI *ui;
uint32_t next_request_id;
kvec_t(ChannelCallFrame *) call_stack;
Dict info;
diff --git a/src/nvim/ui_defs.h b/src/nvim/ui_defs.h
@@ -72,6 +72,7 @@ typedef struct {
uint32_t nevents; ///< number of distinct events (top-level args to "redraw"
uint32_t ncalls; ///< number of calls made to the current event (plus one for the name!)
bool flushed_events; ///< events where sent to client without "flush" event
+ bool incomplete_event; ///< incomplete event might be pending
size_t ncells_pending; ///< total number of cells since last buffer flush