diff options
Diffstat (limited to 'src/nvim/api/ui.c')
-rw-r--r-- | src/nvim/api/ui.c | 617 |
1 files changed, 446 insertions, 171 deletions
diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c index 1703d49296..7ba5251c60 100644 --- a/src/nvim/api/ui.c +++ b/src/nvim/api/ui.c @@ -1,3 +1,6 @@ +// This is an open source non-commercial project. Dear PVS-Studio, please check +// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com + #include <assert.h> #include <stddef.h> #include <stdint.h> @@ -8,120 +11,264 @@ #include "nvim/memory.h" #include "nvim/map.h" #include "nvim/msgpack_rpc/channel.h" +#include "nvim/api/ui.h" #include "nvim/api/private/defs.h" #include "nvim/api/private/helpers.h" +#include "nvim/popupmnu.h" +#include "nvim/cursor_shape.h" +#include "nvim/highlight.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "api/ui.c.generated.h" +# include "ui_events_remote.generated.h" #endif typedef struct { uint64_t channel_id; Array buffer; + + int hl_id; // current higlight for legacy put event + Integer cursor_row, cursor_col; // Intended visibule cursor position + + // Position of legacy cursor, used both for drawing and visible user cursor. + Integer client_row, client_col; } UIData; static PMap(uint64_t) *connected_uis = NULL; void remote_ui_init(void) - FUNC_API_NOEXPORT + FUNC_API_NOEXPORT { connected_uis = pmap_new(uint64_t)(); } void remote_ui_disconnect(uint64_t channel_id) - FUNC_API_NOEXPORT + FUNC_API_NOEXPORT { UI *ui = pmap_get(uint64_t)(connected_uis, channel_id); if (!ui) { return; } UIData *data = ui->data; - // destroy pending screen updates - api_free_array(data->buffer); + api_free_array(data->buffer); // Destroy pending screen updates. pmap_del(uint64_t)(connected_uis, channel_id); xfree(ui->data); + ui->data = NULL; // Flag UI as "stopped". ui_detach_impl(ui); xfree(ui); } -void ui_attach(uint64_t channel_id, Integer width, Integer height, - Boolean enable_rgb, Error *err) +/// Wait until ui has connected on stdio channel. +void remote_ui_wait_for_attach(void) + FUNC_API_NOEXPORT +{ + Channel *channel = find_channel(CHAN_STDIO); + if (!channel) { + // this function should only be called in --embed mode, stdio channel + // can be assumed. + abort(); + } + + LOOP_PROCESS_EVENTS_UNTIL(&main_loop, channel->events, -1, + pmap_has(uint64_t)(connected_uis, CHAN_STDIO)); +} + +void nvim_ui_attach(uint64_t channel_id, Integer width, Integer height, + Dictionary options, Error *err) + FUNC_API_SINCE(1) FUNC_API_REMOTE_ONLY { if (pmap_has(uint64_t)(connected_uis, channel_id)) { - api_set_error(err, Exception, _("UI already attached for channel")); + api_set_error(err, kErrorTypeException, + "UI already attached to channel: %" PRId64, channel_id); return; } if (width <= 0 || height <= 0) { - api_set_error(err, Validation, - _("Expected width > 0 and height > 0")); + api_set_error(err, kErrorTypeValidation, + "Expected width > 0 and height > 0"); return; } - UIData *data = xmalloc(sizeof(UIData)); - data->channel_id = channel_id; - data->buffer = (Array)ARRAY_DICT_INIT; UI *ui = xcalloc(1, sizeof(UI)); ui->width = (int)width; ui->height = (int)height; - ui->rgb = enable_rgb; - ui->data = data; - ui->resize = remote_ui_resize; - ui->clear = remote_ui_clear; - ui->eol_clear = remote_ui_eol_clear; - ui->cursor_goto = remote_ui_cursor_goto; + ui->rgb = true; + ui->grid_resize = remote_ui_grid_resize; + ui->grid_clear = remote_ui_grid_clear; + ui->grid_cursor_goto = remote_ui_grid_cursor_goto; + ui->mode_info_set = remote_ui_mode_info_set; ui->update_menu = remote_ui_update_menu; ui->busy_start = remote_ui_busy_start; ui->busy_stop = remote_ui_busy_stop; ui->mouse_on = remote_ui_mouse_on; ui->mouse_off = remote_ui_mouse_off; ui->mode_change = remote_ui_mode_change; - ui->set_scroll_region = remote_ui_set_scroll_region; - ui->scroll = remote_ui_scroll; - ui->highlight_set = remote_ui_highlight_set; - ui->put = remote_ui_put; + ui->grid_scroll = remote_ui_grid_scroll; + ui->hl_attr_define = remote_ui_hl_attr_define; + ui->raw_line = remote_ui_raw_line; ui->bell = remote_ui_bell; ui->visual_bell = remote_ui_visual_bell; - ui->update_fg = remote_ui_update_fg; - ui->update_bg = remote_ui_update_bg; - ui->update_sp = remote_ui_update_sp; + ui->default_colors_set = remote_ui_default_colors_set; ui->flush = remote_ui_flush; ui->suspend = remote_ui_suspend; ui->set_title = remote_ui_set_title; ui->set_icon = remote_ui_set_icon; + ui->option_set = remote_ui_option_set; + ui->event = remote_ui_event; + ui->inspect = remote_ui_inspect; + + memset(ui->ui_ext, 0, sizeof(ui->ui_ext)); + + for (size_t i = 0; i < options.size; i++) { + ui_set_option(ui, true, options.items[i].key, options.items[i].value, err); + if (ERROR_SET(err)) { + xfree(ui); + return; + } + } + + if (ui->ui_ext[kUIHlState] || ui->ui_ext[kUIMultigrid]) { + ui->ui_ext[kUILinegrid] = true; + } + + UIData *data = xmalloc(sizeof(UIData)); + data->channel_id = channel_id; + data->buffer = (Array)ARRAY_DICT_INIT; + data->hl_id = 0; + data->client_col = -1; + ui->data = data; + pmap_put(uint64_t)(connected_uis, channel_id, ui); ui_attach_impl(ui); - return; } -void ui_detach(uint64_t channel_id, Error *err) +/// @deprecated +void ui_attach(uint64_t channel_id, Integer width, Integer height, + Boolean enable_rgb, Error *err) +{ + Dictionary opts = ARRAY_DICT_INIT; + PUT(opts, "rgb", BOOLEAN_OBJ(enable_rgb)); + nvim_ui_attach(channel_id, width, height, opts, err); + api_free_dictionary(opts); +} + +void nvim_ui_detach(uint64_t channel_id, Error *err) + FUNC_API_SINCE(1) FUNC_API_REMOTE_ONLY { if (!pmap_has(uint64_t)(connected_uis, channel_id)) { - api_set_error(err, Exception, _("UI is not attached for channel")); + api_set_error(err, kErrorTypeException, + "UI not attached to channel: %" PRId64, channel_id); + return; } remote_ui_disconnect(channel_id); } -Object ui_try_resize(uint64_t channel_id, Integer width, - Integer height, Error *err) + +void nvim_ui_try_resize(uint64_t channel_id, Integer width, + Integer height, Error *err) + FUNC_API_SINCE(1) FUNC_API_REMOTE_ONLY { if (!pmap_has(uint64_t)(connected_uis, channel_id)) { - api_set_error(err, Exception, _("UI is not attached for channel")); + api_set_error(err, kErrorTypeException, + "UI not attached to channel: %" PRId64, channel_id); + return; } if (width <= 0 || height <= 0) { - api_set_error(err, Validation, - _("Expected width > 0 and height > 0")); - return NIL; + api_set_error(err, kErrorTypeValidation, + "Expected width > 0 and height > 0"); + return; } UI *ui = pmap_get(uint64_t)(connected_uis, channel_id); ui->width = (int)width; ui->height = (int)height; ui_refresh(); - return NIL; } -static void push_call(UI *ui, char *name, Array args) +void nvim_ui_set_option(uint64_t channel_id, String name, + Object value, Error *error) + FUNC_API_SINCE(1) FUNC_API_REMOTE_ONLY +{ + if (!pmap_has(uint64_t)(connected_uis, channel_id)) { + api_set_error(error, kErrorTypeException, + "UI not attached to channel: %" PRId64, channel_id); + return; + } + UI *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 *error) +{ + if (strequal(name.data, "rgb")) { + if (value.type != kObjectTypeBoolean) { + api_set_error(error, kErrorTypeValidation, "rgb must be a Boolean"); + return; + } + ui->rgb = value.data.boolean; + // A little drastic, but only legacy uis need to use this option + if (!init) { + ui_refresh(); + } + return; + } + + // LEGACY: Deprecated option, use `ext_cmdline` instead. + bool is_popupmenu = strequal(name.data, "popupmenu_external"); + + for (UIExtension i = 0; i < kUIExtCount; i++) { + if (strequal(name.data, ui_ext_names[i]) + || (i == kUIPopupmenu && is_popupmenu)) { + if (value.type != kObjectTypeBoolean) { + api_set_error(error, kErrorTypeValidation, "%s must be a Boolean", + name.data); + return; + } + bool boolval = value.data.boolean; + if (!init && i == kUILinegrid && boolval != ui->ui_ext[i]) { + // There shouldn't be a reason for an UI to do this ever + // so explicitly don't support this. + api_set_error(error, kErrorTypeValidation, + "ext_linegrid option cannot be changed"); + } + ui->ui_ext[i] = boolval; + if (!init) { + ui_set_ext_option(ui, i, boolval); + } + return; + } + } + + api_set_error(error, kErrorTypeValidation, "No such UI option: %s", + name.data); +} + +/// Tell nvim to resize a grid. Nvim sends grid_resize event with the +/// requested grid size is within size limits and with maximum allowed size +/// otherwise. +/// +/// On invalid grid handle, fails with error. +/// +/// @param grid The handle of the grid to be changed. +/// @param width The new requested width. +/// @param height The new requested height. +void nvim_ui_try_resize_grid(uint64_t channel_id, Integer grid, Integer width, + Integer height, Error *error) + FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY +{ + if (!pmap_has(uint64_t)(connected_uis, channel_id)) { + api_set_error(error, kErrorTypeException, + "UI not attached to channel: %" PRId64, channel_id); + return; + } + + ui_grid_resize((handle_T)grid, (int)width, (int)height, error); +} + +/// Pushes data into UI.UIData, to be consumed later by remote_ui_flush(). +static void push_call(UI *ui, const char *name, Array args) { Array call = ARRAY_DICT_INIT; UIData *data = ui->data; @@ -143,201 +290,329 @@ static void push_call(UI *ui, char *name, Array args) kv_A(data->buffer, kv_size(data->buffer) - 1).data.array = call; } -static void remote_ui_resize(UI *ui, int width, int height) +static void remote_ui_grid_clear(UI *ui, Integer grid) { Array args = ARRAY_DICT_INIT; - ADD(args, INTEGER_OBJ(width)); - ADD(args, INTEGER_OBJ(height)); - push_call(ui, "resize", args); -} - -static void remote_ui_clear(UI *ui) -{ - Array args = ARRAY_DICT_INIT; - push_call(ui, "clear", args); -} - -static void remote_ui_eol_clear(UI *ui) -{ - Array args = ARRAY_DICT_INIT; - push_call(ui, "eol_clear", args); -} - -static void remote_ui_cursor_goto(UI *ui, int row, int col) -{ - Array args = ARRAY_DICT_INIT; - ADD(args, INTEGER_OBJ(row)); - ADD(args, INTEGER_OBJ(col)); - push_call(ui, "cursor_goto", args); + if (ui->ui_ext[kUILinegrid]) { + ADD(args, INTEGER_OBJ(grid)); + } + const char *name = ui->ui_ext[kUILinegrid] ? "grid_clear" : "clear"; + push_call(ui, name, args); } -static void remote_ui_update_menu(UI *ui) +static void remote_ui_grid_resize(UI *ui, Integer grid, + Integer width, Integer height) { Array args = ARRAY_DICT_INIT; - push_call(ui, "update_menu", args); + if (ui->ui_ext[kUILinegrid]) { + ADD(args, INTEGER_OBJ(grid)); + } + ADD(args, INTEGER_OBJ(width)); + ADD(args, INTEGER_OBJ(height)); + const char *name = ui->ui_ext[kUILinegrid] ? "grid_resize" : "resize"; + push_call(ui, name, args); } -static void remote_ui_busy_start(UI *ui) +static void remote_ui_grid_scroll(UI *ui, Integer grid, Integer top, + Integer bot, Integer left, Integer right, + Integer rows, Integer cols) { - Array args = ARRAY_DICT_INIT; - push_call(ui, "busy_start", args); + if (ui->ui_ext[kUILinegrid]) { + Array args = ARRAY_DICT_INIT; + ADD(args, INTEGER_OBJ(grid)); + ADD(args, INTEGER_OBJ(top)); + ADD(args, INTEGER_OBJ(bot)); + ADD(args, INTEGER_OBJ(left)); + ADD(args, INTEGER_OBJ(right)); + ADD(args, INTEGER_OBJ(rows)); + ADD(args, INTEGER_OBJ(cols)); + push_call(ui, "grid_scroll", args); + } else { + Array args = ARRAY_DICT_INIT; + ADD(args, INTEGER_OBJ(top)); + ADD(args, INTEGER_OBJ(bot-1)); + ADD(args, INTEGER_OBJ(left)); + ADD(args, INTEGER_OBJ(right-1)); + push_call(ui, "set_scroll_region", args); + + args = (Array)ARRAY_DICT_INIT; + ADD(args, INTEGER_OBJ(rows)); + push_call(ui, "scroll", args); + + // some clients have "clear" being affected by scroll region, + // so reset it. + args = (Array)ARRAY_DICT_INIT; + ADD(args, INTEGER_OBJ(0)); + ADD(args, INTEGER_OBJ(ui->height-1)); + ADD(args, INTEGER_OBJ(0)); + ADD(args, INTEGER_OBJ(ui->width-1)); + push_call(ui, "set_scroll_region", args); + } } -static void remote_ui_busy_stop(UI *ui) +static void remote_ui_default_colors_set(UI *ui, Integer rgb_fg, + Integer rgb_bg, Integer rgb_sp, + Integer cterm_fg, Integer cterm_bg) { Array args = ARRAY_DICT_INIT; - push_call(ui, "busy_stop", args); + ADD(args, INTEGER_OBJ(rgb_fg)); + ADD(args, INTEGER_OBJ(rgb_bg)); + ADD(args, INTEGER_OBJ(rgb_sp)); + ADD(args, INTEGER_OBJ(cterm_fg)); + ADD(args, INTEGER_OBJ(cterm_bg)); + push_call(ui, "default_colors_set", args); + + // Deprecated + if (!ui->ui_ext[kUILinegrid]) { + args = (Array)ARRAY_DICT_INIT; + ADD(args, INTEGER_OBJ(ui->rgb ? rgb_fg : cterm_fg - 1)); + push_call(ui, "update_fg", args); + + args = (Array)ARRAY_DICT_INIT; + ADD(args, INTEGER_OBJ(ui->rgb ? rgb_bg : cterm_bg - 1)); + push_call(ui, "update_bg", args); + + args = (Array)ARRAY_DICT_INIT; + ADD(args, INTEGER_OBJ(ui->rgb ? rgb_sp : -1)); + push_call(ui, "update_sp", args); + } } -static void remote_ui_mouse_on(UI *ui) +static void remote_ui_hl_attr_define(UI *ui, Integer id, HlAttrs rgb_attrs, + HlAttrs cterm_attrs, Array info) { + if (!ui->ui_ext[kUILinegrid]) { + return; + } Array args = ARRAY_DICT_INIT; - push_call(ui, "mouse_on", args); -} -static void remote_ui_mouse_off(UI *ui) -{ - Array args = ARRAY_DICT_INIT; - push_call(ui, "mouse_off", args); -} + ADD(args, INTEGER_OBJ(id)); + ADD(args, DICTIONARY_OBJ(hlattrs2dict(rgb_attrs, true))); + ADD(args, DICTIONARY_OBJ(hlattrs2dict(cterm_attrs, false))); -static void remote_ui_mode_change(UI *ui, int mode) -{ - Array args = ARRAY_DICT_INIT; - if (mode == INSERT) { - ADD(args, STRING_OBJ(cstr_to_string("insert"))); - } else if (mode == REPLACE) { - ADD(args, STRING_OBJ(cstr_to_string("replace"))); + if (ui->ui_ext[kUIHlState]) { + ADD(args, ARRAY_OBJ(copy_array(info))); } else { - assert(mode == NORMAL); - ADD(args, STRING_OBJ(cstr_to_string("normal"))); + ADD(args, ARRAY_OBJ((Array)ARRAY_DICT_INIT)); } - push_call(ui, "mode_change", args); -} -static void remote_ui_set_scroll_region(UI *ui, int top, int bot, int left, - int right) -{ - Array args = ARRAY_DICT_INIT; - ADD(args, INTEGER_OBJ(top)); - ADD(args, INTEGER_OBJ(bot)); - ADD(args, INTEGER_OBJ(left)); - ADD(args, INTEGER_OBJ(right)); - push_call(ui, "set_scroll_region", args); + push_call(ui, "hl_attr_define", args); } -static void remote_ui_scroll(UI *ui, int count) +static void remote_ui_highlight_set(UI *ui, int id) { Array args = ARRAY_DICT_INIT; - ADD(args, INTEGER_OBJ(count)); - push_call(ui, "scroll", args); -} - -static void remote_ui_highlight_set(UI *ui, HlAttrs attrs) -{ - Array args = ARRAY_DICT_INIT; - Dictionary hl = ARRAY_DICT_INIT; - - if (attrs.bold) { - PUT(hl, "bold", BOOLEAN_OBJ(true)); - } - - if (attrs.underline) { - PUT(hl, "underline", BOOLEAN_OBJ(true)); - } - - if (attrs.undercurl) { - PUT(hl, "undercurl", BOOLEAN_OBJ(true)); - } - - if (attrs.italic) { - PUT(hl, "italic", BOOLEAN_OBJ(true)); - } - - if (attrs.reverse) { - PUT(hl, "reverse", BOOLEAN_OBJ(true)); - } - - if (attrs.foreground != -1) { - PUT(hl, "foreground", INTEGER_OBJ(attrs.foreground)); - } + UIData *data = ui->data; - if (attrs.background != -1) { - PUT(hl, "background", INTEGER_OBJ(attrs.background)); - } - if (attrs.special != -1) { - PUT(hl, "special", INTEGER_OBJ(attrs.special)); + if (data->hl_id == id) { + return; } + data->hl_id = id; + Dictionary hl = hlattrs2dict(syn_attr2entry(id), ui->rgb); ADD(args, DICTIONARY_OBJ(hl)); push_call(ui, "highlight_set", args); } -static void remote_ui_put(UI *ui, uint8_t *data, size_t size) +/// "true" cursor used only for input focus +static void remote_ui_grid_cursor_goto(UI *ui, Integer grid, Integer row, + Integer col) { - Array args = ARRAY_DICT_INIT; - String str = { .data = xmemdupz(data, size), .size = size }; - ADD(args, STRING_OBJ(str)); - push_call(ui, "put", args); + if (ui->ui_ext[kUILinegrid]) { + Array args = ARRAY_DICT_INIT; + ADD(args, INTEGER_OBJ(grid)); + ADD(args, INTEGER_OBJ(row)); + ADD(args, INTEGER_OBJ(col)); + push_call(ui, "grid_cursor_goto", args); + } else { + UIData *data = ui->data; + data->cursor_row = row; + data->cursor_col = col; + remote_ui_cursor_goto(ui, row, col); + } } -static void remote_ui_bell(UI *ui) +/// emulated cursor used both for drawing and for input focus +static void remote_ui_cursor_goto(UI *ui, Integer row, Integer col) { + UIData *data = ui->data; + if (data->client_row == row && data->client_col == col) { + return; + } + data->client_row = row; + data->client_col = col; Array args = ARRAY_DICT_INIT; - push_call(ui, "bell", args); + ADD(args, INTEGER_OBJ(row)); + ADD(args, INTEGER_OBJ(col)); + push_call(ui, "cursor_goto", args); } -static void remote_ui_visual_bell(UI *ui) +static void remote_ui_put(UI *ui, const char *cell) { + UIData *data = ui->data; + data->client_col++; Array args = ARRAY_DICT_INIT; - push_call(ui, "visual_bell", args); + ADD(args, STRING_OBJ(cstr_to_string(cell))); + push_call(ui, "put", args); } -static void remote_ui_update_fg(UI *ui, int fg) +static void remote_ui_raw_line(UI *ui, Integer grid, Integer row, + Integer startcol, Integer endcol, + Integer clearcol, Integer clearattr, + Boolean wrap, const schar_T *chunk, + const sattr_T *attrs) { - Array args = ARRAY_DICT_INIT; - ADD(args, INTEGER_OBJ(fg)); - push_call(ui, "update_fg", args); + UIData *data = ui->data; + if (ui->ui_ext[kUILinegrid]) { + Array args = ARRAY_DICT_INIT; + ADD(args, INTEGER_OBJ(grid)); + ADD(args, INTEGER_OBJ(row)); + ADD(args, INTEGER_OBJ(startcol)); + Array cells = ARRAY_DICT_INIT; + int repeat = 0; + size_t ncells = (size_t)(endcol-startcol); + int last_hl = -1; + for (size_t i = 0; i < ncells; i++) { + repeat++; + if (i == ncells-1 || attrs[i] != attrs[i+1] + || STRCMP(chunk[i], chunk[i+1])) { + Array cell = ARRAY_DICT_INIT; + ADD(cell, STRING_OBJ(cstr_to_string((const char *)chunk[i]))); + if (attrs[i] != last_hl || repeat > 1) { + ADD(cell, INTEGER_OBJ(attrs[i])); + last_hl = attrs[i]; + } + if (repeat > 1) { + ADD(cell, INTEGER_OBJ(repeat)); + } + ADD(cells, ARRAY_OBJ(cell)); + repeat = 0; + } + } + if (endcol < clearcol) { + Array cell = ARRAY_DICT_INIT; + ADD(cell, STRING_OBJ(cstr_to_string(" "))); + ADD(cell, INTEGER_OBJ(clearattr)); + ADD(cell, INTEGER_OBJ(clearcol-endcol)); + ADD(cells, ARRAY_OBJ(cell)); + } + ADD(args, ARRAY_OBJ(cells)); + + push_call(ui, "grid_line", args); + } else { + for (int i = 0; i < endcol-startcol; i++) { + remote_ui_cursor_goto(ui, row, startcol+i); + remote_ui_highlight_set(ui, attrs[i]); + remote_ui_put(ui, (const char *)chunk[i]); + if (utf_ambiguous_width(utf_ptr2char(chunk[i]))) { + data->client_col = -1; // force cursor update + } + } + if (endcol < clearcol) { + remote_ui_cursor_goto(ui, row, endcol); + remote_ui_highlight_set(ui, (int)clearattr); + // legacy eol_clear was only ever used with cleared attributes + // so be on the safe side + if (clearattr == 0 && clearcol == Columns) { + Array args = ARRAY_DICT_INIT; + push_call(ui, "eol_clear", args); + } else { + for (Integer c = endcol; c < clearcol; c++) { + remote_ui_put(ui, " "); + } + } + } + } } -static void remote_ui_update_bg(UI *ui, int bg) +static void remote_ui_flush(UI *ui) { - Array args = ARRAY_DICT_INIT; - ADD(args, INTEGER_OBJ(bg)); - push_call(ui, "update_bg", args); + UIData *data = ui->data; + if (data->buffer.size > 0) { + if (!ui->ui_ext[kUILinegrid]) { + remote_ui_cursor_goto(ui, data->cursor_row, data->cursor_col); + } + push_call(ui, "flush", (Array)ARRAY_DICT_INIT); + rpc_send_event(data->channel_id, "redraw", data->buffer); + data->buffer = (Array)ARRAY_DICT_INIT; + } } -static void remote_ui_update_sp(UI *ui, int sp) +static Array translate_contents(UI *ui, Array contents) { - Array args = ARRAY_DICT_INIT; - ADD(args, INTEGER_OBJ(sp)); - push_call(ui, "update_sp", args); + Array new_contents = ARRAY_DICT_INIT; + for (size_t i = 0; i < contents.size; i++) { + Array item = contents.items[i].data.array; + 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); + ADD(new_item, DICTIONARY_OBJ(rgb_attrs)); + } else { + ADD(new_item, DICTIONARY_OBJ((Dictionary)ARRAY_DICT_INIT)); + } + ADD(new_item, copy_object(item.items[1])); + ADD(new_contents, ARRAY_OBJ(new_item)); + } + return new_contents; } -static void remote_ui_flush(UI *ui) +static Array translate_firstarg(UI *ui, Array args) { - UIData *data = ui->data; - channel_send_event(data->channel_id, "redraw", data->buffer); - data->buffer = (Array)ARRAY_DICT_INIT; -} + Array new_args = ARRAY_DICT_INIT; + Array contents = args.items[0].data.array; -static void remote_ui_suspend(UI *ui) -{ - Array args = ARRAY_DICT_INIT; - push_call(ui, "suspend", args); + ADD(new_args, ARRAY_OBJ(translate_contents(ui, contents))); + for (size_t i = 1; i < args.size; i++) { + ADD(new_args, copy_object(args.items[i])); + } + return new_args; } -static void remote_ui_set_title(UI *ui, char *title) +static void remote_ui_event(UI *ui, char *name, Array args, bool *args_consumed) { - Array args = ARRAY_DICT_INIT; - ADD(args, STRING_OBJ(cstr_to_string(title))); - push_call(ui, "set_title", args); + if (!ui->ui_ext[kUILinegrid]) { + // the representation of highlights in cmdline changed, translate back + // never consumes args + if (strequal(name, "cmdline_show")) { + Array new_args = translate_firstarg(ui, args); + push_call(ui, name, new_args); + return; + } else if (strequal(name, "cmdline_block_show")) { + Array new_args = ARRAY_DICT_INIT; + Array block = args.items[0].data.array; + Array new_block = ARRAY_DICT_INIT; + for (size_t i = 0; i < block.size; i++) { + ADD(new_block, + ARRAY_OBJ(translate_contents(ui, block.items[i].data.array))); + } + ADD(new_args, ARRAY_OBJ(new_block)); + push_call(ui, name, new_args); + return; + } else if (strequal(name, "cmdline_block_append")) { + Array new_args = translate_firstarg(ui, args); + push_call(ui, name, new_args); + return; + } + } + + Array my_args = ARRAY_DICT_INIT; + // Objects are currently single-reference + // make a copy, but only if necessary + if (*args_consumed) { + for (size_t i = 0; i < args.size; i++) { + ADD(my_args, copy_object(args.items[i])); + } + } else { + my_args = args; + *args_consumed = true; + } + push_call(ui, name, my_args); } -static void remote_ui_set_icon(UI *ui, char *icon) +static void remote_ui_inspect(UI *ui, Dictionary *info) { - Array args = ARRAY_DICT_INIT; - ADD(args, STRING_OBJ(cstr_to_string(icon))); - push_call(ui, "set_icon", args); + UIData *data = ui->data; + PUT(*info, "chan", INTEGER_OBJ((Integer)data->channel_id)); } |