aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/generators/gen_api_dispatch.lua
diff options
context:
space:
mode:
authorJosh Rahm <joshuarahm@gmail.com>2023-11-30 20:35:25 +0000
committerJosh Rahm <joshuarahm@gmail.com>2023-11-30 20:35:25 +0000
commit1b7b916b7631ddf73c38e3a0070d64e4636cb2f3 (patch)
treecd08258054db80bb9a11b1061bb091c70b76926a /src/nvim/generators/gen_api_dispatch.lua
parenteaa89c11d0f8aefbb512de769c6c82f61a8baca3 (diff)
parent4a8bf24ac690004aedf5540fa440e788459e5e34 (diff)
downloadrneovim-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.lua258
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()