mirror of
https://github.com/neovim/neovim.git
synced 2025-09-07 03:48:18 +00:00
Merge pull request #19409 from bfredl/uiunpack
perf(ui): some ui_client fixes/optimizations before externalized TUI
This commit is contained in:
@@ -949,7 +949,8 @@ def fmt_doxygen_xml_as_vimhelp(filename, target):
|
|||||||
|
|
||||||
func_doc = "\n".join(split_lines)
|
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
|
fns_txt[name] = func_doc
|
||||||
|
|
||||||
return ('\n\n'.join(list(fns_txt.values())),
|
return ('\n\n'.join(list(fns_txt.values())),
|
||||||
|
@@ -36,6 +36,7 @@ typedef enum {
|
|||||||
kMessageTypeRequest = 0,
|
kMessageTypeRequest = 0,
|
||||||
kMessageTypeResponse = 1,
|
kMessageTypeResponse = 1,
|
||||||
kMessageTypeNotification = 2,
|
kMessageTypeNotification = 2,
|
||||||
|
kMessageTypeRedrawEvent = 3,
|
||||||
} MessageType;
|
} MessageType;
|
||||||
|
|
||||||
/// Mask for all internal calls
|
/// Mask for all internal calls
|
||||||
|
@@ -86,6 +86,12 @@
|
|||||||
name.capacity = maxsize; \
|
name.capacity = maxsize; \
|
||||||
name.items = name##__items; \
|
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 cbuf_as_string(d, s) ((String) { .data = d, .size = s })
|
||||||
|
|
||||||
#define STATIC_CSTR_AS_STRING(s) ((String) { .data = s, .size = sizeof(s) - 1 })
|
#define STATIC_CSTR_AS_STRING(s) ((String) { .data = s, .size = sizeof(s) - 1 })
|
||||||
|
@@ -748,8 +748,10 @@ static void remote_ui_hl_attr_define(UI *ui, Integer id, HlAttrs rgb_attrs, HlAt
|
|||||||
UIData *data = ui->data;
|
UIData *data = ui->data;
|
||||||
Array args = data->call_buf;
|
Array args = data->call_buf;
|
||||||
ADD_C(args, INTEGER_OBJ(id));
|
ADD_C(args, INTEGER_OBJ(id));
|
||||||
ADD_C(args, DICTIONARY_OBJ(hlattrs2dict(rgb_attrs, true)));
|
MAXSIZE_TEMP_DICT(rgb, 16);
|
||||||
ADD_C(args, DICTIONARY_OBJ(hlattrs2dict(cterm_attrs, false)));
|
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]) {
|
if (ui->ui_ext[kUIHlState]) {
|
||||||
ADD_C(args, ARRAY_OBJ(info));
|
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);
|
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)
|
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;
|
return;
|
||||||
}
|
}
|
||||||
data->hl_id = id;
|
data->hl_id = id;
|
||||||
Dictionary hl = hlattrs2dict(syn_attr2entry(id), ui->rgb);
|
MAXSIZE_TEMP_DICT(dict, 16);
|
||||||
|
ADD_C(args, DICTIONARY_OBJ(hlattrs2dict(&dict, syn_attr2entry(id), ui->rgb)));
|
||||||
ADD_C(args, DICTIONARY_OBJ(hl));
|
|
||||||
push_call(ui, "highlight_set", args);
|
push_call(ui, "highlight_set", args);
|
||||||
api_free_dictionary(kv_A(args, 0).data.dictionary);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// "true" cursor used only for input focus
|
/// "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;
|
Array new_item = ARRAY_DICT_INIT;
|
||||||
int attr = (int)item.items[0].data.integer;
|
int attr = (int)item.items[0].data.integer;
|
||||||
if (attr) {
|
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));
|
ADD(new_item, DICTIONARY_OBJ(rgb_attrs));
|
||||||
} else {
|
} else {
|
||||||
ADD(new_item, DICTIONARY_OBJ((Dictionary)ARRAY_DICT_INIT));
|
ADD(new_item, DICTIONARY_OBJ((Dictionary)ARRAY_DICT_INIT));
|
||||||
|
@@ -2256,3 +2256,11 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error *
|
|||||||
|
|
||||||
return result;
|
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 : "");
|
||||||
|
}
|
||||||
|
@@ -91,7 +91,7 @@ local deprecated_aliases = require("api.dispatch_deprecated")
|
|||||||
for _,f in ipairs(shallowcopy(functions)) do
|
for _,f in ipairs(shallowcopy(functions)) do
|
||||||
local ismethod = false
|
local ismethod = false
|
||||||
if startswith(f.name, "nvim_") then
|
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
|
f.since = -1
|
||||||
elseif f.since == nil then
|
elseif f.since == nil then
|
||||||
print("Function "..f.name.." lacks since field.\n")
|
print("Function "..f.name.." lacks since field.\n")
|
||||||
@@ -149,7 +149,7 @@ local exported_attributes = {'name', 'return_type', 'method',
|
|||||||
'since', 'deprecated_since'}
|
'since', 'deprecated_since'}
|
||||||
local exported_functions = {}
|
local exported_functions = {}
|
||||||
for _,f in ipairs(functions) do
|
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 = {}
|
local f_exported = {}
|
||||||
for _,attr in ipairs(exported_attributes) do
|
for _,attr in ipairs(exported_attributes) do
|
||||||
f_exported[attr] = f[attr]
|
f_exported[attr] = f[attr]
|
||||||
|
@@ -128,4 +128,13 @@ typedef struct {
|
|||||||
const char *start; ///< Location where region starts.
|
const char *start; ///< Location where region starts.
|
||||||
} StlClickRecord;
|
} StlClickRecord;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int args[3];
|
||||||
|
int icell;
|
||||||
|
int ncells;
|
||||||
|
int coloff;
|
||||||
|
int cur_attr;
|
||||||
|
int clear_width;
|
||||||
|
} GridLineEvent;
|
||||||
|
|
||||||
#endif // NVIM_GRID_DEFS_H
|
#endif // NVIM_GRID_DEFS_H
|
||||||
|
@@ -719,93 +719,107 @@ Dictionary hl_get_attr_by_id(Integer attr_id, Boolean rgb, Error *err)
|
|||||||
return dic;
|
return dic;
|
||||||
}
|
}
|
||||||
|
|
||||||
return hlattrs2dict(syn_attr2entry((int)attr_id), rgb);
|
return hlattrs2dict(NULL, syn_attr2entry((int)attr_id), rgb);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Converts an HlAttrs into Dictionary
|
/// 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[in] aep data to convert
|
||||||
/// @param use_rgb use 'gui*' settings if true, else resorts to 'cterm*'
|
/// @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;
|
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) {
|
if (mask & HL_BOLD) {
|
||||||
PUT(hl, "bold", BOOLEAN_OBJ(true));
|
PUT_C(hl, "bold", BOOLEAN_OBJ(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mask & HL_STANDOUT) {
|
if (mask & HL_STANDOUT) {
|
||||||
PUT(hl, "standout", BOOLEAN_OBJ(true));
|
PUT_C(hl, "standout", BOOLEAN_OBJ(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mask & HL_UNDERLINE) {
|
if (mask & HL_UNDERLINE) {
|
||||||
PUT(hl, "underline", BOOLEAN_OBJ(true));
|
PUT_C(hl, "underline", BOOLEAN_OBJ(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mask & HL_UNDERCURL) {
|
if (mask & HL_UNDERCURL) {
|
||||||
PUT(hl, "undercurl", BOOLEAN_OBJ(true));
|
PUT_C(hl, "undercurl", BOOLEAN_OBJ(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mask & HL_UNDERDOUBLE) {
|
if (mask & HL_UNDERDOUBLE) {
|
||||||
PUT(hl, "underdouble", BOOLEAN_OBJ(true));
|
PUT_C(hl, "underdouble", BOOLEAN_OBJ(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mask & HL_UNDERDOTTED) {
|
if (mask & HL_UNDERDOTTED) {
|
||||||
PUT(hl, "underdotted", BOOLEAN_OBJ(true));
|
PUT_C(hl, "underdotted", BOOLEAN_OBJ(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mask & HL_UNDERDASHED) {
|
if (mask & HL_UNDERDASHED) {
|
||||||
PUT(hl, "underdashed", BOOLEAN_OBJ(true));
|
PUT_C(hl, "underdashed", BOOLEAN_OBJ(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mask & HL_ITALIC) {
|
if (mask & HL_ITALIC) {
|
||||||
PUT(hl, "italic", BOOLEAN_OBJ(true));
|
PUT_C(hl, "italic", BOOLEAN_OBJ(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mask & HL_INVERSE) {
|
if (mask & HL_INVERSE) {
|
||||||
PUT(hl, "reverse", BOOLEAN_OBJ(true));
|
PUT_C(hl, "reverse", BOOLEAN_OBJ(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mask & HL_STRIKETHROUGH) {
|
if (mask & HL_STRIKETHROUGH) {
|
||||||
PUT(hl, "strikethrough", BOOLEAN_OBJ(true));
|
PUT_C(hl, "strikethrough", BOOLEAN_OBJ(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (use_rgb) {
|
if (use_rgb) {
|
||||||
if (mask & HL_FG_INDEXED) {
|
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) {
|
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) {
|
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) {
|
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) {
|
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 {
|
} else {
|
||||||
if (ae.cterm_fg_color != 0) {
|
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) {
|
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) {
|
if (ae.hl_blend > -1) {
|
||||||
PUT(hl, "blend", INTEGER_OBJ(ae.hl_blend));
|
PUT_C(hl, "blend", INTEGER_OBJ(ae.hl_blend));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (hl_alloc) {
|
||||||
|
*hl_alloc = hl;
|
||||||
return 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)
|
HlAttrs dict2hlattrs(Dict(highlight) *dict, bool use_rgb, int *link_id, Error *err)
|
||||||
|
@@ -1409,7 +1409,7 @@ Dictionary get_global_hl_defs(void)
|
|||||||
Dictionary attrs = ARRAY_DICT_INIT;
|
Dictionary attrs = ARRAY_DICT_INIT;
|
||||||
HlGroup *h = &hl_table[i - 1];
|
HlGroup *h = &hl_table[i - 1];
|
||||||
if (h->sg_attr > 0) {
|
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) {
|
} else if (h->sg_link > 0) {
|
||||||
const char *link = (const char *)hl_table[h->sg_link - 1].sg_name;
|
const char *link = (const char *)hl_table[h->sg_link - 1].sg_name;
|
||||||
PUT(attrs, "link", STRING_OBJ(cstr_to_string(link)));
|
PUT(attrs, "link", STRING_OBJ(cstr_to_string(link)));
|
||||||
|
@@ -202,7 +202,6 @@ void early_init(mparm_T *paramp)
|
|||||||
set_lang_var(); // set v:lang and v:ctype
|
set_lang_var(); // set v:lang and v:ctype
|
||||||
|
|
||||||
init_signs();
|
init_signs();
|
||||||
ui_comp_syn_init();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef MAKE_LIB
|
#ifdef MAKE_LIB
|
||||||
@@ -320,6 +319,7 @@ int main(int argc, char **argv)
|
|||||||
no_wait_return = true;
|
no_wait_return = true;
|
||||||
|
|
||||||
init_highlight(true, false); // Default highlight groups.
|
init_highlight(true, false); // Default highlight groups.
|
||||||
|
ui_comp_syn_init();
|
||||||
TIME_MSG("init highlight");
|
TIME_MSG("init highlight");
|
||||||
|
|
||||||
// Set the break level after the terminal is initialized.
|
// Set the break level after the terminal is initialized.
|
||||||
|
@@ -239,7 +239,14 @@ static void parse_msgpack(Channel *channel)
|
|||||||
{
|
{
|
||||||
Unpacker *p = channel->rpc.unpacker;
|
Unpacker *p = channel->rpc.unpacker;
|
||||||
while (unpacker_advance(p)) {
|
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);
|
ChannelCallFrame *frame = kv_last(channel->rpc.call_stack);
|
||||||
if (p->request_id != frame->request_id) {
|
if (p->request_id != frame->request_id) {
|
||||||
char buf[256];
|
char buf[256];
|
||||||
|
@@ -6,6 +6,7 @@
|
|||||||
#include "nvim/memory.h"
|
#include "nvim/memory.h"
|
||||||
#include "nvim/msgpack_rpc/helpers.h"
|
#include "nvim/msgpack_rpc/helpers.h"
|
||||||
#include "nvim/msgpack_rpc/unpacker.h"
|
#include "nvim/msgpack_rpc/unpacker.h"
|
||||||
|
#include "nvim/ui_client.h"
|
||||||
|
|
||||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||||
# include "msgpack_rpc/unpacker.c.generated.h"
|
# 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
|
// 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.
|
// 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
|
// 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
|
// objects. For the moment "redraw/grid_line" uses a hand-rolled decoder,
|
||||||
// a blizzard of small objects for each screen cell.
|
// 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)
|
bool unpacker_advance(Unpacker *p)
|
||||||
{
|
{
|
||||||
@@ -292,9 +303,28 @@ bool unpacker_advance(Unpacker *p)
|
|||||||
if (!unpacker_parse_header(p)) {
|
if (!unpacker_parse_header(p)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
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;
|
p->state = p->type == kMessageTypeResponse ? 1 : 2;
|
||||||
arena_start(&p->arena, &p->reuse_blk);
|
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;
|
int result;
|
||||||
|
|
||||||
@@ -310,13 +340,173 @@ rerun:
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (p->state == 1) {
|
done:
|
||||||
|
switch (p->state) {
|
||||||
|
case 1:
|
||||||
p->error = p->result;
|
p->error = p->result;
|
||||||
p->state = 2;
|
p->state = 2;
|
||||||
goto rerun;
|
goto rerun;
|
||||||
|
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 {
|
} else {
|
||||||
assert(p->state == 2);
|
|
||||||
p->state = 0;
|
p->state = 0;
|
||||||
}
|
}
|
||||||
return true;
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -32,8 +32,13 @@ struct Unpacker {
|
|||||||
Error unpack_error;
|
Error unpack_error;
|
||||||
|
|
||||||
Arena arena;
|
Arena arena;
|
||||||
// one lenght free-list of reusable blocks
|
// one length free-list of reusable blocks
|
||||||
ArenaMem reuse_blk;
|
ArenaMem reuse_blk;
|
||||||
|
|
||||||
|
int nevents;
|
||||||
|
int ncalls;
|
||||||
|
UIClientHandler ui_handler;
|
||||||
|
GridLineEvent *grid_line_event;
|
||||||
};
|
};
|
||||||
|
|
||||||
// unrecovareble error. unpack_error should be set!
|
// unrecovareble error. unpack_error should be set!
|
||||||
|
@@ -348,7 +348,8 @@ void ui_attach_impl(UI *ui, uint64_t chanid)
|
|||||||
if (ui_count == MAX_UI_COUNT) {
|
if (ui_count == MAX_UI_COUNT) {
|
||||||
abort();
|
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);
|
ui_comp_attach(ui);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -502,6 +503,9 @@ handle_T ui_cursor_grid(void)
|
|||||||
|
|
||||||
void ui_flush(void)
|
void ui_flush(void)
|
||||||
{
|
{
|
||||||
|
if (!ui_active()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
cmdline_ui_flush();
|
cmdline_ui_flush();
|
||||||
win_ui_flush();
|
win_ui_flush();
|
||||||
msg_ext_ui_flush();
|
msg_ext_ui_flush();
|
||||||
|
@@ -22,11 +22,6 @@
|
|||||||
# include "ui_events_client.generated.h"
|
# include "ui_events_client.generated.h"
|
||||||
#endif
|
#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)
|
void ui_client_init(uint64_t chan)
|
||||||
{
|
{
|
||||||
Array args = ARRAY_DICT_INIT;
|
Array args = ARRAY_DICT_INIT;
|
||||||
@@ -46,37 +41,23 @@ void ui_client_init(uint64_t chan)
|
|||||||
ui_client_channel_id = chan;
|
ui_client_channel_id = chan;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handler for "redraw" events sent by the NVIM server
|
UIClientHandler ui_client_get_redraw_handler(const char *name, size_t name_len, Error *error)
|
||||||
|
{
|
||||||
|
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
|
||||||
///
|
///
|
||||||
/// This function will be called by handle_request (in msgpack_rpc/channel.c)
|
/// async 'redraw' events, which are expected when nvim acts as an ui client.
|
||||||
/// The individual ui_events sent by the server are individually handled
|
/// get handled in msgpack_rpc/unpacker.c and directy dispatched to handlers
|
||||||
/// by their respective handlers defined in ui_events_client.generated.h
|
/// of specific ui events, like ui_client_event_grid_resize and so on.
|
||||||
///
|
|
||||||
/// @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)
|
Object handle_ui_client_redraw(uint64_t channel_id, Array args, Error *error)
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < args.size; i++) {
|
api_set_error(error, kErrorTypeValidation, "'redraw' cannot be sent as a request");
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return NIL;
|
return NIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,88 +101,30 @@ void ui_client_event_grid_resize(Array args)
|
|||||||
Integer height = args.items[2].data.integer;
|
Integer height = args.items[2].data.integer;
|
||||||
ui_call_grid_resize(grid, width, height);
|
ui_call_grid_resize(grid, width, height);
|
||||||
|
|
||||||
if (buf_size < (size_t)width) {
|
if (grid_line_buf_size < (size_t)width) {
|
||||||
xfree(buf_char);
|
xfree(grid_line_buf_char);
|
||||||
xfree(buf_attr);
|
xfree(grid_line_buf_attr);
|
||||||
buf_size = (size_t)width;
|
grid_line_buf_size = (size_t)width;
|
||||||
buf_char = xmalloc(buf_size * sizeof(schar_T));
|
grid_line_buf_char = xmalloc(grid_line_buf_size * sizeof(schar_T));
|
||||||
buf_attr = xmalloc(buf_size * sizeof(sattr_T));
|
grid_line_buf_attr = xmalloc(grid_line_buf_size * sizeof(sattr_T));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ui_client_event_grid_line(Array args)
|
void ui_client_event_grid_line(Array args)
|
||||||
|
FUNC_ATTR_NORETURN
|
||||||
{
|
{
|
||||||
if (args.size < 4
|
abort(); // unreachable
|
||||||
|| 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;
|
void ui_client_event_raw_line(GridLineEvent *g)
|
||||||
Integer row = args.items[1].data.integer;
|
{
|
||||||
Integer startcol = args.items[2].data.integer;
|
int grid = g->args[0], row = g->args[1], startcol = g->args[2];
|
||||||
Array cells = args.items[3].data.array;
|
Integer endcol = startcol + g->coloff;
|
||||||
|
Integer clearcol = endcol + g->clear_width;
|
||||||
|
|
||||||
// TODO(hlpr98): Accommodate other LineFlags when included in grid_line
|
// TODO(hlpr98): Accommodate other LineFlags when included in grid_line
|
||||||
LineFlags lineflags = 0;
|
LineFlags lineflags = 0;
|
||||||
|
|
||||||
size_t j = 0;
|
ui_call_raw_line(grid, row, startcol, endcol, clearcol, g->cur_attr, lineflags,
|
||||||
int cur_attr = 0;
|
(const schar_T *)grid_line_buf_char, grid_line_buf_attr);
|
||||||
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'");
|
|
||||||
}
|
}
|
||||||
|
@@ -2,12 +2,18 @@
|
|||||||
#define NVIM_UI_CLIENT_H
|
#define NVIM_UI_CLIENT_H
|
||||||
|
|
||||||
#include "nvim/api/private/defs.h"
|
#include "nvim/api/private/defs.h"
|
||||||
|
#include "nvim/grid_defs.h"
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
const char *name;
|
const char *name;
|
||||||
void (*fn)(Array args);
|
void (*fn)(Array args);
|
||||||
} UIClientHandler;
|
} 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
|
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||||
# include "ui_client.h.generated.h"
|
# include "ui_client.h.generated.h"
|
||||||
|
|
||||||
|
@@ -879,9 +879,13 @@ describe('put command', function()
|
|||||||
ine of words 2]], curbuf_contents())
|
ine of words 2]], curbuf_contents())
|
||||||
end)
|
end)
|
||||||
|
|
||||||
local function bell_test(actions, should_ring)
|
local screen
|
||||||
local screen = Screen.new()
|
setup(function()
|
||||||
|
screen = Screen.new()
|
||||||
screen:attach()
|
screen:attach()
|
||||||
|
end)
|
||||||
|
|
||||||
|
local function bell_test(actions, should_ring)
|
||||||
if should_ring then
|
if should_ring then
|
||||||
-- check bell is not set by nvim before the action
|
-- check bell is not set by nvim before the action
|
||||||
screen:sleep(50)
|
screen:sleep(50)
|
||||||
@@ -899,7 +903,6 @@ describe('put command', function()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end, unchanged=(not should_ring)}
|
end, unchanged=(not should_ring)}
|
||||||
screen:detach()
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it('should not ring the bell with gp at end of line', function()
|
it('should not ring the bell with gp at end of line', function()
|
||||||
|
@@ -215,7 +215,7 @@ describe('ui/cursor', function()
|
|||||||
m.hl_id = 60
|
m.hl_id = 60
|
||||||
m.attr = {background = Screen.colors.DarkGray}
|
m.attr = {background = Screen.colors.DarkGray}
|
||||||
end
|
end
|
||||||
if m.id_lm then m.id_lm = 65 end
|
if m.id_lm then m.id_lm = 61 end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Assert the new expectation.
|
-- Assert the new expectation.
|
||||||
|
@@ -51,7 +51,7 @@ describe('UI receives option updates', function()
|
|||||||
end)
|
end)
|
||||||
|
|
||||||
it('on attach #11372', function()
|
it('on attach #11372', function()
|
||||||
clear()
|
clear{args_rm={'--headless'}}
|
||||||
local evs = {}
|
local evs = {}
|
||||||
screen = Screen.new(20,5)
|
screen = Screen.new(20,5)
|
||||||
-- Override mouse_on/mouse_off handlers.
|
-- Override mouse_on/mouse_off handlers.
|
||||||
@@ -88,6 +88,13 @@ describe('UI receives option updates', function()
|
|||||||
eq(expected, screen.options)
|
eq(expected, screen.options)
|
||||||
end)
|
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")
|
command("set pumblend=-1")
|
||||||
expected.pumblend = 0
|
expected.pumblend = 0
|
||||||
screen:expect(function()
|
screen:expect(function()
|
||||||
|
Reference in New Issue
Block a user