diff options
author | bfredl <bjorn.linse@gmail.com> | 2022-07-19 13:08:11 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-07-19 13:08:11 +0200 |
commit | 101fd04ee2036b125a93f3e71dbaa4ae6917ce8b (patch) | |
tree | 424ebcabeecf4c0e88cb5b7b7cb6e407bc11de63 | |
parent | 93bd6fb2c8e1f68a48169a63caae1fc0b4797a8a (diff) | |
parent | f87c8245133dd8116a9bab2d2e89f9b26967c7a8 (diff) | |
download | rneovim-101fd04ee2036b125a93f3e71dbaa4ae6917ce8b.tar.gz rneovim-101fd04ee2036b125a93f3e71dbaa4ae6917ce8b.tar.bz2 rneovim-101fd04ee2036b125a93f3e71dbaa4ae6917ce8b.zip |
Merge pull request #19409 from bfredl/uiunpack
perf(ui): some ui_client fixes/optimizations before externalized TUI
-rwxr-xr-x | scripts/gen_vimdoc.py | 3 | ||||
-rw-r--r-- | src/nvim/api/private/defs.h | 1 | ||||
-rw-r--r-- | src/nvim/api/private/helpers.h | 6 | ||||
-rw-r--r-- | src/nvim/api/ui.c | 17 | ||||
-rw-r--r-- | src/nvim/api/vim.c | 8 | ||||
-rw-r--r-- | src/nvim/generators/gen_api_dispatch.lua | 4 | ||||
-rw-r--r-- | src/nvim/grid_defs.h | 9 | ||||
-rw-r--r-- | src/nvim/highlight.c | 58 | ||||
-rw-r--r-- | src/nvim/highlight_group.c | 2 | ||||
-rw-r--r-- | src/nvim/main.c | 2 | ||||
-rw-r--r-- | src/nvim/msgpack_rpc/channel.c | 9 | ||||
-rw-r--r-- | src/nvim/msgpack_rpc/unpacker.c | 208 | ||||
-rw-r--r-- | src/nvim/msgpack_rpc/unpacker.h | 7 | ||||
-rw-r--r-- | src/nvim/ui.c | 6 | ||||
-rw-r--r-- | src/nvim/ui_client.c | 137 | ||||
-rw-r--r-- | src/nvim/ui_client.h | 6 | ||||
-rw-r--r-- | test/functional/editor/put_spec.lua | 9 | ||||
-rw-r--r-- | test/functional/ui/cursor_spec.lua | 2 | ||||
-rw-r--r-- | test/functional/ui/options_spec.lua | 9 |
19 files changed, 342 insertions, 161 deletions
diff --git a/scripts/gen_vimdoc.py b/scripts/gen_vimdoc.py index 74b35b4e9b..c17742ddaf 100755 --- a/scripts/gen_vimdoc.py +++ b/scripts/gen_vimdoc.py @@ -949,7 +949,8 @@ def fmt_doxygen_xml_as_vimhelp(filename, target): func_doc = "\n".join(split_lines) - if name.startswith(CONFIG[target]['fn_name_prefix']): + if (name.startswith(CONFIG[target]['fn_name_prefix']) + and name != "nvim_error_event"): fns_txt[name] = func_doc return ('\n\n'.join(list(fns_txt.values())), diff --git a/src/nvim/api/private/defs.h b/src/nvim/api/private/defs.h index b1e0dd364c..9c7e59e4b3 100644 --- a/src/nvim/api/private/defs.h +++ b/src/nvim/api/private/defs.h @@ -36,6 +36,7 @@ typedef enum { kMessageTypeRequest = 0, kMessageTypeResponse = 1, kMessageTypeNotification = 2, + kMessageTypeRedrawEvent = 3, } MessageType; /// Mask for all internal calls diff --git a/src/nvim/api/private/helpers.h b/src/nvim/api/private/helpers.h index a4348d8b44..224ce41aae 100644 --- a/src/nvim/api/private/helpers.h +++ b/src/nvim/api/private/helpers.h @@ -86,6 +86,12 @@ name.capacity = maxsize; \ name.items = name##__items; \ +#define MAXSIZE_TEMP_DICT(name, maxsize) \ + Dictionary name = ARRAY_DICT_INIT; \ + KeyValuePair 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 }) diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c index 54ce838b9b..aa7bed1132 100644 --- a/src/nvim/api/ui.c +++ b/src/nvim/api/ui.c @@ -748,8 +748,10 @@ static void remote_ui_hl_attr_define(UI *ui, Integer id, HlAttrs rgb_attrs, HlAt 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))); + MAXSIZE_TEMP_DICT(rgb, 16); + MAXSIZE_TEMP_DICT(cterm, 16); + ADD_C(args, DICTIONARY_OBJ(hlattrs2dict(&rgb, rgb_attrs, true))); + ADD_C(args, DICTIONARY_OBJ(hlattrs2dict(&cterm, cterm_attrs, false))); if (ui->ui_ext[kUIHlState]) { ADD_C(args, ARRAY_OBJ(info)); @@ -758,9 +760,6 @@ static void remote_ui_hl_attr_define(UI *ui, Integer id, HlAttrs rgb_attrs, HlAt } 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) @@ -772,11 +771,9 @@ static void remote_ui_highlight_set(UI *ui, int id) return; } data->hl_id = id; - Dictionary hl = hlattrs2dict(syn_attr2entry(id), ui->rgb); - - ADD_C(args, DICTIONARY_OBJ(hl)); + MAXSIZE_TEMP_DICT(dict, 16); + ADD_C(args, DICTIONARY_OBJ(hlattrs2dict(&dict, syn_attr2entry(id), ui->rgb))); push_call(ui, "highlight_set", args); - api_free_dictionary(kv_A(args, 0).data.dictionary); } /// "true" cursor used only for input focus @@ -963,7 +960,7 @@ static Array translate_contents(UI *ui, Array contents) Array new_item = ARRAY_DICT_INIT; int attr = (int)item.items[0].data.integer; if (attr) { - Dictionary rgb_attrs = hlattrs2dict(syn_attr2entry(attr), ui->rgb); + Dictionary rgb_attrs = hlattrs2dict(NULL, syn_attr2entry(attr), ui->rgb); ADD(new_item, DICTIONARY_OBJ(rgb_attrs)); } else { ADD(new_item, DICTIONARY_OBJ((Dictionary)ARRAY_DICT_INIT)); diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 56516b2ac7..5d941890db 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -2256,3 +2256,11 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error * return result; } + +void nvim_error_event(uint64_t channel_id, Integer lvl, String data) + FUNC_API_REMOTE_ONLY +{ + // TODO(bfredl): consider printing message to user, as will be relevant + // if we fork nvim processes as async workers + ELOG("async error on channel %" PRId64 ": %s", channel_id, data.size ? data.data : ""); +} diff --git a/src/nvim/generators/gen_api_dispatch.lua b/src/nvim/generators/gen_api_dispatch.lua index 4cf282770d..b167767f7a 100644 --- a/src/nvim/generators/gen_api_dispatch.lua +++ b/src/nvim/generators/gen_api_dispatch.lua @@ -91,7 +91,7 @@ local deprecated_aliases = require("api.dispatch_deprecated") for _,f in ipairs(shallowcopy(functions)) do local ismethod = false if startswith(f.name, "nvim_") then - if startswith(f.name, "nvim__") then + if startswith(f.name, "nvim__") or f.name == "nvim_error_event" then f.since = -1 elseif f.since == nil then print("Function "..f.name.." lacks since field.\n") @@ -149,7 +149,7 @@ local exported_attributes = {'name', 'return_type', 'method', 'since', 'deprecated_since'} local exported_functions = {} for _,f in ipairs(functions) do - if not startswith(f.name, "nvim__") then + if not (startswith(f.name, "nvim__") or f.name == "nvim_error_event") then local f_exported = {} for _,attr in ipairs(exported_attributes) do f_exported[attr] = f[attr] diff --git a/src/nvim/grid_defs.h b/src/nvim/grid_defs.h index 1571340849..9252b8a371 100644 --- a/src/nvim/grid_defs.h +++ b/src/nvim/grid_defs.h @@ -128,4 +128,13 @@ typedef struct { const char *start; ///< Location where region starts. } StlClickRecord; +typedef struct { + int args[3]; + int icell; + int ncells; + int coloff; + int cur_attr; + int clear_width; +} GridLineEvent; + #endif // NVIM_GRID_DEFS_H diff --git a/src/nvim/highlight.c b/src/nvim/highlight.c index 0f20eb1905..83d819d3ec 100644 --- a/src/nvim/highlight.c +++ b/src/nvim/highlight.c @@ -719,93 +719,107 @@ Dictionary hl_get_attr_by_id(Integer attr_id, Boolean rgb, Error *err) return dic; } - return hlattrs2dict(syn_attr2entry((int)attr_id), rgb); + return hlattrs2dict(NULL, syn_attr2entry((int)attr_id), rgb); } /// Converts an HlAttrs into Dictionary /// +/// @param[out] hl optional pre-allocated dictionary for return value +/// if present, must be allocated with at least 16 elements! /// @param[in] aep data to convert /// @param use_rgb use 'gui*' settings if true, else resorts to 'cterm*' -Dictionary hlattrs2dict(HlAttrs ae, bool use_rgb) +Dictionary hlattrs2dict(Dictionary *hl_alloc, HlAttrs ae, bool use_rgb) { - Dictionary hl = ARRAY_DICT_INIT; int mask = use_rgb ? ae.rgb_ae_attr : ae.cterm_ae_attr; + Dictionary hl = ARRAY_DICT_INIT; + if (hl_alloc) { + hl = *hl_alloc; + } else { + kv_ensure_space(hl, 16); + } if (mask & HL_BOLD) { - PUT(hl, "bold", BOOLEAN_OBJ(true)); + PUT_C(hl, "bold", BOOLEAN_OBJ(true)); } if (mask & HL_STANDOUT) { - PUT(hl, "standout", BOOLEAN_OBJ(true)); + PUT_C(hl, "standout", BOOLEAN_OBJ(true)); } if (mask & HL_UNDERLINE) { - PUT(hl, "underline", BOOLEAN_OBJ(true)); + PUT_C(hl, "underline", BOOLEAN_OBJ(true)); } if (mask & HL_UNDERCURL) { - PUT(hl, "undercurl", BOOLEAN_OBJ(true)); + PUT_C(hl, "undercurl", BOOLEAN_OBJ(true)); } if (mask & HL_UNDERDOUBLE) { - PUT(hl, "underdouble", BOOLEAN_OBJ(true)); + PUT_C(hl, "underdouble", BOOLEAN_OBJ(true)); } if (mask & HL_UNDERDOTTED) { - PUT(hl, "underdotted", BOOLEAN_OBJ(true)); + PUT_C(hl, "underdotted", BOOLEAN_OBJ(true)); } if (mask & HL_UNDERDASHED) { - PUT(hl, "underdashed", BOOLEAN_OBJ(true)); + PUT_C(hl, "underdashed", BOOLEAN_OBJ(true)); } if (mask & HL_ITALIC) { - PUT(hl, "italic", BOOLEAN_OBJ(true)); + PUT_C(hl, "italic", BOOLEAN_OBJ(true)); } if (mask & HL_INVERSE) { - PUT(hl, "reverse", BOOLEAN_OBJ(true)); + PUT_C(hl, "reverse", BOOLEAN_OBJ(true)); } if (mask & HL_STRIKETHROUGH) { - PUT(hl, "strikethrough", BOOLEAN_OBJ(true)); + PUT_C(hl, "strikethrough", BOOLEAN_OBJ(true)); } if (use_rgb) { if (mask & HL_FG_INDEXED) { - PUT(hl, "fg_indexed", BOOLEAN_OBJ(true)); + PUT_C(hl, "fg_indexed", BOOLEAN_OBJ(true)); } if (mask & HL_BG_INDEXED) { - PUT(hl, "bg_indexed", BOOLEAN_OBJ(true)); + PUT_C(hl, "bg_indexed", BOOLEAN_OBJ(true)); } if (ae.rgb_fg_color != -1) { - PUT(hl, "foreground", INTEGER_OBJ(ae.rgb_fg_color)); + PUT_C(hl, "foreground", INTEGER_OBJ(ae.rgb_fg_color)); } if (ae.rgb_bg_color != -1) { - PUT(hl, "background", INTEGER_OBJ(ae.rgb_bg_color)); + PUT_C(hl, "background", INTEGER_OBJ(ae.rgb_bg_color)); } if (ae.rgb_sp_color != -1) { - PUT(hl, "special", INTEGER_OBJ(ae.rgb_sp_color)); + PUT_C(hl, "special", INTEGER_OBJ(ae.rgb_sp_color)); } } else { if (ae.cterm_fg_color != 0) { - PUT(hl, "foreground", INTEGER_OBJ(ae.cterm_fg_color - 1)); + PUT_C(hl, "foreground", INTEGER_OBJ(ae.cterm_fg_color - 1)); } if (ae.cterm_bg_color != 0) { - PUT(hl, "background", INTEGER_OBJ(ae.cterm_bg_color - 1)); + PUT_C(hl, "background", INTEGER_OBJ(ae.cterm_bg_color - 1)); } } if (ae.hl_blend > -1) { - PUT(hl, "blend", INTEGER_OBJ(ae.hl_blend)); + PUT_C(hl, "blend", INTEGER_OBJ(ae.hl_blend)); } - return hl; + if (hl_alloc) { + *hl_alloc = hl; + return hl; + } else { + Dictionary allocated = copy_dictionary(hl); + kv_destroy(hl); + return allocated; + } } HlAttrs dict2hlattrs(Dict(highlight) *dict, bool use_rgb, int *link_id, Error *err) diff --git a/src/nvim/highlight_group.c b/src/nvim/highlight_group.c index d958b7b344..5027454222 100644 --- a/src/nvim/highlight_group.c +++ b/src/nvim/highlight_group.c @@ -1409,7 +1409,7 @@ Dictionary get_global_hl_defs(void) Dictionary attrs = ARRAY_DICT_INIT; HlGroup *h = &hl_table[i - 1]; if (h->sg_attr > 0) { - attrs = hlattrs2dict(syn_attr2entry(h->sg_attr), true); + attrs = hlattrs2dict(NULL, syn_attr2entry(h->sg_attr), true); } else if (h->sg_link > 0) { const char *link = (const char *)hl_table[h->sg_link - 1].sg_name; PUT(attrs, "link", STRING_OBJ(cstr_to_string(link))); diff --git a/src/nvim/main.c b/src/nvim/main.c index b06b9630e2..c9403d9f19 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -202,7 +202,6 @@ void early_init(mparm_T *paramp) set_lang_var(); // set v:lang and v:ctype init_signs(); - ui_comp_syn_init(); } #ifdef MAKE_LIB @@ -320,6 +319,7 @@ int main(int argc, char **argv) no_wait_return = true; init_highlight(true, false); // Default highlight groups. + ui_comp_syn_init(); TIME_MSG("init highlight"); // Set the break level after the terminal is initialized. diff --git a/src/nvim/msgpack_rpc/channel.c b/src/nvim/msgpack_rpc/channel.c index de01443313..1ca68979f4 100644 --- a/src/nvim/msgpack_rpc/channel.c +++ b/src/nvim/msgpack_rpc/channel.c @@ -239,7 +239,14 @@ static void parse_msgpack(Channel *channel) { Unpacker *p = channel->rpc.unpacker; while (unpacker_advance(p)) { - if (p->type == kMessageTypeResponse) { + if (p->type == kMessageTypeRedrawEvent) { + if (p->grid_line_event) { + ui_client_event_raw_line(p->grid_line_event); + } else if (p->ui_handler.fn != NULL && p->result.type == kObjectTypeArray) { + p->ui_handler.fn(p->result.data.array); + arena_mem_free(arena_finish(&p->arena), &p->reuse_blk); + } + } else if (p->type == kMessageTypeResponse) { ChannelCallFrame *frame = kv_last(channel->rpc.call_stack); if (p->request_id != frame->request_id) { char buf[256]; diff --git a/src/nvim/msgpack_rpc/unpacker.c b/src/nvim/msgpack_rpc/unpacker.c index 26c1843026..d73557ec11 100644 --- a/src/nvim/msgpack_rpc/unpacker.c +++ b/src/nvim/msgpack_rpc/unpacker.c @@ -6,6 +6,7 @@ #include "nvim/memory.h" #include "nvim/msgpack_rpc/helpers.h" #include "nvim/msgpack_rpc/unpacker.h" +#include "nvim/ui_client.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "msgpack_rpc/unpacker.c.generated.h" @@ -280,10 +281,20 @@ error: // is relatively small, just ~10 bytes + the method name. Thus we can simply refuse // to advance the stream beyond the header until it can be parsed in its entirety. // -// Of course, later on, we want to specialize state 2 into sub-states depending +// Later on, we want to specialize state 2 into more sub-states depending // on the specific method. "nvim_exec_lua" should just decode direct into lua -// objects, and "redraw/grid_line" should use a hand-rolled decoder to avoid -// a blizzard of small objects for each screen cell. +// objects. For the moment "redraw/grid_line" uses a hand-rolled decoder, +// to avoid a blizzard of small objects for each screen cell. +// +// <0>[2, "redraw", <10>[{11}["method", <12>[args], <12>[args], ...], <11>[...], ...]] +// +// Where [args] gets unpacked as an Array. Note: first {11} is not saved as a state. +// +// When method is "grid_line", we furthermore decode a cell at a time like: +// +// <0>[2, "redraw", <10>[{11}["grid_line", <13>[g, r, c, [<14>[cell], <14>[cell], ...]], ...], <11>[...], ...]] +// +// where [cell] is [char, repeat, attr], where 'repeat' and 'attr' is optional bool unpacker_advance(Unpacker *p) { @@ -292,8 +303,27 @@ bool unpacker_advance(Unpacker *p) if (!unpacker_parse_header(p)) { return false; } - p->state = p->type == kMessageTypeResponse ? 1 : 2; - arena_start(&p->arena, &p->reuse_blk); + if (p->type == kMessageTypeNotification && p->handler.fn == handle_ui_client_redraw) { + p->type = kMessageTypeRedrawEvent; + p->state = 10; + } else { + p->state = p->type == kMessageTypeResponse ? 1 : 2; + arena_start(&p->arena, &p->reuse_blk); + } + } + + if (p->state >= 10 && p->state != 12) { + if (!unpacker_parse_redraw(p)) { + return false; + } + + if (p->state == 14) { + // grid_line event already unpacked + goto done; + } else { + // unpack other ui events using mpack_parse() + arena_start(&p->arena, &p->reuse_blk); + } } int result; @@ -310,13 +340,173 @@ rerun: return false; } - if (p->state == 1) { +done: + switch (p->state) { + case 1: p->error = p->result; p->state = 2; goto rerun; - } else { - assert(p->state == 2); + case 2: p->state = 0; + return true; + case 12: + case 14: + p->ncalls--; + if (p->ncalls > 0) { + p->state = (p->state == 14) ? 13 : 12; + } else if (p->nevents > 0) { + p->state = 11; + } else { + p->state = 0; + } + return true; + default: + abort(); + } +} + +bool unpacker_parse_redraw(Unpacker *p) +{ + mpack_token_t tok; + int result; + + const char *data = p->read_ptr; + size_t size = p->read_size; + GridLineEvent *g = p->grid_line_event; + +#define NEXT_TYPE(tok, typ) \ + result = mpack_rtoken(&data, &size, &tok); \ + if (result == MPACK_EOF) { \ + return false; \ + } else if (result || (tok.type != typ \ + && !(typ == MPACK_TOKEN_STR && tok.type == MPACK_TOKEN_BIN) \ + && !(typ == MPACK_TOKEN_SINT && tok.type == MPACK_TOKEN_UINT))) { \ + p->state = -1; \ + return false; \ + } + +redo: + switch (p->state) { + case 10: + NEXT_TYPE(tok, MPACK_TOKEN_ARRAY); + p->nevents = (int)tok.length; + FALLTHROUGH; + + case 11: + NEXT_TYPE(tok, MPACK_TOKEN_ARRAY); + p->ncalls = (int)tok.length; + + if (p->ncalls-- == 0) { + p->state = -1; + return false; + } + + NEXT_TYPE(tok, MPACK_TOKEN_STR); + if (tok.length > size) { + return false; + } + + p->ui_handler = ui_client_get_redraw_handler(data, tok.length, NULL); + data += tok.length; + size -= tok.length; + + p->nevents--; + p->read_ptr = data; + p->read_size = size; + if (p->ui_handler.fn != ui_client_event_grid_line) { + p->state = 12; + if (p->grid_line_event) { + arena_mem_free(arena_finish(&p->arena), &p->reuse_blk); + p->grid_line_event = NULL; + } + return true; + } else { + p->state = 13; + arena_start(&p->arena, &p->reuse_blk); + p->grid_line_event = arena_alloc(&p->arena, sizeof *p->grid_line_event, true); + g = p->grid_line_event; + FALLTHROUGH; + } + + case 13: + NEXT_TYPE(tok, MPACK_TOKEN_ARRAY); + int eventarrsize = (int)tok.length; + if (eventarrsize != 4) { + p->state = -1; + return false; + } + + for (int i = 0; i < 3; i++) { + NEXT_TYPE(tok, MPACK_TOKEN_UINT); + g->args[i] = (int)tok.data.value.lo; + } + + NEXT_TYPE(tok, MPACK_TOKEN_ARRAY); + g->ncells = (int)tok.length; + g->icell = 0; + g->coloff = 0; + g->cur_attr = -1; + + p->read_ptr = data; + p->read_size = size; + p->state = 14; + FALLTHROUGH; + + case 14: + assert(g->icell < g->ncells); + + NEXT_TYPE(tok, MPACK_TOKEN_ARRAY); + int cellarrsize = (int)tok.length; + if (cellarrsize < 1 || cellarrsize > 3) { + p->state = -1; + return false; + } + + NEXT_TYPE(tok, MPACK_TOKEN_STR); + if (tok.length > size) { + return false; + } + + const char *cellbuf = data; + size_t cellsize = tok.length; + data += cellsize; + size -= cellsize; + + if (cellarrsize >= 2) { + NEXT_TYPE(tok, MPACK_TOKEN_SINT); + g->cur_attr = (int)tok.data.value.lo; + } + + int repeat = 1; + if (cellarrsize >= 3) { + NEXT_TYPE(tok, MPACK_TOKEN_UINT); + repeat = (int)tok.data.value.lo; + } + + g->clear_width = 0; + if (g->icell == g->ncells - 1 && cellsize == 1 && cellbuf[0] == ' ' && repeat > 1) { + g->clear_width = repeat; + } else { + for (int r = 0; r < repeat; r++) { + if (g->coloff >= (int)grid_line_buf_size) { + p->state = -1; + return false; + } + memcpy(grid_line_buf_char[g->coloff], cellbuf, cellsize); + grid_line_buf_char[g->coloff][cellsize] = NUL; + grid_line_buf_attr[g->coloff++] = g->cur_attr; + } + } + + g->icell++; + p->read_ptr = data; + p->read_size = size; + if (g->icell == g->ncells) { + return true; + } + goto redo; + + default: + abort(); } - return true; } diff --git a/src/nvim/msgpack_rpc/unpacker.h b/src/nvim/msgpack_rpc/unpacker.h index e0dc6f0a68..f39439be63 100644 --- a/src/nvim/msgpack_rpc/unpacker.h +++ b/src/nvim/msgpack_rpc/unpacker.h @@ -32,8 +32,13 @@ struct Unpacker { Error unpack_error; Arena arena; - // one lenght free-list of reusable blocks + // one length free-list of reusable blocks ArenaMem reuse_blk; + + int nevents; + int ncalls; + UIClientHandler ui_handler; + GridLineEvent *grid_line_event; }; // unrecovareble error. unpack_error should be set! diff --git a/src/nvim/ui.c b/src/nvim/ui.c index a49e9df9ee..e958f02e32 100644 --- a/src/nvim/ui.c +++ b/src/nvim/ui.c @@ -348,7 +348,8 @@ void ui_attach_impl(UI *ui, uint64_t chanid) if (ui_count == MAX_UI_COUNT) { abort(); } - if (!ui->ui_ext[kUIMultigrid] && !ui->ui_ext[kUIFloatDebug]) { + if (!ui->ui_ext[kUIMultigrid] && !ui->ui_ext[kUIFloatDebug] + && !ui_client_channel_id) { ui_comp_attach(ui); } @@ -502,6 +503,9 @@ handle_T ui_cursor_grid(void) void ui_flush(void) { + if (!ui_active()) { + return; + } cmdline_ui_flush(); win_ui_flush(); msg_ext_ui_flush(); diff --git a/src/nvim/ui_client.c b/src/nvim/ui_client.c index be01538f67..09e971f03f 100644 --- a/src/nvim/ui_client.c +++ b/src/nvim/ui_client.c @@ -22,11 +22,6 @@ # include "ui_events_client.generated.h" #endif -// Temporary buffer for converting a single grid_line event -static size_t buf_size = 0; -static schar_T *buf_char = NULL; -static sattr_T *buf_attr = NULL; - void ui_client_init(uint64_t chan) { Array args = ARRAY_DICT_INIT; @@ -46,37 +41,23 @@ void ui_client_init(uint64_t chan) ui_client_channel_id = chan; } -/// Handler for "redraw" events sent by the NVIM server -/// -/// This function will be called by handle_request (in msgpack_rpc/channel.c) -/// The individual ui_events sent by the server are individually handled -/// by their respective handlers defined in ui_events_client.generated.h -/// -/// @note The "flush" event is called only once and only after handling all -/// the other events -/// @param channel_id: The id of the rpc channel -/// @param uidata: The dense array containing the ui_events sent by the server -/// @param[out] err Error details, if any -Object handle_ui_client_redraw(uint64_t channel_id, Array args, Error *error) +UIClientHandler ui_client_get_redraw_handler(const char *name, size_t name_len, Error *error) { - for (size_t i = 0; i < args.size; i++) { - Array call = args.items[i].data.array; - String name = call.items[0].data.string; - - int hash = ui_client_handler_hash(name.data, name.size); - if (hash < 0) { - ELOG("No ui client handler for %s", name.size ? name.data : "<empty>"); - continue; - } - UIClientHandler handler = event_handlers[hash]; - - // fprintf(stderr, "%s: %zu\n", name.data, call.size-1); - DLOG("Invoke ui client handler for %s", name.data); - for (size_t j = 1; j < call.size; j++) { - handler.fn(call.items[j].data.array); - } + int hash = ui_client_handler_hash(name, name_len); + if (hash < 0) { + return (UIClientHandler){ NULL, NULL }; } + return event_handlers[hash]; +} +/// Placeholder for _sync_ requests with 'redraw' method name +/// +/// async 'redraw' events, which are expected when nvim acts as an ui client. +/// get handled in msgpack_rpc/unpacker.c and directy dispatched to handlers +/// of specific ui events, like ui_client_event_grid_resize and so on. +Object handle_ui_client_redraw(uint64_t channel_id, Array args, Error *error) +{ + api_set_error(error, kErrorTypeValidation, "'redraw' cannot be sent as a request"); return NIL; } @@ -120,88 +101,30 @@ void ui_client_event_grid_resize(Array args) Integer height = args.items[2].data.integer; ui_call_grid_resize(grid, width, height); - if (buf_size < (size_t)width) { - xfree(buf_char); - xfree(buf_attr); - buf_size = (size_t)width; - buf_char = xmalloc(buf_size * sizeof(schar_T)); - buf_attr = xmalloc(buf_size * sizeof(sattr_T)); + if (grid_line_buf_size < (size_t)width) { + xfree(grid_line_buf_char); + xfree(grid_line_buf_attr); + grid_line_buf_size = (size_t)width; + grid_line_buf_char = xmalloc(grid_line_buf_size * sizeof(schar_T)); + grid_line_buf_attr = xmalloc(grid_line_buf_size * sizeof(sattr_T)); } } void ui_client_event_grid_line(Array args) + FUNC_ATTR_NORETURN { - if (args.size < 4 - || args.items[0].type != kObjectTypeInteger - || args.items[1].type != kObjectTypeInteger - || args.items[2].type != kObjectTypeInteger - || args.items[3].type != kObjectTypeArray) { - goto error; - } + abort(); // unreachable +} - Integer grid = args.items[0].data.integer; - Integer row = args.items[1].data.integer; - Integer startcol = args.items[2].data.integer; - Array cells = args.items[3].data.array; +void ui_client_event_raw_line(GridLineEvent *g) +{ + int grid = g->args[0], row = g->args[1], startcol = g->args[2]; + Integer endcol = startcol + g->coloff; + Integer clearcol = endcol + g->clear_width; // TODO(hlpr98): Accommodate other LineFlags when included in grid_line LineFlags lineflags = 0; - size_t j = 0; - int cur_attr = 0; - int clear_attr = 0; - int clear_width = 0; - for (size_t i = 0; i < cells.size; i++) { - if (cells.items[i].type != kObjectTypeArray) { - goto error; - } - Array cell = cells.items[i].data.array; - - if (cell.size < 1 || cell.items[0].type != kObjectTypeString) { - goto error; - } - String sstring = cell.items[0].data.string; - - char *schar = sstring.data; - int repeat = 1; - if (cell.size >= 2) { - if (cell.items[1].type != kObjectTypeInteger - || cell.items[1].data.integer < 0) { - goto error; - } - cur_attr = (int)cell.items[1].data.integer; - } - - if (cell.size >= 3) { - if (cell.items[2].type != kObjectTypeInteger - || cell.items[2].data.integer < 0) { - goto error; - } - repeat = (int)cell.items[2].data.integer; - } - - if (i == cells.size - 1 && sstring.size == 1 && sstring.data[0] == ' ' && repeat > 1) { - clear_width = repeat; - break; - } - - for (int r = 0; r < repeat; r++) { - if (j >= buf_size) { - goto error; // _YIKES_ - } - STRLCPY(buf_char[j], schar, sizeof(schar_T)); - buf_attr[j++] = cur_attr; - } - } - - Integer endcol = startcol + (int)j; - Integer clearcol = endcol + clear_width; - clear_attr = cur_attr; - - ui_call_raw_line(grid, row, startcol, endcol, clearcol, clear_attr, lineflags, - (const schar_T *)buf_char, (const sattr_T *)buf_attr); - return; - -error: - ELOG("Error handling ui event 'grid_line'"); + ui_call_raw_line(grid, row, startcol, endcol, clearcol, g->cur_attr, lineflags, + (const schar_T *)grid_line_buf_char, grid_line_buf_attr); } diff --git a/src/nvim/ui_client.h b/src/nvim/ui_client.h index 41d9fa6227..311dafaa0b 100644 --- a/src/nvim/ui_client.h +++ b/src/nvim/ui_client.h @@ -2,12 +2,18 @@ #define NVIM_UI_CLIENT_H #include "nvim/api/private/defs.h" +#include "nvim/grid_defs.h" typedef struct { const char *name; void (*fn)(Array args); } UIClientHandler; +// Temporary buffer for converting a single grid_line event +EXTERN size_t grid_line_buf_size INIT(= 0); +EXTERN schar_T *grid_line_buf_char INIT(= NULL); +EXTERN sattr_T *grid_line_buf_attr INIT(= NULL); + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "ui_client.h.generated.h" diff --git a/test/functional/editor/put_spec.lua b/test/functional/editor/put_spec.lua index cc9fce8f67..5050edff5c 100644 --- a/test/functional/editor/put_spec.lua +++ b/test/functional/editor/put_spec.lua @@ -879,9 +879,13 @@ describe('put command', function() ine of words 2]], curbuf_contents()) end) - local function bell_test(actions, should_ring) - local screen = Screen.new() + local screen + setup(function() + screen = Screen.new() screen:attach() + end) + + local function bell_test(actions, should_ring) if should_ring then -- check bell is not set by nvim before the action screen:sleep(50) @@ -899,7 +903,6 @@ describe('put command', function() end end end, unchanged=(not should_ring)} - screen:detach() end it('should not ring the bell with gp at end of line', function() diff --git a/test/functional/ui/cursor_spec.lua b/test/functional/ui/cursor_spec.lua index a1423c98a8..03cd4bfd06 100644 --- a/test/functional/ui/cursor_spec.lua +++ b/test/functional/ui/cursor_spec.lua @@ -215,7 +215,7 @@ describe('ui/cursor', function() m.hl_id = 60 m.attr = {background = Screen.colors.DarkGray} end - if m.id_lm then m.id_lm = 65 end + if m.id_lm then m.id_lm = 61 end end -- Assert the new expectation. diff --git a/test/functional/ui/options_spec.lua b/test/functional/ui/options_spec.lua index c2b0bcdb64..8d7c404637 100644 --- a/test/functional/ui/options_spec.lua +++ b/test/functional/ui/options_spec.lua @@ -51,7 +51,7 @@ describe('UI receives option updates', function() end) it('on attach #11372', function() - clear() + clear{args_rm={'--headless'}} local evs = {} screen = Screen.new(20,5) -- Override mouse_on/mouse_off handlers. @@ -88,6 +88,13 @@ describe('UI receives option updates', function() eq(expected, screen.options) end) + command("set pumblend=50") + expected.pumblend = 50 + screen:expect(function() + eq(expected, screen.options) + end) + + -- check handling of out-of-bounds value command("set pumblend=-1") expected.pumblend = 0 screen:expect(function() |