diff options
author | Josh Rahm <joshuarahm@gmail.com> | 2023-11-30 20:35:25 +0000 |
---|---|---|
committer | Josh Rahm <joshuarahm@gmail.com> | 2023-11-30 20:35:25 +0000 |
commit | 1b7b916b7631ddf73c38e3a0070d64e4636cb2f3 (patch) | |
tree | cd08258054db80bb9a11b1061bb091c70b76926a /src/nvim/generators/gen_api_dispatch.lua | |
parent | eaa89c11d0f8aefbb512de769c6c82f61a8baca3 (diff) | |
parent | 4a8bf24ac690004aedf5540fa440e788459e5e34 (diff) | |
download | rneovim-aucmd_textputpost.tar.gz rneovim-aucmd_textputpost.tar.bz2 rneovim-aucmd_textputpost.zip |
Merge remote-tracking branch 'upstream/master' into aucmd_textputpostaucmd_textputpost
Diffstat (limited to 'src/nvim/generators/gen_api_dispatch.lua')
-rw-r--r-- | src/nvim/generators/gen_api_dispatch.lua | 258 |
1 files changed, 180 insertions, 78 deletions
diff --git a/src/nvim/generators/gen_api_dispatch.lua b/src/nvim/generators/gen_api_dispatch.lua index 35f6bf8455..9720cca477 100644 --- a/src/nvim/generators/gen_api_dispatch.lua +++ b/src/nvim/generators/gen_api_dispatch.lua @@ -1,38 +1,23 @@ -local mpack = require('mpack') - --- we need at least 4 arguments since the last two are output files -if arg[1] == '--help' then - print('Usage: genmsgpack.lua args') - print('Args: 1: source directory') - print(' 2: dispatch output file (dispatch_wrappers.generated.h)') - print(' 3: functions metadata output file (funcs_metadata.generated.h)') - print(' 4: API metadata output file (api_metadata.mpack)') - print(' 5: lua C bindings output file (lua_api_c_bindings.generated.c)') - print(' rest: C files where API functions are defined') -end -assert(#arg >= 4) -local functions = {} +local mpack = vim.mpack -local nvimdir = arg[1] -package.path = nvimdir .. '/?.lua;' .. package.path +local hashy = require'generators.hashy' -_G.vim = loadfile(nvimdir..'/../../runtime/lua/vim/shared.lua')() -_G.vim.inspect = loadfile(nvimdir..'/../../runtime/lua/vim/inspect.lua')() +assert(#arg >= 5) +-- output h file with generated dispatch functions (dispatch_wrappers.generated.h) +local dispatch_outputf = arg[1] +-- output h file with packed metadata (funcs_metadata.generated.h) +local funcs_metadata_outputf = arg[2] +-- output metadata mpack file, for use by other build scripts (api_metadata.mpack) +local mpack_outputf = arg[3] +local lua_c_bindings_outputf = arg[4] -- lua_api_c_bindings.generated.c +local keysets_outputf = arg[5] -- keysets_defs.generated.h -local hashy = require'generators.hashy' +local functions = {} -- names of all headers relative to the source root (for inclusion in the -- generated file) local headers = {} --- output h file with generated dispatch functions -local dispatch_outputf = arg[2] --- output h file with packed metadata -local funcs_metadata_outputf = arg[3] --- output metadata mpack file, for use by other build scripts -local mpack_outputf = arg[4] -local lua_c_bindings_outputf = arg[5] - -- set of function names, used to detect duplicates local function_names = {} @@ -42,6 +27,69 @@ local function startswith(String,Start) return string.sub(String,1,string.len(Start))==Start end +local function add_function(fn) + local public = startswith(fn.name, "nvim_") or fn.deprecated_since + if public and not fn.noexport then + functions[#functions + 1] = fn + function_names[fn.name] = true + if #fn.parameters >= 2 and fn.parameters[2][1] == 'Array' and fn.parameters[2][2] == 'uidata' then + -- function receives the "args" as a parameter + fn.receives_array_args = true + -- remove the args parameter + table.remove(fn.parameters, 2) + end + if #fn.parameters ~= 0 and fn.parameters[1][2] == 'channel_id' then + -- this function should receive the channel id + fn.receives_channel_id = true + -- remove the parameter since it won't be passed by the api client + table.remove(fn.parameters, 1) + end + if #fn.parameters ~= 0 and fn.parameters[#fn.parameters][1] == 'error' then + -- function can fail if the last parameter type is 'Error' + fn.can_fail = true + -- remove the error parameter, msgpack has it's own special field + -- for specifying errors + fn.parameters[#fn.parameters] = nil + end + if #fn.parameters ~= 0 and fn.parameters[#fn.parameters][1] == 'arena' then + -- return value is allocated in an arena + fn.arena_return = true + fn.parameters[#fn.parameters] = nil + end + if #fn.parameters ~= 0 and fn.parameters[#fn.parameters][1] == 'lstate' then + fn.has_lua_imp = true + fn.parameters[#fn.parameters] = nil + end + end +end + +local keysets = {} + +local function add_keyset(val) + local keys = {} + local types = {} + local is_set_name = 'is_set__' .. val.keyset_name .. '_' + local has_optional = false + for i,field in ipairs(val.fields) do + if field.type ~= 'Object' then + types[field.name] = field.type + end + if field.name ~= is_set_name and field.type ~= 'OptionalKeys' then + table.insert(keys, field.name) + else + if i > 1 then + error("'is_set__{type}_' must be first if present") + elseif field.name ~= is_set_name then + error(val.keyset_name..": name of first key should be "..is_set_name) + elseif field.type ~= 'OptionalKeys' then + error("'"..is_set_name.."' must have type 'OptionalKeys'") + end + has_optional = true + end + end + table.insert(keysets, {name=val.keyset_name, keys=keys, types=types, has_optional=has_optional}) +end + -- read each input file, parse and append to the api metadata for i = 6, #arg do local full_path = arg[i] @@ -55,39 +103,11 @@ for i = 6, #arg do local tmp = c_grammar.grammar:match(input:read('*all')) for j = 1, #tmp do - local fn = tmp[j] - local public = startswith(fn.name, "nvim_") or fn.deprecated_since - if public and not fn.noexport then - functions[#functions + 1] = tmp[j] - function_names[fn.name] = true - if #fn.parameters >= 2 and fn.parameters[2][1] == 'Array' and fn.parameters[2][2] == 'uidata' then - -- function receives the "args" as a parameter - fn.receives_array_args = true - -- remove the args parameter - table.remove(fn.parameters, 2) - end - if #fn.parameters ~= 0 and fn.parameters[1][2] == 'channel_id' then - -- this function should receive the channel id - fn.receives_channel_id = true - -- remove the parameter since it won't be passed by the api client - table.remove(fn.parameters, 1) - end - if #fn.parameters ~= 0 and fn.parameters[#fn.parameters][1] == 'error' then - -- function can fail if the last parameter type is 'Error' - fn.can_fail = true - -- remove the error parameter, msgpack has it's own special field - -- for specifying errors - fn.parameters[#fn.parameters] = nil - end - if #fn.parameters ~= 0 and fn.parameters[#fn.parameters][1] == 'arena' then - -- return value is allocated in an arena - fn.arena_return = true - fn.parameters[#fn.parameters] = nil - end - if #fn.parameters ~= 0 and fn.parameters[#fn.parameters][1] == 'lstate' then - fn.has_lua_imp = true - fn.parameters[#fn.parameters] = nil - end + local val = tmp[j] + if val.keyset_name then + add_keyset(val) + else + add_function(val) end end input:close() @@ -187,7 +207,7 @@ end -- serialize the API metadata using msgpack and embed into the resulting -- binary for easy querying by clients local funcs_metadata_output = io.open(funcs_metadata_outputf, 'wb') -local packed = mpack.pack(exported_functions) +local packed = mpack.encode(exported_functions) local dump_bin_array = require("generators.dump_bin_array") dump_bin_array(funcs_metadata_output, 'funcs_metadata', packed) funcs_metadata_output:close() @@ -195,6 +215,7 @@ funcs_metadata_output:close() -- start building the dispatch wrapper output local output = io.open(dispatch_outputf, 'wb') +local keysets_defs = io.open(keysets_outputf, 'wb') -- =========================================================================== -- NEW API FILES MUST GO HERE. @@ -203,10 +224,11 @@ local output = io.open(dispatch_outputf, 'wb') -- so that the dispatcher can find the C functions that you are creating! -- =========================================================================== output:write([[ +#include "nvim/ex_docmd.h" +#include "nvim/ex_getln.h" #include "nvim/log.h" -#include "nvim/map.h" +#include "nvim/map_defs.h" #include "nvim/msgpack_rpc/helpers.h" -#include "nvim/vim.h" #include "nvim/api/autocmd.h" #include "nvim/api/buffer.h" @@ -224,6 +246,62 @@ output:write([[ ]]) +for _,k in ipairs(keysets) do + local c_name = {} + + for i = 1,#k.keys do + -- some keys, like "register" are c keywords and get + -- escaped with a trailing _ in the struct. + if vim.endswith(k.keys[i], "_") then + local orig = k.keys[i] + k.keys[i] = string.sub(k.keys[i],1, #(k.keys[i]) - 1) + c_name[k.keys[i]] = orig + k.types[k.keys[i]] = k.types[orig] + end + end + + local neworder, hashfun = hashy.hashy_hash(k.name, k.keys, function (idx) + return k.name.."_table["..idx.."].str" + end) + + keysets_defs:write("extern KeySetLink "..k.name.."_table[];\n") + + local function typename(type) + if type ~= nil then + return "kObjectType"..type + else + return "kObjectTypeNil" + end + end + + output:write("KeySetLink "..k.name.."_table[] = {\n") + for i, key in ipairs(neworder) do + local ind = -1 + if k.has_optional then + ind = i + keysets_defs:write("#define KEYSET_OPTIDX_"..k.name.."__"..key.." "..ind.."\n") + end + output:write(' {"'..key..'", offsetof(KeyDict_'..k.name..", "..(c_name[key] or key).."), "..typename(k.types[key])..", "..ind.."},\n") + end + output:write(' {NULL, 0, kObjectTypeNil, -1},\n') + output:write("};\n\n") + + output:write(hashfun) + + output:write([[ +KeySetLink *KeyDict_]]..k.name..[[_get_field(const char *str, size_t len) +{ + int hash = ]]..k.name..[[_hash(str, len); + if (hash == -1) { + return NULL; + } + return &]]..k.name..[[_table[hash]; +} + +]]) + keysets_defs:write("#define api_free_keydict_"..k.name.."(x) api_free_keydict(x, "..k.name.."_table)\n") +end + local function real_type(type) local rv = type local rmatch = string.match(type, "Dict%(([_%w]+)%)") @@ -257,9 +335,8 @@ for i = 1, #functions do output:write('Object handle_'..fn.name..'(uint64_t channel_id, Array args, Arena* arena, Error *error)') output:write('\n{') - output:write('\n#if MIN_LOG_LEVEL <= LOGLVL_DBG') - output:write('\n logmsg(LOGLVL_DBG, "RPC: ", NULL, -1, true, "ch %" PRIu64 ": invoke ' - ..fn.name..'", channel_id);') + output:write('\n#ifdef NVIM_LOG_DEBUG') + output:write('\n DLOG("RPC: ch %" PRIu64 ": invoke '..fn.name..'", channel_id);') output:write('\n#endif') output:write('\n Object ret = NIL;') -- Declare/initialize variables that will hold converted arguments @@ -337,8 +414,13 @@ for i = 1, #functions do args[#args + 1] = converted end - if fn.check_textlock then - output:write('\n if (textlock != 0) {') + if fn.textlock then + output:write('\n if (text_locked()) {') + output:write('\n api_set_error(error, kErrorTypeException, "%s", get_text_locked_msg());') + output:write('\n goto cleanup;') + output:write('\n }\n') + elseif fn.textlock_allow_cmdwin then + output:write('\n if (textlock != 0 || expr_map_locked()) {') output:write('\n api_set_error(error, kErrorTypeException, "%s", e_textlock);') output:write('\n goto cleanup;') output:write('\n }\n') @@ -444,8 +526,9 @@ output:write(hashfun) output:close() +functions.keysets = keysets local mpack_output = io.open(mpack_outputf, 'wb') -mpack_output:write(mpack.pack(functions)) +mpack_output:write(mpack.encode(functions)) mpack_output:close() local function include_headers(output_handle, headers_to_include) @@ -467,15 +550,16 @@ end output = io.open(lua_c_bindings_outputf, 'wb') output:write([[ -// 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 <lua.h> #include <lualib.h> #include <lauxlib.h> +#include "nvim/ex_docmd.h" +#include "nvim/ex_getln.h" #include "nvim/func_attr.h" #include "nvim/api/private/defs.h" #include "nvim/api/private/helpers.h" +#include "nvim/api/private/dispatch.h" #include "nvim/lua/converter.h" #include "nvim/lua/executor.h" #include "nvim/memory.h" @@ -493,6 +577,7 @@ local function process_function(fn) static int %s(lua_State *lstate) { Error err = ERROR_INIT; + char *err_param = 0; if (lua_gettop(lstate) != %i) { api_set_error(&err, kErrorTypeValidation, "Expected %i argument%s"); goto exit_0; @@ -512,9 +597,16 @@ local function process_function(fn) ]], fn.name)) end - if fn.check_textlock then + if fn.textlock then write_shifted_output(output, [[ - if (textlock != 0) { + if (text_locked()) { + api_set_error(&err, kErrorTypeException, "%s", get_text_locked_msg()); + goto exit_0; + } + ]]) + elseif fn.textlock_allow_cmdwin then + write_shifted_output(output, [[ + if (textlock != 0 || expr_map_locked()) { api_set_error(&err, kErrorTypeException, "%s", e_textlock); goto exit_0; } @@ -533,19 +625,22 @@ local function process_function(fn) extra = "true, " end local errshift = 0 + local seterr = '' if string.match(param_type, '^KeyDict_') then write_shifted_output(output, string.format([[ - %s %s = { 0 }; nlua_pop_keydict(lstate, &%s, %s_get_field, %s&err);]], param_type, cparam, cparam, param_type, extra)) + %s %s = { 0 }; nlua_pop_keydict(lstate, &%s, %s_get_field, &err_param, &err);]], param_type, cparam, cparam, param_type)) cparam = '&'..cparam errshift = 1 -- free incomplete dict on error else write_shifted_output(output, string.format([[ const %s %s = nlua_pop_%s(lstate, %s&err);]], param[1], cparam, param_type, extra)) + seterr = [[ + err_param = "]]..param[2]..[[";]] end write_shifted_output(output, string.format([[ - if (ERROR_SET(&err)) { + if (ERROR_SET(&err)) {]]..seterr..[[ goto exit_%u; } @@ -589,9 +684,14 @@ local function process_function(fn) exit_0: if (ERROR_SET(&err)) { luaL_where(lstate, 1); + if (err_param) { + lua_pushstring(lstate, "Invalid '"); + lua_pushstring(lstate, err_param); + lua_pushstring(lstate, "': "); + } lua_pushstring(lstate, err.msg); api_clear_error(&err); - lua_concat(lstate, 2); + lua_concat(lstate, err_param ? 5 : 2); return lua_error(lstate); } ]] @@ -620,9 +720,10 @@ local function process_function(fn) } ]], return_type)) else + local special = (fn.since ~= nil and fn.since < 11) write_shifted_output(output, string.format([[ - nlua_push_%s(lstate, ret, true); - ]], return_type)) + nlua_push_%s(lstate, ret, %s); + ]], return_type, tostring(special))) end write_shifted_output(output, string.format([[ @@ -670,3 +771,4 @@ output:write([[ ]]) output:close() +keysets_defs:close() |