From 543e0256c19f397921a332e06b423215fd9aecb5 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 30 Nov 2023 15:51:05 +0800 Subject: build: don't define FUNC_ATTR_* as empty in headers (#26317) FUNC_ATTR_* should only be used in .c files with generated headers. Defining FUNC_ATTR_* as empty in headers causes misuses of them to be silently ignored. Instead don't define them by default, and only define them as empty after a .c file has included its generated header. --- src/nvim/api/ui.c | 1 - 1 file changed, 1 deletion(-) (limited to 'src/nvim/api/ui.c') diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c index 836a68546c..7e64ce9cd1 100644 --- a/src/nvim/api/ui.c +++ b/src/nvim/api/ui.c @@ -17,7 +17,6 @@ #include "nvim/eval.h" #include "nvim/event/loop.h" #include "nvim/event/wstream.h" -#include "nvim/func_attr.h" #include "nvim/globals.h" #include "nvim/grid.h" #include "nvim/highlight.h" -- cgit From f6e5366d0077e9f171651f37282cb5c47629d1b6 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 2 Dec 2023 07:55:44 +0800 Subject: refactor: free more reachable memory with EXITFREE (#26349) Discovered using __sanitizer_print_memory_profile(). --- src/nvim/api/ui.c | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) (limited to 'src/nvim/api/ui.c') diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c index 7e64ce9cd1..b73c026d57 100644 --- a/src/nvim/api/ui.c +++ b/src/nvim/api/ui.c @@ -106,21 +106,36 @@ static void mpack_str(char **buf, const char *str) *buf += len; } +static void remote_ui_destroy(UI *ui) + FUNC_ATTR_NONNULL_ALL +{ + UIData *data = ui->data; + kv_destroy(data->call_buf); + XFREE_CLEAR(ui->term_name); + xfree(ui); +} + void remote_ui_disconnect(uint64_t channel_id) { UI *ui = pmap_get(uint64_t)(&connected_uis, channel_id); if (!ui) { return; } - UIData *data = ui->data; - kv_destroy(data->call_buf); pmap_del(uint64_t)(&connected_uis, channel_id, NULL); ui_detach_impl(ui, channel_id); + remote_ui_destroy(ui); +} - // Destroy `ui`. - XFREE_CLEAR(ui->term_name); - xfree(ui); +#ifdef EXITFREE +void remote_ui_free_all_mem(void) +{ + UI *ui; + map_foreach_value(&connected_uis, ui, { + remote_ui_destroy(ui); + }); + map_destroy(uint64_t, &connected_uis); } +#endif /// Wait until ui has connected on stdio channel if only_stdio /// is true, otherwise any channel. -- cgit From 69bc519b53ebf78fd95c8256468e7d538ebcb948 Mon Sep 17 00:00:00 2001 From: dundargoc Date: Tue, 12 Dec 2023 15:40:21 +0100 Subject: refactor: move non-symbols to defs.h headers --- src/nvim/api/ui.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/api/ui.c') diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c index b73c026d57..678d23fbeb 100644 --- a/src/nvim/api/ui.c +++ b/src/nvim/api/ui.c @@ -15,7 +15,7 @@ #include "nvim/autocmd.h" #include "nvim/channel.h" #include "nvim/eval.h" -#include "nvim/event/loop.h" +#include "nvim/event/defs.h" #include "nvim/event/wstream.h" #include "nvim/globals.h" #include "nvim/grid.h" -- cgit From af93a74a0f4afa9a3a4f55ffdf28141eaf776d22 Mon Sep 17 00:00:00 2001 From: dundargoc Date: Mon, 18 Dec 2023 10:55:23 +0100 Subject: refactor: run IWYU on entire repo Reference: https://github.com/neovim/neovim/issues/6371. --- src/nvim/api/ui.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/nvim/api/ui.c') diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c index 678d23fbeb..82d42d652d 100644 --- a/src/nvim/api/ui.c +++ b/src/nvim/api/ui.c @@ -15,7 +15,8 @@ #include "nvim/autocmd.h" #include "nvim/channel.h" #include "nvim/eval.h" -#include "nvim/event/defs.h" +#include "nvim/event/loop.h" +#include "nvim/event/multiqueue.h" #include "nvim/event/wstream.h" #include "nvim/globals.h" #include "nvim/grid.h" -- cgit From aeb053907d2f27713764e345b00a6618e23220d8 Mon Sep 17 00:00:00 2001 From: bfredl Date: Wed, 3 Jan 2024 13:31:39 +0100 Subject: refactor(options): use schar_T representation for fillchars and listchars A bit big, but practically it was a lot simpler to change over all fillchars and all listchars at once, to not need to maintain two parallel implementations. This is mostly an internal refactor, but it also removes an arbitrary limitation: that 'fillchars' and 'listchars' values can only be single-codepoint characters. Now any character which fits into a single screen cell can be used. --- src/nvim/api/ui.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) (limited to 'src/nvim/api/ui.c') diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c index 82d42d652d..271e58b851 100644 --- a/src/nvim/api/ui.c +++ b/src/nvim/api/ui.c @@ -41,7 +41,7 @@ static PMap(uint64_t) connected_uis = MAP_INIT; -#define mpack_w(b, byte) *(*b)++ = (char)(byte); +#define mpack_w(b, byte) *(*(b))++ = (char)(byte); static void mpack_w2(char **b, uint32_t v) { *(*b)++ = (char)((v >> 8) & 0xff); @@ -98,10 +98,9 @@ static char *mpack_array_dyn16(char **buf) return pos; } -static void mpack_str(char **buf, const char *str) +static void mpack_str(char **buf, const char *str, size_t len) { assert(sizeof(schar_T) - 1 < 0x20); - size_t len = strlen(str); mpack_w(buf, 0xa0 | len); memcpy(*buf, str, len); *buf += len; @@ -566,7 +565,7 @@ static void flush_event(UIData *data) // [2, "redraw", [...]] mpack_array(buf, 3); mpack_uint(buf, 2); - mpack_str(buf, "redraw"); + mpack_str(buf, S_LEN("redraw")); data->nevents_pos = mpack_array_dyn16(buf); } } @@ -607,7 +606,7 @@ static bool prepare_call(UI *ui, const char *name) data->cur_event = name; char **buf = &data->buf_wptr; data->ncalls_pos = mpack_array_dyn16(buf); - mpack_str(buf, name); + mpack_str(buf, name, strlen(name)); data->nevents++; data->ncalls = 1; return true; @@ -640,17 +639,18 @@ static void push_call(UI *ui, const char *name, Array args) remote_ui_flush_buf(ui); } - if (data->pack_totlen > UI_BUF_SIZE - strlen(name) - 20) { + size_t name_len = strlen(name); + if (data->pack_totlen > UI_BUF_SIZE - name_len - 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->temp_buf = xmalloc(20 + name_len + 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_str(buf, S_LEN("redraw")); mpack_array(buf, 1); mpack_array(buf, 2); - mpack_str(buf, name); + mpack_str(buf, name, name_len); } else { prepare_call(ui, name); } @@ -895,9 +895,9 @@ void remote_ui_raw_line(UI *ui, Integer grid, Integer row, Integer startcol, Int uint32_t csize = (repeat > 1) ? 3 : ((attrs[i] != last_hl) ? 2 : 1); nelem++; mpack_array(buf, csize); - char sc_buf[MAX_SCHAR_SIZE]; - schar_get(sc_buf, chunk[i]); - mpack_str(buf, sc_buf); + char *size_byte = (*buf)++; + size_t len = schar_get_adv(buf, chunk[i]); + *size_byte = (char)(0xa0 | len); if (csize >= 2) { mpack_uint(buf, (uint32_t)attrs[i]); if (csize >= 3) { @@ -916,7 +916,7 @@ void remote_ui_raw_line(UI *ui, Integer grid, Integer row, Integer startcol, Int nelem++; data->ncells_pending += 1; mpack_array(buf, 3); - mpack_str(buf, " "); + mpack_str(buf, S_LEN(" ")); mpack_uint(buf, (uint32_t)clearattr); mpack_uint(buf, (uint32_t)(clearcol - endcol)); } -- cgit From 1813661a6197c76ea6621284570aca1d56597099 Mon Sep 17 00:00:00 2001 From: dundargoc Date: Thu, 4 Jan 2024 15:38:16 +0100 Subject: refactor(IWYU): fix headers Remove `export` pramgas from defs headers as it causes IWYU to believe that the definitions from the defs headers comes from main header, which is not what we really want. --- src/nvim/api/ui.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'src/nvim/api/ui.c') diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c index 271e58b851..b42c274411 100644 --- a/src/nvim/api/ui.c +++ b/src/nvim/api/ui.c @@ -13,20 +13,26 @@ #include "nvim/api/private/validate.h" #include "nvim/api/ui.h" #include "nvim/autocmd.h" +#include "nvim/autocmd_defs.h" #include "nvim/channel.h" +#include "nvim/channel_defs.h" #include "nvim/eval.h" +#include "nvim/event/defs.h" #include "nvim/event/loop.h" #include "nvim/event/multiqueue.h" #include "nvim/event/wstream.h" #include "nvim/globals.h" #include "nvim/grid.h" +#include "nvim/grid_defs.h" #include "nvim/highlight.h" #include "nvim/macros_defs.h" #include "nvim/main.h" #include "nvim/map_defs.h" #include "nvim/mbyte.h" #include "nvim/memory.h" +#include "nvim/memory_defs.h" #include "nvim/msgpack_rpc/channel.h" +#include "nvim/msgpack_rpc/channel_defs.h" #include "nvim/msgpack_rpc/helpers.h" #include "nvim/option.h" #include "nvim/types_defs.h" -- cgit From 6ea6b3fee27d51607ca4a5ace46dbc38a4481bcb Mon Sep 17 00:00:00 2001 From: Gregory Anders <8965202+gpanders@users.noreply.github.com> Date: Wed, 24 Jan 2024 16:36:25 -0600 Subject: feat(ui): add support for OSC 8 hyperlinks (#27109) Extmarks can contain URLs which can then be drawn in any supporting UI. In the TUI, for example, URLs are "drawn" by emitting the OSC 8 control sequence to the TTY. On terminals which support the OSC 8 sequence this will create clickable hyperlinks. URLs are treated as inline highlights in the decoration subsystem, so are included in the `DecorSignHighlight` structure. However, unlike other inline highlights they use allocated memory which must be freed, so they set the `ext` flag in `DecorInline` so that their lifetimes are managed along with other allocated memory like virtual text. The decoration subsystem then adds the URLs as a new highlight attribute. The highlight subsystem maintains a set of unique URLs to avoid duplicating allocations for the same string. To attach a URL to an existing highlight attribute we call `hl_add_url` which finds the URL in the set (allocating and adding it if it does not exist) and sets the `url` highlight attribute to the index of the URL in the set (using an index helps keep the size of the `HlAttrs` struct small). This has the potential to lead to an increase in highlight attributes if a URL is used over a range that contains many different highlight attributes, because now each existing attribute must be combined with the URL. In practice, however, URLs typically span a range containing a single highlight (e.g. link text in Markdown), so this is likely just a pathological edge case. When a new highlight attribute is defined with a URL it is copied to all attached UIs with the `hl_attr_define` UI event. The TUI manages its own set of URLs (just like the highlight subsystem) to minimize allocations. The TUI keeps track of which URL is "active" for the cell it is printing. If no URL is active and a cell containing a URL is printed, the opening OSC 8 sequence is emitted and that URL becomes the actively tracked URL. If the cursor is moved while in the middle of a URL span, we emit the terminating OSC sequence to prevent the hyperlink from spanning multiple lines. This does not support nested hyperlinks, but that is a rare (and, frankly, bizarre) use case. If a valid use case for nested hyperlinks ever presents itself we can address that issue then. --- src/nvim/api/ui.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'src/nvim/api/ui.c') diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c index b42c274411..f955b315a8 100644 --- a/src/nvim/api/ui.c +++ b/src/nvim/api/ui.c @@ -784,6 +784,14 @@ void remote_ui_hl_attr_define(UI *ui, Integer id, HlAttrs rgb_attrs, HlAttrs cte MAXSIZE_TEMP_DICT(cterm, HLATTRS_DICT_SIZE); hlattrs2dict(&rgb, NULL, rgb_attrs, true, false); hlattrs2dict(&cterm, NULL, rgb_attrs, false, false); + + // URLs are not added in hlattrs2dict since they are used only by UIs and not by the highlight + // system. So we add them here. + if (rgb_attrs.url >= 0) { + const char *url = hl_get_url((uint32_t)rgb_attrs.url); + PUT_C(rgb, "url", STRING_OBJ(cstr_as_string((char *)url))); + } + ADD_C(args, DICTIONARY_OBJ(rgb)); ADD_C(args, DICTIONARY_OBJ(cterm)); -- cgit From a090d43d61b5de1add5624fc55e4a9dead5b7ece Mon Sep 17 00:00:00 2001 From: Fred Sundvik Date: Mon, 5 Feb 2024 14:39:54 +0200 Subject: fix: splitting of big UI messages Determine the needed buffer space first, instead of trying to revert the effect of prepare_call if message does not fit. The previous code did not revert the full state, which caused corrupted messages to be sent. So, rather than trying to fix all of that, with fragile and hard to read code as a result, the code is now much more simple, although slightly slower. --- src/nvim/api/ui.c | 101 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 53 insertions(+), 48 deletions(-) (limited to 'src/nvim/api/ui.c') diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c index f955b315a8..03e7e11601 100644 --- a/src/nvim/api/ui.c +++ b/src/nvim/api/ui.c @@ -585,7 +585,6 @@ static inline int write_cb(void *vdata, const char *buf, size_t len) data->pack_totlen += len; if (!data->temp_buf && UI_BUF_SIZE - BUF_POS(data) < len) { - data->buf_overflow = true; return 0; } @@ -595,14 +594,42 @@ static inline int write_cb(void *vdata, const char *buf, size_t len) return 0; } -static bool prepare_call(UI *ui, const char *name) +static inline int size_cb(void *vdata, const char *buf, size_t len) +{ + UIData *data = (UIData *)vdata; + if (!buf) { + return 0; + } + + data->pack_totlen += len; + return 0; +} + +static void prepare_call(UI *ui, const char *name, size_t size_needed) { UIData *data = ui->data; + size_t name_len = strlen(name); + const size_t overhead = name_len + 20; + bool oversized_message = size_needed + overhead > UI_BUF_SIZE; - if (BUF_POS(data) > UI_BUF_SIZE - EVENT_BUF_SIZE) { + if (oversized_message || BUF_POS(data) > UI_BUF_SIZE - size_needed - overhead) { remote_ui_flush_buf(ui); } + if (oversized_message) { + // TODO(bfredl): manually testable by setting UI_BUF_SIZE to 1024 (mode_info_set) + data->temp_buf = xmalloc(20 + name_len + size_needed); + data->buf_wptr = data->temp_buf; + char **buf = &data->buf_wptr; + mpack_array(buf, 3); + mpack_uint(buf, 2); + mpack_str(buf, S_LEN("redraw")); + mpack_array(buf, 1); + mpack_array(buf, 2); + mpack_str(buf, name, name_len); + return; + } + // 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" @@ -615,64 +642,42 @@ static bool prepare_call(UI *ui, const char *name) mpack_str(buf, name, strlen(name)); data->nevents++; data->ncalls = 1; - return true; + return; } +} - return false; +static void send_oversized_message(UIData *data) +{ + 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; + } } /// 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; + // First determine the needed size + msgpack_packer_init(&pac, data, size_cb); + msgpack_rpc_from_array(args, &pac); + // Then send the actual message + prepare_call(ui, name, data->pack_totlen); 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); - } - size_t name_len = strlen(name); - if (data->pack_totlen > UI_BUF_SIZE - name_len - 20) { - // TODO(bfredl): manually testable by setting UI_BUF_SIZE to 1024 (mode_info_set) - data->temp_buf = xmalloc(20 + name_len + 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, S_LEN("redraw")); - mpack_array(buf, 1); - mpack_array(buf, 2); - mpack_str(buf, name, name_len); - } 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; - } + // Oversized messages need to be sent immediately + if (data->temp_buf) { + send_oversized_message(data); } + data->ncalls++; } @@ -867,7 +872,7 @@ void remote_ui_raw_line(UI *ui, Integer grid, Integer row, Integer startcol, Int { UIData *data = ui->data; if (ui->ui_ext[kUILinegrid]) { - prepare_call(ui, "grid_line"); + prepare_call(ui, "grid_line", EVENT_BUF_SIZE); data->ncalls++; char **buf = &data->buf_wptr; @@ -896,7 +901,7 @@ void remote_ui_raw_line(UI *ui, Integer grid, Integer row, Integer startcol, Int mpack_bool(buf, false); remote_ui_flush_buf(ui); - prepare_call(ui, "grid_line"); + prepare_call(ui, "grid_line", EVENT_BUF_SIZE); data->ncalls++; mpack_array(buf, 5); mpack_uint(buf, (uint32_t)grid); -- cgit From e0e5b7f0ba1b0440bdc2b557e2b2cfae24706cbd Mon Sep 17 00:00:00 2001 From: bfredl Date: Fri, 9 Feb 2024 11:42:40 +0100 Subject: refactor(api): make cstr_as_string accept "const char*" In the context a String inside an Object/Dictionary etc is consumed, it is considered to be read-only. --- src/nvim/api/ui.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/nvim/api/ui.c') diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c index f955b315a8..c6e868ca88 100644 --- a/src/nvim/api/ui.c +++ b/src/nvim/api/ui.c @@ -789,7 +789,7 @@ void remote_ui_hl_attr_define(UI *ui, Integer id, HlAttrs rgb_attrs, HlAttrs cte // system. So we add them here. if (rgb_attrs.url >= 0) { const char *url = hl_get_url((uint32_t)rgb_attrs.url); - PUT_C(rgb, "url", STRING_OBJ(cstr_as_string((char *)url))); + PUT_C(rgb, "url", CSTR_AS_OBJ(url)); } ADD_C(args, DICTIONARY_OBJ(rgb)); @@ -857,7 +857,7 @@ void remote_ui_put(UI *ui, const char *cell) UIData *data = ui->data; data->client_col++; Array args = data->call_buf; - ADD_C(args, CSTR_AS_OBJ((char *)cell)); + ADD_C(args, CSTR_AS_OBJ(cell)); push_call(ui, "put", args); } -- cgit From e9510211f0b957606685344be97350c29e3ea638 Mon Sep 17 00:00:00 2001 From: bfredl Date: Fri, 9 Feb 2024 12:40:48 +0100 Subject: refactor(api): use arena for nvim_list_uis() --- src/nvim/api/ui.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) (limited to 'src/nvim/api/ui.c') diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c index c6e868ca88..5c8ebfb861 100644 --- a/src/nvim/api/ui.c +++ b/src/nvim/api/ui.c @@ -247,10 +247,9 @@ void nvim_ui_attach(uint64_t channel_id, Integer width, Integer height, Dictiona void ui_attach(uint64_t channel_id, Integer width, Integer height, Boolean enable_rgb, Error *err) FUNC_API_DEPRECATED_SINCE(1) { - Dictionary opts = ARRAY_DICT_INIT; - PUT(opts, "rgb", BOOLEAN_OBJ(enable_rgb)); + MAXSIZE_TEMP_DICT(opts, 1); + PUT_C(opts, "rgb", BOOLEAN_OBJ(enable_rgb)); nvim_ui_attach(channel_id, width, height, opts, err); - api_free_dictionary(opts); } /// Tells the nvim server if focus was gained or lost by the GUI @@ -1113,9 +1112,3 @@ void remote_ui_event(UI *ui, char *name, Array args) free_ret: arena_mem_free(arena_finish(&arena)); } - -void remote_ui_inspect(UI *ui, Dictionary *info) -{ - UIData *data = ui->data; - PUT(*info, "chan", INTEGER_OBJ((Integer)data->channel_id)); -} -- cgit From d60412b18e4e21f301baa2ac3f3fb7be89655e4b Mon Sep 17 00:00:00 2001 From: bfredl Date: Mon, 12 Feb 2024 20:40:27 +0100 Subject: refactor(eval): use arena when converting typvals to Object Note: this contains two _temporary_ changes which can be reverted once the Arena vs no-Arena distinction in API wrappers has been removed. Both nlua_push_Object and object_to_vim_take_luaref() has been changed to take the object argument as a pointer. This is not going to be necessary once these are only used with arena (or not at all) allocated Objects. The object_to_vim() variant which leaves luaref untouched might need to stay for a little longer. --- src/nvim/api/ui.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/nvim/api/ui.c') diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c index c7280253c2..140b520cb5 100644 --- a/src/nvim/api/ui.c +++ b/src/nvim/api/ui.c @@ -1029,12 +1029,12 @@ static Array translate_contents(UI *ui, Array contents, Arena *arena) if (attr) { Dictionary rgb_attrs = arena_dict(arena, HLATTRS_DICT_SIZE); hlattrs2dict(&rgb_attrs, NULL, syn_attr2entry(attr), ui->rgb, false); - ADD(new_item, DICTIONARY_OBJ(rgb_attrs)); + ADD_C(new_item, DICTIONARY_OBJ(rgb_attrs)); } else { - ADD(new_item, DICTIONARY_OBJ((Dictionary)ARRAY_DICT_INIT)); + ADD_C(new_item, DICTIONARY_OBJ((Dictionary)ARRAY_DICT_INIT)); } - ADD(new_item, item.items[1]); - ADD(new_contents, ARRAY_OBJ(new_item)); + ADD_C(new_item, item.items[1]); + ADD_C(new_contents, ARRAY_OBJ(new_item)); } return new_contents; } -- cgit From 146333ca123ab85397eb089345569cd9ed2d405a Mon Sep 17 00:00:00 2001 From: bfredl Date: Sat, 17 Feb 2024 20:33:11 +0100 Subject: refactor(api): use arena for channel info and terminal info --- src/nvim/api/ui.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/api/ui.c') diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c index 140b520cb5..d5843caa96 100644 --- a/src/nvim/api/ui.c +++ b/src/nvim/api/ui.c @@ -1046,7 +1046,7 @@ static Array translate_firstarg(UI *ui, Array args, Arena *arena) ADD_C(new_args, ARRAY_OBJ(translate_contents(ui, contents, arena))); for (size_t i = 1; i < args.size; i++) { - ADD(new_args, args.items[i]); + ADD_C(new_args, args.items[i]); } return new_args; } -- cgit From 9beb40a4db5613601fc1a4b828a44e5977eca046 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Thu, 15 Feb 2024 17:16:04 +0000 Subject: feat(docs): replace lua2dox.lua Problem: The documentation flow (`gen_vimdoc.py`) has several issues: - it's not very versatile - depends on doxygen - doesn't work well with Lua code as it requires an awkward filter script to convert it into pseudo-C. - The intermediate XML files and filters makes it too much like a rube goldberg machine. Solution: Re-implement the flow using Lua, LPEG and treesitter. - `gen_vimdoc.py` is now replaced with `gen_vimdoc.lua` and replicates a portion of the logic. - `lua2dox.lua` is gone! - No more XML files. - Doxygen is now longer used and instead we now use: - LPEG for comment parsing (see `scripts/luacats_grammar.lua` and `scripts/cdoc_grammar.lua`). - LPEG for C parsing (see `scripts/cdoc_parser.lua`) - Lua patterns for Lua parsing (see `scripts/luacats_parser.lua`). - Treesitter for Markdown parsing (see `scripts/text_utils.lua`). - The generated `runtime/doc/*.mpack` files have been removed. - `scripts/gen_eval_files.lua` now instead uses `scripts/cdoc_parser.lua` directly. - Text wrapping is implemented in `scripts/text_utils.lua` and appears to produce more consistent results (the main contributer to the diff of this change). --- src/nvim/api/ui.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/nvim/api/ui.c') diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c index d5843caa96..0f016d2f29 100644 --- a/src/nvim/api/ui.c +++ b/src/nvim/api/ui.c @@ -165,7 +165,7 @@ void remote_ui_wait_for_attach(bool only_stdio) /// Activates UI events on the channel. /// -/// Entry point of all UI clients. Allows |\-\-embed| to continue startup. +/// Entry point of all UI clients. Allows |--embed| to continue startup. /// Implies that the client is ready to show the UI. Adds the client to the /// list of UIs. |nvim_list_uis()| /// @@ -541,7 +541,7 @@ void nvim_ui_pum_set_bounds(uint64_t channel_id, Float width, Float height, Floa /// /// @param channel_id /// @param event Event name -/// @param payload Event payload +/// @param value Event payload /// @param[out] err Error details, if any. void nvim_ui_term_event(uint64_t channel_id, String event, Object value, Error *err) FUNC_API_SINCE(12) FUNC_API_REMOTE_ONLY -- cgit From dc37c1550bed46fffbb677d343cdc5bc94056219 Mon Sep 17 00:00:00 2001 From: bfredl Date: Sun, 25 Feb 2024 15:02:48 +0100 Subject: refactor(msgpack): allow flushing buffer while packing msgpack Before, we needed to always pack an entire msgpack_rpc Object to a continous memory buffer before sending it out to a channel. But this is generally wasteful. it is better to just flush whatever is in the buffer and then continue packing to a new buffer. This is also done for the UI event packer where there are some extra logic to "finish" of an existing batch of nevents/ncalls. This doesn't really stop us from flushing the buffer, just that we need to update the state machine accordingly so the next call to prepare_call() always will start with a new event (even though the buffer might contain overflow data from a large event). --- src/nvim/api/ui.c | 235 ++++++++++++++++-------------------------------------- 1 file changed, 70 insertions(+), 165 deletions(-) (limited to 'src/nvim/api/ui.c') diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c index 0f016d2f29..638a4fdcc4 100644 --- a/src/nvim/api/ui.c +++ b/src/nvim/api/ui.c @@ -12,6 +12,7 @@ #include "nvim/api/private/helpers.h" #include "nvim/api/private/validate.h" #include "nvim/api/ui.h" +#include "nvim/assert_defs.h" #include "nvim/autocmd.h" #include "nvim/autocmd_defs.h" #include "nvim/channel.h" @@ -33,12 +34,12 @@ #include "nvim/memory_defs.h" #include "nvim/msgpack_rpc/channel.h" #include "nvim/msgpack_rpc/channel_defs.h" -#include "nvim/msgpack_rpc/helpers.h" +#include "nvim/msgpack_rpc/packer.h" #include "nvim/option.h" #include "nvim/types_defs.h" #include "nvim/ui.h" -#define BUF_POS(data) ((size_t)((data)->buf_wptr - (data)->buf)) +#define BUF_POS(data) ((size_t)((data)->packer.ptr - (data)->packer.startptr)) #ifdef INCLUDE_GENERATED_DECLARATIONS # include "api/ui.c.generated.h" @@ -47,55 +48,6 @@ 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_bool(char **buf, bool val) -{ - mpack_w(buf, 0xc2 | (val ? 1 : 0)); -} - -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); @@ -104,9 +56,9 @@ static char *mpack_array_dyn16(char **buf) return pos; } -static void mpack_str(char **buf, const char *str, size_t len) +static void mpack_str_small(char **buf, const char *str, size_t len) { - assert(sizeof(schar_T) - 1 < 0x20); + assert(len < 0x20); mpack_w(buf, 0xa0 | len); memcpy(*buf, str, len); *buf += len; @@ -117,6 +69,7 @@ static void remote_ui_destroy(UI *ui) { UIData *data = ui->data; kv_destroy(data->call_buf); + xfree(data->packer.startptr); XFREE_CLEAR(ui->term_name); xfree(ui); } @@ -231,8 +184,13 @@ void nvim_ui_attach(uint64_t channel_id, Integer width, Integer height, Dictiona data->ncalls_pos = NULL; data->ncalls = 0; data->ncells_pending = 0; - data->buf_wptr = data->buf; - data->temp_buf = NULL; + data->packer = (PackerBuffer) { + .startptr = NULL, + .ptr = NULL, + .endptr = NULL, + .packer_flush = ui_flush_callback, + .anydata = data, + }; data->wildmenu_active = false; data->call_buf = (Array)ARRAY_DICT_INIT; kv_ensure_space(data->call_buf, 16); @@ -561,72 +519,29 @@ void nvim_ui_term_event(uint64_t channel_id, String event, Object value, Error * static void flush_event(UIData *data) { if (data->cur_event) { - mpack_w2(&data->ncalls_pos, data->ncalls); + mpack_w2(&data->ncalls_pos, 1 + data->ncalls); data->cur_event = NULL; + data->ncalls_pos = NULL; + data->ncalls = 0; } - 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, S_LEN("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) { - return 0; - } - - memcpy(data->buf_wptr, buf, len); - data->buf_wptr += len; - - return 0; } -static inline int size_cb(void *vdata, const char *buf, size_t len) +static void ui_alloc_buf(UIData *data) { - UIData *data = (UIData *)vdata; - if (!buf) { - return 0; - } - - data->pack_totlen += len; - return 0; + data->packer.startptr = alloc_block(); + data->packer.ptr = data->packer.startptr; + data->packer.endptr = data->packer.startptr + UI_BUF_SIZE; } -static void prepare_call(UI *ui, const char *name, size_t size_needed) +static void prepare_call(UI *ui, const char *name) { UIData *data = ui->data; - size_t name_len = strlen(name); - const size_t overhead = name_len + 20; - bool oversized_message = size_needed + overhead > UI_BUF_SIZE; - - if (oversized_message || BUF_POS(data) > UI_BUF_SIZE - size_needed - overhead) { - remote_ui_flush_buf(ui); + if (data->packer.startptr && BUF_POS(data) > UI_BUF_SIZE - EVENT_BUF_SIZE) { + ui_flush_buf(data); } - if (oversized_message) { - // TODO(bfredl): manually testable by setting UI_BUF_SIZE to 1024 (mode_info_set) - data->temp_buf = xmalloc(20 + name_len + size_needed); - data->buf_wptr = data->temp_buf; - char **buf = &data->buf_wptr; - mpack_array(buf, 3); - mpack_uint(buf, 2); - mpack_str(buf, S_LEN("redraw")); - mpack_array(buf, 1); - mpack_array(buf, 2); - mpack_str(buf, name, name_len); - return; + if (data->packer.startptr == NULL) { + ui_alloc_buf(data); } // To optimize data transfer(especially for "grid_line"), we bundle adjacent @@ -634,26 +549,23 @@ static void prepare_call(UI *ui, const char *name, size_t size_needed) // method call is different from "name" if (!data->cur_event || !strequal(data->cur_event, name)) { + char **buf = &data->packer.ptr; + if (!data->nevents_pos) { + // [2, "redraw", [...]] + mpack_array(buf, 3); + mpack_uint(buf, 2); + mpack_str_small(buf, S_LEN("redraw")); + data->nevents_pos = mpack_array_dyn16(buf); + assert(data->cur_event == NULL); + } flush_event(data); data->cur_event = name; - char **buf = &data->buf_wptr; data->ncalls_pos = mpack_array_dyn16(buf); - mpack_str(buf, name, strlen(name)); + mpack_str_small(buf, name, strlen(name)); data->nevents++; data->ncalls = 1; - return; - } -} - -static void send_oversized_message(UIData *data) -{ - 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; + } else { + data->ncalls++; } } @@ -661,23 +573,15 @@ static void send_oversized_message(UIData *data) static void push_call(UI *ui, const char *name, Array args) { UIData *data = ui->data; + prepare_call(ui, name); + mpack_object_array(args, &data->packer); +} - msgpack_packer pac; - data->pack_totlen = 0; - // First determine the needed size - msgpack_packer_init(&pac, data, size_cb); - msgpack_rpc_from_array(args, &pac); - // Then send the actual message - prepare_call(ui, name, data->pack_totlen); - msgpack_packer_init(&pac, data, write_cb); - msgpack_rpc_from_array(args, &pac); - - // Oversized messages need to be sent immediately - if (data->temp_buf) { - send_oversized_message(data); - } - - data->ncalls++; +static void ui_flush_callback(PackerBuffer *packer) +{ + UIData *data = packer->anydata; + ui_flush_buf(data); + ui_alloc_buf(data); } void remote_ui_grid_clear(UI *ui, Integer grid) @@ -869,12 +773,15 @@ void remote_ui_raw_line(UI *ui, Integer grid, Integer row, Integer startcol, Int Integer clearcol, Integer clearattr, LineFlags flags, const schar_T *chunk, const sattr_T *attrs) { + // If MAX_SCHAR_SIZE is made larger, we need to refactor implementation below + // to not only use FIXSTR (only up to 0x20 bytes) + STATIC_ASSERT(MAX_SCHAR_SIZE - 1 < 0x20, "SCHAR doesn't fit in fixstr"); + UIData *data = ui->data; if (ui->ui_ext[kUILinegrid]) { - prepare_call(ui, "grid_line", EVENT_BUF_SIZE); - data->ncalls++; + prepare_call(ui, "grid_line"); - char **buf = &data->buf_wptr; + char **buf = &data->packer.ptr; mpack_array(buf, 5); mpack_uint(buf, (uint32_t)grid); mpack_uint(buf, (uint32_t)row); @@ -898,10 +805,9 @@ void remote_ui_raw_line(UI *ui, Integer grid, Integer row, Integer startcol, Int // We only ever set the wrap field on the final "grid_line" event for the line. mpack_bool(buf, false); - remote_ui_flush_buf(ui); + ui_flush_buf(data); - prepare_call(ui, "grid_line", EVENT_BUF_SIZE); - data->ncalls++; + prepare_call(ui, "grid_line"); mpack_array(buf, 5); mpack_uint(buf, (uint32_t)grid); mpack_uint(buf, (uint32_t)row); @@ -934,7 +840,7 @@ void remote_ui_raw_line(UI *ui, Integer grid, Integer row, Integer startcol, Int nelem++; data->ncells_pending += 1; mpack_array(buf, 3); - mpack_str(buf, S_LEN(" ")); + mpack_str_small(buf, S_LEN(" ")); mpack_uint(buf, (uint32_t)clearattr); mpack_uint(buf, (uint32_t)(clearcol - endcol)); } @@ -943,7 +849,7 @@ void remote_ui_raw_line(UI *ui, Integer grid, Integer row, Integer startcol, Int if (data->ncells_pending > 500) { // pass off cells to UI to let it start processing them - remote_ui_flush_buf(ui); + ui_flush_buf(data); } } else { for (int i = 0; i < endcol - startcol; i++) { @@ -977,28 +883,27 @@ void remote_ui_raw_line(UI *ui, Integer grid, Integer row, Integer startcol, Int /// /// This might happen multiple times before the actual ui_flush, if the /// total redraw size is large! -void remote_ui_flush_buf(UI *ui) +static void ui_flush_buf(UIData *data) { - UIData *data = ui->data; - if (!data->nevents_pos) { + if (!data->packer.startptr || !BUF_POS(data)) { return; } - if (data->cur_event) { - flush_event(data); + + flush_event(data); + if (data->nevents_pos != NULL) { + mpack_w2(&data->nevents_pos, data->nevents); + data->nevents = 0; + data->nevents_pos = NULL; } - 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); + WBuffer *buf = wstream_new_buffer(data->packer.startptr, BUF_POS(data), 1, free_block); 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; + data->packer.startptr = NULL; + data->packer.ptr = NULL; + + // we have sent events to the client, but possibly not yet the final "flush" event. + data->flushed_events = true; data->ncells_pending = 0; } @@ -1014,7 +919,7 @@ void remote_ui_flush(UI *ui) remote_ui_cursor_goto(ui, data->cursor_row, data->cursor_col); } push_call(ui, "flush", (Array)ARRAY_DICT_INIT); - remote_ui_flush_buf(ui); + ui_flush_buf(data); data->flushed_events = false; } } -- cgit From e534ec47db4bf5e110c828ca3d875f5685dcfdde Mon Sep 17 00:00:00 2001 From: bfredl Date: Fri, 8 Mar 2024 09:15:21 +0100 Subject: refactor(ui): remove outdated UI vs UIData distinction Just some basic spring cleaning. In the distant past, not all UI:s where remote UI:s. They still aren't, but both of the "UI" and "UIData" structs are now only for remote UI:s. Thus join them as "RemoteUI". --- src/nvim/api/ui.c | 279 +++++++++++++++++++++++++----------------------------- 1 file changed, 131 insertions(+), 148 deletions(-) (limited to 'src/nvim/api/ui.c') diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c index 638a4fdcc4..02433b037c 100644 --- a/src/nvim/api/ui.c +++ b/src/nvim/api/ui.c @@ -39,7 +39,7 @@ #include "nvim/types_defs.h" #include "nvim/ui.h" -#define BUF_POS(data) ((size_t)((data)->packer.ptr - (data)->packer.startptr)) +#define BUF_POS(ui) ((size_t)((ui)->packer.ptr - (ui)->packer.startptr)) #ifdef INCLUDE_GENERATED_DECLARATIONS # include "api/ui.c.generated.h" @@ -64,19 +64,18 @@ static void mpack_str_small(char **buf, const char *str, size_t len) *buf += len; } -static void remote_ui_destroy(UI *ui) +static void remote_ui_destroy(RemoteUI *ui) FUNC_ATTR_NONNULL_ALL { - UIData *data = ui->data; - kv_destroy(data->call_buf); - xfree(data->packer.startptr); + kv_destroy(ui->call_buf); + xfree(ui->packer.startptr); XFREE_CLEAR(ui->term_name); xfree(ui); } void remote_ui_disconnect(uint64_t channel_id) { - UI *ui = pmap_get(uint64_t)(&connected_uis, channel_id); + RemoteUI *ui = pmap_get(uint64_t)(&connected_uis, channel_id); if (!ui) { return; } @@ -88,7 +87,7 @@ void remote_ui_disconnect(uint64_t channel_id) #ifdef EXITFREE void remote_ui_free_all_mem(void) { - UI *ui; + RemoteUI *ui; map_foreach_value(&connected_uis, ui, { remote_ui_destroy(ui); }); @@ -146,7 +145,7 @@ void nvim_ui_attach(uint64_t channel_id, Integer width, Integer height, Dictiona "Expected width > 0 and height > 0"); return; } - UI *ui = xcalloc(1, sizeof(UI)); + RemoteUI *ui = xcalloc(1, sizeof(RemoteUI)); ui->width = (int)width; ui->height = (int)height; ui->pum_row = -1.0; @@ -173,27 +172,26 @@ void nvim_ui_attach(uint64_t channel_id, Integer width, Integer height, Dictiona ui->ui_ext[kUICmdline] = true; } - UIData *data = ui->data; - data->channel_id = channel_id; - 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->ncells_pending = 0; - data->packer = (PackerBuffer) { + ui->channel_id = channel_id; + ui->cur_event = NULL; + ui->hl_id = 0; + ui->client_col = -1; + ui->nevents_pos = NULL; + ui->nevents = 0; + ui->flushed_events = false; + ui->ncalls_pos = NULL; + ui->ncalls = 0; + ui->ncells_pending = 0; + ui->packer = (PackerBuffer) { .startptr = NULL, .ptr = NULL, .endptr = NULL, .packer_flush = ui_flush_callback, - .anydata = data, + .anydata = ui, }; - data->wildmenu_active = false; - data->call_buf = (Array)ARRAY_DICT_INIT; - kv_ensure_space(data->call_buf, 16); + ui->wildmenu_active = false; + ui->call_buf = (Array)ARRAY_DICT_INIT; + kv_ensure_space(ui->call_buf, 16); pmap_put(uint64_t)(&connected_uis, channel_id, ui); ui_attach_impl(ui, channel_id); @@ -245,7 +243,7 @@ void nvim_ui_detach(uint64_t channel_id, Error *err) } // TODO(bfredl): use me to detach a specific ui from the server -void remote_ui_stop(UI *ui) +void remote_ui_stop(RemoteUI *ui) { } @@ -264,7 +262,7 @@ void nvim_ui_try_resize(uint64_t channel_id, Integer width, Integer height, Erro return; } - UI *ui = pmap_get(uint64_t)(&connected_uis, channel_id); + RemoteUI *ui = pmap_get(uint64_t)(&connected_uis, channel_id); ui->width = (int)width; ui->height = (int)height; ui_refresh(); @@ -278,12 +276,12 @@ void nvim_ui_set_option(uint64_t channel_id, String name, Object value, Error *e "UI not attached to channel: %" PRId64, channel_id); return; } - UI *ui = pmap_get(uint64_t)(&connected_uis, channel_id); + RemoteUI *ui = pmap_get(uint64_t)(&connected_uis, channel_id); ui_set_option(ui, false, name, value, error); } -static void ui_set_option(UI *ui, bool init, String name, Object value, Error *err) +static void ui_set_option(RemoteUI *ui, bool init, String name, Object value, Error *err) { if (strequal(name.data, "override")) { VALIDATE_T("override", kObjectTypeBoolean, value.type, { @@ -431,7 +429,7 @@ void nvim_ui_pum_set_height(uint64_t channel_id, Integer height, Error *err) return; } - UI *ui = pmap_get(uint64_t)(&connected_uis, channel_id); + RemoteUI *ui = pmap_get(uint64_t)(&connected_uis, channel_id); if (!ui->ui_ext[kUIPopupmenu]) { api_set_error(err, kErrorTypeValidation, "It must support the ext_popupmenu option"); @@ -467,7 +465,7 @@ void nvim_ui_pum_set_bounds(uint64_t channel_id, Float width, Float height, Floa return; } - UI *ui = pmap_get(uint64_t)(&connected_uis, channel_id); + RemoteUI *ui = pmap_get(uint64_t)(&connected_uis, channel_id); if (!ui->ui_ext[kUIPopupmenu]) { api_set_error(err, kErrorTypeValidation, "UI must support the ext_popupmenu option"); @@ -516,78 +514,75 @@ void nvim_ui_term_event(uint64_t channel_id, String event, Object value, Error * } } -static void flush_event(UIData *data) +static void flush_event(RemoteUI *ui) { - if (data->cur_event) { - mpack_w2(&data->ncalls_pos, 1 + data->ncalls); - data->cur_event = NULL; - data->ncalls_pos = NULL; - data->ncalls = 0; + if (ui->cur_event) { + mpack_w2(&ui->ncalls_pos, 1 + ui->ncalls); + ui->cur_event = NULL; + ui->ncalls_pos = NULL; + ui->ncalls = 0; } } -static void ui_alloc_buf(UIData *data) +static void ui_alloc_buf(RemoteUI *ui) { - data->packer.startptr = alloc_block(); - data->packer.ptr = data->packer.startptr; - data->packer.endptr = data->packer.startptr + UI_BUF_SIZE; + ui->packer.startptr = alloc_block(); + ui->packer.ptr = ui->packer.startptr; + ui->packer.endptr = ui->packer.startptr + UI_BUF_SIZE; } -static void prepare_call(UI *ui, const char *name) +static void prepare_call(RemoteUI *ui, const char *name) { - UIData *data = ui->data; - if (data->packer.startptr && BUF_POS(data) > UI_BUF_SIZE - EVENT_BUF_SIZE) { - ui_flush_buf(data); + if (ui->packer.startptr && BUF_POS(ui) > UI_BUF_SIZE - EVENT_BUF_SIZE) { + ui_flush_buf(ui); } - if (data->packer.startptr == NULL) { - ui_alloc_buf(data); + if (ui->packer.startptr == NULL) { + ui_alloc_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 (!data->cur_event || !strequal(data->cur_event, name)) { - char **buf = &data->packer.ptr; - if (!data->nevents_pos) { + if (!ui->cur_event || !strequal(ui->cur_event, name)) { + char **buf = &ui->packer.ptr; + if (!ui->nevents_pos) { // [2, "redraw", [...]] mpack_array(buf, 3); mpack_uint(buf, 2); mpack_str_small(buf, S_LEN("redraw")); - data->nevents_pos = mpack_array_dyn16(buf); - assert(data->cur_event == NULL); + ui->nevents_pos = mpack_array_dyn16(buf); + assert(ui->cur_event == NULL); } - flush_event(data); - data->cur_event = name; - data->ncalls_pos = mpack_array_dyn16(buf); + flush_event(ui); + ui->cur_event = name; + ui->ncalls_pos = mpack_array_dyn16(buf); mpack_str_small(buf, name, strlen(name)); - data->nevents++; - data->ncalls = 1; + ui->nevents++; + ui->ncalls = 1; } else { - data->ncalls++; + ui->ncalls++; } } -/// Pushes data into UI.UIData, to be consumed later by remote_ui_flush(). -static void push_call(UI *ui, const char *name, Array args) +/// Pushes data into RemoteUI, to be consumed later by remote_ui_flush(). +static void push_call(RemoteUI *ui, const char *name, Array args) { - UIData *data = ui->data; prepare_call(ui, name); - mpack_object_array(args, &data->packer); + mpack_object_array(args, &ui->packer); } static void ui_flush_callback(PackerBuffer *packer) { - UIData *data = packer->anydata; - ui_flush_buf(data); - ui_alloc_buf(data); + RemoteUI *ui = packer->anydata; + ui_flush_buf(ui); + ui_alloc_buf(ui); } -void remote_ui_grid_clear(UI *ui, Integer grid) +void remote_ui_grid_clear(RemoteUI *ui, Integer grid) { - UIData *data = ui->data; - Array args = data->call_buf; + Array args = ui->call_buf; if (ui->ui_ext[kUILinegrid]) { ADD_C(args, INTEGER_OBJ(grid)); } @@ -595,14 +590,13 @@ void remote_ui_grid_clear(UI *ui, Integer grid) push_call(ui, name, args); } -void remote_ui_grid_resize(UI *ui, Integer grid, Integer width, Integer height) +void remote_ui_grid_resize(RemoteUI *ui, Integer grid, Integer width, Integer height) { - UIData *data = ui->data; - Array args = data->call_buf; + Array args = ui->call_buf; if (ui->ui_ext[kUILinegrid]) { ADD_C(args, INTEGER_OBJ(grid)); } else { - data->client_col = -1; // force cursor update + ui->client_col = -1; // force cursor update } ADD_C(args, INTEGER_OBJ(width)); ADD_C(args, INTEGER_OBJ(height)); @@ -610,12 +604,11 @@ void remote_ui_grid_resize(UI *ui, Integer grid, Integer width, Integer height) push_call(ui, name, args); } -void remote_ui_grid_scroll(UI *ui, Integer grid, Integer top, Integer bot, Integer left, +void remote_ui_grid_scroll(RemoteUI *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 = data->call_buf; + Array args = ui->call_buf; ADD_C(args, INTEGER_OBJ(grid)); ADD_C(args, INTEGER_OBJ(top)); ADD_C(args, INTEGER_OBJ(bot)); @@ -625,20 +618,20 @@ void remote_ui_grid_scroll(UI *ui, Integer grid, Integer top, Integer bot, Integ ADD_C(args, INTEGER_OBJ(cols)); push_call(ui, "grid_scroll", args); } else { - Array args = data->call_buf; + Array args = ui->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 = data->call_buf; + args = ui->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 = data->call_buf; + args = ui->call_buf; ADD_C(args, INTEGER_OBJ(0)); ADD_C(args, INTEGER_OBJ(ui->height - 1)); ADD_C(args, INTEGER_OBJ(0)); @@ -647,14 +640,13 @@ void remote_ui_grid_scroll(UI *ui, Integer grid, Integer top, Integer bot, Integ } } -void remote_ui_default_colors_set(UI *ui, Integer rgb_fg, Integer rgb_bg, Integer rgb_sp, +void remote_ui_default_colors_set(RemoteUI *ui, Integer rgb_fg, Integer rgb_bg, Integer rgb_sp, Integer cterm_fg, Integer cterm_bg) { if (!ui->ui_ext[kUITermColors]) { HL_SET_DEFAULT_COLORS(rgb_fg, rgb_bg, rgb_sp); } - UIData *data = ui->data; - Array args = data->call_buf; + Array args = ui->call_buf; ADD_C(args, INTEGER_OBJ(rgb_fg)); ADD_C(args, INTEGER_OBJ(rgb_bg)); ADD_C(args, INTEGER_OBJ(rgb_sp)); @@ -664,29 +656,28 @@ void remote_ui_default_colors_set(UI *ui, Integer rgb_fg, Integer rgb_bg, Intege // Deprecated if (!ui->ui_ext[kUILinegrid]) { - args = data->call_buf; + args = ui->call_buf; ADD_C(args, INTEGER_OBJ(ui->rgb ? rgb_fg : cterm_fg - 1)); push_call(ui, "update_fg", args); - args = data->call_buf; + args = ui->call_buf; ADD_C(args, INTEGER_OBJ(ui->rgb ? rgb_bg : cterm_bg - 1)); push_call(ui, "update_bg", args); - args = data->call_buf; + args = ui->call_buf; ADD_C(args, INTEGER_OBJ(ui->rgb ? rgb_sp : -1)); push_call(ui, "update_sp", args); } } -void remote_ui_hl_attr_define(UI *ui, Integer id, HlAttrs rgb_attrs, HlAttrs cterm_attrs, +void remote_ui_hl_attr_define(RemoteUI *ui, Integer id, HlAttrs rgb_attrs, HlAttrs cterm_attrs, Array info) { if (!ui->ui_ext[kUILinegrid]) { return; } - UIData *data = ui->data; - Array args = data->call_buf; + Array args = ui->call_buf; ADD_C(args, INTEGER_OBJ(id)); MAXSIZE_TEMP_DICT(rgb, HLATTRS_DICT_SIZE); MAXSIZE_TEMP_DICT(cterm, HLATTRS_DICT_SIZE); @@ -712,15 +703,14 @@ void remote_ui_hl_attr_define(UI *ui, Integer id, HlAttrs rgb_attrs, HlAttrs cte push_call(ui, "hl_attr_define", args); } -void remote_ui_highlight_set(UI *ui, int id) +void remote_ui_highlight_set(RemoteUI *ui, int id) { - UIData *data = ui->data; - Array args = data->call_buf; + Array args = ui->call_buf; - if (data->hl_id == id) { + if (ui->hl_id == id) { return; } - data->hl_id = id; + ui->hl_id = id; MAXSIZE_TEMP_DICT(dict, HLATTRS_DICT_SIZE); hlattrs2dict(&dict, NULL, syn_attr2entry(id), ui->rgb, false); ADD_C(args, DICTIONARY_OBJ(dict)); @@ -728,48 +718,44 @@ void remote_ui_highlight_set(UI *ui, int id) } /// "true" cursor used only for input focus -void remote_ui_grid_cursor_goto(UI *ui, Integer grid, Integer row, Integer col) +void remote_ui_grid_cursor_goto(RemoteUI *ui, Integer grid, Integer row, Integer col) { if (ui->ui_ext[kUILinegrid]) { - UIData *data = ui->data; - Array args = data->call_buf; + Array args = ui->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; - data->cursor_row = row; - data->cursor_col = col; + ui->cursor_row = row; + ui->cursor_col = col; remote_ui_cursor_goto(ui, row, col); } } /// emulated cursor used both for drawing and for input focus -void remote_ui_cursor_goto(UI *ui, Integer row, Integer col) +void remote_ui_cursor_goto(RemoteUI *ui, Integer row, Integer col) { - UIData *data = ui->data; - if (data->client_row == row && data->client_col == col) { + if (ui->client_row == row && ui->client_col == col) { return; } - data->client_row = row; - data->client_col = col; - Array args = data->call_buf; + ui->client_row = row; + ui->client_col = col; + Array args = ui->call_buf; ADD_C(args, INTEGER_OBJ(row)); ADD_C(args, INTEGER_OBJ(col)); push_call(ui, "cursor_goto", args); } -void remote_ui_put(UI *ui, const char *cell) +void remote_ui_put(RemoteUI *ui, const char *cell) { - UIData *data = ui->data; - data->client_col++; - Array args = data->call_buf; + ui->client_col++; + Array args = ui->call_buf; ADD_C(args, CSTR_AS_OBJ(cell)); push_call(ui, "put", args); } -void remote_ui_raw_line(UI *ui, Integer grid, Integer row, Integer startcol, Integer endcol, +void remote_ui_raw_line(RemoteUI *ui, Integer grid, Integer row, Integer startcol, Integer endcol, Integer clearcol, Integer clearattr, LineFlags flags, const schar_T *chunk, const sattr_T *attrs) { @@ -777,11 +763,10 @@ void remote_ui_raw_line(UI *ui, Integer grid, Integer row, Integer startcol, Int // to not only use FIXSTR (only up to 0x20 bytes) STATIC_ASSERT(MAX_SCHAR_SIZE - 1 < 0x20, "SCHAR doesn't fit in fixstr"); - UIData *data = ui->data; if (ui->ui_ext[kUILinegrid]) { prepare_call(ui, "grid_line"); - char **buf = &data->packer.ptr; + char **buf = &ui->packer.ptr; mpack_array(buf, 5); mpack_uint(buf, (uint32_t)grid); mpack_uint(buf, (uint32_t)row); @@ -796,7 +781,7 @@ void remote_ui_raw_line(UI *ui, Integer grid, Integer row, Integer startcol, Int for (size_t i = 0; i < ncells; i++) { repeat++; if (i == ncells - 1 || attrs[i] != attrs[i + 1] || chunk[i] != chunk[i + 1]) { - if (UI_BUF_SIZE - BUF_POS(data) < 2 * (1 + 2 + sizeof(schar_T) + 5 + 5) + 1) { + if (UI_BUF_SIZE - BUF_POS(ui) < 2 * (1 + 2 + sizeof(schar_T) + 5 + 5) + 1) { // 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 @@ -805,7 +790,7 @@ void remote_ui_raw_line(UI *ui, Integer grid, Integer row, Integer startcol, Int // We only ever set the wrap field on the final "grid_line" event for the line. mpack_bool(buf, false); - ui_flush_buf(data); + ui_flush_buf(ui); prepare_call(ui, "grid_line"); mpack_array(buf, 5); @@ -828,7 +813,7 @@ void remote_ui_raw_line(UI *ui, Integer grid, Integer row, Integer startcol, Int mpack_uint(buf, repeat); } } - data->ncells_pending += MIN(repeat, 2); + ui->ncells_pending += MIN(repeat, 2); last_hl = attrs[i]; repeat = 0; was_space = chunk[i] == schar_from_ascii(' '); @@ -838,7 +823,7 @@ void remote_ui_raw_line(UI *ui, Integer grid, Integer row, Integer startcol, Int // no more cells to clear, so there is no ambiguity about what to clear. if (endcol < clearcol || was_space) { nelem++; - data->ncells_pending += 1; + ui->ncells_pending += 1; mpack_array(buf, 3); mpack_str_small(buf, S_LEN(" ")); mpack_uint(buf, (uint32_t)clearattr); @@ -847,9 +832,9 @@ void remote_ui_raw_line(UI *ui, Integer grid, Integer row, Integer startcol, Int mpack_w2(&lenpos, nelem); mpack_bool(buf, flags & kLineFlagWrap); - if (data->ncells_pending > 500) { + if (ui->ncells_pending > 500) { // pass off cells to UI to let it start processing them - ui_flush_buf(data); + ui_flush_buf(ui); } } else { for (int i = 0; i < endcol - startcol; i++) { @@ -859,7 +844,7 @@ void remote_ui_raw_line(UI *ui, Integer grid, Integer row, Integer startcol, Int schar_get(sc_buf, chunk[i]); remote_ui_put(ui, sc_buf); if (utf_ambiguous_width(utf_ptr2char(sc_buf))) { - data->client_col = -1; // force cursor update + ui->client_col = -1; // force cursor update } } if (endcol < clearcol) { @@ -883,48 +868,47 @@ void remote_ui_raw_line(UI *ui, Integer grid, Integer row, Integer startcol, Int /// /// This might happen multiple times before the actual ui_flush, if the /// total redraw size is large! -static void ui_flush_buf(UIData *data) +static void ui_flush_buf(RemoteUI *ui) { - if (!data->packer.startptr || !BUF_POS(data)) { + if (!ui->packer.startptr || !BUF_POS(ui)) { return; } - flush_event(data); - if (data->nevents_pos != NULL) { - mpack_w2(&data->nevents_pos, data->nevents); - data->nevents = 0; - data->nevents_pos = NULL; + flush_event(ui); + if (ui->nevents_pos != NULL) { + mpack_w2(&ui->nevents_pos, ui->nevents); + ui->nevents = 0; + ui->nevents_pos = NULL; } - WBuffer *buf = wstream_new_buffer(data->packer.startptr, BUF_POS(data), 1, free_block); - rpc_write_raw(data->channel_id, buf); + WBuffer *buf = wstream_new_buffer(ui->packer.startptr, BUF_POS(ui), 1, free_block); + rpc_write_raw(ui->channel_id, buf); - data->packer.startptr = NULL; - data->packer.ptr = NULL; + ui->packer.startptr = NULL; + ui->packer.ptr = NULL; // we have sent events to the client, but possibly not yet the final "flush" event. - data->flushed_events = true; - data->ncells_pending = 0; + ui->flushed_events = true; + ui->ncells_pending = 0; } /// 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. -void remote_ui_flush(UI *ui) +void remote_ui_flush(RemoteUI *ui) { - UIData *data = ui->data; - if (data->nevents > 0 || data->flushed_events) { + if (ui->nevents > 0 || ui->flushed_events) { if (!ui->ui_ext[kUILinegrid]) { - remote_ui_cursor_goto(ui, data->cursor_row, data->cursor_col); + remote_ui_cursor_goto(ui, ui->cursor_row, ui->cursor_col); } push_call(ui, "flush", (Array)ARRAY_DICT_INIT); - ui_flush_buf(data); - data->flushed_events = false; + ui_flush_buf(ui); + ui->flushed_events = false; } } -static Array translate_contents(UI *ui, Array contents, Arena *arena) +static Array translate_contents(RemoteUI *ui, Array contents, Arena *arena) { Array new_contents = arena_array(arena, contents.size); for (size_t i = 0; i < contents.size; i++) { @@ -944,7 +928,7 @@ static Array translate_contents(UI *ui, Array contents, Arena *arena) return new_contents; } -static Array translate_firstarg(UI *ui, Array args, Arena *arena) +static Array translate_firstarg(RemoteUI *ui, Array args, Arena *arena) { Array new_args = arena_array(arena, args.size); Array contents = args.items[0].data.array; @@ -956,10 +940,9 @@ static Array translate_firstarg(UI *ui, Array args, Arena *arena) return new_args; } -void remote_ui_event(UI *ui, char *name, Array args) +void remote_ui_event(RemoteUI *ui, char *name, Array args) { Arena arena = ARENA_EMPTY; - UIData *data = ui->data; if (!ui->ui_ext[kUILinegrid]) { // the representation of highlights in cmdline changed, translate back // never consumes args @@ -968,7 +951,7 @@ void remote_ui_event(UI *ui, char *name, Array args) push_call(ui, name, new_args); goto free_ret; } else if (strequal(name, "cmdline_block_show")) { - Array new_args = data->call_buf; + Array new_args = ui->call_buf; Array block = args.items[0].data.array; Array new_block = arena_array(&arena, block.size); for (size_t i = 0; i < block.size; i++) { @@ -987,10 +970,10 @@ void remote_ui_event(UI *ui, char *name, Array args) // Back-compat: translate popupmenu_xx to legacy wildmenu_xx. if (ui->ui_ext[kUIWildmenu]) { if (strequal(name, "popupmenu_show")) { - data->wildmenu_active = (args.items[4].data.integer == -1) - || !ui->ui_ext[kUIPopupmenu]; - if (data->wildmenu_active) { - Array new_args = data->call_buf; + ui->wildmenu_active = (args.items[4].data.integer == -1) + || !ui->ui_ext[kUIPopupmenu]; + if (ui->wildmenu_active) { + Array new_args = ui->call_buf; Array items = args.items[0].data.array; Array new_items = arena_array(&arena, items.size); for (size_t i = 0; i < items.size; i++) { @@ -999,18 +982,18 @@ void remote_ui_event(UI *ui, char *name, Array args) ADD_C(new_args, ARRAY_OBJ(new_items)); push_call(ui, "wildmenu_show", new_args); if (args.items[1].data.integer != -1) { - Array new_args2 = data->call_buf; + Array new_args2 = ui->call_buf; ADD_C(new_args2, args.items[1]); push_call(ui, "wildmenu_select", new_args2); } goto free_ret; } } else if (strequal(name, "popupmenu_select")) { - if (data->wildmenu_active) { + if (ui->wildmenu_active) { name = "wildmenu_select"; } } else if (strequal(name, "popupmenu_hide")) { - if (data->wildmenu_active) { + if (ui->wildmenu_active) { name = "wildmenu_hide"; } } -- cgit From ade1b12f49c3b3914c74847d791eb90ea90b56b7 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Fri, 8 Mar 2024 12:25:18 +0000 Subject: docs: support inline markdown - Tags are now created with `[tag]()` - References are now created with `[tag]` - Code spans are no longer wrapped --- src/nvim/api/ui.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/api/ui.c') diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c index 02433b037c..692e3f95fc 100644 --- a/src/nvim/api/ui.c +++ b/src/nvim/api/ui.c @@ -410,7 +410,7 @@ void nvim_ui_try_resize_grid(uint64_t channel_id, Integer grid, Integer width, I } /// Tells Nvim the number of elements displaying in the popupmenu, to decide -/// and movement. +/// [] and [] movement. /// /// @param channel_id /// @param height Popupmenu height, must be greater than zero. -- cgit