mirror of
				https://github.com/neovim/neovim.git
				synced 2025-11-04 01:34:25 +00:00 
			
		
		
		
	api: auto generate api function wrappers for viml
This commit is contained in:
		@@ -41,18 +41,20 @@ c_proto = Ct(
 | 
				
			|||||||
  )
 | 
					  )
 | 
				
			||||||
grammar = Ct((c_proto + c_comment + c_preproc + ws) ^ 1)
 | 
					grammar = Ct((c_proto + c_comment + c_preproc + ws) ^ 1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
-- we need at least 2 arguments since the last one is the output file
 | 
					-- we need at least 3 arguments since the last two are output files
 | 
				
			||||||
assert(#arg >= 1)
 | 
					assert(#arg >= 2)
 | 
				
			||||||
functions = {}
 | 
					functions = {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
-- names of all headers relative to the source root (for inclusion in the
 | 
					-- names of all headers relative to the source root (for inclusion in the
 | 
				
			||||||
-- generated file)
 | 
					-- generated file)
 | 
				
			||||||
headers = {}
 | 
					headers = {}
 | 
				
			||||||
-- output file(dispatch function + metadata serialized with msgpack)
 | 
					-- output c file(dispatch function + metadata serialized with msgpack)
 | 
				
			||||||
outputf = arg[#arg]
 | 
					outputf = arg[#arg-1]
 | 
				
			||||||
 | 
					-- output mpack file (metadata)
 | 
				
			||||||
 | 
					mpack_outputf = arg[#arg]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
-- read each input file, parse and append to the api metadata
 | 
					-- read each input file, parse and append to the api metadata
 | 
				
			||||||
for i = 1, #arg - 1 do
 | 
					for i = 1, #arg - 2 do
 | 
				
			||||||
  local full_path = arg[i]
 | 
					  local full_path = arg[i]
 | 
				
			||||||
  local parts = {}
 | 
					  local parts = {}
 | 
				
			||||||
  for part in string.gmatch(full_path, '[^/]+') do
 | 
					  for part in string.gmatch(full_path, '[^/]+') do
 | 
				
			||||||
@@ -165,7 +167,7 @@ for i = 1, #functions do
 | 
				
			|||||||
  local fn = functions[i]
 | 
					  local fn = functions[i]
 | 
				
			||||||
  local args = {}
 | 
					  local args = {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  output:write('static Object handle_'..fn.name..'(uint64_t channel_id, uint64_t request_id, Array args, Error *error)')
 | 
					  output:write('Object handle_'..fn.name..'(uint64_t channel_id, uint64_t request_id, Array args, Error *error)')
 | 
				
			||||||
  output:write('\n{')
 | 
					  output:write('\n{')
 | 
				
			||||||
  output:write('\n  Object ret = NIL;')
 | 
					  output:write('\n  Object ret = NIL;')
 | 
				
			||||||
  -- Declare/initialize variables that will hold converted arguments
 | 
					  -- Declare/initialize variables that will hold converted arguments
 | 
				
			||||||
@@ -311,3 +313,7 @@ MsgpackRpcRequestHandler msgpack_rpc_get_handler_for(const char *name,
 | 
				
			|||||||
]])
 | 
					]])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
output:close()
 | 
					output:close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					mpack_output = io.open(mpack_outputf, 'wb')
 | 
				
			||||||
 | 
					mpack_output:write(packed)
 | 
				
			||||||
 | 
					mpack_output:close()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,8 @@
 | 
				
			|||||||
 | 
					mpack = require('mpack')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
local nvimsrcdir = arg[1]
 | 
					local nvimsrcdir = arg[1]
 | 
				
			||||||
local autodir = arg[2]
 | 
					local autodir = arg[2]
 | 
				
			||||||
 | 
					local metadata_file = arg[3]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if nvimsrcdir == '--help' then
 | 
					if nvimsrcdir == '--help' then
 | 
				
			||||||
  print([[
 | 
					  print([[
 | 
				
			||||||
@@ -18,9 +21,20 @@ local funcsfname = autodir .. '/funcs.generated.h'
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
local funcspipe = io.open(funcsfname .. '.hsh', 'w')
 | 
					local funcspipe = io.open(funcsfname .. '.hsh', 'w')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
local funcs = require('eval')
 | 
					local funcs = require('eval').funcs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
for name, def in pairs(funcs.funcs) do
 | 
					local metadata = mpack.unpack(io.open(arg[3], 'rb'):read("*all"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					for i,fun in ipairs(metadata) do
 | 
				
			||||||
 | 
					  funcs['api_'..fun.name] = {
 | 
				
			||||||
 | 
					    args=#fun.parameters,
 | 
				
			||||||
 | 
					    func='api_wrapper',
 | 
				
			||||||
 | 
					    data='handle_'..fun.name,
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					for name, def in pairs(funcs) do
 | 
				
			||||||
  args = def.args or 0
 | 
					  args = def.args or 0
 | 
				
			||||||
  if type(args) == 'number' then
 | 
					  if type(args) == 'number' then
 | 
				
			||||||
    args = {args, args}
 | 
					    args = {args, args}
 | 
				
			||||||
@@ -28,7 +42,8 @@ for name, def in pairs(funcs.funcs) do
 | 
				
			|||||||
    args[2] = 'MAX_FUNC_ARGS'
 | 
					    args[2] = 'MAX_FUNC_ARGS'
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
  func = def.func or ('f_' .. name)
 | 
					  func = def.func or ('f_' .. name)
 | 
				
			||||||
  local val = ('{ %s, %s, &%s }'):format(args[1], args[2], func)
 | 
					  data = def.data or "NULL"
 | 
				
			||||||
 | 
					  local val = ('{ %s, %s, &%s, %s }'):format(args[1], args[2], func, data)
 | 
				
			||||||
  funcspipe:write(name .. '\n' .. val .. '\n')
 | 
					  funcspipe:write(name .. '\n' .. val .. '\n')
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
funcspipe:close()
 | 
					funcspipe:close()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,6 +14,7 @@ set(GENERATED_DIR ${PROJECT_BINARY_DIR}/src/nvim/auto)
 | 
				
			|||||||
set(DISPATCH_GENERATOR ${PROJECT_SOURCE_DIR}/scripts/gendispatch.lua)
 | 
					set(DISPATCH_GENERATOR ${PROJECT_SOURCE_DIR}/scripts/gendispatch.lua)
 | 
				
			||||||
file(GLOB API_HEADERS api/*.h)
 | 
					file(GLOB API_HEADERS api/*.h)
 | 
				
			||||||
file(GLOB MSGPACK_RPC_HEADERS msgpack_rpc/*.h)
 | 
					file(GLOB MSGPACK_RPC_HEADERS msgpack_rpc/*.h)
 | 
				
			||||||
 | 
					set(API_METADATA ${PROJECT_BINARY_DIR}/api_metadata.mpack)
 | 
				
			||||||
set(HEADER_GENERATOR ${PROJECT_SOURCE_DIR}/scripts/gendeclarations.lua)
 | 
					set(HEADER_GENERATOR ${PROJECT_SOURCE_DIR}/scripts/gendeclarations.lua)
 | 
				
			||||||
set(GENERATED_INCLUDES_DIR ${PROJECT_BINARY_DIR}/include)
 | 
					set(GENERATED_INCLUDES_DIR ${PROJECT_BINARY_DIR}/include)
 | 
				
			||||||
set(GENERATED_API_DISPATCH ${GENERATED_DIR}/api/private/dispatch.c)
 | 
					set(GENERATED_API_DISPATCH ${GENERATED_DIR}/api/private/dispatch.c)
 | 
				
			||||||
@@ -192,8 +193,8 @@ add_custom_command(OUTPUT ${GENERATED_UNICODE_TABLES}
 | 
				
			|||||||
    ${EASTASIANWIDTH_FILE}
 | 
					    ${EASTASIANWIDTH_FILE}
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
add_custom_command(OUTPUT ${GENERATED_API_DISPATCH}
 | 
					add_custom_command(OUTPUT ${GENERATED_API_DISPATCH} ${API_METADATA}
 | 
				
			||||||
  COMMAND ${LUA_PRG} ${DISPATCH_GENERATOR} ${API_HEADERS} ${GENERATED_API_DISPATCH}
 | 
					  COMMAND ${LUA_PRG} ${DISPATCH_GENERATOR} ${API_HEADERS} ${GENERATED_API_DISPATCH} ${API_METADATA}
 | 
				
			||||||
  DEPENDS
 | 
					  DEPENDS
 | 
				
			||||||
    ${API_HEADERS}
 | 
					    ${API_HEADERS}
 | 
				
			||||||
    ${MSGPACK_RPC_HEADERS}
 | 
					    ${MSGPACK_RPC_HEADERS}
 | 
				
			||||||
@@ -220,10 +221,10 @@ add_custom_command(OUTPUT ${GENERATED_EX_CMDS_ENUM} ${GENERATED_EX_CMDS_DEFS}
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
add_custom_command(OUTPUT ${GENERATED_FUNCS}
 | 
					add_custom_command(OUTPUT ${GENERATED_FUNCS}
 | 
				
			||||||
  COMMAND ${LUA_PRG} ${FUNCS_GENERATOR}
 | 
					  COMMAND ${LUA_PRG} ${FUNCS_GENERATOR}
 | 
				
			||||||
    ${PROJECT_SOURCE_DIR}/src/nvim ${GENERATED_DIR}
 | 
					    ${PROJECT_SOURCE_DIR}/src/nvim ${GENERATED_DIR} ${API_METADATA}
 | 
				
			||||||
  COMMAND $<TARGET_FILE:genhash>
 | 
					  COMMAND $<TARGET_FILE:genhash>
 | 
				
			||||||
    ${GENERATED_FUNCS_HASH_INPUT} ${GENERATED_FUNCS} functions functions VimLFuncDef "NOFUNC"
 | 
					    ${GENERATED_FUNCS_HASH_INPUT} ${GENERATED_FUNCS} functions functions VimLFuncDef "NOFUNC"
 | 
				
			||||||
  DEPENDS ${FUNCS_GENERATOR} ${EVAL_DEFS_FILE} genhash
 | 
					    DEPENDS ${FUNCS_GENERATOR} ${EVAL_DEFS_FILE} ${API_METADATA} genhash
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
add_custom_command(OUTPUT ${GENERATED_EVENTS_ENUM} ${GENERATED_EVENTS_NAMES_MAP}
 | 
					add_custom_command(OUTPUT ${GENERATED_EVENTS_ENUM} ${GENERATED_EVENTS_NAMES_MAP}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,13 +3,15 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#include "nvim/api/private/defs.h"
 | 
					#include "nvim/api/private/defs.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef Object (*ApiDispatchWrapper)(uint64_t channel_id,
 | 
				
			||||||
 | 
					                                     uint64_t request_id,
 | 
				
			||||||
 | 
					                                     Array args,
 | 
				
			||||||
 | 
					                                     Error *error);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// The rpc_method_handlers table, used in msgpack_rpc_dispatch(), stores
 | 
					/// The rpc_method_handlers table, used in msgpack_rpc_dispatch(), stores
 | 
				
			||||||
/// functions of this type.
 | 
					/// functions of this type.
 | 
				
			||||||
typedef struct {
 | 
					typedef struct {
 | 
				
			||||||
  Object (*fn)(uint64_t channel_id,
 | 
					  ApiDispatchWrapper fn;
 | 
				
			||||||
               uint64_t request_id,
 | 
					 | 
				
			||||||
               Array args,
 | 
					 | 
				
			||||||
               Error *error);
 | 
					 | 
				
			||||||
  bool async;  // function is always safe to run immediately instead of being
 | 
					  bool async;  // function is always safe to run immediately instead of being
 | 
				
			||||||
               // put in a request queue for handling when nvim waits for input.
 | 
					               // put in a request queue for handling when nvim waits for input.
 | 
				
			||||||
} MsgpackRpcRequestHandler;
 | 
					} MsgpackRpcRequestHandler;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -445,13 +445,14 @@ typedef struct {
 | 
				
			|||||||
} timer_T;
 | 
					} timer_T;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Prototype of C function that implements VimL function
 | 
					/// Prototype of C function that implements VimL function
 | 
				
			||||||
typedef void (*VimLFunc)(typval_T *args, typval_T *rvar);
 | 
					typedef void (*VimLFunc)(typval_T *args, typval_T *rvar, void *data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Structure holding VimL function definition
 | 
					/// Structure holding VimL function definition
 | 
				
			||||||
typedef struct fst {
 | 
					typedef struct fst {
 | 
				
			||||||
  uint8_t min_argc;  ///< Minimal number of arguments.
 | 
					  uint8_t min_argc;  ///< Minimal number of arguments.
 | 
				
			||||||
  uint8_t max_argc;  ///< Maximal number of arguments.
 | 
					  uint8_t max_argc;  ///< Maximal number of arguments.
 | 
				
			||||||
  VimLFunc func;     ///< Function implementation.
 | 
					  VimLFunc func;     ///< Function implementation.
 | 
				
			||||||
 | 
					  void *data;        ///< Userdata for function implementation.
 | 
				
			||||||
} VimLFuncDef;
 | 
					} VimLFuncDef;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
KHASH_MAP_INIT_STR(functions, VimLFuncDef)
 | 
					KHASH_MAP_INIT_STR(functions, VimLFuncDef)
 | 
				
			||||||
@@ -6981,7 +6982,7 @@ call_func (
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      // Find the function name in the table, call its implementation.
 | 
					      // Find the function name in the table, call its implementation.
 | 
				
			||||||
      VimLFuncDef *const fdef = find_internal_func((char *) fname);
 | 
					      VimLFuncDef *const fdef = find_internal_func((char *)fname);
 | 
				
			||||||
      if (fdef != NULL) {
 | 
					      if (fdef != NULL) {
 | 
				
			||||||
        if (argcount < fdef->min_argc) {
 | 
					        if (argcount < fdef->min_argc) {
 | 
				
			||||||
          error = ERROR_TOOFEW;
 | 
					          error = ERROR_TOOFEW;
 | 
				
			||||||
@@ -6989,7 +6990,7 @@ call_func (
 | 
				
			|||||||
          error = ERROR_TOOMANY;
 | 
					          error = ERROR_TOOMANY;
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
          argvars[argcount].v_type = VAR_UNKNOWN;
 | 
					          argvars[argcount].v_type = VAR_UNKNOWN;
 | 
				
			||||||
          fdef->func(argvars, rettv);
 | 
					          fdef->func(argvars, rettv, fdef->data);
 | 
				
			||||||
          error = ERROR_NONE;
 | 
					          error = ERROR_NONE;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
@@ -7100,13 +7101,10 @@ static inline int get_float_arg(typval_T *argvars, float_T *f)
 | 
				
			|||||||
// Some versions of glibc on i386 have an optimization that makes it harder to
 | 
					// Some versions of glibc on i386 have an optimization that makes it harder to
 | 
				
			||||||
// call math functions indirectly from inside an inlined function, causing
 | 
					// call math functions indirectly from inside an inlined function, causing
 | 
				
			||||||
// compile-time errors. Avoid `inline` in that case. #3072
 | 
					// compile-time errors. Avoid `inline` in that case. #3072
 | 
				
			||||||
#ifndef ARCH_32
 | 
					static void float_op_wrapper(typval_T *argvars, typval_T *rettv, void *data)
 | 
				
			||||||
inline
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
static void float_op_wrapper(typval_T *argvars, typval_T *rettv,
 | 
					 | 
				
			||||||
                             float_T (*function)(float_T))
 | 
					 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
  float_T f;
 | 
					  float_T f;
 | 
				
			||||||
 | 
					  float_T (*function)(float_T) = data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  rettv->v_type = VAR_FLOAT;
 | 
					  rettv->v_type = VAR_FLOAT;
 | 
				
			||||||
  if (get_float_arg(argvars, &f) == OK) {
 | 
					  if (get_float_arg(argvars, &f) == OK) {
 | 
				
			||||||
@@ -7116,6 +7114,34 @@ static void float_op_wrapper(typval_T *argvars, typval_T *rettv,
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void api_wrapper(typval_T *argvars, typval_T *rettv, void *data)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  ApiDispatchWrapper fn = data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Array args = ARRAY_DICT_INIT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  for (typval_T *tv = argvars; tv->v_type != VAR_UNKNOWN; tv++) {
 | 
				
			||||||
 | 
					    ADD(args, vim_to_object(tv));
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Error err = ERROR_INIT;
 | 
				
			||||||
 | 
					  Object result = fn(-1, -1, args, &err);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (err.set) {
 | 
				
			||||||
 | 
					    vim_report_error(cstr_as_string(err.msg));
 | 
				
			||||||
 | 
					    goto end;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (!object_to_vim(result, rettv, &err)) {
 | 
				
			||||||
 | 
					    EMSG2(_("Error converting the call result: %s"), err.msg);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					end:
 | 
				
			||||||
 | 
					  // All arguments were freed already, but we still need to free the array
 | 
				
			||||||
 | 
					  xfree(args.items);
 | 
				
			||||||
 | 
					  api_free_object(result);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * "abs(expr)" function
 | 
					 * "abs(expr)" function
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
@@ -7515,14 +7541,6 @@ static void f_asin(typval_T *argvars, typval_T *rettv)
 | 
				
			|||||||
  float_op_wrapper(argvars, rettv, &asin);
 | 
					  float_op_wrapper(argvars, rettv, &asin);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					 | 
				
			||||||
 * "atan()" function
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
static void f_atan(typval_T *argvars, typval_T *rettv)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
  float_op_wrapper(argvars, rettv, &atan);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * "atan2()" function
 | 
					 * "atan2()" function
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
@@ -20115,7 +20133,7 @@ void free_all_functions(void)
 | 
				
			|||||||
int translated_function_exists(char_u *name)
 | 
					int translated_function_exists(char_u *name)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
  if (builtin_function(name, -1)) {
 | 
					  if (builtin_function(name, -1)) {
 | 
				
			||||||
    return find_internal_func((char *) name) != NULL;
 | 
					    return find_internal_func((char *)name) != NULL;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  return find_func(name) != NULL;
 | 
					  return find_func(name) != NULL;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -33,7 +33,7 @@ return {
 | 
				
			|||||||
    assert_notequal={args={2, 3}},
 | 
					    assert_notequal={args={2, 3}},
 | 
				
			||||||
    assert_notmatch={args={2, 3}},
 | 
					    assert_notmatch={args={2, 3}},
 | 
				
			||||||
    assert_true={args={1, 2}},
 | 
					    assert_true={args={1, 2}},
 | 
				
			||||||
    atan={args=1},
 | 
					    atan={args=1, func="float_op_wrapper", data="atan"},
 | 
				
			||||||
    atan2={args=2},
 | 
					    atan2={args=2},
 | 
				
			||||||
    browse={args=4},
 | 
					    browse={args=4},
 | 
				
			||||||
    browsedir={args=2},
 | 
					    browsedir={args=2},
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user