From 794d2744f33562326172801ddd729853e7135347 Mon Sep 17 00:00:00 2001 From: hlpr98 Date: Sat, 12 Mar 2022 21:08:29 +0100 Subject: [PATCH 1/5] feat(ui): implement ui_client event handlers --- src/nvim/CMakeLists.txt | 5 + src/nvim/api/ui_events.in.h | 2 +- src/nvim/generators/c_grammar.lua | 1 + src/nvim/generators/gen_api_ui_events.lua | 62 +++++++++++- src/nvim/map.c | 1 + src/nvim/map.h | 2 + src/nvim/ui_client.c | 114 ++++++++++++++++++++++ src/nvim/ui_client.h | 4 + 8 files changed, 187 insertions(+), 4 deletions(-) mode change 100644 => 100755 src/nvim/CMakeLists.txt mode change 100644 => 100755 src/nvim/generators/gen_api_ui_events.lua diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt old mode 100644 new mode 100755 index 96e5d1e77c..34d112a756 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -39,6 +39,7 @@ set(GENERATED_UI_EVENTS ${GENERATED_DIR}/ui_events.generated.h) set(GENERATED_UI_EVENTS_CALL ${GENERATED_DIR}/ui_events_call.generated.h) set(GENERATED_UI_EVENTS_REMOTE ${GENERATED_DIR}/ui_events_remote.generated.h) set(GENERATED_UI_EVENTS_BRIDGE ${GENERATED_DIR}/ui_events_bridge.generated.h) +set(GENERATED_UI_EVENTS_REDRAW ${GENERATED_DIR}/ui_events_redraw.generated.h) set(GENERATED_UI_EVENTS_METADATA ${GENERATED_DIR}/api/private/ui_events_metadata.generated.h) set(GENERATED_EX_CMDS_ENUM ${GENERATED_INCLUDES_DIR}/ex_cmds_enum.generated.h) set(GENERATED_EX_CMDS_DEFS ${GENERATED_DIR}/ex_cmds_defs.generated.h) @@ -271,6 +272,7 @@ foreach(sfile ${NVIM_SOURCES} "${GENERATED_UI_EVENTS_REMOTE}" "${GENERATED_UI_EVENTS_BRIDGE}" "${GENERATED_KEYSETS}" + "${GENERATED_UI_EVENTS_REDRAW}" ) get_filename_component(full_d ${sfile} PATH) file(RELATIVE_PATH d "${CMAKE_CURRENT_LIST_DIR}" "${full_d}") @@ -368,6 +370,7 @@ add_custom_command( ${GENERATED_UI_EVENTS_REMOTE} ${GENERATED_UI_EVENTS_BRIDGE} ${GENERATED_UI_EVENTS_METADATA} + ${GENERATED_UI_EVENTS_REDRAW} COMMAND ${LUA_PRG} ${API_UI_EVENTS_GENERATOR} ${CMAKE_CURRENT_LIST_DIR} ${CMAKE_CURRENT_LIST_DIR}/api/ui_events.in.h ${GENERATED_UI_EVENTS} @@ -375,6 +378,8 @@ add_custom_command( ${GENERATED_UI_EVENTS_REMOTE} ${GENERATED_UI_EVENTS_BRIDGE} ${GENERATED_UI_EVENTS_METADATA} + ${GENERATED_UI_EVENTS_REDRAW} + DEPENDS ${API_UI_EVENTS_GENERATOR} ${GENERATOR_C_GRAMMAR} diff --git a/src/nvim/api/ui_events.in.h b/src/nvim/api/ui_events.in.h index 03fe5c5058..78826072a7 100644 --- a/src/nvim/api/ui_events.in.h +++ b/src/nvim/api/ui_events.in.h @@ -84,7 +84,7 @@ void grid_clear(Integer grid) void grid_cursor_goto(Integer grid, Integer row, Integer col) FUNC_API_SINCE(5) FUNC_API_REMOTE_IMPL FUNC_API_COMPOSITOR_IMPL; void grid_line(Integer grid, Integer row, Integer col_start, Array data) - FUNC_API_SINCE(5) FUNC_API_REMOTE_ONLY; + FUNC_API_SINCE(5) FUNC_API_REMOTE_ONLY FUNC_API_CLIENT_IMPL; void grid_scroll(Integer grid, Integer top, Integer bot, Integer left, Integer right, Integer rows, Integer cols) FUNC_API_SINCE(5) FUNC_API_REMOTE_IMPL FUNC_API_COMPOSITOR_IMPL; diff --git a/src/nvim/generators/c_grammar.lua b/src/nvim/generators/c_grammar.lua index f35817c466..70a7be86b5 100644 --- a/src/nvim/generators/c_grammar.lua +++ b/src/nvim/generators/c_grammar.lua @@ -49,6 +49,7 @@ local c_proto = Ct( (fill * Cg((P('FUNC_API_REMOTE_IMPL') * Cc(true)), 'remote_impl') ^ -1) * (fill * Cg((P('FUNC_API_BRIDGE_IMPL') * Cc(true)), 'bridge_impl') ^ -1) * (fill * Cg((P('FUNC_API_COMPOSITOR_IMPL') * Cc(true)), 'compositor_impl') ^ -1) * + (fill * Cg((P('FUNC_API_CLIENT_IMPL') * Cc(true)), 'client_impl') ^ -1) * fill * P(';') ) diff --git a/src/nvim/generators/gen_api_ui_events.lua b/src/nvim/generators/gen_api_ui_events.lua old mode 100644 new mode 100755 index 3cb117d8b5..3cb63c3837 --- a/src/nvim/generators/gen_api_ui_events.lua +++ b/src/nvim/generators/gen_api_ui_events.lua @@ -3,15 +3,16 @@ local mpack = require('mpack') local nvimdir = arg[1] package.path = nvimdir .. '/?.lua;' .. package.path -assert(#arg == 7) +assert(#arg == 8) local input = io.open(arg[2], 'rb') local proto_output = io.open(arg[3], 'wb') local call_output = io.open(arg[4], 'wb') local remote_output = io.open(arg[5], 'wb') local bridge_output = io.open(arg[6], 'wb') local metadata_output = io.open(arg[7], 'wb') +local redraw_output = io.open(arg[8], 'wb') -local c_grammar = require('generators.c_grammar') +c_grammar = require('generators.c_grammar') local events = c_grammar.grammar:match(input:read('*all')) local function write_signature(output, ev, prefix, notype) @@ -50,6 +51,35 @@ local function write_arglist(output, ev, need_copy) end end +function extract_and_write_arglist(output, ev) + local hlattrs_args_count = 0 + for j = 1, #ev.parameters do + local param = ev.parameters[j] + local kind = param[1] + output:write(' '..kind..' arg_'..j..' = ') + if kind == 'HlAttrs' then + -- The first HlAttrs argument is rgb_attrs and second is cterm_attrs + output:write('redraw_dict2hlattrs(args.items['..(j-1)..'].data.dictionary, '..(hlattrs_args_count == 0 and 'true' or 'false')..');\n') + hlattrs_args_count = hlattrs_args_count + 1 + elseif kind == 'Object' then + output:write('args.items['..(j-1)..'];\n') + else + output:write('args.items['..(j-1)..'].data.'..string.lower(kind)..';\n') + end + end +end + +function call_ui_event_method(output, ev) + output:write(' ui_call_'..ev.name..'(') + for j = 1, #ev.parameters do + output:write('arg_'..j) + if j ~= #ev.parameters then + output:write(', ') + end + end + output:write(');\n') +end + for i = 1, #events do local ev = events[i] assert(ev.return_type == 'void') @@ -160,12 +190,38 @@ for i = 1, #events do call_output:write(";\n") call_output:write("}\n\n") end + + if (not ev.remote_only) and (not ev.noexport) and (not ev.client_impl) then + redraw_output:write('void ui_redraw_event_'..ev.name..'(Array args)\n{\n') + extract_and_write_arglist(redraw_output, ev) + call_ui_event_method(redraw_output, ev) + redraw_output:write('}\n\n') + end end +-- Generate the map_init method for redraw handlers +redraw_output:write([[ +void redraw_methods_table_init(void) +{ + +]]) + +for i = 1, #events do + local fn = events[i] + if (not fn.noexport) and ((not fn.remote_only) or fn.client_impl) then + redraw_output:write(' add_redraw_event_handler('.. + '(String) {.data = "'..fn.name..'", '.. + '.size = sizeof("'..fn.name..'") - 1}, '.. + '(ApiRedrawWrapper) ui_redraw_event_'..fn.name..');\n') + end +end + +redraw_output:write('\n}\n\n') + proto_output:close() call_output:close() remote_output:close() -bridge_output:close() +redraw_output:close() -- don't expose internal attributes like "impl_name" in public metadata local exported_attributes = {'name', 'parameters', diff --git a/src/nvim/map.c b/src/nvim/map.c index 4e39eb8c07..260a27c485 100644 --- a/src/nvim/map.c +++ b/src/nvim/map.c @@ -179,6 +179,7 @@ MAP_IMPL(HlEntry, int, DEFAULT_INITIALIZER) MAP_IMPL(String, handle_T, 0) MAP_IMPL(String, int, DEFAULT_INITIALIZER) MAP_IMPL(int, String, DEFAULT_INITIALIZER) +MAP_IMPL(String, ApiRedrawWrapper, NULL) MAP_IMPL(ColorKey, ColorItem, COLOR_ITEM_INITIALIZER) diff --git a/src/nvim/map.h b/src/nvim/map.h index 00f72386a7..0fa3fbd83d 100644 --- a/src/nvim/map.h +++ b/src/nvim/map.h @@ -8,6 +8,7 @@ #include "nvim/extmark_defs.h" #include "nvim/highlight_defs.h" #include "nvim/map_defs.h" +#include "nvim/ui_client.h" #if defined(__NetBSD__) # undef uint64_t @@ -48,6 +49,7 @@ MAP_DECLS(HlEntry, int) MAP_DECLS(String, handle_T) MAP_DECLS(String, int) MAP_DECLS(int, String) +MAP_DECLS(String, ApiRedrawWrapper) MAP_DECLS(ColorKey, ColorItem) diff --git a/src/nvim/ui_client.c b/src/nvim/ui_client.c index 4a435aac4d..b23ceefb6f 100644 --- a/src/nvim/ui_client.c +++ b/src/nvim/ui_client.c @@ -6,11 +6,22 @@ #include #include "nvim/vim.h" +#include "nvim/log.h" +#include "nvim/map.h" #include "nvim/ui_client.h" #include "nvim/api/private/helpers.h" #include "nvim/msgpack_rpc/channel.h" #include "nvim/api/private/dispatch.h" #include "nvim/ui.h" +#include "nvim/highlight.h" +#include "nvim/screen.h" + +static Map(String, ApiRedrawWrapper) redraw_methods = MAP_INIT; + +static void add_redraw_event_handler(String method, ApiRedrawWrapper handler) +{ + map_put(String, ApiRedrawWrapper)(&redraw_methods, method, handler); +} void ui_client_init(uint64_t chan) { @@ -68,3 +79,106 @@ void ui_client_execute(uint64_t chan) getout(0); } + +/// @param name Redraw method name +/// @param name_len name size (includes terminating NUL) +ApiRedrawWrapper get_redraw_event_handler(const char *name, size_t name_len, Error *error) +{ + String m = { .data = (char *)name, .size = name_len }; + ApiRedrawWrapper rv = + map_get(String, ApiRedrawWrapper)(&redraw_methods, m); + + if (!rv) { + api_set_error(error, kErrorTypeException, "Invalid method: %.*s", + m.size > 0 ? (int)m.size : (int)sizeof(""), + m.size > 0 ? m.data : ""); + } + return rv; +} + +static HlAttrs redraw_dict2hlattrs(Dictionary redraw_dict, bool rgb) +{ + Error err = ERROR_INIT; + Dict(highlight) dict = { 0 }; + if (!api_dict_to_keydict(&dict, KeyDict_highlight_get_field, redraw_dict, &err)) { + // TODO(bfredl): log "err" + return HLATTRS_INIT; + } + return dict2hlattrs(&dict, true, NULL, &err); +} + +#ifdef INCLUDE_GENERATED_DECLARATIONS +#include "ui_events_redraw.generated.h" +#endif + +void ui_redraw_event_grid_line(Array args) +{ + 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; + Integer endcol, clearcol, clearattr; + // TODO(hlpr98): Accomodate other LineFlags when included in grid_line + LineFlags lineflags = 0; + schar_T *chunk; + sattr_T *attrs; + size_t size_of_cells = cells.size; + size_t no_of_cells = size_of_cells; + endcol = startcol; + + // checking if clearcol > endcol + if (!STRCMP(cells.items[size_of_cells-1].data.array + .items[0].data.string.data, " ") + && cells.items[size_of_cells-1].data.array.size == 3) { + no_of_cells = size_of_cells - 1; + } + + // getting endcol + for (size_t i = 0; i < no_of_cells; i++) { + endcol++; + if (cells.items[i].data.array.size == 3) { + endcol += cells.items[i].data.array.items[2].data.integer - 1; + } + } + + if (!STRCMP(cells.items[size_of_cells-1].data.array + .items[0].data.string.data, " ") + && cells.items[size_of_cells-1].data.array.size == 3) { + clearattr = cells.items[size_of_cells-1].data.array.items[1].data.integer; + clearcol = endcol + cells.items[size_of_cells-1].data.array + .items[2].data.integer; + } else { + clearattr = 0; + clearcol = endcol; + } + + size_t ncells = (size_t)(endcol - startcol); + chunk = xmalloc(ncells * sizeof(schar_T) + 1); + attrs = xmalloc(ncells * sizeof(sattr_T) + 1); + + size_t j = 0; + size_t k = 0; + for (size_t i = 0; i < no_of_cells; i++) { + STRCPY(chunk[j++], cells.items[i].data.array.items[0].data.string.data); + if (cells.items[i].data.array.size == 3) { + // repeat present + for (size_t i_intr = 1; + i_intr < (size_t)cells.items[i].data.array.items[2].data.integer; + i_intr++) { + STRCPY(chunk[j++], cells.items[i].data.array.items[0].data.string.data); + attrs[k++] = (sattr_T)cells.items[i].data.array.items[1].data.integer; + } + } else if (cells.items[i].data.array.size == 2) { + // repeat = 1 but attrs != last_hl + attrs[k++] = (sattr_T)cells.items[i].data.array.items[1].data.integer; + } + if (j > k) { + // attrs == last_hl + attrs[k] = attrs[k-1]; + k++; + } + } + + ui_call_raw_line(grid, row, startcol, endcol, clearcol, clearattr, lineflags, + (const schar_T *)chunk, (const sattr_T *)attrs); +} diff --git a/src/nvim/ui_client.h b/src/nvim/ui_client.h index 067f78d5c5..a120f2b334 100644 --- a/src/nvim/ui_client.h +++ b/src/nvim/ui_client.h @@ -3,7 +3,11 @@ #include "nvim/api/private/defs.h" +typedef void (*ApiRedrawWrapper)(Array args); + #ifdef INCLUDE_GENERATED_DECLARATIONS #include "ui_client.h.generated.h" +#include "ui_events_redraw.h.generated.h" #endif + #endif // NVIM_UI_CLIENT_H From 534edce3c4972d1c8da44fbcf60e7946c09a5612 Mon Sep 17 00:00:00 2001 From: bfredl Date: Sat, 12 Mar 2022 23:17:32 +0100 Subject: [PATCH 2/5] feat(ui): invoke ui client handlers --- src/nvim/main.c | 12 +++++++---- src/nvim/msgpack_rpc/channel.c | 8 ++----- src/nvim/ui_client.c | 39 ++++++++++++++++------------------ 3 files changed, 28 insertions(+), 31 deletions(-) diff --git a/src/nvim/main.c b/src/nvim/main.c index 95ef306745..d67b47e82c 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -112,7 +112,6 @@ static const char *err_too_many_args = N_("Too many edit arguments"); static const char *err_extra_cmd = N_("Too many \"+command\", \"-c command\" or \"--cmd command\" arguments"); - void event_init(void) { loop_init(&main_loop, NULL); @@ -344,6 +343,12 @@ int main(int argc, char **argv) TIME_MSG("init screen for UI"); } + if (ui_client_channel_id) { + ui_client_init(ui_client_channel_id); + ui_client_execute(ui_client_channel_id); + abort(); // unreachable + } + init_default_mappings(); // Default mappings. TIME_MSG("init default mappings"); @@ -840,9 +845,8 @@ static void remote_request(mparm_T *params, int remote_args, exit(1); } - ui_client_init(chan); - ui_client_execute(chan); - abort(); // unreachable + ui_client_channel_id = chan; + return; } Array args = ARRAY_DICT_INIT; diff --git a/src/nvim/msgpack_rpc/channel.c b/src/nvim/msgpack_rpc/channel.c index f4e836fa81..48ecd5d0ea 100644 --- a/src/nvim/msgpack_rpc/channel.c +++ b/src/nvim/msgpack_rpc/channel.c @@ -547,12 +547,8 @@ void rpc_close(Channel *channel) channel->rpc.closed = true; channel_decref(channel); - if (channel->id == ui_client_channel_id) { - // TODO(bfredl): handle this in ui_client, where os_exit() is safe - exit(0); - } - - if (channel->streamtype == kChannelStreamStdio) { + if (channel->streamtype == kChannelStreamStdio + || channel->id == ui_client_channel_id) { multiqueue_put(main_loop.fast_events, exit_event, 0); } } diff --git a/src/nvim/ui_client.c b/src/nvim/ui_client.c index b23ceefb6f..c13bfc2d07 100644 --- a/src/nvim/ui_client.c +++ b/src/nvim/ui_client.c @@ -26,21 +26,21 @@ static void add_redraw_event_handler(String method, ApiRedrawWrapper handler) void ui_client_init(uint64_t chan) { Array args = ARRAY_DICT_INIT; - int width = 80; - int height = 25; + int width = Columns; + int height = Rows; Dictionary opts = ARRAY_DICT_INIT; PUT(opts, "rgb", BOOLEAN_OBJ(true)); PUT(opts, "ext_linegrid", BOOLEAN_OBJ(true)); PUT(opts, "ext_termcolors", BOOLEAN_OBJ(true)); - // TODO(bfredl): use the size of the client UI ADD(args, INTEGER_OBJ((int)width)); ADD(args, INTEGER_OBJ((int)height)); ADD(args, DICTIONARY_OBJ(opts)); rpc_send_event(chan, "nvim_ui_attach", args); msgpack_rpc_add_redraw(); // GAME! + redraw_methods_table_init(); ui_client_channel_id = chan; } @@ -61,9 +61,22 @@ Object ui_client_handle_redraw(uint64_t channel_id, Array args, Error *error) { for (size_t i = 0; i < args.size; i++) { Array call = args.items[i].data.array; - char *method_name = call.items[0].data.string.data; + String name = call.items[0].data.string; + + ApiRedrawWrapper handler = map_get(String, ApiRedrawWrapper)(&redraw_methods, name); + if (!handler) { + ELOG("No redraw handler by name: %s", name.size ? name.data : ""); + continue; + } + + // fprintf(stderr, "%s: %zu\n", name.data, call.size-1); + + DLOG("Invoke redraw handler by name: %s", name.data); + for (size_t j = 1; j < call.size; j++) { + Array internal_call_args = call.items[j].data.array; + handler(internal_call_args); + } - fprintf(stderr, "%s: %zu\n", method_name, call.size-1); } return NIL; } @@ -80,22 +93,6 @@ void ui_client_execute(uint64_t chan) getout(0); } -/// @param name Redraw method name -/// @param name_len name size (includes terminating NUL) -ApiRedrawWrapper get_redraw_event_handler(const char *name, size_t name_len, Error *error) -{ - String m = { .data = (char *)name, .size = name_len }; - ApiRedrawWrapper rv = - map_get(String, ApiRedrawWrapper)(&redraw_methods, m); - - if (!rv) { - api_set_error(error, kErrorTypeException, "Invalid method: %.*s", - m.size > 0 ? (int)m.size : (int)sizeof(""), - m.size > 0 ? m.data : ""); - } - return rv; -} - static HlAttrs redraw_dict2hlattrs(Dictionary redraw_dict, bool rgb) { Error err = ERROR_INIT; From ca23f2ed308b46665e5c8e677c4012cfa7453490 Mon Sep 17 00:00:00 2001 From: bfredl Date: Sun, 13 Mar 2022 14:57:57 +0100 Subject: [PATCH 3/5] refactor(ui): use "ui_client" instead of "redraw" as general prefix --- src/nvim/CMakeLists.txt | 9 +++--- src/nvim/generators/gen_api_ui_events.lua | 32 ++++++++++----------- src/nvim/map.c | 2 +- src/nvim/map.h | 2 +- src/nvim/ui_client.c | 35 +++++++++++------------ src/nvim/ui_client.h | 4 +-- 6 files changed, 40 insertions(+), 44 deletions(-) diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index 34d112a756..8e17f94abc 100755 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -39,7 +39,7 @@ set(GENERATED_UI_EVENTS ${GENERATED_DIR}/ui_events.generated.h) set(GENERATED_UI_EVENTS_CALL ${GENERATED_DIR}/ui_events_call.generated.h) set(GENERATED_UI_EVENTS_REMOTE ${GENERATED_DIR}/ui_events_remote.generated.h) set(GENERATED_UI_EVENTS_BRIDGE ${GENERATED_DIR}/ui_events_bridge.generated.h) -set(GENERATED_UI_EVENTS_REDRAW ${GENERATED_DIR}/ui_events_redraw.generated.h) +set(GENERATED_UI_EVENTS_CLIENT ${GENERATED_DIR}/ui_events_client.generated.h) set(GENERATED_UI_EVENTS_METADATA ${GENERATED_DIR}/api/private/ui_events_metadata.generated.h) set(GENERATED_EX_CMDS_ENUM ${GENERATED_INCLUDES_DIR}/ex_cmds_enum.generated.h) set(GENERATED_EX_CMDS_DEFS ${GENERATED_DIR}/ex_cmds_defs.generated.h) @@ -272,7 +272,7 @@ foreach(sfile ${NVIM_SOURCES} "${GENERATED_UI_EVENTS_REMOTE}" "${GENERATED_UI_EVENTS_BRIDGE}" "${GENERATED_KEYSETS}" - "${GENERATED_UI_EVENTS_REDRAW}" + "${GENERATED_UI_EVENTS_CLIENT}" ) get_filename_component(full_d ${sfile} PATH) file(RELATIVE_PATH d "${CMAKE_CURRENT_LIST_DIR}" "${full_d}") @@ -370,7 +370,7 @@ add_custom_command( ${GENERATED_UI_EVENTS_REMOTE} ${GENERATED_UI_EVENTS_BRIDGE} ${GENERATED_UI_EVENTS_METADATA} - ${GENERATED_UI_EVENTS_REDRAW} + ${GENERATED_UI_EVENTS_CLIENT} COMMAND ${LUA_PRG} ${API_UI_EVENTS_GENERATOR} ${CMAKE_CURRENT_LIST_DIR} ${CMAKE_CURRENT_LIST_DIR}/api/ui_events.in.h ${GENERATED_UI_EVENTS} @@ -378,8 +378,7 @@ add_custom_command( ${GENERATED_UI_EVENTS_REMOTE} ${GENERATED_UI_EVENTS_BRIDGE} ${GENERATED_UI_EVENTS_METADATA} - ${GENERATED_UI_EVENTS_REDRAW} - + ${GENERATED_UI_EVENTS_CLIENT} DEPENDS ${API_UI_EVENTS_GENERATOR} ${GENERATOR_C_GRAMMAR} diff --git a/src/nvim/generators/gen_api_ui_events.lua b/src/nvim/generators/gen_api_ui_events.lua index 3cb63c3837..cdf4538e9f 100755 --- a/src/nvim/generators/gen_api_ui_events.lua +++ b/src/nvim/generators/gen_api_ui_events.lua @@ -10,9 +10,9 @@ local call_output = io.open(arg[4], 'wb') local remote_output = io.open(arg[5], 'wb') local bridge_output = io.open(arg[6], 'wb') local metadata_output = io.open(arg[7], 'wb') -local redraw_output = io.open(arg[8], 'wb') +local client_output = io.open(arg[8], 'wb') -c_grammar = require('generators.c_grammar') +local c_grammar = require('generators.c_grammar') local events = c_grammar.grammar:match(input:read('*all')) local function write_signature(output, ev, prefix, notype) @@ -51,7 +51,7 @@ local function write_arglist(output, ev, need_copy) end end -function extract_and_write_arglist(output, ev) +local function extract_and_write_arglist(output, ev) local hlattrs_args_count = 0 for j = 1, #ev.parameters do local param = ev.parameters[j] @@ -59,7 +59,7 @@ function extract_and_write_arglist(output, ev) output:write(' '..kind..' arg_'..j..' = ') if kind == 'HlAttrs' then -- The first HlAttrs argument is rgb_attrs and second is cterm_attrs - output:write('redraw_dict2hlattrs(args.items['..(j-1)..'].data.dictionary, '..(hlattrs_args_count == 0 and 'true' or 'false')..');\n') + output:write('ui_client_dict2hlattrs(args.items['..(j-1)..'].data.dictionary, '..(hlattrs_args_count == 0 and 'true' or 'false')..');\n') hlattrs_args_count = hlattrs_args_count + 1 elseif kind == 'Object' then output:write('args.items['..(j-1)..'];\n') @@ -69,7 +69,7 @@ function extract_and_write_arglist(output, ev) end end -function call_ui_event_method(output, ev) +local function call_ui_event_method(output, ev) output:write(' ui_call_'..ev.name..'(') for j = 1, #ev.parameters do output:write('arg_'..j) @@ -192,16 +192,16 @@ for i = 1, #events do end if (not ev.remote_only) and (not ev.noexport) and (not ev.client_impl) then - redraw_output:write('void ui_redraw_event_'..ev.name..'(Array args)\n{\n') - extract_and_write_arglist(redraw_output, ev) - call_ui_event_method(redraw_output, ev) - redraw_output:write('}\n\n') + client_output:write('void ui_client_event_'..ev.name..'(Array args)\n{\n') + extract_and_write_arglist(client_output, ev) + call_ui_event_method(client_output, ev) + client_output:write('}\n\n') end end --- Generate the map_init method for redraw handlers -redraw_output:write([[ -void redraw_methods_table_init(void) +-- Generate the map_init method for client handlers +client_output:write([[ +void ui_client_methods_table_init(void) { ]]) @@ -209,19 +209,19 @@ void redraw_methods_table_init(void) for i = 1, #events do local fn = events[i] if (not fn.noexport) and ((not fn.remote_only) or fn.client_impl) then - redraw_output:write(' add_redraw_event_handler('.. + client_output:write(' add_ui_client_event_handler('.. '(String) {.data = "'..fn.name..'", '.. '.size = sizeof("'..fn.name..'") - 1}, '.. - '(ApiRedrawWrapper) ui_redraw_event_'..fn.name..');\n') + '(UIClientHandler) ui_client_event_'..fn.name..');\n') end end -redraw_output:write('\n}\n\n') +client_output:write('\n}\n\n') proto_output:close() call_output:close() remote_output:close() -redraw_output:close() +client_output:close() -- don't expose internal attributes like "impl_name" in public metadata local exported_attributes = {'name', 'parameters', diff --git a/src/nvim/map.c b/src/nvim/map.c index 260a27c485..b3f48ad5d6 100644 --- a/src/nvim/map.c +++ b/src/nvim/map.c @@ -179,7 +179,7 @@ MAP_IMPL(HlEntry, int, DEFAULT_INITIALIZER) MAP_IMPL(String, handle_T, 0) MAP_IMPL(String, int, DEFAULT_INITIALIZER) MAP_IMPL(int, String, DEFAULT_INITIALIZER) -MAP_IMPL(String, ApiRedrawWrapper, NULL) +MAP_IMPL(String, UIClientHandler, NULL) MAP_IMPL(ColorKey, ColorItem, COLOR_ITEM_INITIALIZER) diff --git a/src/nvim/map.h b/src/nvim/map.h index 0fa3fbd83d..693ef50127 100644 --- a/src/nvim/map.h +++ b/src/nvim/map.h @@ -49,7 +49,7 @@ MAP_DECLS(HlEntry, int) MAP_DECLS(String, handle_T) MAP_DECLS(String, int) MAP_DECLS(int, String) -MAP_DECLS(String, ApiRedrawWrapper) +MAP_DECLS(String, UIClientHandler) MAP_DECLS(ColorKey, ColorItem) diff --git a/src/nvim/ui_client.c b/src/nvim/ui_client.c index c13bfc2d07..daae257027 100644 --- a/src/nvim/ui_client.c +++ b/src/nvim/ui_client.c @@ -16,11 +16,11 @@ #include "nvim/highlight.h" #include "nvim/screen.h" -static Map(String, ApiRedrawWrapper) redraw_methods = MAP_INIT; +static Map(String, UIClientHandler) ui_client_handlers = MAP_INIT; -static void add_redraw_event_handler(String method, ApiRedrawWrapper handler) +static void add_ui_client_event_handler(String method, UIClientHandler handler) { - map_put(String, ApiRedrawWrapper)(&redraw_methods, method, handler); + map_put(String, UIClientHandler)(&ui_client_handlers, method, handler); } void ui_client_init(uint64_t chan) @@ -40,17 +40,16 @@ void ui_client_init(uint64_t chan) rpc_send_event(chan, "nvim_ui_attach", args); msgpack_rpc_add_redraw(); // GAME! - redraw_methods_table_init(); + // TODO(bfredl): use a keyset instead + ui_client_methods_table_init(); ui_client_channel_id = chan; } /// Handler for "redraw" events sent by the NVIM server /// -/// This is just a stub. The mentioned functionality will be implemented. -/// -/// This function will be called by handle_request (in msgpack_rpc/channle.c) +/// 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_redraw.generated.h +/// 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 @@ -63,21 +62,19 @@ Object ui_client_handle_redraw(uint64_t channel_id, Array args, Error *error) Array call = args.items[i].data.array; String name = call.items[0].data.string; - ApiRedrawWrapper handler = map_get(String, ApiRedrawWrapper)(&redraw_methods, name); + UIClientHandler handler = map_get(String, UIClientHandler)(&ui_client_handlers, name); if (!handler) { - ELOG("No redraw handler by name: %s", name.size ? name.data : ""); + ELOG("No ui client handler for %s", name.size ? name.data : ""); continue; } // fprintf(stderr, "%s: %zu\n", name.data, call.size-1); - - DLOG("Invoke redraw handler by name: %s", name.data); + DLOG("Invoke ui client handler for %s", name.data); for (size_t j = 1; j < call.size; j++) { - Array internal_call_args = call.items[j].data.array; - handler(internal_call_args); + handler(call.items[j].data.array); } - } + return NIL; } @@ -93,11 +90,11 @@ void ui_client_execute(uint64_t chan) getout(0); } -static HlAttrs redraw_dict2hlattrs(Dictionary redraw_dict, bool rgb) +static HlAttrs ui_client_dict2hlattrs(Dictionary d, bool rgb) { Error err = ERROR_INIT; Dict(highlight) dict = { 0 }; - if (!api_dict_to_keydict(&dict, KeyDict_highlight_get_field, redraw_dict, &err)) { + if (!api_dict_to_keydict(&dict, KeyDict_highlight_get_field, d, &err)) { // TODO(bfredl): log "err" return HLATTRS_INIT; } @@ -105,10 +102,10 @@ static HlAttrs redraw_dict2hlattrs(Dictionary redraw_dict, bool rgb) } #ifdef INCLUDE_GENERATED_DECLARATIONS -#include "ui_events_redraw.generated.h" +#include "ui_events_client.generated.h" #endif -void ui_redraw_event_grid_line(Array args) +void ui_client_event_grid_line(Array args) { Integer grid = args.items[0].data.integer; Integer row = args.items[1].data.integer; diff --git a/src/nvim/ui_client.h b/src/nvim/ui_client.h index a120f2b334..253deecc52 100644 --- a/src/nvim/ui_client.h +++ b/src/nvim/ui_client.h @@ -3,11 +3,11 @@ #include "nvim/api/private/defs.h" -typedef void (*ApiRedrawWrapper)(Array args); +typedef void (*UIClientHandler)(Array args); #ifdef INCLUDE_GENERATED_DECLARATIONS #include "ui_client.h.generated.h" -#include "ui_events_redraw.h.generated.h" +#include "ui_events_client.h.generated.h" #endif #endif // NVIM_UI_CLIENT_H From c0b4d931e12910f67cc3eade664247ea2d2bb913 Mon Sep 17 00:00:00 2001 From: bfredl Date: Sun, 13 Mar 2022 16:02:53 +0100 Subject: [PATCH 4/5] refactor(ui): make ui_client_event_grid_line typesafe --- src/nvim/api/ui_events.in.h | 2 +- src/nvim/ui_client.c | 139 ++++++++++++++++++++++-------------- 2 files changed, 85 insertions(+), 56 deletions(-) diff --git a/src/nvim/api/ui_events.in.h b/src/nvim/api/ui_events.in.h index 78826072a7..db348359eb 100644 --- a/src/nvim/api/ui_events.in.h +++ b/src/nvim/api/ui_events.in.h @@ -78,7 +78,7 @@ void hl_attr_define(Integer id, HlAttrs rgb_attrs, HlAttrs cterm_attrs, void hl_group_set(String name, Integer id) FUNC_API_SINCE(6) FUNC_API_BRIDGE_IMPL; void grid_resize(Integer grid, Integer width, Integer height) - FUNC_API_SINCE(5) FUNC_API_REMOTE_IMPL FUNC_API_COMPOSITOR_IMPL; + FUNC_API_SINCE(5) FUNC_API_REMOTE_IMPL FUNC_API_COMPOSITOR_IMPL FUNC_API_CLIENT_IMPL; void grid_clear(Integer grid) FUNC_API_SINCE(5) FUNC_API_REMOTE_IMPL; void grid_cursor_goto(Integer grid, Integer row, Integer col) diff --git a/src/nvim/ui_client.c b/src/nvim/ui_client.c index daae257027..3914a4e199 100644 --- a/src/nvim/ui_client.c +++ b/src/nvim/ui_client.c @@ -18,6 +18,11 @@ static Map(String, UIClientHandler) ui_client_handlers = MAP_INIT; +// 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; + static void add_ui_client_event_handler(String method, UIClientHandler handler) { map_put(String, UIClientHandler)(&ui_client_handlers, method, handler); @@ -105,74 +110,98 @@ static HlAttrs ui_client_dict2hlattrs(Dictionary d, bool rgb) #include "ui_events_client.generated.h" #endif +void ui_client_event_grid_resize(Array args) +{ + // TODO: typesafe! + Integer grid = args.items[0].data.integer; + Integer width = args.items[1].data.integer; + 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)); + } +} + void ui_client_event_grid_line(Array args) { + 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; + } + 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; - Integer endcol, clearcol, clearattr; + + Integer endcol, clearcol; // TODO(hlpr98): Accomodate other LineFlags when included in grid_line LineFlags lineflags = 0; - schar_T *chunk; - sattr_T *attrs; - size_t size_of_cells = cells.size; - size_t no_of_cells = size_of_cells; endcol = startcol; - // checking if clearcol > endcol - if (!STRCMP(cells.items[size_of_cells-1].data.array - .items[0].data.string.data, " ") - && cells.items[size_of_cells-1].data.array.size == 3) { - no_of_cells = size_of_cells - 1; - } - - // getting endcol - for (size_t i = 0; i < no_of_cells; i++) { - endcol++; - if (cells.items[i].data.array.size == 3) { - endcol += cells.items[i].data.array.items[2].data.integer - 1; - } - } - - if (!STRCMP(cells.items[size_of_cells-1].data.array - .items[0].data.string.data, " ") - && cells.items[size_of_cells-1].data.array.size == 3) { - clearattr = cells.items[size_of_cells-1].data.array.items[1].data.integer; - clearcol = endcol + cells.items[size_of_cells-1].data.array - .items[2].data.integer; - } else { - clearattr = 0; - clearcol = endcol; - } - - size_t ncells = (size_t)(endcol - startcol); - chunk = xmalloc(ncells * sizeof(schar_T) + 1); - attrs = xmalloc(ncells * sizeof(sattr_T) + 1); - size_t j = 0; - size_t k = 0; - for (size_t i = 0; i < no_of_cells; i++) { - STRCPY(chunk[j++], cells.items[i].data.array.items[0].data.string.data); - if (cells.items[i].data.array.size == 3) { - // repeat present - for (size_t i_intr = 1; - i_intr < (size_t)cells.items[i].data.array.items[2].data.integer; - i_intr++) { - STRCPY(chunk[j++], cells.items[i].data.array.items[0].data.string.data); - attrs[k++] = (sattr_T)cells.items[i].data.array.items[1].data.integer; - } - } else if (cells.items[i].data.array.size == 2) { - // repeat = 1 but attrs != last_hl - attrs[k++] = (sattr_T)cells.items[i].data.array.items[1].data.integer; + 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; } - if (j > k) { - // attrs == last_hl - attrs[k] = attrs[k-1]; - k++; + 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_ + } + STRCPY(buf_char[j], schar); + buf_attr[j++] = cur_attr; } } - ui_call_raw_line(grid, row, startcol, endcol, clearcol, clearattr, lineflags, - (const schar_T *)chunk, (const sattr_T *)attrs); + endcol = startcol + (int)j; + clearcol = endcol + clear_width; + clear_attr = cur_attr; + + ui_call_raw_line(grid, row, startcol, endcol, clearcol, clear_attr, lineflags, + buf_char, buf_attr); + return; + +error: + ELOG("malformatted 'grid_line' event"); } From f01d203b70f426c1538813b3bacb4483e914ab44 Mon Sep 17 00:00:00 2001 From: bfredl Date: Tue, 15 Mar 2022 00:23:14 +0100 Subject: [PATCH 5/5] refactor(ui): make auto-generated ui client handlers typesafe --- src/nvim/generators/gen_api_ui_events.lua | 26 +++++++++++++++++------ src/nvim/ui_client.c | 15 +++++++++---- 2 files changed, 31 insertions(+), 10 deletions(-) diff --git a/src/nvim/generators/gen_api_ui_events.lua b/src/nvim/generators/gen_api_ui_events.lua index cdf4538e9f..5e70442dce 100755 --- a/src/nvim/generators/gen_api_ui_events.lua +++ b/src/nvim/generators/gen_api_ui_events.lua @@ -51,8 +51,25 @@ local function write_arglist(output, ev, need_copy) end end -local function extract_and_write_arglist(output, ev) +local function call_ui_event_method(output, ev) + output:write('void ui_client_event_'..ev.name..'(Array args)\n{\n') + local hlattrs_args_count = 0 + if #ev.parameters > 0 then + output:write(' if (args.size < '..(#ev.parameters)) + for j = 1, #ev.parameters do + local kind = ev.parameters[j][1] + if kind ~= "Object" then + if kind == 'HlAttrs' then kind = 'Dictionary' end + output:write('\n || args.items['..(j-1)..'].type != kObjectType'..kind..'') + end + end + output:write(') {\n') + output:write(' ELOG("Error handling ui event \''..ev.name..'\'");\n') + output:write(' return;\n') + output:write(' }\n') + end + for j = 1, #ev.parameters do local param = ev.parameters[j] local kind = param[1] @@ -67,9 +84,7 @@ local function extract_and_write_arglist(output, ev) output:write('args.items['..(j-1)..'].data.'..string.lower(kind)..';\n') end end -end -local function call_ui_event_method(output, ev) output:write(' ui_call_'..ev.name..'(') for j = 1, #ev.parameters do output:write('arg_'..j) @@ -78,6 +93,8 @@ local function call_ui_event_method(output, ev) end end output:write(');\n') + + output:write('}\n\n') end for i = 1, #events do @@ -192,10 +209,7 @@ for i = 1, #events do end if (not ev.remote_only) and (not ev.noexport) and (not ev.client_impl) then - client_output:write('void ui_client_event_'..ev.name..'(Array args)\n{\n') - extract_and_write_arglist(client_output, ev) call_ui_event_method(client_output, ev) - client_output:write('}\n\n') end end diff --git a/src/nvim/ui_client.c b/src/nvim/ui_client.c index 3914a4e199..4fad3e0709 100644 --- a/src/nvim/ui_client.c +++ b/src/nvim/ui_client.c @@ -112,7 +112,14 @@ static HlAttrs ui_client_dict2hlattrs(Dictionary d, bool rgb) void ui_client_event_grid_resize(Array args) { - // TODO: typesafe! + if (args.size < 3 + || args.items[0].type != kObjectTypeInteger + || args.items[1].type != kObjectTypeInteger + || args.items[2].type != kObjectTypeInteger) { + ELOG("Error handling ui event 'grid_resize'"); + return; + } + Integer grid = args.items[0].data.integer; Integer width = args.items[1].data.integer; Integer height = args.items[2].data.integer; @@ -189,7 +196,7 @@ void ui_client_event_grid_line(Array args) if (j >= buf_size) { goto error; // _YIKES_ } - STRCPY(buf_char[j], schar); + STRLCPY(buf_char[j], schar, sizeof(schar_T)); buf_attr[j++] = cur_attr; } } @@ -199,9 +206,9 @@ void ui_client_event_grid_line(Array args) clear_attr = cur_attr; ui_call_raw_line(grid, row, startcol, endcol, clearcol, clear_attr, lineflags, - buf_char, buf_attr); + (const schar_T *)buf_char, (const sattr_T *)buf_attr); return; error: - ELOG("malformatted 'grid_line' event"); + ELOG("Error handling ui event 'grid_line'"); }