mirror of
https://github.com/neovim/neovim.git
synced 2024-12-29 14:41:06 -07:00
Merge pull request #17708 from bfredl/ui_client
feat(ui): UI client episode II: event handlers
This commit is contained in:
commit
4e5e0076cb
4
src/nvim/CMakeLists.txt
Normal file → Executable file
4
src/nvim/CMakeLists.txt
Normal file → Executable file
@ -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_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)
|
||||
@ -271,6 +272,7 @@ foreach(sfile ${NVIM_SOURCES}
|
||||
"${GENERATED_UI_EVENTS_REMOTE}"
|
||||
"${GENERATED_UI_EVENTS_BRIDGE}"
|
||||
"${GENERATED_KEYSETS}"
|
||||
"${GENERATED_UI_EVENTS_CLIENT}"
|
||||
)
|
||||
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_CLIENT}
|
||||
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,7 @@ add_custom_command(
|
||||
${GENERATED_UI_EVENTS_REMOTE}
|
||||
${GENERATED_UI_EVENTS_BRIDGE}
|
||||
${GENERATED_UI_EVENTS_METADATA}
|
||||
${GENERATED_UI_EVENTS_CLIENT}
|
||||
DEPENDS
|
||||
${API_UI_EVENTS_GENERATOR}
|
||||
${GENERATOR_C_GRAMMAR}
|
||||
|
@ -78,13 +78,13 @@ 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)
|
||||
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;
|
||||
|
@ -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(';')
|
||||
)
|
||||
|
||||
|
74
src/nvim/generators/gen_api_ui_events.lua
Normal file → Executable file
74
src/nvim/generators/gen_api_ui_events.lua
Normal file → Executable file
@ -3,13 +3,14 @@ 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 client_output = io.open(arg[8], 'wb')
|
||||
|
||||
local c_grammar = require('generators.c_grammar')
|
||||
local events = c_grammar.grammar:match(input:read('*all'))
|
||||
@ -50,6 +51,52 @@ local function write_arglist(output, ev, need_copy)
|
||||
end
|
||||
end
|
||||
|
||||
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]
|
||||
output:write(' '..kind..' arg_'..j..' = ')
|
||||
if kind == 'HlAttrs' then
|
||||
-- The first HlAttrs argument is rgb_attrs and second is cterm_attrs
|
||||
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')
|
||||
else
|
||||
output:write('args.items['..(j-1)..'].data.'..string.lower(kind)..';\n')
|
||||
end
|
||||
end
|
||||
|
||||
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')
|
||||
|
||||
output:write('}\n\n')
|
||||
end
|
||||
|
||||
for i = 1, #events do
|
||||
local ev = events[i]
|
||||
assert(ev.return_type == 'void')
|
||||
@ -160,12 +207,35 @@ 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
|
||||
call_ui_event_method(client_output, ev)
|
||||
end
|
||||
end
|
||||
|
||||
-- Generate the map_init method for client handlers
|
||||
client_output:write([[
|
||||
void ui_client_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
|
||||
client_output:write(' add_ui_client_event_handler('..
|
||||
'(String) {.data = "'..fn.name..'", '..
|
||||
'.size = sizeof("'..fn.name..'") - 1}, '..
|
||||
'(UIClientHandler) ui_client_event_'..fn.name..');\n')
|
||||
end
|
||||
end
|
||||
|
||||
client_output:write('\n}\n\n')
|
||||
|
||||
proto_output:close()
|
||||
call_output:close()
|
||||
remote_output:close()
|
||||
bridge_output:close()
|
||||
client_output:close()
|
||||
|
||||
-- don't expose internal attributes like "impl_name" in public metadata
|
||||
local exported_attributes = {'name', 'parameters',
|
||||
|
@ -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;
|
||||
|
@ -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, UIClientHandler, NULL)
|
||||
|
||||
MAP_IMPL(ColorKey, ColorItem, COLOR_ITEM_INITIALIZER)
|
||||
|
||||
|
@ -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, UIClientHandler)
|
||||
|
||||
MAP_DECLS(ColorKey, ColorItem)
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -6,40 +6,55 @@
|
||||
#include <assert.h>
|
||||
|
||||
#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, 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);
|
||||
}
|
||||
|
||||
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!
|
||||
// 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
|
||||
@ -50,10 +65,21 @@ 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;
|
||||
|
||||
fprintf(stderr, "%s: %zu\n", method_name, call.size-1);
|
||||
UIClientHandler handler = map_get(String, UIClientHandler)(&ui_client_handlers, name);
|
||||
if (!handler) {
|
||||
ELOG("No ui client handler for %s", name.size ? name.data : "<empty>");
|
||||
continue;
|
||||
}
|
||||
|
||||
// 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(call.items[j].data.array);
|
||||
}
|
||||
}
|
||||
|
||||
return NIL;
|
||||
}
|
||||
|
||||
@ -68,3 +94,121 @@ void ui_client_execute(uint64_t chan)
|
||||
|
||||
getout(0);
|
||||
}
|
||||
|
||||
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, d, &err)) {
|
||||
// TODO(bfredl): log "err"
|
||||
return HLATTRS_INIT;
|
||||
}
|
||||
return dict2hlattrs(&dict, true, NULL, &err);
|
||||
}
|
||||
|
||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||
#include "ui_events_client.generated.h"
|
||||
#endif
|
||||
|
||||
void ui_client_event_grid_resize(Array args)
|
||||
{
|
||||
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;
|
||||
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;
|
||||
// TODO(hlpr98): Accomodate other LineFlags when included in grid_line
|
||||
LineFlags lineflags = 0;
|
||||
endcol = startcol;
|
||||
|
||||
size_t j = 0;
|
||||
int cur_attr = 0;
|
||||
int clear_attr = 0;
|
||||
int clear_width = 0;
|
||||
for (size_t i = 0; i < cells.size; i++) {
|
||||
if (cells.items[i].type != kObjectTypeArray) {
|
||||
goto error;
|
||||
}
|
||||
Array cell = cells.items[i].data.array;
|
||||
|
||||
if (cell.size < 1 || cell.items[0].type != kObjectTypeString) {
|
||||
goto error;
|
||||
}
|
||||
String sstring = cell.items[0].data.string;
|
||||
|
||||
char *schar = sstring.data;
|
||||
int repeat = 1;
|
||||
if (cell.size >= 2) {
|
||||
if (cell.items[1].type != kObjectTypeInteger
|
||||
|| cell.items[1].data.integer < 0) {
|
||||
goto error;
|
||||
}
|
||||
cur_attr = (int)cell.items[1].data.integer;
|
||||
}
|
||||
|
||||
if (cell.size >= 3) {
|
||||
if (cell.items[2].type != kObjectTypeInteger
|
||||
|| cell.items[2].data.integer < 0) {
|
||||
goto error;
|
||||
}
|
||||
repeat = (int)cell.items[2].data.integer;
|
||||
}
|
||||
|
||||
if (i == cells.size - 1 && sstring.size == 1 && sstring.data[0] == ' ' && repeat > 1) {
|
||||
clear_width = repeat;
|
||||
break;
|
||||
}
|
||||
|
||||
for (int r = 0; r < repeat; r++) {
|
||||
if (j >= buf_size) {
|
||||
goto error; // _YIKES_
|
||||
}
|
||||
STRLCPY(buf_char[j], schar, sizeof(schar_T));
|
||||
buf_attr[j++] = cur_attr;
|
||||
}
|
||||
}
|
||||
|
||||
endcol = startcol + (int)j;
|
||||
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'");
|
||||
}
|
||||
|
@ -3,7 +3,11 @@
|
||||
|
||||
#include "nvim/api/private/defs.h"
|
||||
|
||||
typedef void (*UIClientHandler)(Array args);
|
||||
|
||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||
#include "ui_client.h.generated.h"
|
||||
#include "ui_events_client.h.generated.h"
|
||||
#endif
|
||||
|
||||
#endif // NVIM_UI_CLIENT_H
|
||||
|
Loading…
Reference in New Issue
Block a user