diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/nvim/api/private/defs.h | 15 | ||||
-rw-r--r-- | src/nvim/api/private/helpers.c | 26 | ||||
-rw-r--r-- | src/nvim/api/private/helpers.h | 14 | ||||
-rw-r--r-- | src/nvim/api/ui.c | 491 | ||||
-rw-r--r-- | src/nvim/api/ui_events.in.h | 2 | ||||
-rw-r--r-- | src/nvim/ex_docmd.c | 2 | ||||
-rw-r--r-- | src/nvim/ex_getln.c | 51 | ||||
-rwxr-xr-x | src/nvim/generators/gen_api_ui_events.lua | 21 | ||||
-rw-r--r-- | src/nvim/lib/kvec.h | 3 | ||||
-rw-r--r-- | src/nvim/memory.c | 1 | ||||
-rw-r--r-- | src/nvim/memory.h | 4 | ||||
-rw-r--r-- | src/nvim/message.c | 9 | ||||
-rw-r--r-- | src/nvim/msgpack_rpc/channel.c | 11 | ||||
-rw-r--r-- | src/nvim/msgpack_rpc/unpacker.c | 4 | ||||
-rw-r--r-- | src/nvim/normal.c | 10 | ||||
-rw-r--r-- | src/nvim/option.c | 6 | ||||
-rw-r--r-- | src/nvim/popupmnu.c | 19 | ||||
-rw-r--r-- | src/nvim/screen.c | 92 | ||||
-rw-r--r-- | src/nvim/ui.c | 17 | ||||
-rw-r--r-- | src/nvim/ui.h | 3 | ||||
-rw-r--r-- | src/nvim/window.c | 48 |
21 files changed, 580 insertions, 269 deletions
diff --git a/src/nvim/api/private/defs.h b/src/nvim/api/private/defs.h index 396fab721d..b1e0dd364c 100644 --- a/src/nvim/api/private/defs.h +++ b/src/nvim/api/private/defs.h @@ -6,9 +6,10 @@ #include <string.h> #include "nvim/func_attr.h" +#include "nvim/lib/kvec.h" #include "nvim/types.h" -#define ARRAY_DICT_INIT { .size = 0, .capacity = 0, .items = NULL } +#define ARRAY_DICT_INIT KV_INITIAL_VALUE #define STRING_INIT { .data = NULL, .size = 0 } #define OBJECT_INIT { .type = kObjectTypeNil } #define ERROR_INIT { .type = kErrorTypeNone, .msg = NULL } @@ -84,18 +85,10 @@ REMOTE_TYPE(Window); REMOTE_TYPE(Tabpage); typedef struct object Object; - -typedef struct { - Object *items; - size_t size, capacity; -} Array; +typedef kvec_t(Object) Array; typedef struct key_value_pair KeyValuePair; - -typedef struct { - KeyValuePair *items; - size_t size, capacity; -} Dictionary; +typedef kvec_t(KeyValuePair) Dictionary; typedef enum { kObjectTypeNil = 0, diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index ff6a4c37e8..693d946088 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -669,6 +669,32 @@ void api_free_string(String value) xfree(value.data); } +Array arena_array(Arena *arena, size_t max_size) +{ + Array arr = ARRAY_DICT_INIT; + kv_fixsize_arena(arena, arr, max_size); + return arr; +} + +Dictionary arena_dict(Arena *arena, size_t max_size) +{ + Dictionary dict = ARRAY_DICT_INIT; + kv_fixsize_arena(arena, dict, max_size); + return dict; +} + +String arena_string(Arena *arena, String str) +{ + if (str.size) { + char *mem = arena_alloc(arena, str.size + 1, false); + memcpy(mem, str.data, str.size); + mem[str.size] = NUL; + return cbuf_as_string(mem, str.size); + } else { + return (String)STRING_INIT; + } +} + void api_free_object(Object value) { switch (value.type) { diff --git a/src/nvim/api/private/helpers.h b/src/nvim/api/private/helpers.h index 8423db4970..a4348d8b44 100644 --- a/src/nvim/api/private/helpers.h +++ b/src/nvim/api/private/helpers.h @@ -63,17 +63,31 @@ #define PUT(dict, k, v) \ kv_push(dict, ((KeyValuePair) { .key = cstr_to_string(k), .value = v })) +#define PUT_C(dict, k, v) \ + kv_push_c(dict, ((KeyValuePair) { .key = cstr_as_string(k), .value = v })) + #define PUT_BOOL(dict, name, condition) PUT(dict, name, BOOLEAN_OBJ(condition)); #define ADD(array, item) \ kv_push(array, item) +#define ADD_C(array, item) \ + kv_push_c(array, item) + #define FIXED_TEMP_ARRAY(name, fixsize) \ Array name = ARRAY_DICT_INIT; \ Object name##__items[fixsize]; \ name.size = fixsize; \ name.items = name##__items; \ +#define MAXSIZE_TEMP_ARRAY(name, maxsize) \ + Array name = ARRAY_DICT_INIT; \ + Object name##__items[maxsize]; \ + name.capacity = maxsize; \ + name.items = name##__items; \ + +#define cbuf_as_string(d, s) ((String) { .data = d, .size = s }) + #define STATIC_CSTR_AS_STRING(s) ((String) { .data = s, .size = sizeof(s) - 1 }) /// Create a new String instance, putting data in allocated memory diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c index dc04eedebc..54ce838b9b 100644 --- a/src/nvim/api/ui.c +++ b/src/nvim/api/ui.c @@ -9,11 +9,13 @@ #include "nvim/api/private/defs.h" #include "nvim/api/private/helpers.h" #include "nvim/api/ui.h" +#include "nvim/channel.h" #include "nvim/cursor_shape.h" #include "nvim/highlight.h" #include "nvim/map.h" #include "nvim/memory.h" #include "nvim/msgpack_rpc/channel.h" +#include "nvim/msgpack_rpc/helpers.h" #include "nvim/option.h" #include "nvim/popupmnu.h" #include "nvim/screen.h" @@ -21,14 +23,32 @@ #include "nvim/vim.h" #include "nvim/window.h" -#ifdef INCLUDE_GENERATED_DECLARATIONS -# include "api/ui.c.generated.h" -# include "ui_events_remote.generated.h" -#endif - typedef struct { uint64_t channel_id; - Array buffer; + +#define UI_BUF_SIZE 4096 ///< total buffer size for pending msgpack data. + /// guranteed size available for each new event (so packing of simple events + /// and the header of grid_line will never fail) +#define EVENT_BUF_SIZE 256 + char buf[UI_BUF_SIZE]; ///< buffer of packed but not yet sent msgpack data + char *buf_wptr; ///< write head of buffer + const char *cur_event; ///< name of current event (might get multiple arglists) + Array call_buf; ///< buffer for constructing a single arg list (max 16 elements!) + + // state for write_cb, while packing a single arglist to msgpack. This + // might fail due to buffer overflow. + size_t pack_totlen; + bool buf_overflow; + char *temp_buf; + + // We start packing the two outermost msgpack arrays before knowing the total + // number of elements. Thus track the location where array size will need + // to be written in the msgpack buffer, once the specifc array is finished. + char *nevents_pos; + char *ncalls_pos; + 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 int hl_id; // Current highlight for legacy put event. Integer cursor_row, cursor_col; // Intended visible cursor position. @@ -38,8 +58,76 @@ typedef struct { bool wildmenu_active; } UIData; +#define BUF_POS(data) ((size_t)((data)->buf_wptr - (data)->buf)) + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "api/ui.c.generated.h" +# include "ui_events_remote.generated.h" +#endif + static PMap(uint64_t) connected_uis = MAP_INIT; +#define mpack_w(b, byte) *(*b)++ = (char)(byte); +static void mpack_w2(char **b, uint32_t v) +{ + *(*b)++ = (char)((v >> 8) & 0xff); + *(*b)++ = (char)(v & 0xff); +} + +static void mpack_w4(char **b, uint32_t v) +{ + *(*b)++ = (char)((v >> 24) & 0xff); + *(*b)++ = (char)((v >> 16) & 0xff); + *(*b)++ = (char)((v >> 8) & 0xff); + *(*b)++ = (char)(v & 0xff); +} + +static void mpack_uint(char **buf, uint32_t val) +{ + if (val > 0xffff) { + mpack_w(buf, 0xce); + mpack_w4(buf, val); + } else if (val > 0xff) { + mpack_w(buf, 0xcd); + mpack_w2(buf, val); + } else if (val > 0x7f) { + mpack_w(buf, 0xcc); + mpack_w(buf, val); + } else { + mpack_w(buf, val); + } +} + +static void mpack_array(char **buf, uint32_t len) +{ + if (len < 0x10) { + mpack_w(buf, 0x90 | len); + } else if (len < 0x10000) { + mpack_w(buf, 0xdc); + mpack_w2(buf, len); + } else { + mpack_w(buf, 0xdd); + mpack_w4(buf, len); + } +} + +static char *mpack_array_dyn16(char **buf) +{ + mpack_w(buf, 0xdc); + char *pos = *buf; + mpack_w2(buf, 0xFFEF); + return pos; +} + +static void mpack_str(char **buf, const char *str) +{ + assert(sizeof(schar_T) - 1 < 0x20); + size_t len = STRLEN(str); + mpack_w(buf, 0xa0 | len); + memcpy(*buf, str, len); + *buf += len; +} + void remote_ui_disconnect(uint64_t channel_id) { UI *ui = pmap_get(uint64_t)(&connected_uis, channel_id); @@ -47,9 +135,9 @@ void remote_ui_disconnect(uint64_t channel_id) return; } UIData *data = ui->data; - api_free_array(data->buffer); // Destroy pending screen updates. + kv_destroy(data->call_buf); pmap_del(uint64_t)(&connected_uis, channel_id); - xfree(ui->data); + xfree(data); ui->data = NULL; // Flag UI as "stopped". ui_detach_impl(ui, channel_id); xfree(ui); @@ -159,10 +247,19 @@ void nvim_ui_attach(uint64_t channel_id, Integer width, Integer height, Dictiona UIData *data = xmalloc(sizeof(UIData)); data->channel_id = channel_id; - data->buffer = (Array)ARRAY_DICT_INIT; + data->cur_event = NULL; data->hl_id = 0; data->client_col = -1; + data->nevents_pos = NULL; + data->nevents = 0; + data->flushed_events = false; + data->ncalls_pos = NULL; + data->ncalls = 0; + data->buf_wptr = data->buf; + data->temp_buf = NULL; data->wildmenu_active = false; + data->call_buf = (Array)ARRAY_DICT_INIT; + kv_ensure_space(data->call_buf, 16); ui->data = data; pmap_put(uint64_t)(&connected_uis, channel_id, ui); @@ -433,34 +530,128 @@ void nvim_ui_pum_set_bounds(uint64_t channel_id, Float width, Float height, Floa ui->pum_pos = true; } -/// Pushes data into UI.UIData, to be consumed later by remote_ui_flush(). -static void push_call(UI *ui, const char *name, Array args) +static void flush_event(UIData *data) +{ + if (data->cur_event) { + mpack_w2(&data->ncalls_pos, data->ncalls); + data->cur_event = NULL; + } + if (!data->nevents_pos) { + assert(BUF_POS(data) == 0); + char **buf = &data->buf_wptr; + // [2, "redraw", [...]] + mpack_array(buf, 3); + mpack_uint(buf, 2); + mpack_str(buf, "redraw"); + data->nevents_pos = mpack_array_dyn16(buf); + } +} + +static inline int write_cb(void *vdata, const char *buf, size_t len) +{ + UIData *data = (UIData *)vdata; + if (!buf) { + return 0; + } + + data->pack_totlen += len; + if (!data->temp_buf && UI_BUF_SIZE - BUF_POS(data) < len) { + data->buf_overflow = true; + return 0; + } + + memcpy(data->buf_wptr, buf, len); + data->buf_wptr += len; + + return 0; +} + +static bool prepare_call(UI *ui, const char *name) { - Array call = ARRAY_DICT_INIT; UIData *data = ui->data; - // To optimize data transfer(especially for "put"), we bundle adjacent + if (BUF_POS(data) > UI_BUF_SIZE - EVENT_BUF_SIZE) { + remote_ui_flush_buf(ui); + } + + // To optimize data transfer(especially for "grid_line"), we bundle adjacent // calls to same method together, so only add a new call entry if the last // method call is different from "name" - if (kv_size(data->buffer)) { - call = kv_A(data->buffer, kv_size(data->buffer) - 1).data.array; - } - if (!kv_size(call) || strcmp(kv_A(call, 0).data.string.data, name)) { - call = (Array)ARRAY_DICT_INIT; - ADD(data->buffer, ARRAY_OBJ(call)); - ADD(call, STRING_OBJ(cstr_to_string(name))); + if (!data->cur_event || !strequal(data->cur_event, name)) { + flush_event(data); + data->cur_event = name; + char **buf = &data->buf_wptr; + data->ncalls_pos = mpack_array_dyn16(buf); + mpack_str(buf, name); + data->nevents++; + data->ncalls = 1; + return true; } - ADD(call, ARRAY_OBJ(args)); - kv_A(data->buffer, kv_size(data->buffer) - 1).data.array = call; + return false; +} + +/// Pushes data into UI.UIData, to be consumed later by remote_ui_flush(). +static void push_call(UI *ui, const char *name, Array args) +{ + UIData *data = ui->data; + bool pending = data->nevents_pos; + char *buf_pos_save = data->buf_wptr; + + bool new_event = prepare_call(ui, name); + + msgpack_packer pac; + data->pack_totlen = 0; + data->buf_overflow = false; + msgpack_packer_init(&pac, data, write_cb); + msgpack_rpc_from_array(args, &pac); + if (data->buf_overflow) { + data->buf_wptr = buf_pos_save; + if (new_event) { + data->cur_event = NULL; + data->nevents--; + } + if (pending) { + remote_ui_flush_buf(ui); + } + + if (data->pack_totlen > UI_BUF_SIZE - strlen(name) - 20) { + // TODO(bfredl): manually testable by setting UI_BUF_SIZE to 1024 (mode_info_set) + data->temp_buf = xmalloc(20 + strlen(name) + data->pack_totlen); + data->buf_wptr = data->temp_buf; + char **buf = &data->buf_wptr; + mpack_array(buf, 3); + mpack_uint(buf, 2); + mpack_str(buf, "redraw"); + mpack_array(buf, 1); + mpack_array(buf, 2); + mpack_str(buf, name); + } else { + prepare_call(ui, name); + } + data->pack_totlen = 0; + data->buf_overflow = false; + msgpack_rpc_from_array(args, &pac); + + if (data->temp_buf) { + size_t size = (size_t)(data->buf_wptr - data->temp_buf); + WBuffer *buf = wstream_new_buffer(data->temp_buf, size, 1, xfree); + rpc_write_raw(data->channel_id, buf); + data->temp_buf = NULL; + data->buf_wptr = data->buf; + data->nevents_pos = NULL; + } + } + data->ncalls++; } static void remote_ui_grid_clear(UI *ui, Integer grid) { - Array args = ARRAY_DICT_INIT; + UIData *data = ui->data; + Array args = data->call_buf; if (ui->ui_ext[kUILinegrid]) { - ADD(args, INTEGER_OBJ(grid)); + ADD_C(args, INTEGER_OBJ(grid)); } const char *name = ui->ui_ext[kUILinegrid] ? "grid_clear" : "clear"; push_call(ui, name, args); @@ -468,12 +659,13 @@ static void remote_ui_grid_clear(UI *ui, Integer grid) static void remote_ui_grid_resize(UI *ui, Integer grid, Integer width, Integer height) { - Array args = ARRAY_DICT_INIT; + UIData *data = ui->data; + Array args = data->call_buf; if (ui->ui_ext[kUILinegrid]) { - ADD(args, INTEGER_OBJ(grid)); + ADD_C(args, INTEGER_OBJ(grid)); } - ADD(args, INTEGER_OBJ(width)); - ADD(args, INTEGER_OBJ(height)); + ADD_C(args, INTEGER_OBJ(width)); + ADD_C(args, INTEGER_OBJ(height)); const char *name = ui->ui_ext[kUILinegrid] ? "grid_resize" : "resize"; push_call(ui, name, args); } @@ -481,35 +673,36 @@ static void remote_ui_grid_resize(UI *ui, Integer grid, Integer width, Integer h static void remote_ui_grid_scroll(UI *ui, Integer grid, Integer top, Integer bot, Integer left, Integer right, Integer rows, Integer cols) { + UIData *data = ui->data; if (ui->ui_ext[kUILinegrid]) { - Array args = ARRAY_DICT_INIT; - ADD(args, INTEGER_OBJ(grid)); - ADD(args, INTEGER_OBJ(top)); - ADD(args, INTEGER_OBJ(bot)); - ADD(args, INTEGER_OBJ(left)); - ADD(args, INTEGER_OBJ(right)); - ADD(args, INTEGER_OBJ(rows)); - ADD(args, INTEGER_OBJ(cols)); + Array args = data->call_buf; + ADD_C(args, INTEGER_OBJ(grid)); + ADD_C(args, INTEGER_OBJ(top)); + ADD_C(args, INTEGER_OBJ(bot)); + ADD_C(args, INTEGER_OBJ(left)); + ADD_C(args, INTEGER_OBJ(right)); + ADD_C(args, INTEGER_OBJ(rows)); + ADD_C(args, INTEGER_OBJ(cols)); push_call(ui, "grid_scroll", args); } else { - Array args = ARRAY_DICT_INIT; - ADD(args, INTEGER_OBJ(top)); - ADD(args, INTEGER_OBJ(bot - 1)); - ADD(args, INTEGER_OBJ(left)); - ADD(args, INTEGER_OBJ(right - 1)); + Array args = data->call_buf; + ADD_C(args, INTEGER_OBJ(top)); + ADD_C(args, INTEGER_OBJ(bot - 1)); + ADD_C(args, INTEGER_OBJ(left)); + ADD_C(args, INTEGER_OBJ(right - 1)); push_call(ui, "set_scroll_region", args); - args = (Array)ARRAY_DICT_INIT; - ADD(args, INTEGER_OBJ(rows)); + args = data->call_buf; + ADD_C(args, INTEGER_OBJ(rows)); push_call(ui, "scroll", args); // some clients have "clear" being affected by scroll region, // so reset it. - args = (Array)ARRAY_DICT_INIT; - ADD(args, INTEGER_OBJ(0)); - ADD(args, INTEGER_OBJ(ui->height - 1)); - ADD(args, INTEGER_OBJ(0)); - ADD(args, INTEGER_OBJ(ui->width - 1)); + args = data->call_buf; + ADD_C(args, INTEGER_OBJ(0)); + ADD_C(args, INTEGER_OBJ(ui->height - 1)); + ADD_C(args, INTEGER_OBJ(0)); + ADD_C(args, INTEGER_OBJ(ui->width - 1)); push_call(ui, "set_scroll_region", args); } } @@ -520,26 +713,27 @@ static void remote_ui_default_colors_set(UI *ui, Integer rgb_fg, Integer rgb_bg, if (!ui->ui_ext[kUITermColors]) { HL_SET_DEFAULT_COLORS(rgb_fg, rgb_bg, rgb_sp); } - Array args = ARRAY_DICT_INIT; - ADD(args, INTEGER_OBJ(rgb_fg)); - ADD(args, INTEGER_OBJ(rgb_bg)); - ADD(args, INTEGER_OBJ(rgb_sp)); - ADD(args, INTEGER_OBJ(cterm_fg)); - ADD(args, INTEGER_OBJ(cterm_bg)); + UIData *data = ui->data; + Array args = data->call_buf; + ADD_C(args, INTEGER_OBJ(rgb_fg)); + ADD_C(args, INTEGER_OBJ(rgb_bg)); + ADD_C(args, INTEGER_OBJ(rgb_sp)); + ADD_C(args, INTEGER_OBJ(cterm_fg)); + ADD_C(args, INTEGER_OBJ(cterm_bg)); push_call(ui, "default_colors_set", args); // Deprecated if (!ui->ui_ext[kUILinegrid]) { - args = (Array)ARRAY_DICT_INIT; - ADD(args, INTEGER_OBJ(ui->rgb ? rgb_fg : cterm_fg - 1)); + args = data->call_buf; + ADD_C(args, INTEGER_OBJ(ui->rgb ? rgb_fg : cterm_fg - 1)); push_call(ui, "update_fg", args); - args = (Array)ARRAY_DICT_INIT; - ADD(args, INTEGER_OBJ(ui->rgb ? rgb_bg : cterm_bg - 1)); + args = data->call_buf; + ADD_C(args, INTEGER_OBJ(ui->rgb ? rgb_bg : cterm_bg - 1)); push_call(ui, "update_bg", args); - args = (Array)ARRAY_DICT_INIT; - ADD(args, INTEGER_OBJ(ui->rgb ? rgb_sp : -1)); + args = data->call_buf; + ADD_C(args, INTEGER_OBJ(ui->rgb ? rgb_sp : -1)); push_call(ui, "update_sp", args); } } @@ -550,25 +744,29 @@ static void remote_ui_hl_attr_define(UI *ui, Integer id, HlAttrs rgb_attrs, HlAt if (!ui->ui_ext[kUILinegrid]) { return; } - Array args = ARRAY_DICT_INIT; - ADD(args, INTEGER_OBJ(id)); - ADD(args, DICTIONARY_OBJ(hlattrs2dict(rgb_attrs, true))); - ADD(args, DICTIONARY_OBJ(hlattrs2dict(cterm_attrs, false))); + UIData *data = ui->data; + Array args = data->call_buf; + ADD_C(args, INTEGER_OBJ(id)); + ADD_C(args, DICTIONARY_OBJ(hlattrs2dict(rgb_attrs, true))); + ADD_C(args, DICTIONARY_OBJ(hlattrs2dict(cterm_attrs, false))); if (ui->ui_ext[kUIHlState]) { - ADD(args, ARRAY_OBJ(copy_array(info))); + ADD_C(args, ARRAY_OBJ(info)); } else { - ADD(args, ARRAY_OBJ((Array)ARRAY_DICT_INIT)); + ADD_C(args, ARRAY_OBJ((Array)ARRAY_DICT_INIT)); } push_call(ui, "hl_attr_define", args); + // TODO(bfredl): could be elided + api_free_dictionary(kv_A(args, 1).data.dictionary); + api_free_dictionary(kv_A(args, 2).data.dictionary); } static void remote_ui_highlight_set(UI *ui, int id) { - Array args = ARRAY_DICT_INIT; UIData *data = ui->data; + Array args = data->call_buf; if (data->hl_id == id) { return; @@ -576,18 +774,20 @@ static void remote_ui_highlight_set(UI *ui, int id) data->hl_id = id; Dictionary hl = hlattrs2dict(syn_attr2entry(id), ui->rgb); - ADD(args, DICTIONARY_OBJ(hl)); + ADD_C(args, DICTIONARY_OBJ(hl)); push_call(ui, "highlight_set", args); + api_free_dictionary(kv_A(args, 0).data.dictionary); } /// "true" cursor used only for input focus static void remote_ui_grid_cursor_goto(UI *ui, Integer grid, Integer row, Integer col) { if (ui->ui_ext[kUILinegrid]) { - Array args = ARRAY_DICT_INIT; - ADD(args, INTEGER_OBJ(grid)); - ADD(args, INTEGER_OBJ(row)); - ADD(args, INTEGER_OBJ(col)); + UIData *data = ui->data; + Array args = data->call_buf; + ADD_C(args, INTEGER_OBJ(grid)); + ADD_C(args, INTEGER_OBJ(row)); + ADD_C(args, INTEGER_OBJ(col)); push_call(ui, "grid_cursor_goto", args); } else { UIData *data = ui->data; @@ -606,9 +806,9 @@ static void remote_ui_cursor_goto(UI *ui, Integer row, Integer col) } data->client_row = row; data->client_col = col; - Array args = ARRAY_DICT_INIT; - ADD(args, INTEGER_OBJ(row)); - ADD(args, INTEGER_OBJ(col)); + Array args = data->call_buf; + ADD_C(args, INTEGER_OBJ(row)); + ADD_C(args, INTEGER_OBJ(col)); push_call(ui, "cursor_goto", args); } @@ -616,8 +816,8 @@ static void remote_ui_put(UI *ui, const char *cell) { UIData *data = ui->data; data->client_col++; - Array args = ARRAY_DICT_INIT; - ADD(args, STRING_OBJ(cstr_to_string(cell))); + Array args = data->call_buf; + ADD_C(args, STRING_OBJ(cstr_as_string((char *)cell))); push_call(ui, "put", args); } @@ -627,41 +827,64 @@ static void remote_ui_raw_line(UI *ui, Integer grid, Integer row, Integer startc { UIData *data = ui->data; if (ui->ui_ext[kUILinegrid]) { - Array args = ARRAY_DICT_INIT; - ADD(args, INTEGER_OBJ(grid)); - ADD(args, INTEGER_OBJ(row)); - ADD(args, INTEGER_OBJ(startcol)); - Array cells = ARRAY_DICT_INIT; - int repeat = 0; + prepare_call(ui, "grid_line"); + data->ncalls++; + + char **buf = &data->buf_wptr; + mpack_array(buf, 4); + mpack_uint(buf, (uint32_t)grid); + mpack_uint(buf, (uint32_t)row); + mpack_uint(buf, (uint32_t)startcol); + char *lenpos = mpack_array_dyn16(buf); + + uint32_t repeat = 0; size_t ncells = (size_t)(endcol - startcol); int last_hl = -1; + uint32_t nelem = 0; for (size_t i = 0; i < ncells; i++) { repeat++; if (i == ncells - 1 || attrs[i] != attrs[i + 1] || STRCMP(chunk[i], chunk[i + 1])) { - Array cell = ARRAY_DICT_INIT; - ADD(cell, STRING_OBJ(cstr_to_string((const char *)chunk[i]))); - if (attrs[i] != last_hl || repeat > 1) { - ADD(cell, INTEGER_OBJ(attrs[i])); - last_hl = attrs[i]; + if (UI_BUF_SIZE - BUF_POS(data) < 2 * (1 + 2 + sizeof(schar_T) + 5 + 5)) { + // close to overflowing the redraw buffer. finish this event, + // flush, and start a new "grid_line" event at the current position. + // For simplicity leave place for the final "clear" element + // as well, hence the factor of 2 in the check. + mpack_w2(&lenpos, nelem); + remote_ui_flush_buf(ui); + + prepare_call(ui, "grid_line"); + data->ncalls++; + mpack_array(buf, 4); + mpack_uint(buf, (uint32_t)grid); + mpack_uint(buf, (uint32_t)row); + mpack_uint(buf, (uint32_t)startcol + (uint32_t)i - repeat + 1); + lenpos = mpack_array_dyn16(buf); + nelem = 0; + last_hl = -1; } - if (repeat > 1) { - ADD(cell, INTEGER_OBJ(repeat)); + uint32_t csize = (repeat > 1) ? 3 : ((attrs[i] != last_hl) ? 2 : 1); + nelem++; + mpack_array(buf, csize); + mpack_str(buf, (const char *)chunk[i]); + if (csize >= 2) { + mpack_uint(buf, (uint32_t)attrs[i]); + if (csize >= 3) { + mpack_uint(buf, repeat); + } } - ADD(cells, ARRAY_OBJ(cell)); + last_hl = attrs[i]; repeat = 0; } } if (endcol < clearcol) { - Array cell = ARRAY_DICT_INIT; - ADD(cell, STRING_OBJ(cstr_to_string(" "))); - ADD(cell, INTEGER_OBJ(clearattr)); - ADD(cell, INTEGER_OBJ(clearcol - endcol)); - ADD(cells, ARRAY_OBJ(cell)); + nelem++; + mpack_array(buf, 3); + mpack_str(buf, " "); + mpack_uint(buf, (uint32_t)clearattr); + mpack_uint(buf, (uint32_t)(clearcol - endcol)); } - ADD(args, ARRAY_OBJ(cells)); - - push_call(ui, "grid_line", args); + mpack_w2(&lenpos, nelem); } else { for (int i = 0; i < endcol - startcol; i++) { remote_ui_cursor_goto(ui, row, startcol + i); @@ -688,16 +911,47 @@ static void remote_ui_raw_line(UI *ui, Integer grid, Integer row, Integer startc } } +/// Flush the internal packing buffer to the client. +/// +/// This might happen multiple times before the actual ui_flush, if the +/// total redraw size is large! +static void remote_ui_flush_buf(UI *ui) +{ + UIData *data = ui->data; + if (!data->nevents_pos) { + return; + } + if (data->cur_event) { + flush_event(data); + } + mpack_w2(&data->nevents_pos, data->nevents); + data->nevents = 0; + data->nevents_pos = NULL; + + // TODO(bfredl): elide copy by a length one free-list like the arena + size_t size = BUF_POS(data); + WBuffer *buf = wstream_new_buffer(xmemdup(data->buf, size), size, 1, xfree); + rpc_write_raw(data->channel_id, buf); + data->buf_wptr = data->buf; + // we have sent events to the client, but possibly not yet the final "flush" + // event. + data->flushed_events = true; +} + +/// An intentional flush (vsync) when Nvim is finished redrawing the screen +/// +/// Clients can know this happened by a final "flush" event at the end of the +/// "redraw" batch. static void remote_ui_flush(UI *ui) { UIData *data = ui->data; - if (data->buffer.size > 0) { + if (data->nevents > 0 || data->flushed_events) { if (!ui->ui_ext[kUILinegrid]) { remote_ui_cursor_goto(ui, data->cursor_row, data->cursor_col); } push_call(ui, "flush", (Array)ARRAY_DICT_INIT); - rpc_send_event(data->channel_id, "redraw", data->buffer); - data->buffer = (Array)ARRAY_DICT_INIT; + remote_ui_flush_buf(ui); + data->flushed_events = false; } } @@ -732,7 +986,7 @@ static Array translate_firstarg(UI *ui, Array args) return new_args; } -static void remote_ui_event(UI *ui, char *name, Array args, bool *args_consumed) +static void remote_ui_event(UI *ui, char *name, Array args) { UIData *data = ui->data; if (!ui->ui_ext[kUILinegrid]) { @@ -741,21 +995,24 @@ static void remote_ui_event(UI *ui, char *name, Array args, bool *args_consumed) if (strequal(name, "cmdline_show")) { Array new_args = translate_firstarg(ui, args); push_call(ui, name, new_args); + api_free_array(new_args); return; } else if (strequal(name, "cmdline_block_show")) { - Array new_args = ARRAY_DICT_INIT; + Array new_args = data->call_buf; Array block = args.items[0].data.array; Array new_block = ARRAY_DICT_INIT; for (size_t i = 0; i < block.size; i++) { ADD(new_block, ARRAY_OBJ(translate_contents(ui, block.items[i].data.array))); } - ADD(new_args, ARRAY_OBJ(new_block)); + ADD_C(new_args, ARRAY_OBJ(new_block)); push_call(ui, name, new_args); + api_free_array(new_block); return; } else if (strequal(name, "cmdline_block_append")) { Array new_args = translate_firstarg(ui, args); push_call(ui, name, new_args); + api_free_array(new_args); return; } } @@ -766,18 +1023,19 @@ static void remote_ui_event(UI *ui, char *name, Array args, bool *args_consumed) data->wildmenu_active = (args.items[4].data.integer == -1) || !ui->ui_ext[kUIPopupmenu]; if (data->wildmenu_active) { - Array new_args = ARRAY_DICT_INIT; + Array new_args = data->call_buf; Array items = args.items[0].data.array; Array new_items = ARRAY_DICT_INIT; for (size_t i = 0; i < items.size; i++) { ADD(new_items, copy_object(items.items[i].data.array.items[0])); } - ADD(new_args, ARRAY_OBJ(new_items)); + ADD_C(new_args, ARRAY_OBJ(new_items)); push_call(ui, "wildmenu_show", new_args); + api_free_array(new_items); if (args.items[1].data.integer != -1) { - Array new_args2 = ARRAY_DICT_INIT; - ADD(new_args2, args.items[1]); - push_call(ui, "wildmenu_select", new_args); + Array new_args2 = data->call_buf; + ADD_C(new_args2, args.items[1]); + push_call(ui, "wildmenu_select", new_args2); } return; } @@ -792,18 +1050,7 @@ static void remote_ui_event(UI *ui, char *name, Array args, bool *args_consumed) } } - Array my_args = ARRAY_DICT_INIT; - // Objects are currently single-reference - // make a copy, but only if necessary - if (*args_consumed) { - for (size_t i = 0; i < args.size; i++) { - ADD(my_args, copy_object(args.items[i])); - } - } else { - my_args = args; - *args_consumed = true; - } - push_call(ui, name, my_args); + push_call(ui, name, args); } static void remote_ui_inspect(UI *ui, Dictionary *info) diff --git a/src/nvim/api/ui_events.in.h b/src/nvim/api/ui_events.in.h index 0030f9edf7..8b7e01e1c3 100644 --- a/src/nvim/api/ui_events.in.h +++ b/src/nvim/api/ui_events.in.h @@ -99,7 +99,7 @@ void raw_line(Integer grid, Integer row, Integer startcol, LineFlags flags, const schar_T *chunk, const sattr_T *attrs) FUNC_API_NOEXPORT FUNC_API_COMPOSITOR_IMPL; -void event(char *name, Array args, bool *args_consumed) +void event(char *name, Array args) FUNC_API_NOEXPORT; void win_pos(Integer grid, Window win, Integer startrow, diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 28b1a7580d..a65e89a9f5 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -7272,7 +7272,7 @@ void tabpage_close_other(tabpage_T *tp, int forceit) redraw_tabline = true; if (h != tabline_height()) { - shell_new_rows(); + win_new_screen_rows(); } } diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 3065dc462b..211eeff45e 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -3309,45 +3309,53 @@ draw_cmdline_no_arabicshape: static void ui_ext_cmdline_show(CmdlineInfo *line) { + Arena arena = ARENA_EMPTY; + arena_start(&arena, &ui_ext_fixblk); Array content = ARRAY_DICT_INIT; if (cmdline_star) { + content = arena_array(&arena, 1); size_t len = 0; for (char_u *p = ccline.cmdbuff; *p; MB_PTR_ADV(p)) { len++; } - char *buf = xmallocz(len); + char *buf = arena_alloc(&arena, len, false); memset(buf, '*', len); - Array item = ARRAY_DICT_INIT; - ADD(item, INTEGER_OBJ(0)); - ADD(item, STRING_OBJ(((String) { .data = buf, .size = len }))); - ADD(content, ARRAY_OBJ(item)); + Array item = arena_array(&arena, 2); + ADD_C(item, INTEGER_OBJ(0)); + ADD_C(item, STRING_OBJ(cbuf_as_string(buf, len))); + ADD_C(content, ARRAY_OBJ(item)); } else if (kv_size(line->last_colors.colors)) { + content = arena_array(&arena, kv_size(line->last_colors.colors)); for (size_t i = 0; i < kv_size(line->last_colors.colors); i++) { CmdlineColorChunk chunk = kv_A(line->last_colors.colors, i); - Array item = ARRAY_DICT_INIT; - ADD(item, INTEGER_OBJ(chunk.attr)); + Array item = arena_array(&arena, 2); + ADD_C(item, INTEGER_OBJ(chunk.attr)); assert(chunk.end >= chunk.start); - ADD(item, STRING_OBJ(cbuf_to_string((char *)line->cmdbuff + chunk.start, - (size_t)(chunk.end - chunk.start)))); - ADD(content, ARRAY_OBJ(item)); + ADD_C(item, STRING_OBJ(cbuf_as_string((char *)line->cmdbuff + chunk.start, + (size_t)(chunk.end - chunk.start)))); + ADD_C(content, ARRAY_OBJ(item)); } } else { - Array item = ARRAY_DICT_INIT; - ADD(item, INTEGER_OBJ(0)); - ADD(item, STRING_OBJ(cstr_to_string((char *)(line->cmdbuff)))); - ADD(content, ARRAY_OBJ(item)); + Array item = arena_array(&arena, 2); + ADD_C(item, INTEGER_OBJ(0)); + ADD_C(item, STRING_OBJ(cstr_as_string((char *)(line->cmdbuff)))); + content = arena_array(&arena, 1); + ADD_C(content, ARRAY_OBJ(item)); } + char charbuf[2] = { (char)line->cmdfirstc, 0 }; ui_call_cmdline_show(content, line->cmdpos, - cchar_to_string((char)line->cmdfirstc), - cstr_to_string((char *)(line->cmdprompt)), + cstr_as_string(charbuf), + cstr_as_string((char *)(line->cmdprompt)), line->cmdindent, line->level); if (line->special_char) { - ui_call_cmdline_special_char(cchar_to_string(line->special_char), + charbuf[0] = line->special_char; + ui_call_cmdline_special_char(cstr_as_string(charbuf), line->special_shift, line->level); } + arena_mem_free(arena_finish(&arena), &ui_ext_fixblk); } void ui_ext_cmdline_block_append(size_t indent, const char *line) @@ -3363,9 +3371,9 @@ void ui_ext_cmdline_block_append(size_t indent, const char *line) ADD(content, ARRAY_OBJ(item)); ADD(cmdline_block, ARRAY_OBJ(content)); if (cmdline_block.size > 1) { - ui_call_cmdline_block_append(copy_array(content)); + ui_call_cmdline_block_append(content); } else { - ui_call_cmdline_block_show(copy_array(cmdline_block)); + ui_call_cmdline_block_show(cmdline_block); } } @@ -3385,7 +3393,7 @@ void cmdline_screen_cleared(void) } if (cmdline_block.size) { - ui_call_cmdline_block_show(copy_array(cmdline_block)); + ui_call_cmdline_block_show(cmdline_block); } int prev_level = ccline.level - 1; @@ -3442,7 +3450,8 @@ void putcmdline(char c, int shift) } msg_no_more = false; } else if (ccline.redraw_state != kCmdRedrawAll) { - ui_call_cmdline_special_char(cchar_to_string(c), shift, + char charbuf[2] = { c, 0 }; + ui_call_cmdline_special_char(cstr_as_string(charbuf), shift, ccline.level); } cursorcmd(); diff --git a/src/nvim/generators/gen_api_ui_events.lua b/src/nvim/generators/gen_api_ui_events.lua index 99dfac05e8..93bbaab74c 100755 --- a/src/nvim/generators/gen_api_ui_events.lua +++ b/src/nvim/generators/gen_api_ui_events.lua @@ -36,20 +36,12 @@ local function write_signature(output, ev, prefix, notype) output:write(')') end -local function write_arglist(output, ev, need_copy) - output:write(' Array args = ARRAY_DICT_INIT;\n') +local function write_arglist(output, ev) for j = 1, #ev.parameters do local param = ev.parameters[j] local kind = string.upper(param[1]) - local do_copy = need_copy and (kind == "ARRAY" or kind == "DICTIONARY" or kind == "STRING" or kind == "OBJECT") - output:write(' ADD(args, ') - if do_copy then - output:write('copy_object(') - end + output:write(' ADD_C(args, ') output:write(kind..'_OBJ('..param[2]..')') - if do_copy then - output:write(')') - end output:write(');\n') end end @@ -119,7 +111,9 @@ for i = 1, #events do remote_output:write('static void remote_ui_'..ev.name) write_signature(remote_output, ev, 'UI *ui') remote_output:write('\n{\n') - write_arglist(remote_output, ev, true) + remote_output:write(' UIData *data = ui->data;\n') + remote_output:write(' Array args = data->call_buf;\n') + write_arglist(remote_output, ev) remote_output:write(' push_call(ui, "'..ev.name..'", args);\n') remote_output:write('}\n\n') end @@ -186,9 +180,10 @@ for i = 1, #events do write_signature(call_output, ev, '') call_output:write('\n{\n') if ev.remote_only then - write_arglist(call_output, ev, false) + call_output:write(' Array args = call_buf;\n') + write_arglist(call_output, ev) call_output:write(' UI_LOG('..ev.name..');\n') - call_output:write(' ui_event("'..ev.name..'", args);\n') + call_output:write(' ui_call_event("'..ev.name..'", args);\n') elseif ev.compositor_impl then call_output:write(' UI_CALL') write_signature(call_output, ev, '!ui->composed, '..ev.name..', ui', true) diff --git a/src/nvim/lib/kvec.h b/src/nvim/lib/kvec.h index 4238088b1c..b5b3adf7d2 100644 --- a/src/nvim/lib/kvec.h +++ b/src/nvim/lib/kvec.h @@ -121,6 +121,9 @@ #define kv_push(v, x) \ (*kv_pushp(v) = (x)) +#define kv_pushp_c(v) ((v).items + ((v).size++)) +#define kv_push_c(v, x) (*kv_pushp_c(v) = (x)) + #define kv_a(v, i) \ (*(((v).capacity <= (size_t)(i) \ ? ((v).capacity = (v).size = (i) + 1, \ diff --git a/src/nvim/memory.c b/src/nvim/memory.c index dd06419391..9d02b83e5f 100644 --- a/src/nvim/memory.c +++ b/src/nvim/memory.c @@ -786,6 +786,7 @@ void free_all_mem(void) decor_free_all_mem(); nlua_free_all_mem(); + ui_free_all_mem(); } #endif diff --git a/src/nvim/memory.h b/src/nvim/memory.h index 0c048e1b5b..63d607c2ce 100644 --- a/src/nvim/memory.h +++ b/src/nvim/memory.h @@ -51,6 +51,10 @@ typedef struct { // inits an empty arena. use arena_start() to actually allocate space! #define ARENA_EMPTY { .cur_blk = NULL, .pos = 0, .size = 0 } +#define kv_fixsize_arena(a, v, s) \ + ((v).capacity = (s), \ + (v).items = (void *)arena_alloc(a, sizeof((v).items[0]) * (v).capacity, true)) + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "memory.h.generated.h" #endif diff --git a/src/nvim/message.c b/src/nvim/message.c index acc3122e93..e192af6aad 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -1068,6 +1068,7 @@ void ex_messages(void *const eap_p) } } ui_call_msg_history_show(entries); + api_free_array(entries); msg_ext_history_visible = true; wait_return(false); } else { @@ -1262,11 +1263,11 @@ void wait_return(int redraw) msg_ext_keep_after_cmdline = true; } - // If the window size changed set_shellsize() will redraw the screen. + // If the screen size changed screen_resize() will redraw the screen. // Otherwise the screen is only redrawn if 'redraw' is set and no ':' // typed. tmpState = State; - State = oldState; // restore State before set_shellsize + State = oldState; // restore State before screen_resize() setmouse(); msg_check(); need_wait_return = false; @@ -3136,12 +3137,13 @@ void msg_ext_ui_flush(void) msg_ext_emit_chunk(); if (msg_ext_chunks.size > 0) { - ui_call_msg_show(cstr_to_string(msg_ext_kind), + ui_call_msg_show(cstr_as_string((char *)msg_ext_kind), msg_ext_chunks, msg_ext_overwrite); if (!msg_ext_overwrite) { msg_ext_visible++; } msg_ext_kind = NULL; + api_free_array(msg_ext_chunks); msg_ext_chunks = (Array)ARRAY_DICT_INIT; msg_ext_cur_len = 0; msg_ext_overwrite = false; @@ -3155,6 +3157,7 @@ void msg_ext_flush_showmode(void) if (ui_has(kUIMessages)) { msg_ext_emit_chunk(); ui_call_msg_showmode(msg_ext_chunks); + api_free_array(msg_ext_chunks); msg_ext_chunks = (Array)ARRAY_DICT_INIT; msg_ext_cur_len = 0; } diff --git a/src/nvim/msgpack_rpc/channel.c b/src/nvim/msgpack_rpc/channel.c index b4f39c4c61..388fa2584c 100644 --- a/src/nvim/msgpack_rpc/channel.c +++ b/src/nvim/msgpack_rpc/channel.c @@ -360,6 +360,17 @@ free_ret: api_clear_error(&error); } +bool rpc_write_raw(uint64_t id, WBuffer *buffer) +{ + Channel *channel = find_rpc_channel(id); + if (!channel) { + wstream_release_wbuffer(buffer); + return false; + } + + return channel_write(channel, buffer); +} + static bool channel_write(Channel *channel, WBuffer *buffer) { bool success; diff --git a/src/nvim/msgpack_rpc/unpacker.c b/src/nvim/msgpack_rpc/unpacker.c index 419349e74d..26c1843026 100644 --- a/src/nvim/msgpack_rpc/unpacker.c +++ b/src/nvim/msgpack_rpc/unpacker.c @@ -11,10 +11,6 @@ # include "msgpack_rpc/unpacker.c.generated.h" #endif -#define kv_fixsize_arena(a, v, s) \ - ((v).capacity = (s), \ - (v).items = (void *)arena_alloc(a, sizeof((v).items[0]) * (v).capacity, true)) - Object unpack(const char *data, size_t size, Error *err) { Unpacker unpacker; diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 35a7ee3319..8e4f78818b 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -2755,13 +2755,13 @@ static void display_showcmd(void) showcmd_is_clear = (len == 0); if (ui_has(kUIMessages)) { - Array content = ARRAY_DICT_INIT; + MAXSIZE_TEMP_ARRAY(content, 1); + MAXSIZE_TEMP_ARRAY(chunk, 2); if (len > 0) { - Array chunk = ARRAY_DICT_INIT; // placeholder for future highlight support - ADD(chunk, INTEGER_OBJ(0)); - ADD(chunk, STRING_OBJ(cstr_to_string((char *)showcmd_buf))); - ADD(content, ARRAY_OBJ(chunk)); + ADD_C(chunk, INTEGER_OBJ(0)); + ADD_C(chunk, STRING_OBJ(cstr_as_string((char *)showcmd_buf))); + ADD_C(content, ARRAY_OBJ(chunk)); } ui_call_msg_showcmd(content); return; diff --git a/src/nvim/option.c b/src/nvim/option.c index 075dd3d4bc..c97c027740 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -4528,7 +4528,7 @@ static char *set_num_option(int opt_idx, char_u *varp, long value, char *errbuf, last_status(false); // (re)set last window status line. } else if (pp == &p_stal) { // (re)set tab page line - shell_new_rows(); // recompute window positions and heights + win_new_screen_rows(); // recompute window positions and heights } else if (pp == &curwin->w_p_fdl) { newFoldLevel(); } else if (pp == &curwin->w_p_fml) { @@ -4617,7 +4617,7 @@ static char *set_num_option(int opt_idx, char_u *varp, long value, char *errbuf, p_columns = MIN_COLUMNS; } - // True max size is defined by check_shellsize() + // True max size is defined by check_screensize() p_lines = MIN(p_lines, INT_MAX); p_columns = MIN(p_columns, INT_MAX); @@ -4635,7 +4635,7 @@ static char *set_num_option(int opt_idx, char_u *varp, long value, char *errbuf, // messages. Rows = (int)p_lines; Columns = (int)p_columns; - check_shellsize(); + check_screensize(); if (cmdline_row > Rows - p_ch && Rows > p_ch) { assert(p_ch >= 0 && Rows - p_ch <= INT_MAX); cmdline_row = (int)(Rows - p_ch); diff --git a/src/nvim/popupmnu.c b/src/nvim/popupmnu.c index 7233a3bb72..db1054eb1f 100644 --- a/src/nvim/popupmnu.c +++ b/src/nvim/popupmnu.c @@ -151,17 +151,20 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i if (pum_external) { if (array_changed) { - Array arr = ARRAY_DICT_INIT; + Arena arena = ARENA_EMPTY; + arena_start(&arena, &ui_ext_fixblk); + Array arr = arena_array(&arena, (size_t)size); for (int i = 0; i < size; i++) { - Array item = ARRAY_DICT_INIT; - ADD(item, STRING_OBJ(cstr_to_string((char *)array[i].pum_text))); - ADD(item, STRING_OBJ(cstr_to_string((char *)array[i].pum_kind))); - ADD(item, STRING_OBJ(cstr_to_string((char *)array[i].pum_extra))); - ADD(item, STRING_OBJ(cstr_to_string((char *)array[i].pum_info))); - ADD(arr, ARRAY_OBJ(item)); + Array item = arena_array(&arena, 4); + ADD_C(item, STRING_OBJ(cstr_as_string((char *)array[i].pum_text))); + ADD_C(item, STRING_OBJ(cstr_as_string((char *)array[i].pum_kind))); + ADD_C(item, STRING_OBJ(cstr_as_string((char *)array[i].pum_extra))); + ADD_C(item, STRING_OBJ(cstr_as_string((char *)array[i].pum_info))); + ADD_C(arr, ARRAY_OBJ(item)); } ui_call_popupmenu_show(arr, selected, pum_win_row, cursor_col, pum_anchor_grid); + arena_mem_free(arena_finish(&arena), &ui_ext_fixblk); } else { ui_call_popupmenu_select(selected); return; @@ -438,7 +441,7 @@ void pum_redraw(void) if (ui_has(kUIMultigrid)) { const char *anchor = pum_above ? "SW" : "NW"; int row_off = pum_above ? -pum_height : 0; - ui_call_win_float_pos(pum_grid.handle, -1, cstr_to_string(anchor), + ui_call_win_float_pos(pum_grid.handle, -1, cstr_as_string((char *)anchor), pum_anchor_grid, pum_row - row_off, pum_col - col_off, false, pum_grid.zindex); } diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 2ee7cd44f7..98f8e30391 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -5578,9 +5578,9 @@ void check_for_delay(bool check_msg_scroll) /// /// There may be some time between setting Rows and Columns and (re)allocating /// default_grid arrays. This happens when starting up and when -/// (manually) changing the shell size. Always use default_grid.rows and +/// (manually) changing the screen size. Always use default_grid.rows and /// default_grid.Columns to access items in default_grid.chars[]. Use Rows -/// and Columns for positioning text etc. where the final size of the shell is +/// and Columns for positioning text etc. where the final size of the screen is /// needed. void screenalloc(void) { @@ -5615,14 +5615,14 @@ retry: */ ++RedrawingDisabled; - // win_new_shellsize will recompute floats position, but tell the + // win_new_screensize will recompute floats position, but tell the // compositor to not redraw them yet ui_comp_set_screen_valid(false); if (msg_grid.chars) { msg_grid_invalid = true; } - win_new_shellsize(); // fit the windows in the new sized shell + win_new_screensize(); // fit the windows in the new sized screen comp_col(); // recompute columns for shown command and ruler @@ -6341,35 +6341,49 @@ void draw_tabline(void) void ui_ext_tabline_update(void) { - Array tabs = ARRAY_DICT_INIT; + Arena arena = ARENA_EMPTY; + arena_start(&arena, &ui_ext_fixblk); + + size_t n_tabs = 0; FOR_ALL_TABS(tp) { - Dictionary tab_info = ARRAY_DICT_INIT; - PUT(tab_info, "tab", TABPAGE_OBJ(tp->handle)); + n_tabs++; + } + + Array tabs = arena_array(&arena, n_tabs); + FOR_ALL_TABS(tp) { + Dictionary tab_info = arena_dict(&arena, 2); + PUT_C(tab_info, "tab", TABPAGE_OBJ(tp->handle)); win_T *cwp = (tp == curtab) ? curwin : tp->tp_curwin; get_trans_bufname(cwp->w_buffer); - PUT(tab_info, "name", STRING_OBJ(cstr_to_string((char *)NameBuff))); + PUT_C(tab_info, "name", STRING_OBJ(arena_string(&arena, cstr_as_string((char *)NameBuff)))); + + ADD_C(tabs, DICTIONARY_OBJ(tab_info)); + } - ADD(tabs, DICTIONARY_OBJ(tab_info)); + size_t n_buffers = 0; + FOR_ALL_BUFFERS(buf) { + n_buffers += buf->b_p_bl ? 1 : 0; } - Array buffers = ARRAY_DICT_INIT; + Array buffers = arena_array(&arena, n_buffers); FOR_ALL_BUFFERS(buf) { // Do not include unlisted buffers if (!buf->b_p_bl) { continue; } - Dictionary buffer_info = ARRAY_DICT_INIT; - PUT(buffer_info, "buffer", BUFFER_OBJ(buf->handle)); + Dictionary buffer_info = arena_dict(&arena, 2); + PUT_C(buffer_info, "buffer", BUFFER_OBJ(buf->handle)); get_trans_bufname(buf); - PUT(buffer_info, "name", STRING_OBJ(cstr_to_string((char *)NameBuff))); + PUT_C(buffer_info, "name", STRING_OBJ(arena_string(&arena, cstr_as_string((char *)NameBuff)))); - ADD(buffers, DICTIONARY_OBJ(buffer_info)); + ADD_C(buffers, DICTIONARY_OBJ(buffer_info)); } ui_call_tabline_update(curtab->handle, tabs, curbuf->handle, buffers); + arena_mem_free(arena_finish(&arena), &ui_ext_fixblk); } /* @@ -6618,11 +6632,11 @@ static void win_redr_ruler(win_T *wp, bool always) } if (ui_has(kUIMessages) && !part_of_status) { - Array content = ARRAY_DICT_INIT; - Array chunk = ARRAY_DICT_INIT; - ADD(chunk, INTEGER_OBJ(attr)); - ADD(chunk, STRING_OBJ(cstr_to_string((char *)buffer))); - ADD(content, ARRAY_OBJ(chunk)); + MAXSIZE_TEMP_ARRAY(content, 1); + MAXSIZE_TEMP_ARRAY(chunk, 2); + ADD_C(chunk, INTEGER_OBJ(attr)); + ADD_C(chunk, STRING_OBJ(cstr_as_string((char *)buffer))); + ADD_C(content, ARRAY_OBJ(chunk)); ui_call_msg_ruler(content); did_show_ext_ruler = true; } else { @@ -6744,7 +6758,7 @@ static void margin_columns_win(win_T *wp, int *left_col, int *right_col) prev_col_off = cur_col_off; } -/// Set dimensions of the Nvim application "shell". +/// Set dimensions of the Nvim application "screen". void screen_resize(int width, int height) { // Avoid recursiveness, can happen when setting the window size causes @@ -6775,7 +6789,7 @@ void screen_resize(int width, int height) Rows = height; Columns = width; - check_shellsize(); + check_screensize(); int max_p_ch = Rows - min_rows() + 1; if (!ui_has(kUIMessages) && p_ch > 0 && p_ch > max_p_ch) { p_ch = max_p_ch ? max_p_ch : 1; @@ -6852,49 +6866,23 @@ void screen_resize(int width, int height) resizing_screen = false; } -/// Check if the new Nvim application "shell" dimensions are valid. +/// Check if the new Nvim application "screen" dimensions are valid. /// Correct it if it's too small or way too big. -void check_shellsize(void) +void check_screensize(void) { + // Limit Rows and Columns to avoid an overflow in Rows * Columns. if (Rows < min_rows()) { // need room for one window and command line Rows = min_rows(); + } else if (Rows > 1000) { + Rows = 1000; } - limit_screen_size(); -} -// Limit Rows and Columns to avoid an overflow in Rows * Columns. -void limit_screen_size(void) -{ if (Columns < MIN_COLUMNS) { Columns = MIN_COLUMNS; } else if (Columns > 10000) { Columns = 10000; } - - if (Rows > 1000) { - Rows = 1000; - } -} - -void win_new_shellsize(void) -{ - static long old_Rows = 0; - static long old_Columns = 0; - - if (old_Rows != Rows) { - // If 'window' uses the whole screen, keep it using that. - // Don't change it when set with "-w size" on the command line. - if (p_window == old_Rows - 1 || (old_Rows == 0 && p_window == 0)) { - p_window = Rows - 1; - } - old_Rows = Rows; - shell_new_rows(); // update window sizes - } - if (old_Columns != Columns) { - old_Columns = Columns; - shell_new_columns(); // update window sizes - } } win_T *get_win_by_grid_handle(handle_T handle) diff --git a/src/nvim/ui.c b/src/nvim/ui.c index 3e715793e6..0ac7f4a55f 100644 --- a/src/nvim/ui.c +++ b/src/nvim/ui.c @@ -64,6 +64,8 @@ static handle_T cursor_grid_handle = DEFAULT_GRID_HANDLE; static bool has_mouse = false; static int pending_has_mouse = -1; +static Array call_buf = ARRAY_DICT_INIT; + #if MIN_LOG_LEVEL > LOGLVL_DBG # define UI_LOG(funname) #else @@ -123,6 +125,12 @@ void ui_init(void) default_grid.handle = 1; msg_grid_adj.target = &default_grid; ui_comp_init(); + kv_ensure_space(call_buf, 16); +} + +void ui_free_all_mem(void) +{ + kv_destroy(call_buf); } void ui_builtin_start(void) @@ -173,15 +181,6 @@ bool ui_active(void) return ui_count > 1; } -void ui_event(char *name, Array args) -{ - bool args_consumed = false; - ui_call_event(name, args, &args_consumed); - if (!args_consumed) { - api_free_array(args); - } -} - void ui_refresh(void) { if (!ui_active()) { diff --git a/src/nvim/ui.h b/src/nvim/ui.h index 1d278010e8..7dd2f5bce3 100644 --- a/src/nvim/ui.h +++ b/src/nvim/ui.h @@ -8,6 +8,7 @@ #include "nvim/api/private/defs.h" #include "nvim/globals.h" #include "nvim/highlight_defs.h" +#include "nvim/memory.h" typedef enum { kUICmdline = 0, @@ -46,6 +47,8 @@ enum { typedef int LineFlags; +EXTERN ArenaMem ui_ext_fixblk INIT(= NULL); + struct ui_t { bool rgb; bool override; ///< Force highest-requested UI capabilities. diff --git a/src/nvim/window.c b/src/nvim/window.c index 1487759d60..1a1f62f2c0 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -893,7 +893,7 @@ void ui_ext_win_position(win_T *wp) wp->w_grid_alloc.zindex = wp->w_float_config.zindex; if (ui_has(kUIMultigrid)) { - String anchor = cstr_to_string(float_anchor_str[c.anchor]); + String anchor = cstr_as_string((char *)float_anchor_str[c.anchor]); ui_call_win_float_pos(wp->w_grid_alloc.handle, wp->handle, anchor, grid->handle, row, col, c.focusable, wp->w_grid_alloc.zindex); @@ -967,8 +967,8 @@ static int check_split_disallowed(void) * "flags": * WSP_ROOM: require enough room for new window * WSP_VERT: vertical split. - * WSP_TOP: open window at the top-left of the shell (help window). - * WSP_BOT: open window at the bottom-right of the shell (quickfix window). + * WSP_TOP: open window at the top-left of the screen (help window). + * WSP_BOT: open window at the bottom-right of the screen (quickfix window). * WSP_HELP: creating the help window, keep layout snapshot * * return FAIL for failure, OK otherwise @@ -2499,7 +2499,7 @@ void close_windows(buf_T *buf, bool keep_curwin) redraw_tabline = true; if (h != tabline_height()) { - shell_new_rows(); + win_new_screen_rows(); } } @@ -2606,7 +2606,7 @@ static bool close_last_window_tabpage(win_T *win, bool free_buf, tabpage_T *prev win_close_othertab(win, free_buf, prev_curtab); if (h != tabline_height()) { - shell_new_rows(); + win_new_screen_rows(); } } entering_window(curwin); @@ -4350,10 +4350,10 @@ static void enter_tabpage(tabpage_T *tp, buf_T *old_curbuf, bool trigger_enter_a } if (curtab->tp_old_Rows != Rows || (old_off != firstwin->w_winrow)) { - shell_new_rows(); + win_new_screen_rows(); } if (curtab->tp_old_Columns != Columns && starting == 0) { - shell_new_columns(); // update window widths + win_new_screen_cols(); // update window widths } lastused_tabpage = old_curtab; @@ -5277,11 +5277,29 @@ static void frame_remove(frame_T *frp) } } -/* - * Called from win_new_shellsize() after Rows changed. - * This only does the current tab page, others must be done when made active. - */ -void shell_new_rows(void) +void win_new_screensize(void) +{ + static long old_Rows = 0; + static long old_Columns = 0; + + if (old_Rows != Rows) { + // If 'window' uses the whole screen, keep it using that. + // Don't change it when set with "-w size" on the command line. + if (p_window == old_Rows - 1 || (old_Rows == 0 && p_window == 0)) { + p_window = Rows - 1; + } + old_Rows = Rows; + win_new_screen_rows(); // update window sizes + } + if (old_Columns != Columns) { + old_Columns = Columns; + win_new_screen_cols(); // update window sizes + } +} +/// Called from win_new_screensize() after Rows changed. +/// +/// This only does the current tab page, others must be done when made active. +void win_new_screen_rows(void) { int h = (int)ROWS_AVAIL; @@ -5305,10 +5323,8 @@ void shell_new_rows(void) curtab->tp_ch_used = p_ch; } -/* - * Called from win_new_shellsize() after Columns changed. - */ -void shell_new_columns(void) +/// Called from win_new_screensize() after Columns changed. +void win_new_screen_cols(void) { if (firstwin == NULL) { // not initialized yet return; |