mirror of
https://github.com/neovim/neovim.git
synced 2026-01-28 15:46:05 +00:00
Note for external UIs: Nvim can now emit multiple "redraw" event batches before a final "flush" event is received. To retain existing behavior, clients should make sure to update visible state at an explicit "flush" event, not just the end of a "redraw" batch of event. * Get rid of copy_object() blizzard in the auto-generated ui_event layer * Special case "grid_line" by encoding screen state directly to msgpack events with no intermediate API events. * Get rid of the arcane notion of referring to the screen as the "shell" * Array and Dictionary are kvec_t:s, so define them as such. * Allow kvec_t:s, such as Arrays and Dictionaries, to be allocated with a predetermined size within an arena. * Eliminate redundant capacity checking when filling such kvec_t:s with values.
262 lines
8.9 KiB
Lua
Executable File
262 lines
8.9 KiB
Lua
Executable File
local mpack = require('mpack')
|
|
|
|
local nvimdir = arg[1]
|
|
package.path = nvimdir .. '/?.lua;' .. package.path
|
|
|
|
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'))
|
|
|
|
_G.vim = loadfile(nvimdir..'/../../runtime/lua/vim/shared.lua')()
|
|
local hashy = require'generators.hashy'
|
|
|
|
local function write_signature(output, ev, prefix, notype)
|
|
output:write('('..prefix)
|
|
if prefix == "" and #ev.parameters == 0 then
|
|
output:write('void')
|
|
end
|
|
for j = 1, #ev.parameters do
|
|
if j > 1 or prefix ~= '' then
|
|
output:write(', ')
|
|
end
|
|
local param = ev.parameters[j]
|
|
if not notype then
|
|
output:write(param[1]..' ')
|
|
end
|
|
output:write(param[2])
|
|
end
|
|
output:write(')')
|
|
end
|
|
|
|
local function write_arglist(output, ev)
|
|
for j = 1, #ev.parameters do
|
|
local param = ev.parameters[j]
|
|
local kind = string.upper(param[1])
|
|
output:write(' ADD_C(args, ')
|
|
output:write(kind..'_OBJ('..param[2]..')')
|
|
output:write(');\n')
|
|
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')
|
|
|
|
if ev.since == nil and not ev.noexport then
|
|
print("Ui event "..ev.name.." lacks since field.\n")
|
|
os.exit(1)
|
|
end
|
|
ev.since = tonumber(ev.since)
|
|
|
|
if not ev.remote_only then
|
|
proto_output:write(' void (*'..ev.name..')')
|
|
write_signature(proto_output, ev, 'UI *ui')
|
|
proto_output:write(';\n')
|
|
|
|
if not ev.remote_impl and not ev.noexport then
|
|
remote_output:write('static void remote_ui_'..ev.name)
|
|
write_signature(remote_output, ev, 'UI *ui')
|
|
remote_output:write('\n{\n')
|
|
remote_output:write(' UIData *data = ui->data;\n')
|
|
remote_output:write(' Array args = data->call_buf;\n')
|
|
write_arglist(remote_output, ev)
|
|
remote_output:write(' push_call(ui, "'..ev.name..'", args);\n')
|
|
remote_output:write('}\n\n')
|
|
end
|
|
|
|
if not ev.bridge_impl and not ev.noexport then
|
|
local send, argv, recv, recv_argv, recv_cleanup = '', '', '', '', ''
|
|
local argc = 1
|
|
for j = 1, #ev.parameters do
|
|
local param = ev.parameters[j]
|
|
local copy = 'copy_'..param[2]
|
|
if param[1] == 'String' then
|
|
send = send..' String copy_'..param[2]..' = copy_string('..param[2]..');\n'
|
|
argv = argv..', '..copy..'.data, INT2PTR('..copy..'.size)'
|
|
recv = (recv..' String '..param[2]..
|
|
' = (String){.data = argv['..argc..'],'..
|
|
'.size = (size_t)argv['..(argc+1)..']};\n')
|
|
recv_argv = recv_argv..', '..param[2]
|
|
recv_cleanup = recv_cleanup..' api_free_string('..param[2]..');\n'
|
|
argc = argc+2
|
|
elseif param[1] == 'Array' then
|
|
send = send..' Array '..copy..' = copy_array('..param[2]..');\n'
|
|
argv = argv..', '..copy..'.items, INT2PTR('..copy..'.size)'
|
|
recv = (recv..' Array '..param[2]..
|
|
' = (Array){.items = argv['..argc..'],'..
|
|
'.size = (size_t)argv['..(argc+1)..']};\n')
|
|
recv_argv = recv_argv..', '..param[2]
|
|
recv_cleanup = recv_cleanup..' api_free_array('..param[2]..');\n'
|
|
argc = argc+2
|
|
elseif param[1] == 'Object' then
|
|
send = send..' Object *'..copy..' = xmalloc(sizeof(Object));\n'
|
|
send = send..' *'..copy..' = copy_object('..param[2]..');\n'
|
|
argv = argv..', '..copy
|
|
recv = recv..' Object '..param[2]..' = *(Object *)argv['..argc..'];\n'
|
|
recv_argv = recv_argv..', '..param[2]
|
|
recv_cleanup = (recv_cleanup..' api_free_object('..param[2]..');\n'..
|
|
' xfree(argv['..argc..']);\n')
|
|
argc = argc+1
|
|
elseif param[1] == 'Integer' or param[1] == 'Boolean' then
|
|
argv = argv..', INT2PTR('..param[2]..')'
|
|
recv_argv = recv_argv..', PTR2INT(argv['..argc..'])'
|
|
argc = argc+1
|
|
else
|
|
assert(false)
|
|
end
|
|
end
|
|
bridge_output:write('static void ui_bridge_'..ev.name..
|
|
'_event(void **argv)\n{\n')
|
|
bridge_output:write(' UI *ui = UI(argv[0]);\n')
|
|
bridge_output:write(recv)
|
|
bridge_output:write(' ui->'..ev.name..'(ui'..recv_argv..');\n')
|
|
bridge_output:write(recv_cleanup)
|
|
bridge_output:write('}\n\n')
|
|
|
|
bridge_output:write('static void ui_bridge_'..ev.name)
|
|
write_signature(bridge_output, ev, 'UI *ui')
|
|
bridge_output:write('\n{\n')
|
|
bridge_output:write(send)
|
|
bridge_output:write(' UI_BRIDGE_CALL(ui, '..ev.name..', '..argc..', ui'..argv..');\n}\n\n')
|
|
end
|
|
end
|
|
|
|
if not (ev.remote_only and ev.remote_impl) then
|
|
call_output:write('void ui_call_'..ev.name)
|
|
write_signature(call_output, ev, '')
|
|
call_output:write('\n{\n')
|
|
if ev.remote_only then
|
|
call_output:write(' Array args = call_buf;\n')
|
|
write_arglist(call_output, ev)
|
|
call_output:write(' UI_LOG('..ev.name..');\n')
|
|
call_output:write(' ui_call_event("'..ev.name..'", args);\n')
|
|
elseif ev.compositor_impl then
|
|
call_output:write(' UI_CALL')
|
|
write_signature(call_output, ev, '!ui->composed, '..ev.name..', ui', true)
|
|
call_output:write(";\n")
|
|
else
|
|
call_output:write(' UI_CALL')
|
|
write_signature(call_output, ev, 'true, '..ev.name..', ui', true)
|
|
call_output:write(";\n")
|
|
end
|
|
call_output:write("}\n\n")
|
|
end
|
|
|
|
if ev.compositor_impl then
|
|
call_output:write('void ui_composed_call_'..ev.name)
|
|
write_signature(call_output, ev, '')
|
|
call_output:write('\n{\n')
|
|
call_output:write(' UI_CALL')
|
|
write_signature(call_output, ev, 'ui->composed, '..ev.name..', ui', true)
|
|
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
|
|
|
|
local client_events = {}
|
|
for _,ev in ipairs(events) do
|
|
if (not ev.noexport) and ((not ev.remote_only) or ev.client_impl) then
|
|
client_events[ev.name] = ev
|
|
end
|
|
end
|
|
|
|
local hashorder, hashfun = hashy.hashy_hash("ui_client_handler", vim.tbl_keys(client_events), function (idx)
|
|
return "event_handlers["..idx.."].name"
|
|
end)
|
|
|
|
client_output:write("static const UIClientHandler event_handlers[] = {\n")
|
|
|
|
for _, name in ipairs(hashorder) do
|
|
client_output:write(' { .name = "'..name..'", .fn = ui_client_event_'..name..'},\n')
|
|
end
|
|
|
|
client_output:write('\n};\n\n')
|
|
client_output:write(hashfun)
|
|
|
|
proto_output:close()
|
|
call_output:close()
|
|
remote_output:close()
|
|
client_output:close()
|
|
|
|
-- don't expose internal attributes like "impl_name" in public metadata
|
|
local exported_attributes = {'name', 'parameters',
|
|
'since', 'deprecated_since'}
|
|
local exported_events = {}
|
|
for _,ev in ipairs(events) do
|
|
local ev_exported = {}
|
|
for _,attr in ipairs(exported_attributes) do
|
|
ev_exported[attr] = ev[attr]
|
|
end
|
|
for _,p in ipairs(ev_exported.parameters) do
|
|
if p[1] == 'HlAttrs' then
|
|
p[1] = 'Dictionary'
|
|
end
|
|
end
|
|
if not ev.noexport then
|
|
exported_events[#exported_events+1] = ev_exported
|
|
end
|
|
end
|
|
|
|
local packed = mpack.pack(exported_events)
|
|
local dump_bin_array = require("generators.dump_bin_array")
|
|
dump_bin_array(metadata_output, 'ui_events_metadata', packed)
|
|
metadata_output:close()
|