From e2143674ae20fbadbea004bd54d6f5dc32b83803 Mon Sep 17 00:00:00 2001 From: Thiago de Arruda Date: Sun, 31 Aug 2014 10:46:28 -0300 Subject: [PATCH 01/25] deps: Update to the experimental msgpack v5 branch Using msgpack v5 will let nvim be more compatible with msgpack libraries for other platforms. This also replaces "raw" references by "bin" which is the new name for msgpack binary data type --- cmake/FindMsgpack.cmake | 6 +++--- scripts/msgpack-gen.lua | 4 ++-- src/nvim/os/msgpack_rpc.c | 18 +++++++++--------- src/nvim/os/msgpack_rpc_helpers.c | 12 ++++++------ third-party/CMakeLists.txt | 19 ++++++++++++++----- 5 files changed, 34 insertions(+), 25 deletions(-) diff --git a/cmake/FindMsgpack.cmake b/cmake/FindMsgpack.cmake index b2c89b13d1..6b3c8bb977 100644 --- a/cmake/FindMsgpack.cmake +++ b/cmake/FindMsgpack.cmake @@ -24,13 +24,13 @@ find_path(MSGPACK_INCLUDE_DIR msgpack.h HINTS ${PC_MSGPACK_INCLUDEDIR} ${PC_MSGPACK_INCLUDE_DIRS} ${LIMIT_SEARCH}) -# If we're asked to use static linkage, add libmsgpackc.a as a preferred library name. +# If we're asked to use static linkage, add libmsgpack.a as a preferred library name. if(MSGPACK_USE_STATIC) list(APPEND MSGPACK_NAMES - "${CMAKE_STATIC_LIBRARY_PREFIX}msgpackc${CMAKE_STATIC_LIBRARY_SUFFIX}") + "${CMAKE_STATIC_LIBRARY_PREFIX}msgpack${CMAKE_STATIC_LIBRARY_SUFFIX}") endif() -list(APPEND MSGPACK_NAMES msgpackc) +list(APPEND MSGPACK_NAMES msgpack) find_library(MSGPACK_LIBRARY NAMES ${MSGPACK_NAMES} HINTS ${PC_MSGPACK_LIBDIR} ${PC_MSGPACK_LIBRARY_DIRS} diff --git a/scripts/msgpack-gen.lua b/scripts/msgpack-gen.lua index 8940cc72f6..43459743e2 100644 --- a/scripts/msgpack-gen.lua +++ b/scripts/msgpack-gen.lua @@ -277,9 +277,9 @@ Object msgpack_rpc_dispatch(uint64_t channel_id, msgpack_object method = req->via.array.ptr[2]; uint64_t method_id = method.via.u64; - if (method.type == MSGPACK_OBJECT_RAW) { + if (method.type == MSGPACK_OBJECT_BIN) { char method_name[]]..(max_fname_len + 1)..[[]; - xstrlcpy(method_name, method.via.raw.ptr, min(method.via.raw.size, ]] ..(max_fname_len)..[[) + 1); + xstrlcpy(method_name, method.via.bin.ptr, min(method.via.bin.size, ]] ..(max_fname_len)..[[) + 1); method_id = map_get(cstr_t, uint64_t)(rpc_method_ids, method_name); if (!method_id) { method_id = UINT64_MAX; diff --git a/src/nvim/os/msgpack_rpc.c b/src/nvim/os/msgpack_rpc.c index c6e2af2f1c..38670e0bf8 100644 --- a/src/nvim/os/msgpack_rpc.c +++ b/src/nvim/os/msgpack_rpc.c @@ -109,8 +109,8 @@ void msgpack_rpc_error(char *msg, msgpack_packer *res) size_t len = strlen(msg); // error message - msgpack_pack_raw(res, len); - msgpack_pack_raw_body(res, msg, len); + msgpack_pack_bin(res, len); + msgpack_pack_bin_body(res, msg, len); // Nil result msgpack_pack_nil(res); } @@ -132,8 +132,8 @@ WBuffer *serialize_request(uint64_t request_id, msgpack_pack_uint64(&pac, request_id); } - msgpack_pack_raw(&pac, method.size); - msgpack_pack_raw_body(&pac, method.data, method.size); + msgpack_pack_bin(&pac, method.size); + msgpack_pack_bin_body(&pac, method.data, method.size); msgpack_rpc_from_array(args, &pac); WBuffer *rv = wstream_new_buffer(xmemdup(sbuffer->data, sbuffer->size), sbuffer->size, @@ -160,8 +160,8 @@ WBuffer *serialize_response(uint64_t response_id, if (err_msg) { String err = {.size = strlen(err_msg), .data = err_msg}; // error message - msgpack_pack_raw(&pac, err.size); - msgpack_pack_raw_body(&pac, err.data, err.size); + msgpack_pack_bin(&pac, err.size); + msgpack_pack_bin_body(&pac, err.data, err.size); // Nil result msgpack_pack_nil(&pac); } else { @@ -195,8 +195,8 @@ WBuffer *serialize_metadata(uint64_t id, // The result is the [channel_id, metadata] array msgpack_pack_array(&pac, 2); msgpack_pack_uint64(&pac, channel_id); - msgpack_pack_raw(&pac, msgpack_metadata_size); - msgpack_pack_raw_body(&pac, msgpack_metadata, msgpack_metadata_size); + msgpack_pack_bin(&pac, msgpack_metadata_size); + msgpack_pack_bin_body(&pac, msgpack_metadata, msgpack_metadata_size); WBuffer *rv = wstream_new_buffer(xmemdup(sbuffer->data, sbuffer->size), sbuffer->size, 1, @@ -235,7 +235,7 @@ static char *msgpack_rpc_validate(uint64_t *response_id, msgpack_object *req) } if (req->via.array.ptr[2].type != MSGPACK_OBJECT_POSITIVE_INTEGER - && req->via.array.ptr[2].type != MSGPACK_OBJECT_RAW) { + && req->via.array.ptr[2].type != MSGPACK_OBJECT_BIN) { return "Method must be a positive integer or a string"; } diff --git a/src/nvim/os/msgpack_rpc_helpers.c b/src/nvim/os/msgpack_rpc_helpers.c index e2c277abe4..eb43c3b2a6 100644 --- a/src/nvim/os/msgpack_rpc_helpers.c +++ b/src/nvim/os/msgpack_rpc_helpers.c @@ -81,12 +81,12 @@ bool msgpack_rpc_to_float(msgpack_object *obj, Float *arg) bool msgpack_rpc_to_string(msgpack_object *obj, String *arg) { - if (obj->type != MSGPACK_OBJECT_RAW) { + if (obj->type != MSGPACK_OBJECT_BIN) { return false; } - arg->data = xmemdupz(obj->via.raw.ptr, obj->via.raw.size); - arg->size = obj->via.raw.size; + arg->data = xmemdupz(obj->via.bin.ptr, obj->via.bin.size); + arg->size = obj->via.bin.size; return true; } @@ -110,7 +110,7 @@ bool msgpack_rpc_to_object(msgpack_object *obj, Object *arg) arg->type = kObjectTypeFloat; return msgpack_rpc_to_float(obj, &arg->data.floating); - case MSGPACK_OBJECT_RAW: + case MSGPACK_OBJECT_BIN: arg->type = kObjectTypeString; return msgpack_rpc_to_string(obj, &arg->data.string); @@ -200,8 +200,8 @@ void msgpack_rpc_from_float(Float result, msgpack_packer *res) void msgpack_rpc_from_string(String result, msgpack_packer *res) { - msgpack_pack_raw(res, result.size); - msgpack_pack_raw_body(res, result.data, result.size); + msgpack_pack_bin(res, result.size); + msgpack_pack_bin_body(res, result.data, result.size); } void msgpack_rpc_from_object(Object result, msgpack_packer *res) diff --git a/third-party/CMakeLists.txt b/third-party/CMakeLists.txt index ff161a7cc7..2f7b4c9256 100644 --- a/third-party/CMakeLists.txt +++ b/third-party/CMakeLists.txt @@ -50,8 +50,8 @@ include(ExternalProject) set(LIBUV_URL https://github.com/joyent/libuv/archive/v0.11.28.tar.gz) set(LIBUV_MD5 1a849ba4fc571d531482ed74bc7aabc4) -set(MSGPACK_URL https://github.com/msgpack/msgpack-c/releases/download/cpp-0.5.8/msgpack-0.5.8.tar.gz) -set(MSGPACK_MD5 ea0bee0939d2980c0df91f0e4843ccc4) +set(MSGPACK_URL https://github.com/msgpack/msgpack-c/archive/0335df55e1a408c0d56d43e46253c952fb8a7f04.tar.gz) +set(MSGPACK_MD5 4c18a1625b586c0d69a0e955ce9a187f) set(LUAJIT_URL http://luajit.org/download/LuaJIT-2.0.3.tar.gz) set(LUAJIT_MD5 f14e9104be513913810cd59c8c658dc0) @@ -92,9 +92,18 @@ if(USE_BUNDLED_MSGPACK) -DEXPECTED_MD5=${MSGPACK_MD5} -DTARGET=msgpack -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/DownloadAndExtractFile.cmake - CONFIGURE_COMMAND ${DEPS_BUILD_DIR}/src/msgpack/configure --disable-shared - --with-pic --prefix=${DEPS_INSTALL_DIR} CC=${DEPS_C_COMPILER} - INSTALL_COMMAND ${MAKE_PRG} install) + CONFIGURE_COMMAND cmake ${DEPS_BUILD_DIR}/src/msgpack + -DMSGPACK_ENABLE_CXX=OFF + -DMSGPACK_BUILD_TESTS=OFF + -DCMAKE_INSTALL_PREFIX=${DEPS_INSTALL_DIR} + -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER} + "-DCMAKE_C_FLAGS:STRING=${CMAKE_C_COMPILER_ARG1} -fPIC" + BUILD_COMMAND ${MAKE_PRG} + INSTALL_COMMAND ${MAKE_PRG} install && + rm ${DEPS_INSTALL_DIR}/lib/libmsgpack.so && + rm ${DEPS_INSTALL_DIR}/lib/libmsgpack.so.3 && + rm ${DEPS_INSTALL_DIR}/lib/libmsgpack.so.4.0.0 + ) list(APPEND THIRD_PARTY_DEPS msgpack) endif() From c39ae3e4d475c868f8ba76116735501ea97dfae1 Mon Sep 17 00:00:00 2001 From: Thiago de Arruda Date: Wed, 3 Sep 2014 11:46:35 -0300 Subject: [PATCH 02/25] map/msgpack-rpc: Declare/define maps rpc method handlers The new map type uses `String` instances as keys to avoid unnecessary copying to zero-terminated buffers. --- scripts/msgpack-gen.lua | 3 +-- src/nvim/map.c | 18 ++++++++++++++++++ src/nvim/map.h | 3 +++ src/nvim/vim.h | 2 ++ 4 files changed, 24 insertions(+), 2 deletions(-) diff --git a/scripts/msgpack-gen.lua b/scripts/msgpack-gen.lua index 43459743e2..7967aad232 100644 --- a/scripts/msgpack-gen.lua +++ b/scripts/msgpack-gen.lua @@ -93,6 +93,7 @@ output:write([[ #include "nvim/map.h" #include "nvim/log.h" +#include "nvim/vim.h" #include "nvim/os/msgpack_rpc.h" #include "nvim/os/msgpack_rpc_helpers.h" #include "nvim/api/private/helpers.h" @@ -268,8 +269,6 @@ end output:write('\n}\n\n') output:write([[ -#define min(X, Y) (X < Y ? X : Y) - Object msgpack_rpc_dispatch(uint64_t channel_id, msgpack_object *req, Error *error) diff --git a/src/nvim/map.c b/src/nvim/map.c index 2e47e8b249..24aa38d67d 100644 --- a/src/nvim/map.c +++ b/src/nvim/map.c @@ -1,10 +1,12 @@ #include #include +#include #include "nvim/map.h" #include "nvim/map_defs.h" #include "nvim/vim.h" #include "nvim/memory.h" +#include "nvim/os/msgpack_rpc.h" #include "nvim/lib/khash.h" @@ -87,7 +89,23 @@ return rv; \ } +static inline khint_t String_hash(String s) +{ + khint_t h = 0; + for (size_t i = 0; i < s.size && s.data[i]; i++) { + h = (h << 5) - h + (uint8_t)s.data[i]; + } + return h; +} + +static inline bool String_eq(String a, String b) +{ + return strncmp(a.data, b.data, min(a.size, b.size)) == 0; +} + + MAP_IMPL(cstr_t, uint64_t, DEFAULT_INITIALIZER) MAP_IMPL(cstr_t, ptr_t, DEFAULT_INITIALIZER) MAP_IMPL(ptr_t, ptr_t, DEFAULT_INITIALIZER) MAP_IMPL(uint64_t, ptr_t, DEFAULT_INITIALIZER) +MAP_IMPL(String, rpc_method_handler_fn, DEFAULT_INITIALIZER) diff --git a/src/nvim/map.h b/src/nvim/map.h index 73698cba22..616516c3e1 100644 --- a/src/nvim/map.h +++ b/src/nvim/map.h @@ -4,6 +4,8 @@ #include #include "nvim/map_defs.h" +#include "nvim/api/private/defs.h" +#include "nvim/os/msgpack_rpc.h" #define MAP_DECLS(T, U) \ KHASH_DECLARE(T##_##U##_map, T, U) \ @@ -23,6 +25,7 @@ MAP_DECLS(cstr_t, uint64_t) MAP_DECLS(cstr_t, ptr_t) MAP_DECLS(ptr_t, ptr_t) MAP_DECLS(uint64_t, ptr_t) +MAP_DECLS(String, rpc_method_handler_fn) #define map_new(T, U) map_##T##_##U##_new #define map_free(T, U) map_##T##_##U##_free diff --git a/src/nvim/vim.h b/src/nvim/vim.h index 6479aeaafb..3ef291ef7c 100644 --- a/src/nvim/vim.h +++ b/src/nvim/vim.h @@ -8,6 +8,8 @@ #ifndef NVIM_VIM_H # define NVIM_VIM_H +#define min(X, Y) (X < Y ? X : Y) + #include "nvim/types.h" #include "nvim/pos.h" // for linenr_T, MAXCOL, etc... From 74aff19691aeea83fa23265719668467db2b049c Mon Sep 17 00:00:00 2001 From: Thiago de Arruda Date: Wed, 3 Sep 2014 14:26:16 -0300 Subject: [PATCH 03/25] msgpack-rpc: Refactor initializer and dispatcher Use Map(String, rpc_method_handler_fn) for storing/retrieving rpc method handlers in msgpack_rpc_init and msgpack_rpc_dispatch. Also refactor serialization/validation functions in the msgpack_rpc.c/msgpack_rpc_helpers.c modules to accept the new STR and BIN types. --- scripts/msgpack-gen.lua | 48 +++++++++++++++++-------------- src/nvim/os/msgpack_rpc.c | 3 +- src/nvim/os/msgpack_rpc_helpers.c | 8 ++++-- 3 files changed, 33 insertions(+), 26 deletions(-) diff --git a/scripts/msgpack-gen.lua b/scripts/msgpack-gen.lua index 7967aad232..f868a67568 100644 --- a/scripts/msgpack-gen.lua +++ b/scripts/msgpack-gen.lua @@ -245,22 +245,24 @@ output:write('\n};\n\n') -- Generate a function that initializes method names with handler functions output:write([[ -static Map(cstr_t, uint64_t) *rpc_method_ids = NULL; +static Map(String, rpc_method_handler_fn) *methods = NULL; void msgpack_rpc_init(void) { - rpc_method_ids = map_new(cstr_t, uint64_t)(); + methods = map_new(String, rpc_method_handler_fn)(); ]]) --- Msgpack strings must be copied to a 0-terminated temporary buffer before --- searching in the map, so we keep track of the maximum method name length in --- order to create the smallest possible buffer for xstrlcpy +-- Keep track of the maximum method name length in order to avoid walking +-- strings longer than that when searching for a method handler local max_fname_len = 0 for i = 1, #api.functions do local fn = api.functions[i] - output:write(' map_put(cstr_t, uint64_t)(rpc_method_ids, "' - ..fn.name..'", '..i..');\n') + output:write(' map_put(String, rpc_method_handler_fn)(methods, '.. + '(String) {.data = "'..fn.name..'", '.. + '.size = sizeof("'..fn.name..'") - 1}, handle_'.. + fn.name..');\n') + if #fn.name > max_fname_len then max_fname_len = #fn.name end @@ -275,22 +277,24 @@ Object msgpack_rpc_dispatch(uint64_t channel_id, { msgpack_object method = req->via.array.ptr[2]; uint64_t method_id = method.via.u64; + rpc_method_handler_fn handler = NULL; - if (method.type == MSGPACK_OBJECT_BIN) { - char method_name[]]..(max_fname_len + 1)..[[]; - xstrlcpy(method_name, method.via.bin.ptr, min(method.via.bin.size, ]] ..(max_fname_len)..[[) + 1); - method_id = map_get(cstr_t, uint64_t)(rpc_method_ids, method_name); - if (!method_id) { - method_id = UINT64_MAX; - } - } + if (method.type == MSGPACK_OBJECT_BIN || method.type == MSGPACK_OBJECT_STR) { +]]) +output:write(' handler = map_get(String, rpc_method_handler_fn)') +output:write('(methods, (String){.data=(char *)method.via.bin.ptr,') +output:write('.size=min(method.via.bin.size, '..max_fname_len..')});\n') +output:write([[ + } else if (method_id <= ]]..#api.functions..[[) { + handler = rpc_method_handlers[method_id]; + } + + if (!handler) { + handler = handle_missing_method; + } + + return handler(channel_id, req, error); +} ]]) -output:write('\n // method_id=0 is specially handled') -output:write('\n assert(method_id > 0);') -output:write('\n'); -output:write('\n rpc_method_handler_fn handler = (method_id <= '..#api.functions..') ?') -output:write('\n rpc_method_handlers[method_id] : handle_missing_method;') -output:write('\n return handler(channel_id, req, error);') -output:write('\n}\n') output:close() diff --git a/src/nvim/os/msgpack_rpc.c b/src/nvim/os/msgpack_rpc.c index 38670e0bf8..cf32736e16 100644 --- a/src/nvim/os/msgpack_rpc.c +++ b/src/nvim/os/msgpack_rpc.c @@ -235,7 +235,8 @@ static char *msgpack_rpc_validate(uint64_t *response_id, msgpack_object *req) } if (req->via.array.ptr[2].type != MSGPACK_OBJECT_POSITIVE_INTEGER - && req->via.array.ptr[2].type != MSGPACK_OBJECT_BIN) { + && req->via.array.ptr[2].type != MSGPACK_OBJECT_BIN + && req->via.array.ptr[2].type != MSGPACK_OBJECT_STR) { return "Method must be a positive integer or a string"; } diff --git a/src/nvim/os/msgpack_rpc_helpers.c b/src/nvim/os/msgpack_rpc_helpers.c index eb43c3b2a6..eaba3b9785 100644 --- a/src/nvim/os/msgpack_rpc_helpers.c +++ b/src/nvim/os/msgpack_rpc_helpers.c @@ -81,12 +81,13 @@ bool msgpack_rpc_to_float(msgpack_object *obj, Float *arg) bool msgpack_rpc_to_string(msgpack_object *obj, String *arg) { - if (obj->type != MSGPACK_OBJECT_BIN) { + if (obj->type == MSGPACK_OBJECT_BIN || obj->type == MSGPACK_OBJECT_STR) { + arg->data = xmemdupz(obj->via.bin.ptr, obj->via.bin.size); + arg->size = obj->via.bin.size; + } else { return false; } - arg->data = xmemdupz(obj->via.bin.ptr, obj->via.bin.size); - arg->size = obj->via.bin.size; return true; } @@ -111,6 +112,7 @@ bool msgpack_rpc_to_object(msgpack_object *obj, Object *arg) return msgpack_rpc_to_float(obj, &arg->data.floating); case MSGPACK_OBJECT_BIN: + case MSGPACK_OBJECT_STR: arg->type = kObjectTypeString; return msgpack_rpc_to_string(obj, &arg->data.string); From 19bc29ee834516ff76944741b25fe158d2282b15 Mon Sep 17 00:00:00 2001 From: Thiago de Arruda Date: Wed, 3 Sep 2014 14:55:55 -0300 Subject: [PATCH 04/25] msgpack-rpc: Move handle_missing_method to msgpack_rpc.c Since that function is not automatically generated, it's best to place it in a normal C module --- scripts/msgpack-gen.lua | 14 +------------- src/nvim/os/msgpack_rpc.c | 10 ++++++++++ 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/scripts/msgpack-gen.lua b/scripts/msgpack-gen.lua index f868a67568..946cff1d11 100644 --- a/scripts/msgpack-gen.lua +++ b/scripts/msgpack-gen.lua @@ -220,18 +220,6 @@ for i = 1, #api.functions do end end -output:write([[ -static Object handle_missing_method(uint64_t channel_id, - msgpack_object *req, - Error *error) -{ - snprintf(error->msg, sizeof(error->msg), "Invalid function id"); - error->set = true; - return NIL; -} - -]]) - -- Generate the table of handler functions indexed by method id output:write([[ static const rpc_method_handler_fn rpc_method_handlers[] = { @@ -290,7 +278,7 @@ output:write([[ } if (!handler) { - handler = handle_missing_method; + handler = msgpack_rpc_handle_missing_method; } return handler(channel_id, req, error); diff --git a/src/nvim/os/msgpack_rpc.c b/src/nvim/os/msgpack_rpc.c index cf32736e16..4a586e8e01 100644 --- a/src/nvim/os/msgpack_rpc.c +++ b/src/nvim/os/msgpack_rpc.c @@ -115,6 +115,16 @@ void msgpack_rpc_error(char *msg, msgpack_packer *res) msgpack_pack_nil(res); } +/// Handler executed when an invalid method name is passed +Object msgpack_rpc_handle_missing_method(uint64_t channel_id, + msgpack_object *req, + Error *error) +{ + snprintf(error->msg, sizeof(error->msg), "Invalid method name"); + error->set = true; + return NIL; +} + /// Serializes a msgpack-rpc request or notification(id == 0) WBuffer *serialize_request(uint64_t request_id, String method, From cd70b9c0152f79887ed548e6cf8992ae2121fedb Mon Sep 17 00:00:00 2001 From: Thiago de Arruda Date: Wed, 3 Sep 2014 16:22:05 -0300 Subject: [PATCH 05/25] msgpack-rpc: Refactor API metadata discovery method A new method is now exposed via msgpack-rpc: "get_api_metadata". This method has the same job as the old method '0', it returns an object with API metadata for use by generators. There's one difference in the return value though: instead of returning a string containing another serialized msgpack document, the metadata object is returned directly(a separate deserialization step by clients is not required). --- scripts/msgpack-gen.lua | 15 +++++++++++++ src/nvim/os/msgpack_rpc.c | 46 ++++++++++++--------------------------- 2 files changed, 29 insertions(+), 32 deletions(-) diff --git a/scripts/msgpack-gen.lua b/scripts/msgpack-gen.lua index 946cff1d11..1516271973 100644 --- a/scripts/msgpack-gen.lua +++ b/scripts/msgpack-gen.lua @@ -123,6 +123,7 @@ end output:write([[ }; const unsigned int msgpack_metadata_size = sizeof(msgpack_metadata); +msgpack_unpacked msgpack_unpacked_metadata; ]]) @@ -237,6 +238,14 @@ static Map(String, rpc_method_handler_fn) *methods = NULL; void msgpack_rpc_init(void) { + msgpack_unpacked_init(&msgpack_unpacked_metadata); + if (msgpack_unpack_next(&msgpack_unpacked_metadata, + (const char *)msgpack_metadata, + msgpack_metadata_size, + NULL) != MSGPACK_UNPACK_SUCCESS) { + abort(); + } + methods = map_new(String, rpc_method_handler_fn)(); ]]) @@ -256,6 +265,12 @@ for i = 1, #api.functions do end end +local metadata_fn = 'get_api_metadata' +output:write(' map_put(String, rpc_method_handler_fn)(methods, '.. + '(String) {.data = "'..metadata_fn..'", '.. + '.size = sizeof("'..metadata_fn..'") - 1}, msgpack_rpc_handle_'.. + metadata_fn..');\n') + output:write('\n}\n\n') output:write([[ diff --git a/src/nvim/os/msgpack_rpc.c b/src/nvim/os/msgpack_rpc.c index 4a586e8e01..b06c8ee597 100644 --- a/src/nvim/os/msgpack_rpc.c +++ b/src/nvim/os/msgpack_rpc.c @@ -17,8 +17,7 @@ # include "os/msgpack_rpc.c.generated.h" #endif -extern const uint8_t msgpack_metadata[]; -extern const unsigned int msgpack_metadata_size; +extern msgpack_unpacked msgpack_unpacked_metadata; /// Validates the basic structure of the msgpack-rpc call and fills `res` /// with the basic response structure. @@ -39,11 +38,6 @@ WBuffer *msgpack_rpc_call(uint64_t channel_id, return serialize_response(response_id, err, NIL, sbuffer); } - if (req->via.array.ptr[2].type == MSGPACK_OBJECT_POSITIVE_INTEGER - && req->via.array.ptr[2].via.u64 == 0) { - return serialize_metadata(response_id, channel_id, sbuffer); - } - // dispatch the call Error error = { .set = false }; Object rv = msgpack_rpc_dispatch(channel_id, req, &error); @@ -125,6 +119,19 @@ Object msgpack_rpc_handle_missing_method(uint64_t channel_id, return NIL; } +/// Handler for retrieving API metadata through a msgpack-rpc call +Object msgpack_rpc_handle_get_api_metadata(uint64_t channel_id, + msgpack_object *req, + Error *error) +{ + Array rv = ARRAY_DICT_INIT; + Object metadata; + msgpack_rpc_to_object(&msgpack_unpacked_metadata.data, &metadata); + ADD(rv, INTEGER_OBJ((int64_t)channel_id)); + ADD(rv, metadata); + return ARRAY_OBJ(rv); +} + /// Serializes a msgpack-rpc request or notification(id == 0) WBuffer *serialize_request(uint64_t request_id, String method, @@ -190,31 +197,6 @@ WBuffer *serialize_response(uint64_t response_id, return rv; } -WBuffer *serialize_metadata(uint64_t id, - uint64_t channel_id, - msgpack_sbuffer *sbuffer) - FUNC_ATTR_NONNULL_ALL -{ - msgpack_packer pac; - msgpack_packer_init(&pac, sbuffer, msgpack_sbuffer_write); - msgpack_pack_array(&pac, 4); - msgpack_pack_int(&pac, 1); - msgpack_pack_uint64(&pac, id); - // Nil error - msgpack_pack_nil(&pac); - // The result is the [channel_id, metadata] array - msgpack_pack_array(&pac, 2); - msgpack_pack_uint64(&pac, channel_id); - msgpack_pack_bin(&pac, msgpack_metadata_size); - msgpack_pack_bin_body(&pac, msgpack_metadata, msgpack_metadata_size); - WBuffer *rv = wstream_new_buffer(xmemdup(sbuffer->data, sbuffer->size), - sbuffer->size, - 1, - free); - msgpack_sbuffer_clear(sbuffer); - return rv; -} - static char *msgpack_rpc_validate(uint64_t *response_id, msgpack_object *req) { // response id not known yet From d5e3cede28858dfe079b782cfea4bce6ebc268c0 Mon Sep 17 00:00:00 2001 From: Thiago de Arruda Date: Wed, 3 Sep 2014 17:19:55 -0300 Subject: [PATCH 06/25] msgpack-rpc: Remove support for integer ids in methods There's no need to have integer and string ids, and since we now fully support msgpack-RPC, support for integer ids was removed. --- scripts/msgpack-gen.lua | 19 +------------------ src/nvim/os/msgpack_rpc.c | 7 +++---- 2 files changed, 4 insertions(+), 22 deletions(-) diff --git a/scripts/msgpack-gen.lua b/scripts/msgpack-gen.lua index 1516271973..36fe07896b 100644 --- a/scripts/msgpack-gen.lua +++ b/scripts/msgpack-gen.lua @@ -60,8 +60,7 @@ for i = 1, #arg - 1 do local tmp = grammar:match(input:read('*all')) for i = 1, #tmp do api.functions[#api.functions + 1] = tmp[i] - local fn_id = #api.functions - local fn = api.functions[fn_id] + local fn = tmp[i] if #fn.parameters ~= 0 and fn.parameters[1][2] == 'channel_id' then -- this function should receive the channel id fn.receives_channel_id = true @@ -75,8 +74,6 @@ for i = 1, #arg - 1 do -- for specifying errors fn.parameters[#fn.parameters] = nil end - -- assign a unique integer id for each api function - fn.id = fn_id end input:close() end @@ -221,17 +218,6 @@ for i = 1, #api.functions do end end --- Generate the table of handler functions indexed by method id -output:write([[ -static const rpc_method_handler_fn rpc_method_handlers[] = { - [0] = (rpc_method_handler_fn)NULL]]) - -for i = 1, #api.functions do - local fn = api.functions[i] - output:write(',\n ['..i..'] = handle_'..fn.name..'') -end -output:write('\n};\n\n') - -- Generate a function that initializes method names with handler functions output:write([[ static Map(String, rpc_method_handler_fn) *methods = NULL; @@ -279,7 +265,6 @@ Object msgpack_rpc_dispatch(uint64_t channel_id, Error *error) { msgpack_object method = req->via.array.ptr[2]; - uint64_t method_id = method.via.u64; rpc_method_handler_fn handler = NULL; if (method.type == MSGPACK_OBJECT_BIN || method.type == MSGPACK_OBJECT_STR) { @@ -288,8 +273,6 @@ output:write(' handler = map_get(String, rpc_method_handler_fn)') output:write('(methods, (String){.data=(char *)method.via.bin.ptr,') output:write('.size=min(method.via.bin.size, '..max_fname_len..')});\n') output:write([[ - } else if (method_id <= ]]..#api.functions..[[) { - handler = rpc_method_handlers[method_id]; } if (!handler) { diff --git a/src/nvim/os/msgpack_rpc.c b/src/nvim/os/msgpack_rpc.c index b06c8ee597..485ef0d04b 100644 --- a/src/nvim/os/msgpack_rpc.c +++ b/src/nvim/os/msgpack_rpc.c @@ -226,10 +226,9 @@ static char *msgpack_rpc_validate(uint64_t *response_id, msgpack_object *req) return "Message type must be 0"; } - if (req->via.array.ptr[2].type != MSGPACK_OBJECT_POSITIVE_INTEGER - && req->via.array.ptr[2].type != MSGPACK_OBJECT_BIN - && req->via.array.ptr[2].type != MSGPACK_OBJECT_STR) { - return "Method must be a positive integer or a string"; + if (req->via.array.ptr[2].type != MSGPACK_OBJECT_BIN + && req->via.array.ptr[2].type != MSGPACK_OBJECT_STR) { + return "Method must be a string"; } if (req->via.array.ptr[3].type != MSGPACK_OBJECT_ARRAY) { From 41a48a3fc7d6f170d0c5ed852f1bf98ea3a89f27 Mon Sep 17 00:00:00 2001 From: Thiago de Arruda Date: Wed, 3 Sep 2014 18:35:38 -0300 Subject: [PATCH 07/25] memory: Use i18n messages for memory-related errors --- src/nvim/memory.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/nvim/memory.c b/src/nvim/memory.c index 50dcca3c84..1c3d6e372c 100644 --- a/src/nvim/memory.c +++ b/src/nvim/memory.c @@ -121,7 +121,8 @@ void *xmalloc(size_t size) void *ret = try_malloc(size); if (!ret) { - OUT_STR("Vim: Error: Out of memory.\n"); + OUT_STR(e_outofmem); + out_char('\n'); preserve_exit(); } return ret; @@ -147,7 +148,8 @@ void *xcalloc(size_t count, size_t size) if (!ret && (!count || !size)) ret = calloc(1, 1); if (!ret) { - OUT_STR("Vim: Error: Out of memory.\n"); + OUT_STR(e_outofmem); + out_char('\n'); preserve_exit(); } } @@ -174,7 +176,8 @@ void *xrealloc(void *ptr, size_t size) if (!ret && !size) ret = realloc(ptr, 1); if (!ret) { - OUT_STR("Vim: Error: Out of memory.\n"); + OUT_STR(e_outofmem); + out_char('\n'); preserve_exit(); } } @@ -194,7 +197,7 @@ void *xmallocz(size_t size) void *ret; if (total_size < size) { - OUT_STR("Vim: Data too large to fit into virtual memory space\n"); + OUT_STR(_("Vim: Data too large to fit into virtual memory space\n")); preserve_exit(); } @@ -316,7 +319,8 @@ char *xstrdup(const char *str) try_to_free_memory(); ret = strdup(str); if (!ret) { - OUT_STR("Vim: Error: Out of memory.\n"); + OUT_STR(e_outofmem); + out_char('\n'); preserve_exit(); } } From 505985b870b4b9d7cae07354518b28dd12ee5b6f Mon Sep 17 00:00:00 2001 From: Thiago de Arruda Date: Wed, 3 Sep 2014 18:43:58 -0300 Subject: [PATCH 08/25] msgpack-rpc: Remove the `msgpack_rpc_unpack` function The `msgpack_rpc_unpack` function was created to work around a deficiency in the msgpack unpack API, which did not let the caller know if parsing failed due to needing more data or to invalid input. The deficiency does not exist in the latest version of `msgpack_unpacker_next`, so it can safely be removed. --- src/nvim/os/channel.c | 15 +++++++++++---- src/nvim/os/msgpack_rpc.c | 36 ------------------------------------ 2 files changed, 11 insertions(+), 40 deletions(-) diff --git a/src/nvim/os/channel.c b/src/nvim/os/channel.c index bad82bc272..bc1bef3a57 100644 --- a/src/nvim/os/channel.c +++ b/src/nvim/os/channel.c @@ -22,8 +22,10 @@ #include "nvim/memory.h" #include "nvim/os_unix.h" #include "nvim/message.h" +#include "nvim/term.h" #include "nvim/map.h" #include "nvim/log.h" +#include "nvim/misc1.h" #include "nvim/lib/kvec.h" #define CHANNEL_BUFFER_SIZE 0xffff @@ -331,11 +333,10 @@ static void parse_msgpack(RStream *rstream, void *data, bool eof) msgpack_unpacked unpacked; msgpack_unpacked_init(&unpacked); - UnpackResult result; + msgpack_unpack_return result; // Deserialize everything we can. - while ((result = msgpack_rpc_unpack(channel->unpacker, &unpacked)) - == kUnpackResultOk) { + while ((result = msgpack_unpacker_next(channel->unpacker, &unpacked))) { if (kv_size(channel->call_stack) && is_rpc_response(&unpacked.data)) { if (is_valid_rpc_response(&unpacked.data, channel)) { call_stack_pop(&unpacked.data, channel); @@ -362,7 +363,13 @@ static void parse_msgpack(RStream *rstream, void *data, bool eof) } } - if (result == kUnpackResultFail) { + if (result == MSGPACK_UNPACK_NOMEM_ERROR) { + OUT_STR(e_outofmem); + out_char('\n'); + preserve_exit(); + } + + if (result == MSGPACK_UNPACK_PARSE_ERROR) { // See src/msgpack/unpack_template.h in msgpack source tree for // causes for this error(search for 'goto _failed') // diff --git a/src/nvim/os/msgpack_rpc.c b/src/nvim/os/msgpack_rpc.c index 485ef0d04b..fe4918b7d9 100644 --- a/src/nvim/os/msgpack_rpc.c +++ b/src/nvim/os/msgpack_rpc.c @@ -57,42 +57,6 @@ WBuffer *msgpack_rpc_call(uint64_t channel_id, return serialize_response(response_id, NULL, rv, sbuffer); } -/// Try to unpack a msgpack document from the data in the unpacker buffer. This -/// function is a replacement to msgpack.h `msgpack_unpack_next` that lets -/// the called know if the unpacking failed due to bad input or due to missing -/// data. -/// -/// @param unpacker The unpacker containing the parse buffer -/// @param result The result which will contain the parsed object -/// @return kUnpackResultOk : An object was parsed -/// kUnpackResultFail : Got bad input -/// kUnpackResultNeedMore: Need more data -UnpackResult msgpack_rpc_unpack(msgpack_unpacker* unpacker, - msgpack_unpacked* result) - FUNC_ATTR_NONNULL_ALL -{ - if (result->zone != NULL) { - msgpack_zone_free(result->zone); - } - - int res = msgpack_unpacker_execute(unpacker); - - if (res > 0) { - result->zone = msgpack_unpacker_release_zone(unpacker); - result->data = msgpack_unpacker_data(unpacker); - msgpack_unpacker_reset(unpacker); - return kUnpackResultOk; - } - - if (res < 0) { - // Since we couldn't parse it, destroy the data consumed so far - msgpack_unpacker_reset(unpacker); - return kUnpackResultFail; - } - - return kUnpackResultNeedMore; -} - /// Finishes the msgpack-rpc call with an error message. /// /// @param msg The error message From d5a60d17fbca33ca96124288e69937a276d3abda Mon Sep 17 00:00:00 2001 From: Thiago de Arruda Date: Sun, 7 Sep 2014 18:48:10 -0300 Subject: [PATCH 09/25] api/msgpack-rpc: Remove specialized array types Specialized array types(BufferArray, WindowArray, etc) were added to the API for two main reasons: - msgpack used to lack a way of serializing appliaction-specific types and there was no obvious way of making an API function accept/return arrays of custom objects such as buffers(which are represented as integers, so clients didn't have a way to distinguish from normal numbers) - Let clients in statically-typed languages that support generics have a better typed API With msgpack 2.0 EXT type the first item is no longer a factor and this commit starts by removing the specialized array types. The second item will be addressed in the future by making the API metadata return extra useful information for statically-typed languages. --- src/nvim/api/buffer.c | 46 ++++++++++--------- src/nvim/api/private/defs.h | 19 -------- src/nvim/api/tabpage.c | 8 ++-- src/nvim/api/vim.c | 37 ++++++++-------- src/nvim/os/msgpack_rpc_helpers.c | 74 ------------------------------- src/nvim/os/msgpack_rpc_helpers.h | 20 --------- 6 files changed, 49 insertions(+), 155 deletions(-) diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c index a268e04559..380de12c89 100644 --- a/src/nvim/api/buffer.c +++ b/src/nvim/api/buffer.c @@ -51,10 +51,10 @@ Integer buffer_get_length(Buffer buffer, Error *err) String buffer_get_line(Buffer buffer, Integer index, Error *err) { String rv = {.size = 0}; - StringArray slice = buffer_get_slice(buffer, index, index, true, true, err); + Array slice = buffer_get_slice(buffer, index, index, true, true, err); if (!err->set && slice.size) { - rv = slice.items[0]; + rv = slice.items[0].data.string; } free(slice.items); @@ -70,7 +70,8 @@ String buffer_get_line(Buffer buffer, Integer index, Error *err) /// @param[out] err Details of an error that may have occurred void buffer_set_line(Buffer buffer, Integer index, String line, Error *err) { - StringArray array = {.items = &line, .size = 1}; + Object l = STRING_OBJ(line); + Array array = {.items = &l, .size = 1}; buffer_set_slice(buffer, index, index, true, true, array, err); } @@ -81,7 +82,7 @@ void buffer_set_line(Buffer buffer, Integer index, String line, Error *err) /// @param[out] err Details of an error that may have occurred void buffer_del_line(Buffer buffer, Integer index, Error *err) { - StringArray array = ARRAY_DICT_INIT; + Array array = ARRAY_DICT_INIT; buffer_set_slice(buffer, index, index, true, true, array, err); } @@ -94,14 +95,14 @@ void buffer_del_line(Buffer buffer, Integer index, Error *err) /// @param include_end True if the slice includes the `end` parameter /// @param[out] err Details of an error that may have occurred /// @return An array of lines -StringArray buffer_get_slice(Buffer buffer, - Integer start, - Integer end, - Boolean include_start, - Boolean include_end, - Error *err) +Array buffer_get_slice(Buffer buffer, + Integer start, + Integer end, + Boolean include_start, + Boolean include_end, + Error *err) { - StringArray rv = ARRAY_DICT_INIT; + Array rv = ARRAY_DICT_INIT; buf_T *buf = find_buffer_by_handle(buffer, err); if (!buf) { @@ -118,7 +119,7 @@ StringArray buffer_get_slice(Buffer buffer, } rv.size = (size_t)(end - start); - rv.items = xcalloc(sizeof(String), rv.size); + rv.items = xcalloc(sizeof(Object), rv.size); for (size_t i = 0; i < rv.size; i++) { int64_t lnum = start + (int64_t)i; @@ -129,13 +130,13 @@ StringArray buffer_get_slice(Buffer buffer, } const char *bufstr = (char *) ml_get_buf(buf, (linenr_T) lnum, false); - rv.items[i] = cstr_to_string(bufstr); + rv.items[i] = STRING_OBJ(cstr_to_string(bufstr)); } end: if (err->set) { for (size_t i = 0; i < rv.size; i++) { - free(rv.items[i].data); + free(rv.items[i].data.string.data); } free(rv.items); @@ -152,15 +153,15 @@ end: /// @param end The last line index /// @param include_start True if the slice includes the `start` parameter /// @param include_end True if the slice includes the `end` parameter -/// @param lines An array of lines to use as replacement(A 0-length array -/// will simply delete the line range) +/// @param replacement An array of lines to use as replacement(A 0-length +// array will simply delete the line range) /// @param[out] err Details of an error that may have occurred void buffer_set_slice(Buffer buffer, Integer start, Integer end, Boolean include_start, Boolean include_end, - StringArray replacement, + Array replacement, Error *err) { buf_T *buf = find_buffer_by_handle(buffer, err); @@ -184,10 +185,15 @@ void buffer_set_slice(Buffer buffer, size_t new_len = replacement.size; size_t old_len = (size_t)(end - start); ssize_t extra = 0; // lines added to text, can be negative - char **lines = (new_len != 0) ? xmalloc(new_len * sizeof(char *)) : NULL; + char **lines = (new_len != 0) ? xcalloc(new_len, sizeof(char *)) : NULL; for (size_t i = 0; i < new_len; i++) { - String l = replacement.items[i]; + if (replacement.items[i].type != kObjectTypeString) { + set_api_error("all items in the replacement array must be strings", err); + goto end; + } + + String l = replacement.items[i].data.string; lines[i] = xmemdupz(l.data, l.size); } @@ -430,7 +436,7 @@ Boolean buffer_is_valid(Buffer buffer) /// to the end of the buffer. /// @param lines An array of lines /// @param[out] err Details of an error that may have occurred -void buffer_insert(Buffer buffer, Integer lnum, StringArray lines, Error *err) +void buffer_insert(Buffer buffer, Integer lnum, Array lines, Error *err) { buffer_set_slice(buffer, lnum, lnum, false, true, lines, err); } diff --git a/src/nvim/api/private/defs.h b/src/nvim/api/private/defs.h index b049412014..071563a628 100644 --- a/src/nvim/api/private/defs.h +++ b/src/nvim/api/private/defs.h @@ -11,12 +11,6 @@ #define POSITION_INIT { .row = 0, .col = 0 } #define REMOTE_TYPE(type) typedef uint64_t type -#define TYPED_ARRAY_OF(type) \ - typedef struct { \ - type *items; \ - size_t size; \ - } type##Array - // Basic types typedef struct { char msg[256]; @@ -38,11 +32,6 @@ REMOTE_TYPE(Tabpage); typedef struct object Object; -TYPED_ARRAY_OF(Buffer); -TYPED_ARRAY_OF(Window); -TYPED_ARRAY_OF(Tabpage); -TYPED_ARRAY_OF(String); - typedef struct { Integer row, col; } Position; @@ -71,10 +60,6 @@ typedef enum { kObjectTypeArray, kObjectTypeDictionary, kObjectTypePosition, - kObjectTypeStringArray, - kObjectTypeBufferArray, - kObjectTypeWindowArray, - kObjectTypeTabpageArray, } ObjectType; struct object { @@ -90,10 +75,6 @@ struct object { Array array; Dictionary dictionary; Position position; - StringArray stringarray; - BufferArray bufferarray; - WindowArray windowarray; - TabpageArray tabpagearray; } data; }; diff --git a/src/nvim/api/tabpage.c b/src/nvim/api/tabpage.c index 535722c087..901f9a6c1a 100644 --- a/src/nvim/api/tabpage.c +++ b/src/nvim/api/tabpage.c @@ -13,9 +13,9 @@ /// @param tabpage The tabpage /// @param[out] err Details of an error that may have occurred /// @return The number of windows in `tabpage` -WindowArray tabpage_get_windows(Tabpage tabpage, Error *err) +Array tabpage_get_windows(Tabpage tabpage, Error *err) { - WindowArray rv = ARRAY_DICT_INIT; + Array rv = ARRAY_DICT_INIT; tabpage_T *tab = find_tab_by_handle(tabpage, err); if (!tab) { @@ -32,14 +32,14 @@ WindowArray tabpage_get_windows(Tabpage tabpage, Error *err) rv.size++; } - rv.items = xmalloc(sizeof(Window) * rv.size); + rv.items = xmalloc(sizeof(Object) * rv.size); size_t i = 0; FOR_ALL_TAB_WINDOWS(tp, wp) { if (tp != tab) { break; } - rv.items[i++] = wp->handle; + rv.items[i++] = WINDOW_OBJ(wp->handle); } return rv; diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index a2c50b4c81..b1d5a067b4 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -149,9 +149,9 @@ Integer vim_strwidth(String str, Error *err) /// Returns a list of paths contained in 'runtimepath' /// /// @return The list of paths -StringArray vim_list_runtime_paths(void) +Array vim_list_runtime_paths(void) { - StringArray rv = ARRAY_DICT_INIT; + Array rv = ARRAY_DICT_INIT; uint8_t *rtp = p_rtp; if (*rtp == NUL) { @@ -168,19 +168,20 @@ StringArray vim_list_runtime_paths(void) } // Allocate memory for the copies - rv.items = xmalloc(sizeof(String) * rv.size); + rv.items = xmalloc(sizeof(Object) * rv.size); // reset the position rtp = p_rtp; // Start copying for (size_t i = 0; i < rv.size && *rtp != NUL; i++) { - rv.items[i].data = xmalloc(MAXPATHL); + rv.items[i].type = kObjectTypeString; + rv.items[i].data.string.data = xmalloc(MAXPATHL); // Copy the path from 'runtimepath' to rv.items[i] int length = copy_option_part(&rtp, - (char_u *)rv.items[i].data, + (char_u *)rv.items[i].data.string.data, MAXPATHL, ","); assert(length >= 0); - rv.items[i].size = (size_t)length; + rv.items[i].data.string.size = (size_t)length; } return rv; @@ -310,9 +311,9 @@ void vim_err_write(String str) /// Gets the current list of buffer handles /// /// @return The number of buffers -BufferArray vim_get_buffers(void) +Array vim_get_buffers(void) { - BufferArray rv = ARRAY_DICT_INIT; + Array rv = ARRAY_DICT_INIT; buf_T *b = firstbuf; while (b) { @@ -320,12 +321,12 @@ BufferArray vim_get_buffers(void) b = b->b_next; } - rv.items = xmalloc(sizeof(Buffer) * rv.size); + rv.items = xmalloc(sizeof(Object) * rv.size); size_t i = 0; b = firstbuf; while (b) { - rv.items[i++] = b->handle; + rv.items[i++] = BUFFER_OBJ(b->handle); b = b->b_next; } @@ -370,9 +371,9 @@ void vim_set_current_buffer(Buffer buffer, Error *err) /// Gets the current list of window handles /// /// @return The number of windows -WindowArray vim_get_windows(void) +Array vim_get_windows(void) { - WindowArray rv = ARRAY_DICT_INIT; + Array rv = ARRAY_DICT_INIT; tabpage_T *tp; win_T *wp; @@ -380,11 +381,11 @@ WindowArray vim_get_windows(void) rv.size++; } - rv.items = xmalloc(sizeof(Window) * rv.size); + rv.items = xmalloc(sizeof(Object) * rv.size); size_t i = 0; FOR_ALL_TAB_WINDOWS(tp, wp) { - rv.items[i++] = wp->handle; + rv.items[i++] = WINDOW_OBJ(wp->handle); } return rv; @@ -426,9 +427,9 @@ void vim_set_current_window(Window window, Error *err) /// Gets the current list of tabpage handles /// /// @return The number of tab pages -TabpageArray vim_get_tabpages(void) +Array vim_get_tabpages(void) { - TabpageArray rv = ARRAY_DICT_INIT; + Array rv = ARRAY_DICT_INIT; tabpage_T *tp = first_tabpage; while (tp) { @@ -436,12 +437,12 @@ TabpageArray vim_get_tabpages(void) tp = tp->tp_next; } - rv.items = xmalloc(sizeof(Tabpage) * rv.size); + rv.items = xmalloc(sizeof(Object) * rv.size); size_t i = 0; tp = first_tabpage; while (tp) { - rv.items[i++] = tp->handle; + rv.items[i++] = TABPAGE_OBJ(tp->handle); tp = tp->tp_next; } diff --git a/src/nvim/os/msgpack_rpc_helpers.c b/src/nvim/os/msgpack_rpc_helpers.c index eaba3b9785..3210f2b6b2 100644 --- a/src/nvim/os/msgpack_rpc_helpers.c +++ b/src/nvim/os/msgpack_rpc_helpers.c @@ -19,42 +19,6 @@ msgpack_pack_uint64(res, result); \ } -#define TYPED_ARRAY_IMPL(t, lt) \ - bool msgpack_rpc_to_##lt##array(msgpack_object *obj, t##Array *arg) \ - { \ - if (obj->type != MSGPACK_OBJECT_ARRAY) { \ - return false; \ - } \ - \ - arg->size = obj->via.array.size; \ - arg->items = xcalloc(obj->via.array.size, sizeof(t)); \ - \ - for (size_t i = 0; i < obj->via.array.size; i++) { \ - if (!msgpack_rpc_to_##lt(obj->via.array.ptr + i, &arg->items[i])) { \ - return false; \ - } \ - } \ - \ - return true; \ - } \ - \ - void msgpack_rpc_from_##lt##array(t##Array result, msgpack_packer *res) \ - { \ - msgpack_pack_array(res, result.size); \ - \ - for (size_t i = 0; i < result.size; i++) { \ - msgpack_rpc_from_##lt(result.items[i], res); \ - } \ - } \ - \ - void msgpack_rpc_free_##lt##array(t##Array value) { \ - for (size_t i = 0; i < value.size; i++) { \ - msgpack_rpc_free_##lt(value.items[i]); \ - } \ - \ - free(value.items); \ - } - bool msgpack_rpc_to_boolean(msgpack_object *obj, Boolean *arg) { *arg = obj->via.boolean; @@ -249,22 +213,6 @@ void msgpack_rpc_from_object(Object result, msgpack_packer *res) msgpack_rpc_from_tabpage(result.data.tabpage, res); break; - case kObjectTypeStringArray: - msgpack_rpc_from_stringarray(result.data.stringarray, res); - break; - - case kObjectTypeBufferArray: - msgpack_rpc_from_bufferarray(result.data.bufferarray, res); - break; - - case kObjectTypeWindowArray: - msgpack_rpc_from_windowarray(result.data.windowarray, res); - break; - - case kObjectTypeTabpageArray: - msgpack_rpc_from_tabpagearray(result.data.tabpagearray, res); - break; - case kObjectTypeDictionary: msgpack_rpc_from_dictionary(result.data.dictionary, res); break; @@ -327,22 +275,6 @@ void msgpack_rpc_free_object(Object value) msgpack_rpc_free_array(value.data.array); break; - case kObjectTypeStringArray: - msgpack_rpc_free_stringarray(value.data.stringarray); - break; - - case kObjectTypeBufferArray: - msgpack_rpc_free_bufferarray(value.data.bufferarray); - break; - - case kObjectTypeWindowArray: - msgpack_rpc_free_windowarray(value.data.windowarray); - break; - - case kObjectTypeTabpageArray: - msgpack_rpc_free_tabpagearray(value.data.tabpagearray); - break; - case kObjectTypeDictionary: msgpack_rpc_free_dictionary(value.data.dictionary); break; @@ -374,9 +306,3 @@ void msgpack_rpc_free_dictionary(Dictionary value) REMOTE_FUNCS_IMPL(Buffer, buffer) REMOTE_FUNCS_IMPL(Window, window) REMOTE_FUNCS_IMPL(Tabpage, tabpage) - -TYPED_ARRAY_IMPL(Buffer, buffer) -TYPED_ARRAY_IMPL(Window, window) -TYPED_ARRAY_IMPL(Tabpage, tabpage) -TYPED_ARRAY_IMPL(String, string) - diff --git a/src/nvim/os/msgpack_rpc_helpers.h b/src/nvim/os/msgpack_rpc_helpers.h index e3d1e756ef..f4d01faa09 100644 --- a/src/nvim/os/msgpack_rpc_helpers.h +++ b/src/nvim/os/msgpack_rpc_helpers.h @@ -34,14 +34,6 @@ bool msgpack_rpc_to_tabpage(msgpack_object *obj, Tabpage *arg) FUNC_ATTR_NONNULL_ALL; bool msgpack_rpc_to_object(msgpack_object *obj, Object *arg) FUNC_ATTR_NONNULL_ALL; -bool msgpack_rpc_to_stringarray(msgpack_object *obj, StringArray *arg) - FUNC_ATTR_NONNULL_ALL; -bool msgpack_rpc_to_bufferarray(msgpack_object *obj, BufferArray *arg) - FUNC_ATTR_NONNULL_ALL; -bool msgpack_rpc_to_windowarray(msgpack_object *obj, WindowArray *arg) - FUNC_ATTR_NONNULL_ALL; -bool msgpack_rpc_to_tabpagearray(msgpack_object *obj, TabpageArray *arg) - FUNC_ATTR_NONNULL_ALL; bool msgpack_rpc_to_array(msgpack_object *obj, Array *arg) FUNC_ATTR_NONNULL_ALL; bool msgpack_rpc_to_dictionary(msgpack_object *obj, Dictionary *arg) @@ -71,14 +63,6 @@ void msgpack_rpc_from_tabpage(Tabpage result, msgpack_packer *res) FUNC_ATTR_NONNULL_ARG(2); void msgpack_rpc_from_object(Object result, msgpack_packer *res) FUNC_ATTR_NONNULL_ARG(2); -void msgpack_rpc_from_stringarray(StringArray result, msgpack_packer *res) - FUNC_ATTR_NONNULL_ARG(2); -void msgpack_rpc_from_bufferarray(BufferArray result, msgpack_packer *res) - FUNC_ATTR_NONNULL_ARG(2); -void msgpack_rpc_from_windowarray(WindowArray result, msgpack_packer *res) - FUNC_ATTR_NONNULL_ARG(2); -void msgpack_rpc_from_tabpagearray(TabpageArray result, msgpack_packer *res) - FUNC_ATTR_NONNULL_ARG(2); void msgpack_rpc_from_array(Array result, msgpack_packer *res) FUNC_ATTR_NONNULL_ARG(2); void msgpack_rpc_from_dictionary(Dictionary result, msgpack_packer *res) @@ -113,10 +97,6 @@ void msgpack_rpc_free_string(String value); #define msgpack_rpc_free_window(value) #define msgpack_rpc_free_tabpage(value) void msgpack_rpc_free_object(Object value); -void msgpack_rpc_free_stringarray(StringArray value); -void msgpack_rpc_free_bufferarray(BufferArray value); -void msgpack_rpc_free_windowarray(WindowArray value); -void msgpack_rpc_free_tabpagearray(TabpageArray value); void msgpack_rpc_free_array(Array value); void msgpack_rpc_free_dictionary(Dictionary value); From 2f566c83d9eee4a8097c9a18eb58dcef6adf894e Mon Sep 17 00:00:00 2001 From: Thiago de Arruda Date: Sun, 7 Sep 2014 20:40:07 -0300 Subject: [PATCH 10/25] api/msgpack-rpc: Parse type information from api/private/defs.h Enhance msgpack-gen.lua to extract custom api type codes from the ObjectType enum in api/private/defs.h. The type information is made available from the api metadata and clients can use to correctly serialize/deserialize these types using msgpack EXT type. --- scripts/msgpack-gen.lua | 25 ++++++++++++++++++++++--- src/nvim/CMakeLists.txt | 4 +++- src/nvim/api/private/defs.h | 20 ++++++++++++-------- 3 files changed, 37 insertions(+), 12 deletions(-) diff --git a/scripts/msgpack-gen.lua b/scripts/msgpack-gen.lua index 36fe07896b..7da52432d6 100644 --- a/scripts/msgpack-gen.lua +++ b/scripts/msgpack-gen.lua @@ -38,9 +38,28 @@ assert(#arg >= 1) -- api metadata api = { functions = {}, - -- Helpers for object-oriented languages - classes = {'Buffer', 'Window', 'Tabpage'} + types = {} } + +-- Extract type codes from api/private/defs.h. The codes are values between +-- comment markers in the ObjectType enum +local typedefs_header = arg[1] +local input = io.open(typedefs_header, 'rb') +local reading_types = false +while true do + local line = input:read('*l'):gsub("^%s*(.-)%s*$", "%1") + if reading_types then + if line == '// end custom types' then + break + end + local type_name = line:gsub("^kObjectType(.-),$", "%1") + api.types[#api.types + 1] = type_name + else + reading_types = line == '// start custom types' + end +end +input:close() + -- names of all headers relative to the source root(for inclusion in the -- generated file) headers = {} @@ -48,7 +67,7 @@ headers = {} outputf = arg[#arg] -- read each input file, parse and append to the api metadata -for i = 1, #arg - 1 do +for i = 2, #arg - 1 do local full_path = arg[i] local parts = {} for part in string.gmatch(full_path, '[^/]+') do diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index 83de3347bd..405cd20486 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -3,6 +3,7 @@ include(CheckLibraryExists) set(GENERATED_DIR ${PROJECT_BINARY_DIR}/src/nvim/auto) set(DISPATCH_GENERATOR ${PROJECT_SOURCE_DIR}/scripts/msgpack-gen.lua) file(GLOB API_HEADERS api/*.h) +file(GLOB API_DEFS api/private/defs.h) set(MSGPACK_RPC_HEADER ${PROJECT_SOURCE_DIR}/src/nvim/os/msgpack_rpc.h) set(MSGPACK_DISPATCH ${GENERATED_DIR}/msgpack_dispatch.c) set(HEADER_GENERATOR ${PROJECT_SOURCE_DIR}/scripts/gendeclarations.lua) @@ -123,9 +124,10 @@ foreach(sfile ${NEOVIM_SOURCES} endforeach() add_custom_command(OUTPUT ${MSGPACK_DISPATCH} - COMMAND ${LUA_PRG} ${DISPATCH_GENERATOR} ${API_HEADERS} ${MSGPACK_DISPATCH} + COMMAND ${LUA_PRG} ${DISPATCH_GENERATOR} ${API_DEFS} ${API_HEADERS} ${MSGPACK_DISPATCH} DEPENDS ${API_HEADERS} + ${API_DEFS} ${MSGPACK_RPC_HEADER} ${DISPATCH_GENERATOR} ) diff --git a/src/nvim/api/private/defs.h b/src/nvim/api/private/defs.h index 071563a628..689594f231 100644 --- a/src/nvim/api/private/defs.h +++ b/src/nvim/api/private/defs.h @@ -49,32 +49,36 @@ typedef struct { } Dictionary; typedef enum { +// The following comments are markers that msgpack-gen.lua uses to extract +// types, don't remove! +// start custom types + kObjectTypePosition, + kObjectTypeBuffer, + kObjectTypeWindow, + kObjectTypeTabpage, +// end custom types kObjectTypeNil, kObjectTypeBoolean, kObjectTypeInteger, kObjectTypeFloat, kObjectTypeString, - kObjectTypeBuffer, - kObjectTypeWindow, - kObjectTypeTabpage, kObjectTypeArray, kObjectTypeDictionary, - kObjectTypePosition, } ObjectType; struct object { ObjectType type; union { + Position position; + Buffer buffer; + Window window; + Tabpage tabpage; Boolean boolean; Integer integer; Float floating; String string; - Buffer buffer; - Window window; - Tabpage tabpage; Array array; Dictionary dictionary; - Position position; } data; }; From 38dcfb60627cbdfa2eaf5d0d991c872c85868f22 Mon Sep 17 00:00:00 2001 From: Thiago de Arruda Date: Mon, 8 Sep 2014 16:03:37 -0300 Subject: [PATCH 11/25] api/msgpack-rpc: Use EXT type to serialize Buffer/Window/Tabpage --- src/nvim/os/msgpack_rpc_helpers.c | 55 ++++++++++++++++++++++++++----- src/nvim/os/msgpack_rpc_helpers.h | 2 ++ src/nvim/os_unix.c | 2 ++ 3 files changed, 51 insertions(+), 8 deletions(-) diff --git a/src/nvim/os/msgpack_rpc_helpers.c b/src/nvim/os/msgpack_rpc_helpers.c index 3210f2b6b2..5d2f53bbcf 100644 --- a/src/nvim/os/msgpack_rpc_helpers.c +++ b/src/nvim/os/msgpack_rpc_helpers.c @@ -7,18 +7,48 @@ #include "nvim/vim.h" #include "nvim/memory.h" -#define REMOTE_FUNCS_IMPL(t, lt) \ +static msgpack_zone zone; +static msgpack_sbuffer sbuffer; + +#define HANDLE_TYPE_CONVERSION_IMPL(t, lt) \ bool msgpack_rpc_to_##lt(msgpack_object *obj, t *arg) \ { \ - *arg = obj->via.u64; \ - return obj->type == MSGPACK_OBJECT_POSITIVE_INTEGER; \ + if (obj->type != MSGPACK_OBJECT_EXT \ + || obj->via.ext.type != kObjectType##t) { \ + return false; \ + } \ + \ + msgpack_object data; \ + msgpack_unpack_return ret = msgpack_unpack(obj->via.ext.ptr, \ + obj->via.ext.size, \ + NULL, \ + &zone, \ + &data); \ + \ + if (ret != MSGPACK_UNPACK_SUCCESS) { \ + return false; \ + } \ + \ + *arg = data.via.u64; \ + return true; \ } \ \ - void msgpack_rpc_from_##lt(t result, msgpack_packer *res) \ + void msgpack_rpc_from_##lt(t o, msgpack_packer *res) \ { \ - msgpack_pack_uint64(res, result); \ + msgpack_packer pac; \ + msgpack_packer_init(&pac, &sbuffer, msgpack_sbuffer_write); \ + msgpack_pack_uint64(&pac, o); \ + msgpack_pack_ext(res, sbuffer.size, kObjectType##t); \ + msgpack_pack_ext_body(res, sbuffer.data, sbuffer.size); \ + msgpack_sbuffer_clear(&sbuffer); \ } +void msgpack_rpc_helpers_init(void) +{ + msgpack_zone_init(&zone, 0xfff); + msgpack_sbuffer_init(&sbuffer); +} + bool msgpack_rpc_to_boolean(msgpack_object *obj, Boolean *arg) { *arg = obj->via.boolean; @@ -88,6 +118,15 @@ bool msgpack_rpc_to_object(msgpack_object *obj, Object *arg) arg->type = kObjectTypeDictionary; return msgpack_rpc_to_dictionary(obj, &arg->data.dictionary); + case MSGPACK_OBJECT_EXT: + switch (obj->via.ext.type) { + case kObjectTypeBuffer: + return msgpack_rpc_to_buffer(obj, &arg->data.buffer); + case kObjectTypeWindow: + return msgpack_rpc_to_window(obj, &arg->data.window); + case kObjectTypeTabpage: + return msgpack_rpc_to_tabpage(obj, &arg->data.tabpage); + } default: return false; } @@ -303,6 +342,6 @@ void msgpack_rpc_free_dictionary(Dictionary value) free(value.items); } -REMOTE_FUNCS_IMPL(Buffer, buffer) -REMOTE_FUNCS_IMPL(Window, window) -REMOTE_FUNCS_IMPL(Tabpage, tabpage) +HANDLE_TYPE_CONVERSION_IMPL(Buffer, buffer) +HANDLE_TYPE_CONVERSION_IMPL(Window, window) +HANDLE_TYPE_CONVERSION_IMPL(Tabpage, tabpage) diff --git a/src/nvim/os/msgpack_rpc_helpers.h b/src/nvim/os/msgpack_rpc_helpers.h index f4d01faa09..77e3d2637e 100644 --- a/src/nvim/os/msgpack_rpc_helpers.h +++ b/src/nvim/os/msgpack_rpc_helpers.h @@ -9,6 +9,8 @@ #include "nvim/func_attr.h" #include "nvim/api/private/defs.h" +void msgpack_rpc_helpers_init(void); + /// Functions for validating and converting from msgpack types to C types. /// These are used by `msgpack_rpc_dispatch` to validate and convert each /// argument. diff --git a/src/nvim/os_unix.c b/src/nvim/os_unix.c index 3fa8b803b2..6c79fbd479 100644 --- a/src/nvim/os_unix.c +++ b/src/nvim/os_unix.c @@ -55,6 +55,7 @@ #include "nvim/os/signal.h" #include "nvim/os/job.h" #include "nvim/os/msgpack_rpc.h" +#include "nvim/os/msgpack_rpc_helpers.h" #if defined(HAVE_SYS_IOCTL_H) # include @@ -166,6 +167,7 @@ void mch_init(void) #endif msgpack_rpc_init(); + msgpack_rpc_helpers_init(); event_init(); } From 2792a0e33c0845fdd5b1b752d99ee573f6534256 Mon Sep 17 00:00:00 2001 From: Thiago de Arruda Date: Tue, 9 Sep 2014 08:59:48 -0300 Subject: [PATCH 12/25] api/msgpack-rpc: Remove Position type, using arrays instead. --- src/nvim/api/buffer.c | 9 +++---- src/nvim/api/private/defs.h | 7 ------ src/nvim/api/window.c | 40 +++++++++++++++++-------------- src/nvim/os/msgpack_rpc_helpers.c | 21 ---------------- src/nvim/os/msgpack_rpc_helpers.h | 4 ---- 5 files changed, 27 insertions(+), 54 deletions(-) diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c index 380de12c89..383e13fd92 100644 --- a/src/nvim/api/buffer.c +++ b/src/nvim/api/buffer.c @@ -447,9 +447,9 @@ void buffer_insert(Buffer buffer, Integer lnum, Array lines, Error *err) /// @param name The mark's name /// @param[out] err Details of an error that may have occurred /// @return The (row, col) tuple -Position buffer_get_mark(Buffer buffer, String name, Error *err) +Array buffer_get_mark(Buffer buffer, String name, Error *err) { - Position rv = POSITION_INIT; + Array rv = ARRAY_DICT_INIT; buf_T *buf = find_buffer_by_handle(buffer, err); if (!buf) { @@ -479,8 +479,9 @@ Position buffer_get_mark(Buffer buffer, String name, Error *err) return rv; } - rv.row = posp->lnum; - rv.col = posp->col; + ADD(rv, INTEGER_OBJ(posp->lnum)); + ADD(rv, INTEGER_OBJ(posp->col)); + return rv; } diff --git a/src/nvim/api/private/defs.h b/src/nvim/api/private/defs.h index 689594f231..11800ed786 100644 --- a/src/nvim/api/private/defs.h +++ b/src/nvim/api/private/defs.h @@ -8,7 +8,6 @@ #define ARRAY_DICT_INIT {.size = 0, .items = NULL} #define STRING_INIT {.data = NULL, .size = 0} #define OBJECT_INIT { .type = kObjectTypeNil } -#define POSITION_INIT { .row = 0, .col = 0 } #define REMOTE_TYPE(type) typedef uint64_t type // Basic types @@ -32,10 +31,6 @@ REMOTE_TYPE(Tabpage); typedef struct object Object; -typedef struct { - Integer row, col; -} Position; - typedef struct { Object *items; size_t size, capacity; @@ -52,7 +47,6 @@ typedef enum { // The following comments are markers that msgpack-gen.lua uses to extract // types, don't remove! // start custom types - kObjectTypePosition, kObjectTypeBuffer, kObjectTypeWindow, kObjectTypeTabpage, @@ -69,7 +63,6 @@ typedef enum { struct object { ObjectType type; union { - Position position; Buffer buffer; Window window; Tabpage tabpage; diff --git a/src/nvim/api/window.c b/src/nvim/api/window.c index 1ab441bed3..967d8acda4 100644 --- a/src/nvim/api/window.c +++ b/src/nvim/api/window.c @@ -33,14 +33,14 @@ Buffer window_get_buffer(Window window, Error *err) /// @param window The window handle /// @param[out] err Details of an error that may have occurred /// @return the (row, col) tuple -Position window_get_cursor(Window window, Error *err) +Array window_get_cursor(Window window, Error *err) { - Position rv = POSITION_INIT; + Array rv = ARRAY_DICT_INIT; win_T *win = find_window_by_handle(window, err); if (win) { - rv.row = win->w_cursor.lnum; - rv.col = win->w_cursor.col; + ADD(rv, INTEGER_OBJ(win->w_cursor.lnum)); + ADD(rv, INTEGER_OBJ(win->w_cursor.col)); } return rv; @@ -51,31 +51,35 @@ Position window_get_cursor(Window window, Error *err) /// @param window The window handle /// @param pos the (row, col) tuple representing the new position /// @param[out] err Details of an error that may have occurred -void window_set_cursor(Window window, Position pos, Error *err) +void window_set_cursor(Window window, Array pos, Error *err) { win_T *win = find_window_by_handle(window, err); + if (pos.size != 2 || pos.items[0].type != kObjectTypeInteger || + pos.items[1].type != kObjectTypeInteger) { + set_api_error("\"pos\" argument must be a [row, col] array", err); + return; + } + if (!win) { return; } - if (pos.row <= 0 || pos.row > win->w_buffer->b_ml.ml_line_count) { + int64_t row = pos.items[0].data.integer; + int64_t col = pos.items[1].data.integer; + + if (row <= 0 || row > win->w_buffer->b_ml.ml_line_count) { set_api_error("cursor position outside buffer", err); return; } - if (pos.row > LONG_MAX || pos.row < LONG_MIN) { - set_api_error("Row value outside range", err); - return; - } - - if (pos.col > INT_MAX || pos.col < INT_MIN) { + if (col > MAXCOL || col < 0) { set_api_error("Column value outside range", err); return; } - win->w_cursor.lnum = (linenr_T)pos.row; - win->w_cursor.col = (colnr_T)pos.col; + win->w_cursor.lnum = (linenr_T)row; + win->w_cursor.col = (colnr_T)col; win->w_cursor.coladd = 0; // When column is out of range silently correct it. check_cursor_col_win(win); @@ -243,14 +247,14 @@ void window_set_option(Window window, String name, Object value, Error *err) /// @param window The window handle /// @param[out] err Details of an error that may have occurred /// @return The (row, col) tuple with the window position -Position window_get_position(Window window, Error *err) +Array window_get_position(Window window, Error *err) { - Position rv = POSITION_INIT; + Array rv = ARRAY_DICT_INIT; win_T *win = find_window_by_handle(window, err); if (win) { - rv.col = win->w_wincol; - rv.row = win->w_winrow; + ADD(rv, INTEGER_OBJ(win->w_winrow)); + ADD(rv, INTEGER_OBJ(win->w_wincol)); } return rv; diff --git a/src/nvim/os/msgpack_rpc_helpers.c b/src/nvim/os/msgpack_rpc_helpers.c index 5d2f53bbcf..67c37fa8f4 100644 --- a/src/nvim/os/msgpack_rpc_helpers.c +++ b/src/nvim/os/msgpack_rpc_helpers.c @@ -132,15 +132,6 @@ bool msgpack_rpc_to_object(msgpack_object *obj, Object *arg) } } -bool msgpack_rpc_to_position(msgpack_object *obj, Position *arg) -{ - return obj->type == MSGPACK_OBJECT_ARRAY - && obj->via.array.size == 2 - && msgpack_rpc_to_integer(obj->via.array.ptr, &arg->row) - && msgpack_rpc_to_integer(obj->via.array.ptr + 1, &arg->col); -} - - bool msgpack_rpc_to_array(msgpack_object *obj, Array *arg) { if (obj->type != MSGPACK_OBJECT_ARRAY) { @@ -236,10 +227,6 @@ void msgpack_rpc_from_object(Object result, msgpack_packer *res) msgpack_rpc_from_array(result.data.array, res); break; - case kObjectTypePosition: - msgpack_rpc_from_position(result.data.position, res); - break; - case kObjectTypeBuffer: msgpack_rpc_from_buffer(result.data.buffer, res); break; @@ -258,13 +245,6 @@ void msgpack_rpc_from_object(Object result, msgpack_packer *res) } } -void msgpack_rpc_from_position(Position result, msgpack_packer *res) -{ - msgpack_pack_array(res, 2);; - msgpack_pack_int64(res, result.row); - msgpack_pack_int64(res, result.col); -} - void msgpack_rpc_from_array(Array result, msgpack_packer *res) { msgpack_pack_array(res, result.size); @@ -300,7 +280,6 @@ void msgpack_rpc_free_object(Object value) case kObjectTypeBoolean: case kObjectTypeInteger: case kObjectTypeFloat: - case kObjectTypePosition: case kObjectTypeBuffer: case kObjectTypeWindow: case kObjectTypeTabpage: diff --git a/src/nvim/os/msgpack_rpc_helpers.h b/src/nvim/os/msgpack_rpc_helpers.h index 77e3d2637e..0eb2d5c77a 100644 --- a/src/nvim/os/msgpack_rpc_helpers.h +++ b/src/nvim/os/msgpack_rpc_helpers.h @@ -24,8 +24,6 @@ bool msgpack_rpc_to_integer(msgpack_object *obj, Integer *arg) FUNC_ATTR_NONNULL_ALL; bool msgpack_rpc_to_float(msgpack_object *obj, Float *arg) FUNC_ATTR_NONNULL_ALL; -bool msgpack_rpc_to_position(msgpack_object *obj, Position *arg) - FUNC_ATTR_NONNULL_ALL; bool msgpack_rpc_to_string(msgpack_object *obj, String *arg) FUNC_ATTR_NONNULL_ALL; bool msgpack_rpc_to_buffer(msgpack_object *obj, Buffer *arg) @@ -53,8 +51,6 @@ void msgpack_rpc_from_integer(Integer result, msgpack_packer *res) FUNC_ATTR_NONNULL_ARG(2); void msgpack_rpc_from_float(Float result, msgpack_packer *res) FUNC_ATTR_NONNULL_ARG(2); -void msgpack_rpc_from_position(Position result, msgpack_packer *res) - FUNC_ATTR_NONNULL_ARG(2); void msgpack_rpc_from_string(String result, msgpack_packer *res) FUNC_ATTR_NONNULL_ARG(2); void msgpack_rpc_from_buffer(Buffer result, msgpack_packer *res) From cac24cb06ddcad0cfb3a9379c3bdd0e8706602f9 Mon Sep 17 00:00:00 2001 From: Thiago de Arruda Date: Tue, 9 Sep 2014 09:36:14 -0300 Subject: [PATCH 13/25] api/msgpack-rpc: Refactor msgpack_rpc_helpers.{c,h} - Move helpers that are specific to API types to api/private/helpers.{c,h} - Include headers with generated declarations - Delete unused macros --- scripts/msgpack-gen.lua | 4 +- src/nvim/api/private/helpers.c | 58 +++++++++++++++++++ src/nvim/api/private/helpers.h | 44 +++++++-------- src/nvim/eval.c | 5 +- src/nvim/ex_cmds2.c | 7 +-- src/nvim/ops.c | 5 +- src/nvim/os/channel.c | 8 +-- src/nvim/os/msgpack_rpc.c | 4 +- src/nvim/os/msgpack_rpc_helpers.c | 85 ++++++++-------------------- src/nvim/os/msgpack_rpc_helpers.h | 92 +------------------------------ src/nvim/os/provider.c | 5 +- 11 files changed, 121 insertions(+), 196 deletions(-) diff --git a/scripts/msgpack-gen.lua b/scripts/msgpack-gen.lua index 7da52432d6..5ef61267f2 100644 --- a/scripts/msgpack-gen.lua +++ b/scripts/msgpack-gen.lua @@ -158,7 +158,7 @@ for i = 1, #api.functions do for j = 1, #fn.parameters do local param = fn.parameters[j] local converted = 'arg_'..j - output:write('\n '..param[1]..' '..converted..' msgpack_rpc_init_'..string.lower(param[1])..';') + output:write('\n '..param[1]..' '..converted..' api_init_'..string.lower(param[1])..';') end output:write('\n') output:write('\n if (req->via.array.ptr[3].via.array.size != '..#fn.parameters..') {') @@ -228,7 +228,7 @@ for i = 1, #api.functions do for j = 1, #fn.parameters do local param = fn.parameters[j] - output:write('\n msgpack_rpc_free_'..string.lower(param[1])..'(arg_'..j..');') + output:write('\n api_free_'..string.lower(param[1])..'(arg_'..j..');') end if fn.return_type ~= 'void' then output:write('\n return ret;\n}\n\n'); diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index f6fb46e1d1..de23481813 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -449,6 +449,64 @@ bool object_to_vim(Object obj, typval_T *tv, Error *err) return true; } +void api_free_string(String value) +{ + if (!value.data) { + return; + } + + free(value.data); +} + +void api_free_object(Object value) +{ + switch (value.type) { + case kObjectTypeNil: + case kObjectTypeBoolean: + case kObjectTypeInteger: + case kObjectTypeFloat: + case kObjectTypeBuffer: + case kObjectTypeWindow: + case kObjectTypeTabpage: + break; + + case kObjectTypeString: + api_free_string(value.data.string); + break; + + case kObjectTypeArray: + api_free_array(value.data.array); + break; + + case kObjectTypeDictionary: + api_free_dictionary(value.data.dictionary); + break; + + default: + abort(); + } +} + +void api_free_array(Array value) +{ + for (size_t i = 0; i < value.size; i++) { + api_free_object(value.items[i]); + } + + free(value.items); +} + +void api_free_dictionary(Dictionary value) +{ + for (size_t i = 0; i < value.size; i++) { + api_free_string(value.items[i].key); + api_free_object(value.items[i].value); + } + + free(value.items); +} + + /// Recursion helper for the `vim_to_object`. This uses a pointer table /// to avoid infinite recursion due to cyclic references /// diff --git a/src/nvim/api/private/helpers.h b/src/nvim/api/private/helpers.h index f1b9dc3bc8..f3ecdaacc4 100644 --- a/src/nvim/api/private/helpers.h +++ b/src/nvim/api/private/helpers.h @@ -51,36 +51,11 @@ .data.array = a \ }) -#define STRINGARRAY_OBJ(a) ((Object) { \ - .type = kObjectTypeStringArray, \ - .data.stringarray = a \ - }) - -#define BUFFERARRAY_OBJ(a) ((Object) { \ - .type = kObjectTypeBufferArray, \ - .data.bufferarray = a \ - }) - -#define WINDOWARRAY_OBJ(a) ((Object) { \ - .type = kObjectTypeWindowArray, \ - .data.windowarray = a \ - }) - -#define TABPAGEARRAY_OBJ(a) ((Object) { \ - .type = kObjectTypeTabpageArray, \ - .data.tabpagearray = a \ - }) - #define DICTIONARY_OBJ(d) ((Object) { \ .type = kObjectTypeDictionary, \ .data.dictionary = d \ }) -#define POSITION_OBJ(p) ((Object) { \ - .type = kObjectTypePosition, \ - .data.position = p \ - }) - #define NIL ((Object) {.type = kObjectTypeNil}) #define PUT(dict, k, v) \ @@ -91,6 +66,25 @@ #define ADD(array, item) \ kv_push(Object, array, item) +// Helpers used by the generated msgpack-rpc api wrappers +#define api_init_boolean +#define api_init_integer +#define api_init_float +#define api_init_string = STRING_INIT +#define api_init_buffer +#define api_init_window +#define api_init_tabpage +#define api_init_object = NIL +#define api_init_array = ARRAY_DICT_INIT +#define api_init_dictionary = ARRAY_DICT_INIT + +#define api_free_boolean(value) +#define api_free_integer(value) +#define api_free_float(value) +#define api_free_buffer(value) +#define api_free_window(value) +#define api_free_tabpage(value) + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "api/private/helpers.h.generated.h" #endif diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 7c0d1b3d32..e987629f3a 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -84,7 +84,6 @@ #include "nvim/os/channel.h" #include "nvim/api/private/helpers.h" #include "nvim/api/vim.h" -#include "nvim/os/msgpack_rpc_helpers.h" #include "nvim/os/dl.h" #include "nvim/os/provider.h" @@ -12725,7 +12724,7 @@ static void f_send_call(typval_T *argvars, typval_T *rettv) _("Error converting the call result")); } - msgpack_rpc_free_object(result); + api_free_object(result); } // "send_event()" function @@ -19239,7 +19238,7 @@ static void script_host_eval(char *method, typval_T *argvars, typval_T *rettv) Error err = {.set = false}; object_to_vim(result, rettv, &err); - msgpack_rpc_free_object(result); + api_free_object(result); if (err.set) { EMSG("Error converting value back to vim"); diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c index f1524ffce9..17905c3046 100644 --- a/src/nvim/ex_cmds2.c +++ b/src/nvim/ex_cmds2.c @@ -55,7 +55,6 @@ #include "nvim/os/shell.h" #include "nvim/os/fs_defs.h" #include "nvim/os/provider.h" -#include "nvim/os/msgpack_rpc_helpers.h" #include "nvim/api/private/helpers.h" #include "nvim/api/private/defs.h" @@ -3264,7 +3263,7 @@ static void script_host_execute(char *method, exarg_T *eap) Object result = provider_call(method, args); // We don't care about the result, so free it just in case a bad provider // returned something - msgpack_rpc_free_object(result); + api_free_object(result); } free(script); @@ -3278,7 +3277,7 @@ static void script_host_execute_file(char *method, exarg_T *eap) Array args = ARRAY_DICT_INIT; ADD(args, STRING_OBJ(cstr_to_string(buffer))); Object result = provider_call(method, args); - msgpack_rpc_free_object(result); + api_free_object(result); } static void script_host_do_range(char *method, exarg_T *eap) @@ -3288,6 +3287,6 @@ static void script_host_do_range(char *method, exarg_T *eap) ADD(args, INTEGER_OBJ(eap->line2)); ADD(args, STRING_OBJ(cstr_to_string((char *)eap->arg))); Object result = provider_call(method, args); - msgpack_rpc_free_object(result); + api_free_object(result); } diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 317d773748..9b98c84be4 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -48,7 +48,6 @@ #include "nvim/undo.h" #include "nvim/window.h" #include "nvim/os/provider.h" -#include "nvim/os/msgpack_rpc_helpers.h" #include "nvim/api/private/helpers.h" /* @@ -5256,7 +5255,7 @@ static void get_clipboard(int name) return; err: - msgpack_rpc_free_object(result); + api_free_object(result); free(reg->y_array); reg->y_array = NULL; reg->y_size = 0; @@ -5287,5 +5286,5 @@ static void set_clipboard(int name) ADD(args, ARRAY_OBJ(lines)); Object result = provider_call("clipboard_set", args); - msgpack_rpc_free_object(result); + api_free_object(result); } diff --git a/src/nvim/os/channel.c b/src/nvim/os/channel.c index bc1bef3a57..5a42db112e 100644 --- a/src/nvim/os/channel.c +++ b/src/nvim/os/channel.c @@ -158,7 +158,7 @@ bool channel_send_event(uint64_t id, char *name, Array args) if (id > 0) { if (!(channel = pmap_get(uint64_t)(channels, id)) || !channel->enabled) { - msgpack_rpc_free_array(args); + api_free_array(args); return false; } send_event(channel, name, args); @@ -186,7 +186,7 @@ bool channel_send_call(uint64_t id, Channel *channel = NULL; if (!(channel = pmap_get(uint64_t)(channels, id)) || !channel->enabled) { - msgpack_rpc_free_array(args); + api_free_array(args); return false; } @@ -199,7 +199,7 @@ bool channel_send_call(uint64_t id, "Channel %" PRIu64 " crossed maximum stack depth", channel->id); *result = STRING_OBJ(cstr_to_string(buf)); - msgpack_rpc_free_array(args); + api_free_array(args); return false; } @@ -448,7 +448,7 @@ static void broadcast_event(char *name, Array args) }); if (!kv_size(subscribed)) { - msgpack_rpc_free_array(args); + api_free_array(args); goto end; } diff --git a/src/nvim/os/msgpack_rpc.c b/src/nvim/os/msgpack_rpc.c index fe4918b7d9..2f347d9b15 100644 --- a/src/nvim/os/msgpack_rpc.c +++ b/src/nvim/os/msgpack_rpc.c @@ -120,7 +120,7 @@ WBuffer *serialize_request(uint64_t request_id, sbuffer->size, refcount, free); - msgpack_rpc_free_array(args); + api_free_array(args); msgpack_sbuffer_clear(sbuffer); return rv; } @@ -156,7 +156,7 @@ WBuffer *serialize_response(uint64_t response_id, sbuffer->size, 1, // responses only go though 1 channel free); - msgpack_rpc_free_object(arg); + api_free_object(arg); msgpack_sbuffer_clear(sbuffer); return rv; } diff --git a/src/nvim/os/msgpack_rpc_helpers.c b/src/nvim/os/msgpack_rpc_helpers.c index 67c37fa8f4..b14de8245c 100644 --- a/src/nvim/os/msgpack_rpc_helpers.c +++ b/src/nvim/os/msgpack_rpc_helpers.c @@ -7,11 +7,16 @@ #include "nvim/vim.h" #include "nvim/memory.h" +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "os/msgpack_rpc_helpers.c.generated.h" +#endif + static msgpack_zone zone; static msgpack_sbuffer sbuffer; #define HANDLE_TYPE_CONVERSION_IMPL(t, lt) \ bool msgpack_rpc_to_##lt(msgpack_object *obj, t *arg) \ + FUNC_ATTR_NONNULL_ALL \ { \ if (obj->type != MSGPACK_OBJECT_EXT \ || obj->via.ext.type != kObjectType##t) { \ @@ -34,6 +39,7 @@ static msgpack_sbuffer sbuffer; } \ \ void msgpack_rpc_from_##lt(t o, msgpack_packer *res) \ + FUNC_ATTR_NONNULL_ARG(2) \ { \ msgpack_packer pac; \ msgpack_packer_init(&pac, &sbuffer, msgpack_sbuffer_write); \ @@ -49,13 +55,19 @@ void msgpack_rpc_helpers_init(void) msgpack_sbuffer_init(&sbuffer); } +HANDLE_TYPE_CONVERSION_IMPL(Buffer, buffer) +HANDLE_TYPE_CONVERSION_IMPL(Window, window) +HANDLE_TYPE_CONVERSION_IMPL(Tabpage, tabpage) + bool msgpack_rpc_to_boolean(msgpack_object *obj, Boolean *arg) + FUNC_ATTR_NONNULL_ALL { *arg = obj->via.boolean; return obj->type == MSGPACK_OBJECT_BOOLEAN; } bool msgpack_rpc_to_integer(msgpack_object *obj, Integer *arg) + FUNC_ATTR_NONNULL_ALL { if (obj->type == MSGPACK_OBJECT_POSITIVE_INTEGER && obj->via.u64 <= INT64_MAX) { @@ -68,12 +80,14 @@ bool msgpack_rpc_to_integer(msgpack_object *obj, Integer *arg) } bool msgpack_rpc_to_float(msgpack_object *obj, Float *arg) + FUNC_ATTR_NONNULL_ALL { *arg = obj->via.dec; return obj->type == MSGPACK_OBJECT_DOUBLE; } bool msgpack_rpc_to_string(msgpack_object *obj, String *arg) + FUNC_ATTR_NONNULL_ALL { if (obj->type == MSGPACK_OBJECT_BIN || obj->type == MSGPACK_OBJECT_STR) { arg->data = xmemdupz(obj->via.bin.ptr, obj->via.bin.size); @@ -86,6 +100,7 @@ bool msgpack_rpc_to_string(msgpack_object *obj, String *arg) } bool msgpack_rpc_to_object(msgpack_object *obj, Object *arg) + FUNC_ATTR_NONNULL_ALL { switch (obj->type) { case MSGPACK_OBJECT_NIL: @@ -133,6 +148,7 @@ bool msgpack_rpc_to_object(msgpack_object *obj, Object *arg) } bool msgpack_rpc_to_array(msgpack_object *obj, Array *arg) + FUNC_ATTR_NONNULL_ALL { if (obj->type != MSGPACK_OBJECT_ARRAY) { return false; @@ -151,6 +167,7 @@ bool msgpack_rpc_to_array(msgpack_object *obj, Array *arg) } bool msgpack_rpc_to_dictionary(msgpack_object *obj, Dictionary *arg) + FUNC_ATTR_NONNULL_ALL { if (obj->type != MSGPACK_OBJECT_MAP) { return false; @@ -176,6 +193,7 @@ bool msgpack_rpc_to_dictionary(msgpack_object *obj, Dictionary *arg) } void msgpack_rpc_from_boolean(Boolean result, msgpack_packer *res) + FUNC_ATTR_NONNULL_ARG(2) { if (result) { msgpack_pack_true(res); @@ -185,22 +203,26 @@ void msgpack_rpc_from_boolean(Boolean result, msgpack_packer *res) } void msgpack_rpc_from_integer(Integer result, msgpack_packer *res) + FUNC_ATTR_NONNULL_ARG(2) { msgpack_pack_int64(res, result); } void msgpack_rpc_from_float(Float result, msgpack_packer *res) + FUNC_ATTR_NONNULL_ARG(2) { msgpack_pack_double(res, result); } void msgpack_rpc_from_string(String result, msgpack_packer *res) + FUNC_ATTR_NONNULL_ARG(2) { msgpack_pack_bin(res, result.size); msgpack_pack_bin_body(res, result.data, result.size); } void msgpack_rpc_from_object(Object result, msgpack_packer *res) + FUNC_ATTR_NONNULL_ARG(2) { switch (result.type) { case kObjectTypeNil: @@ -246,6 +268,7 @@ void msgpack_rpc_from_object(Object result, msgpack_packer *res) } void msgpack_rpc_from_array(Array result, msgpack_packer *res) + FUNC_ATTR_NONNULL_ARG(2) { msgpack_pack_array(res, result.size); @@ -255,6 +278,7 @@ void msgpack_rpc_from_array(Array result, msgpack_packer *res) } void msgpack_rpc_from_dictionary(Dictionary result, msgpack_packer *res) + FUNC_ATTR_NONNULL_ARG(2) { msgpack_pack_map(res, result.size); @@ -263,64 +287,3 @@ void msgpack_rpc_from_dictionary(Dictionary result, msgpack_packer *res) msgpack_rpc_from_object(result.items[i].value, res); } } - -void msgpack_rpc_free_string(String value) -{ - if (!value.data) { - return; - } - - free(value.data); -} - -void msgpack_rpc_free_object(Object value) -{ - switch (value.type) { - case kObjectTypeNil: - case kObjectTypeBoolean: - case kObjectTypeInteger: - case kObjectTypeFloat: - case kObjectTypeBuffer: - case kObjectTypeWindow: - case kObjectTypeTabpage: - break; - - case kObjectTypeString: - msgpack_rpc_free_string(value.data.string); - break; - - case kObjectTypeArray: - msgpack_rpc_free_array(value.data.array); - break; - - case kObjectTypeDictionary: - msgpack_rpc_free_dictionary(value.data.dictionary); - break; - - default: - abort(); - } -} - -void msgpack_rpc_free_array(Array value) -{ - for (uint32_t i = 0; i < value.size; i++) { - msgpack_rpc_free_object(value.items[i]); - } - - free(value.items); -} - -void msgpack_rpc_free_dictionary(Dictionary value) -{ - for (uint32_t i = 0; i < value.size; i++) { - msgpack_rpc_free_string(value.items[i].key); - msgpack_rpc_free_object(value.items[i].value); - } - - free(value.items); -} - -HANDLE_TYPE_CONVERSION_IMPL(Buffer, buffer) -HANDLE_TYPE_CONVERSION_IMPL(Window, window) -HANDLE_TYPE_CONVERSION_IMPL(Tabpage, tabpage) diff --git a/src/nvim/os/msgpack_rpc_helpers.h b/src/nvim/os/msgpack_rpc_helpers.h index 0eb2d5c77a..aede6b1587 100644 --- a/src/nvim/os/msgpack_rpc_helpers.h +++ b/src/nvim/os/msgpack_rpc_helpers.h @@ -6,97 +6,11 @@ #include -#include "nvim/func_attr.h" #include "nvim/api/private/defs.h" -void msgpack_rpc_helpers_init(void); - -/// Functions for validating and converting from msgpack types to C types. -/// These are used by `msgpack_rpc_dispatch` to validate and convert each -/// argument. -/// -/// @param obj The object to convert -/// @param[out] arg A pointer to the avalue -/// @return true if the conversion succeeded, false otherwise -bool msgpack_rpc_to_boolean(msgpack_object *obj, Boolean *arg) - FUNC_ATTR_NONNULL_ALL; -bool msgpack_rpc_to_integer(msgpack_object *obj, Integer *arg) - FUNC_ATTR_NONNULL_ALL; -bool msgpack_rpc_to_float(msgpack_object *obj, Float *arg) - FUNC_ATTR_NONNULL_ALL; -bool msgpack_rpc_to_string(msgpack_object *obj, String *arg) - FUNC_ATTR_NONNULL_ALL; -bool msgpack_rpc_to_buffer(msgpack_object *obj, Buffer *arg) - FUNC_ATTR_NONNULL_ALL; -bool msgpack_rpc_to_window(msgpack_object *obj, Window *arg) - FUNC_ATTR_NONNULL_ALL; -bool msgpack_rpc_to_tabpage(msgpack_object *obj, Tabpage *arg) - FUNC_ATTR_NONNULL_ALL; -bool msgpack_rpc_to_object(msgpack_object *obj, Object *arg) - FUNC_ATTR_NONNULL_ALL; -bool msgpack_rpc_to_array(msgpack_object *obj, Array *arg) - FUNC_ATTR_NONNULL_ALL; -bool msgpack_rpc_to_dictionary(msgpack_object *obj, Dictionary *arg) - FUNC_ATTR_NONNULL_ALL; - -/// Functions for converting from C types to msgpack types. -/// These are used by `msgpack_rpc_dispatch` to convert return values -/// from the API -/// -/// @param result A pointer to the result -/// @param res A packer that contains the response -void msgpack_rpc_from_boolean(Boolean result, msgpack_packer *res) - FUNC_ATTR_NONNULL_ARG(2); -void msgpack_rpc_from_integer(Integer result, msgpack_packer *res) - FUNC_ATTR_NONNULL_ARG(2); -void msgpack_rpc_from_float(Float result, msgpack_packer *res) - FUNC_ATTR_NONNULL_ARG(2); -void msgpack_rpc_from_string(String result, msgpack_packer *res) - FUNC_ATTR_NONNULL_ARG(2); -void msgpack_rpc_from_buffer(Buffer result, msgpack_packer *res) - FUNC_ATTR_NONNULL_ARG(2); -void msgpack_rpc_from_window(Window result, msgpack_packer *res) - FUNC_ATTR_NONNULL_ARG(2); -void msgpack_rpc_from_tabpage(Tabpage result, msgpack_packer *res) - FUNC_ATTR_NONNULL_ARG(2); -void msgpack_rpc_from_object(Object result, msgpack_packer *res) - FUNC_ATTR_NONNULL_ARG(2); -void msgpack_rpc_from_array(Array result, msgpack_packer *res) - FUNC_ATTR_NONNULL_ARG(2); -void msgpack_rpc_from_dictionary(Dictionary result, msgpack_packer *res) - FUNC_ATTR_NONNULL_ARG(2); - -/// Helpers for initializing types that may be freed later -#define msgpack_rpc_init_boolean -#define msgpack_rpc_init_integer -#define msgpack_rpc_init_float -#define msgpack_rpc_init_position -#define msgpack_rpc_init_string = STRING_INIT -#define msgpack_rpc_init_buffer -#define msgpack_rpc_init_window -#define msgpack_rpc_init_tabpage -#define msgpack_rpc_init_object = {.type = kObjectTypeNil} -#define msgpack_rpc_init_stringarray = ARRAY_DICT_INIT -#define msgpack_rpc_init_bufferarray = ARRAY_DICT_INIT -#define msgpack_rpc_init_windowarray = ARRAY_DICT_INIT -#define msgpack_rpc_init_tabpagearray = ARRAY_DICT_INIT -#define msgpack_rpc_init_array = ARRAY_DICT_INIT -#define msgpack_rpc_init_dictionary = ARRAY_DICT_INIT - -/// Helpers for freeing arguments/return value -/// -/// @param value The value to be freed -#define msgpack_rpc_free_boolean(value) -#define msgpack_rpc_free_integer(value) -#define msgpack_rpc_free_float(value) -#define msgpack_rpc_free_position(value) -void msgpack_rpc_free_string(String value); -#define msgpack_rpc_free_buffer(value) -#define msgpack_rpc_free_window(value) -#define msgpack_rpc_free_tabpage(value) -void msgpack_rpc_free_object(Object value); -void msgpack_rpc_free_array(Array value); -void msgpack_rpc_free_dictionary(Dictionary value); +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "os/msgpack_rpc_helpers.h.generated.h" +#endif #endif // NVIM_OS_MSGPACK_RPC_HELPERS_H diff --git a/src/nvim/os/provider.c b/src/nvim/os/provider.c index 07e757fe0e..57c2e57a3c 100644 --- a/src/nvim/os/provider.c +++ b/src/nvim/os/provider.c @@ -14,7 +14,6 @@ #include "nvim/log.h" #include "nvim/map.h" #include "nvim/message.h" -#include "nvim/os/msgpack_rpc_helpers.h" #define FEATURE_COUNT (sizeof(features) / sizeof(features[0])) @@ -109,7 +108,7 @@ Object provider_call(char *method, Array args) "Provider for \"%s\" is not available", method); report_error(buf); - msgpack_rpc_free_array(args); + api_free_array(args); return NIL; } @@ -119,7 +118,7 @@ Object provider_call(char *method, Array args) if (error) { report_error(result.data.string.data); - msgpack_rpc_free_object(result); + api_free_object(result); return NIL; } From af61a286b20b8435e016d6210b99e642cd59df8c Mon Sep 17 00:00:00 2001 From: Thiago de Arruda Date: Tue, 9 Sep 2014 14:11:05 -0300 Subject: [PATCH 14/25] main: Rename --embedded-mode and --api-msgpack-metadata options --embedded-mode -> --embed --api-msgpack-metadata -> --api-info --- .ci/api-python.sh | 2 +- src/nvim/main.c | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.ci/api-python.sh b/.ci/api-python.sh index a3e9b652a6..acdbcc2666 100644 --- a/.ci/api-python.sh +++ b/.ci/api-python.sh @@ -23,7 +23,7 @@ fi valgrind_check "$tmpdir" -export NVIM_SPAWN_ARGV="[\"valgrind\", \"-q\", \"--track-origins=yes\", \"--leak-check=yes\", \"--suppressions=$suppressions\", \"--log-file=$tmpdir/valgrind-%p.log\", \"../build/bin/nvim\", \"-u\", \"NONE\", \"--embedded-mode\"]" +export NVIM_SPAWN_ARGV="[\"valgrind\", \"-q\", \"--track-origins=yes\", \"--leak-check=yes\", \"--suppressions=$suppressions\", \"--log-file=$tmpdir/valgrind-%p.log\", \"../build/bin/nvim\", \"-u\", \"NONE\", \"--embed\"]" if ! nosetests --verbosity=2 --nologcapture; then valgrind_check "$tmpdir" exit 1 diff --git a/src/nvim/main.c b/src/nvim/main.c index ebd9f7f3ff..4b440ba92a 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -1026,12 +1026,12 @@ static void command_line_scan(mparm_T *parmp) msg_putchar('\n'); msg_didout = FALSE; mch_exit(0); - } else if (STRICMP(argv[0] + argv_idx, "api-msgpack-metadata") == 0) { + } else if (STRICMP(argv[0] + argv_idx, "api-info") == 0) { for (unsigned int i = 0; i\tWrite all typed commands to file ")); main_msg(_("--startuptime \tWrite startup timing messages to ")); main_msg(_("-i \t\tUse instead of .viminfo")); - main_msg(_("--api-msgpack-metadata\tDump API metadata information and exit")); - main_msg(_("--embedded-mode\tUse stdin/stdout as a msgpack-rpc channel. " + main_msg(_("--api-info\t\tDump API metadata serialized to msgpack and exit")); + main_msg(_("--embed\t\tUse stdin/stdout as a msgpack-rpc channel. " "This can be used for embedding Neovim into other programs")); main_msg(_("-h or --help\tPrint Help (this message) and exit")); main_msg(_("--version\t\tPrint version information and exit")); From 551b76c516dee88ccdab49d3bbc2cccc7dfcb0cc Mon Sep 17 00:00:00 2001 From: Thiago de Arruda Date: Tue, 9 Sep 2014 15:04:29 -0300 Subject: [PATCH 15/25] api/msgpack-rpc: Expose channel_from_job to vimscript as api_spawn --- src/nvim/eval.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++ src/nvim/globals.h | 1 + 2 files changed, 59 insertions(+) diff --git a/src/nvim/eval.c b/src/nvim/eval.c index e987629f3a..8b370e9948 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -6306,6 +6306,7 @@ static struct fst { {"acos", 1, 1, f_acos}, /* WJMc */ {"add", 2, 2, f_add}, {"and", 2, 2, f_and}, + {"api_spawn", 1, 2, f_api_spawn}, {"append", 2, 2, f_append}, {"argc", 0, 0, f_argc}, {"argidx", 0, 0, f_argidx}, @@ -7053,6 +7054,63 @@ static void f_and(typval_T *argvars, typval_T *rettv) & get_tv_number_chk(&argvars[1], NULL); } +// "api_spawn(prog, argv)" function +static void f_api_spawn(typval_T *argvars, typval_T *rettv) +{ + rettv->v_type = VAR_NUMBER; + rettv->vval.v_number = 0; + + if (check_restricted() || check_secure()) { + return; + } + + if (argvars[0].v_type != VAR_STRING + || (argvars[1].v_type != VAR_LIST && argvars[1].v_type != VAR_UNKNOWN)) { + // Wrong argument types + EMSG(_(e_invarg)); + return; + } + + list_T *args = NULL; + int argsl = 0; + if (argvars[1].v_type == VAR_LIST) { + args = argvars[1].vval.v_list; + argsl = args->lv_len; + // Assert that all list items are strings + for (listitem_T *arg = args->lv_first; arg != NULL; arg = arg->li_next) { + if (arg->li_tv.v_type != VAR_STRING) { + EMSG(_(e_invarg)); + return; + } + } + } + + // Allocate extra memory for the argument vector and the NULL pointer + int argvl = argsl + 2; + char **argv = xmalloc(sizeof(char_u *) * argvl); + + // Copy program name + argv[0] = xstrdup((char *)argvars[0].vval.v_string); + + int i = 1; + // Copy arguments to the vector + if (argsl > 0) { + for (listitem_T *arg = args->lv_first; arg != NULL; arg = arg->li_next) { + argv[i++] = xstrdup((char *)arg->li_tv.vval.v_string); + } + } + + // The last item of argv must be NULL + argv[i] = NULL; + uint64_t channel_id = channel_from_job(argv); + + if (!channel_id) { + EMSG(_(e_api_spawn_failed)); + } + + rettv->vval.v_number = (varnumber_T)channel_id; +} + /* * "append(lnum, string/list)" function */ diff --git a/src/nvim/globals.h b/src/nvim/globals.h index 3b9a9ae64b..49a4a2f604 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -1098,6 +1098,7 @@ EXTERN garray_T error_ga * Excluded are errors that are only used once and debugging messages. */ EXTERN char_u e_abort[] INIT(= N_("E470: Command aborted")); +EXTERN char_u e_api_spawn_failed[] INIT(= N_("E903: Could not spawn API job")); EXTERN char_u e_argreq[] INIT(= N_("E471: Argument required")); EXTERN char_u e_backslash[] INIT(= N_("E10: \\ should be followed by /, ? or &")); EXTERN char_u e_cmdwin[] INIT(= N_( From 3f15d34056fa5f8bc4d97efffc3f239aa68395d0 Mon Sep 17 00:00:00 2001 From: Thiago de Arruda Date: Tue, 9 Sep 2014 15:32:41 -0300 Subject: [PATCH 16/25] job: Fix crash when passing a non-executable path to job_start --- src/nvim/os/job.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/nvim/os/job.c b/src/nvim/os/job.c index 9deca9de74..9fb2a49e50 100644 --- a/src/nvim/os/job.c +++ b/src/nvim/os/job.c @@ -197,6 +197,12 @@ Job *job_start(char **argv, job->stdio[2].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE; job->stdio[2].data.stream = (uv_stream_t *)&job->proc_stderr; + // Give all handles a reference to the job + handle_set_job((uv_handle_t *)&job->proc, job); + handle_set_job((uv_handle_t *)&job->proc_stdin, job); + handle_set_job((uv_handle_t *)&job->proc_stdout, job); + handle_set_job((uv_handle_t *)&job->proc_stderr, job); + // Spawn the job if (uv_spawn(uv_default_loop(), &job->proc, &job->proc_opts) != 0) { free_job(job); @@ -204,12 +210,6 @@ Job *job_start(char **argv, return NULL; } - // Give all handles a reference to the job - handle_set_job((uv_handle_t *)&job->proc, job); - handle_set_job((uv_handle_t *)&job->proc_stdin, job); - handle_set_job((uv_handle_t *)&job->proc_stdout, job); - handle_set_job((uv_handle_t *)&job->proc_stderr, job); - job->in = wstream_new(maxmem); wstream_set_stream(job->in, (uv_stream_t *)&job->proc_stdin); // Start the readable streams From 03f4d17fc9d72c8862daf575804e0104e3aeb31b Mon Sep 17 00:00:00 2001 From: Thiago de Arruda Date: Tue, 9 Sep 2014 15:49:03 -0300 Subject: [PATCH 17/25] wstream: Fix close/free The current code was leading to an invalid free when the wstream was closed --- src/nvim/os/wstream.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/nvim/os/wstream.c b/src/nvim/os/wstream.c index 44463c7c88..00a53d1628 100644 --- a/src/nvim/os/wstream.c +++ b/src/nvim/os/wstream.c @@ -72,12 +72,12 @@ WStream * wstream_new(size_t maxmem) /// @param wstream The `WStream` instance void wstream_free(WStream *wstream) { if (!wstream->pending_reqs) { - handle_set_wstream((uv_handle_t *)wstream->stream, NULL); if (wstream->free_handle) { uv_close((uv_handle_t *)wstream->stream, close_cb); + } else { + handle_set_wstream((uv_handle_t *)wstream->stream, NULL); + free(wstream); } - - free(wstream); } else { wstream->freed = true; } @@ -238,12 +238,7 @@ static void release_wbuffer(WBuffer *buffer) static void close_cb(uv_handle_t *handle) { - WStream *wstream = handle_get_wstream(handle); - - if (wstream) { - free(wstream); - } - + free(handle_get_wstream(handle)); free(handle->data); free(handle); } From 50609029301d0baac36d7500c2d80da9efb41861 Mon Sep 17 00:00:00 2001 From: Thiago de Arruda Date: Wed, 10 Sep 2014 09:32:29 -0300 Subject: [PATCH 18/25] api/msgpack-rpc: Implement `channel_close` and expose to vimscript Simple function for closing a channel by id --- src/nvim/eval.c | 21 +++++++++++++++++++++ src/nvim/os/channel.c | 25 +++++++++++++++++++++++-- 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 8b370e9948..8becb29b26 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -6306,6 +6306,7 @@ static struct fst { {"acos", 1, 1, f_acos}, /* WJMc */ {"add", 2, 2, f_add}, {"and", 2, 2, f_and}, + {"api_close", 1, 1, f_api_close}, {"api_spawn", 1, 2, f_api_spawn}, {"append", 2, 2, f_append}, {"argc", 0, 0, f_argc}, @@ -7054,6 +7055,26 @@ static void f_and(typval_T *argvars, typval_T *rettv) & get_tv_number_chk(&argvars[1], NULL); } +// "api_close(prog, argv)" function +static void f_api_close(typval_T *argvars, typval_T *rettv) +{ + rettv->v_type = VAR_NUMBER; + rettv->vval.v_number = 0; + + if (check_restricted() || check_secure()) { + return; + } + + if (argvars[0].v_type != VAR_NUMBER) { + // Wrong argument types + EMSG(_(e_invarg)); + return; + } + + rettv->vval.v_number = channel_close(argvars[0].vval.v_number); +} + + // "api_spawn(prog, argv)" function static void f_api_spawn(typval_T *argvars, typval_T *rettv) { diff --git a/src/nvim/os/channel.c b/src/nvim/os/channel.c index 5a42db112e..ad8f378dfc 100644 --- a/src/nvim/os/channel.c +++ b/src/nvim/os/channel.c @@ -267,6 +267,23 @@ void channel_unsubscribe(uint64_t id, char *event) unsubscribe(channel, event); } +/// Closes a channel +/// +/// @param id The channel id +/// @return true if successful, false otherwise +bool channel_close(uint64_t id) +{ + Channel *channel; + + if (!(channel = pmap_get(uint64_t)(channels, id)) || !channel->enabled) { + return false; + } + + channel_kill(channel); + channel->enabled = false; + return true; +} + /// Creates an API channel from stdin/stdout. This is used when embedding /// Neovim static void channel_from_stdio(void) @@ -496,7 +513,13 @@ static void close_channel(Channel *channel) pmap_free(cstr_t)(channel->subscribed_events); kv_destroy(channel->call_stack); + channel_kill(channel); + free(channel); +} + +static void channel_kill(Channel *channel) +{ if (channel->is_job) { if (channel->data.job) { job_stop(channel->data.job); @@ -511,8 +534,6 @@ static void close_channel(Channel *channel) mch_exit(0); } } - - free(channel); } static void close_cb(uv_handle_t *handle) From a1ce3a3acc9f5e617e9d7e9542d58953d2e9546f Mon Sep 17 00:00:00 2001 From: Thiago de Arruda Date: Wed, 10 Sep 2014 10:11:45 -0300 Subject: [PATCH 19/25] provider: Major refactor - Providers for features are now registered as a unit. For example, instead of calling `register_provider("clipboard_get")` and `register_provider("clipboard_set")`, clients call `register_provider("clipboard")` and nvim will assume it implements all methods of the "clipboard" feature - Bootstrapping code was removed. With the `api_spawn` function exposed to vimscript, it's no longer necessary and will be handled by plugins distributed with nvim. - Now the `has` function will return true if there's a live channel that has registered as a provider for the feature. - 'initpython'/'initclipboard' options were removed - A new API function was exposed: `vim_discover_features` which returns an object with information about pluggable features such as 'python' or 'clipboard' --- src/nvim/api/vim.c | 18 +++-- src/nvim/option.c | 6 -- src/nvim/option_defs.h | 2 - src/nvim/os/provider.c | 170 ++++++++++++++--------------------------- 4 files changed, 68 insertions(+), 128 deletions(-) diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index b1d5a067b4..e14c427dc1 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -502,22 +502,28 @@ void vim_unsubscribe(uint64_t channel_id, String event) channel_unsubscribe(channel_id, e); } -/// Registers the channel as the provider for `method`. This fails if -/// a provider for `method` is already registered. +/// Registers the channel as the provider for `feature`. This fails if +/// a provider for `feature` is already provided by another channel. /// /// @param channel_id The channel id -/// @param method The method name +/// @param feature The feature name /// @param[out] err Details of an error that may have occurred -void vim_register_provider(uint64_t channel_id, String method, Error *err) +void vim_register_provider(uint64_t channel_id, String feature, Error *err) { char buf[METHOD_MAXLEN]; - xstrlcpy(buf, method.data, sizeof(buf)); + xstrlcpy(buf, feature.data, sizeof(buf)); if (!provider_register(buf, channel_id)) { - set_api_error("Provider already registered", err); + set_api_error("Feature doesn't exist", err); } } +/// Returns a feature->method list dictionary for all pluggable features +Dictionary vim_discover_features(void) +{ + return provider_get_all(); +} + /// Writes a message to vim output or error buffer. The string is split /// and flushed after each newline. Incomplete lines are kept for writing /// later. diff --git a/src/nvim/option.c b/src/nvim/option.c index b9becebbf4..b26b6ed4cc 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -974,12 +974,6 @@ static struct vimoption {"infercase", "inf", P_BOOL|P_VI_DEF, (char_u *)&p_inf, PV_INF, {(char_u *)FALSE, (char_u *)0L} SCRIPTID_INIT}, - {"initclipboard","icpb",P_STRING|P_VI_DEF|P_SECURE, - (char_u *)&p_icpb, PV_NONE, - {(char_u *)"", (char_u *)0L} SCRIPTID_INIT}, - {"initpython","ipy",P_STRING|P_VI_DEF|P_SECURE, - (char_u *)&p_ipy, PV_NONE, - {(char_u *)"", (char_u *)0L} SCRIPTID_INIT}, {"insertmode", "im", P_BOOL|P_VI_DEF|P_VIM, (char_u *)&p_im, PV_NONE, {(char_u *)FALSE, (char_u *)0L} SCRIPTID_INIT}, diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h index 555e9166d6..cd61b6427c 100644 --- a/src/nvim/option_defs.h +++ b/src/nvim/option_defs.h @@ -631,8 +631,6 @@ EXTERN int p_write; /* 'write' */ EXTERN int p_wa; /* 'writeany' */ EXTERN int p_wb; /* 'writebackup' */ EXTERN long p_wd; /* 'writedelay' */ -EXTERN char *p_ipy; // 'initpython' -EXTERN char *p_icpb; // 'initclipboard' /* * "indir" values for buffer-local opions. diff --git a/src/nvim/os/provider.c b/src/nvim/os/provider.c index 57c2e57a3c..e56e33aa1a 100644 --- a/src/nvim/os/provider.c +++ b/src/nvim/os/provider.c @@ -17,33 +17,31 @@ #define FEATURE_COUNT (sizeof(features) / sizeof(features[0])) -#define FEATURE(feature_name, provider_bootstrap_command, ...) { \ +#define FEATURE(feature_name, ...) { \ .name = feature_name, \ - .bootstrap_command = provider_bootstrap_command, \ - .argv = NULL, \ .channel_id = 0, \ .methods = (char *[]){__VA_ARGS__, NULL} \ } -static struct feature { - char *name, **bootstrap_command, **argv, **methods; +typedef struct { + char *name, **methods; size_t name_length; uint64_t channel_id; -} features[] = { +} Feature; + +static Feature features[] = { FEATURE("python", - &p_ipy, "python_execute", "python_execute_file", "python_do_range", "python_eval"), FEATURE("clipboard", - &p_icpb, "clipboard_get", "clipboard_set") }; -static Map(cstr_t, uint64_t) *registered_providers = NULL; +static PMap(cstr_t) *registered_providers = NULL; #ifdef INCLUDE_GENERATED_DECLARATIONS # include "os/provider.c.generated.h" @@ -52,60 +50,57 @@ static Map(cstr_t, uint64_t) *registered_providers = NULL; void provider_init(void) { - registered_providers = map_new(cstr_t, uint64_t)(); + registered_providers = pmap_new(cstr_t)(); } bool provider_has_feature(char *name) { - for (size_t i = 0; i < FEATURE_COUNT; i++) { - struct feature *f = &features[i]; - if (!STRICMP(name, f->name)) { - return f->channel_id || can_execute(f); - } - } - - return false; + Feature *f = find_feature(name); + return f != NULL && channel_exists(f->channel_id); } -bool provider_available(char *method) +bool provider_register(char *name, uint64_t channel_id) { - return map_has(cstr_t, uint64_t)(registered_providers, method); -} + Feature *f = find_feature(name); -bool provider_register(char *method, uint64_t channel_id) -{ - if (map_has(cstr_t, uint64_t)(registered_providers, method)) { + if (!f) { return false; } - // First check if this method is part of a feature, and if so, update - // the feature structure with the channel id - struct feature *f = get_feature_for(method); - if (f) { - DLOG("Registering provider for \"%s\" " - "which is part of the \"%s\" feature", - method, - f->name); - f->channel_id = channel_id; + if (f->channel_id && channel_exists(f->channel_id)) { + ILOG("Feature \"%s\" is already provided by another channel" + "(will be replaced)", name); } - map_put(cstr_t, uint64_t)(registered_providers, xstrdup(method), channel_id); - ILOG("Registered channel %" PRIu64 " as the provider for \"%s\"", + DLOG("Registering provider for \"%s\"", name); + f->channel_id = channel_id; + + // Associate all method names with the feature struct + size_t i; + char *method; + for (method = f->methods[i = 0]; method; method = f->methods[++i]) { + pmap_put(cstr_t)(registered_providers, method, f); + DLOG("Channel \"%" PRIu64 "\" will be sent requests for \"%s\"", + channel_id, + method); + } + + ILOG("Registered channel %" PRIu64 " as the provider for the \"%s\" feature", channel_id, - method); + name); return true; } Object provider_call(char *method, Array args) { - uint64_t channel_id = get_provider_for(method); + Feature *f = pmap_get(cstr_t)(registered_providers, method); - if (!channel_id) { + if (!f || !channel_exists(f->channel_id)) { char buf[256]; snprintf(buf, sizeof(buf), - "Provider for \"%s\" is not available", + "Provider for method \"%s\" is not available", method); report_error(buf); api_free_array(args); @@ -114,7 +109,7 @@ Object provider_call(char *method, Array args) bool error = false; Object result = NIL; - channel_send_call(channel_id, method, args, &result, &error); + channel_send_call(f->channel_id, method, args, &result, &error); if (error) { report_error(result.data.string.data); @@ -125,62 +120,36 @@ Object provider_call(char *method, Array args) return result; } -static uint64_t get_provider_for(char *method) +Dictionary provider_get_all(void) { - uint64_t channel_id = map_get(cstr_t, uint64_t)(registered_providers, method); + Dictionary rv = ARRAY_DICT_INIT; - if (channel_id) { - return channel_id; + for (size_t i = 0; i < FEATURE_COUNT; i++) { + Array methods = ARRAY_DICT_INIT; + Feature *f = &features[i]; + + size_t j; + char *method; + for (method = f->methods[j = 0]; method; method = f->methods[++j]) { + ADD(methods, STRING_OBJ(cstr_to_string(method))); + } + + PUT(rv, f->name, ARRAY_OBJ(methods)); } - // Try to bootstrap if the method is part of a feature - struct feature *f = get_feature_for(method); - - if (!f || !can_execute(f)) { - ELOG("Cannot bootstrap provider for \"%s\"", method); - goto err; - } - - if (f->channel_id) { - ELOG("Already bootstrapped provider for \"%s\"", f->name); - goto err; - } - - f->channel_id = channel_from_job(f->argv); - - if (!f->channel_id) { - ELOG("The provider for \"%s\" failed to bootstrap", f->name); - goto err; - } - - return f->channel_id; - -err: - // Ensure we won't try to restart the provider - if (f) { - f->bootstrap_command = NULL; - f->channel_id = 0; - } - return 0; + return rv; } -static bool can_execute(struct feature *f) +static Feature * find_feature(char *name) { - if (!f->bootstrap_command) { - return false; + for (size_t i = 0; i < FEATURE_COUNT; i++) { + Feature *f = &features[i]; + if (!STRICMP(name, f->name)) { + return f; + } } - char *cmd = *f->bootstrap_command; - - if (!cmd || !strlen(cmd)) { - return false; - } - - if (!f->argv) { - f->argv = shell_build_argv((uint8_t *)cmd, NULL); - } - - return os_can_exe((uint8_t *)f->argv[0]); + return NULL; } static void report_error(char *str) @@ -188,30 +157,3 @@ static void report_error(char *str) vim_err_write((String) {.data = str, .size = strlen(str)}); vim_err_write((String) {.data = "\n", .size = 1}); } - -static bool feature_has_method(struct feature *f, char *method) -{ - size_t i; - char *m; - - for (m = f->methods[i = 0]; m; m = f->methods[++i]) { - if (!STRCMP(method, m)) { - return true; - } - } - - return false; -} - - -static struct feature *get_feature_for(char *method) -{ - for (size_t i = 0; i < FEATURE_COUNT; i++) { - struct feature *f = &features[i]; - if (feature_has_method(f, method)) { - return f; - } - } - - return NULL; -} From fa01ea8ead051f9b933afc895824fb673f273671 Mon Sep 17 00:00:00 2001 From: Thiago de Arruda Date: Wed, 10 Sep 2014 10:39:20 -0300 Subject: [PATCH 20/25] runtime: Add script for bootstrapping the python host --- runtime/plugin/python_setup.vim | 44 +++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 runtime/plugin/python_setup.vim diff --git a/runtime/plugin/python_setup.vim b/runtime/plugin/python_setup.vim new file mode 100644 index 0000000000..7b2d800f74 --- /dev/null +++ b/runtime/plugin/python_setup.vim @@ -0,0 +1,44 @@ +" Nvim plugin for loading python extensions via an external interpreter +if exists("did_python_setup") || &cp + finish +endif +let did_python_setup = 1 + + +let s:get_version = + \ ' -c "import sys; sys.stdout.write(str(sys.version_info.major))"' + +" To load the python host a python 2 executable must be available +if exists('python_interpreter') + \ && executable(g:python_interpreter) + \ && system(g:python_interpreter.s:get_version) == "2" + let s:python_interpreter = g:python_interpreter +elseif executable('python') && + \ system('python'.s:get_version) == "2" + let s:python_interpreter = 'python' +elseif executable('python2') && + \ system('python2'.s:get_version) == "2" + " In some distros, python3 is the default python + let s:python_interpreter = 'python2' +else + finish +endif + +" Execute python, import neovim and print a string. If import_result matches +" the printed string, we can probably start the host +let s:import_result = substitute(system( + \ s:python_interpreter .' -c "import neovim; print \"ok\""'), + \ '^[\s\n]*\(ok\)[\s\n]*$', '\1', '') +if s:import_result != 'ok' + finish +endif + +let s:pyhost_id = api_spawn(s:python_interpreter, + \ ['-c', 'import neovim; neovim.start_host()']) +" Evaluate an expression in the script host as an additional sanity check, and +" to block until all providers have been registered(or else some plugins loaded +" by the user's vimrc would not get has('python') == 1 +if send_call(s:pyhost_id, 'python_eval', '"o" + "k"') != 'ok' || !has('python') + " Something went wrong + api_close(s:pyhost_id) +endif From d29b62daabd88e3c7ee9a979f4feae5612e3fbaf Mon Sep 17 00:00:00 2001 From: Thiago de Arruda Date: Thu, 11 Sep 2014 10:34:51 -0300 Subject: [PATCH 21/25] api: initialize capacity in the array_dict_macro --- src/nvim/api/private/defs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nvim/api/private/defs.h b/src/nvim/api/private/defs.h index 11800ed786..bae1819172 100644 --- a/src/nvim/api/private/defs.h +++ b/src/nvim/api/private/defs.h @@ -5,7 +5,7 @@ #include #include -#define ARRAY_DICT_INIT {.size = 0, .items = NULL} +#define ARRAY_DICT_INIT {.size = 0, .capacity = 0, .items = NULL} #define STRING_INIT {.data = NULL, .size = 0} #define OBJECT_INIT { .type = kObjectTypeNil } #define REMOTE_TYPE(type) typedef uint64_t type From 15ca58d79f8cc8565c1a2b2581029cf7901b5fbd Mon Sep 17 00:00:00 2001 From: Thiago de Arruda Date: Thu, 11 Sep 2014 10:35:52 -0300 Subject: [PATCH 22/25] api: Implement `vim_report_error` function This function is used to report errors caused by remote functions called by channel_send_call --- src/nvim/api/vim.c | 10 ++++++++++ src/nvim/eval.c | 12 ++++++++---- src/nvim/os/provider.c | 10 ++-------- 3 files changed, 20 insertions(+), 12 deletions(-) diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index e14c427dc1..25454761ea 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -308,6 +308,16 @@ void vim_err_write(String str) write_msg(str, true); } +/// Higher level error reporting function that ensures all str contents +/// are written by sending a trailing linefeed to `vim_wrr_write` +/// +/// @param str The message +void vim_report_error(String str) +{ + vim_err_write(str); + vim_err_write((String) {.data = "\n", .size = 1}); +} + /// Gets the current list of buffer handles /// /// @return The number of buffers diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 8becb29b26..d8c2e73150 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -12796,13 +12796,17 @@ static void f_send_call(typval_T *argvars, typval_T *rettv) return; } + if (errored) { + vim_report_error(result.data.string); + goto end; + } + Error conversion_error = {.set = false}; - if (errored || !object_to_vim(result, rettv, &conversion_error)) { - EMSG(errored ? - result.data.string.data : - _("Error converting the call result")); + if (!object_to_vim(result, rettv, &conversion_error)) { + EMSG(_("Error converting the call result")); } +end: api_free_object(result); } diff --git a/src/nvim/os/provider.c b/src/nvim/os/provider.c index e56e33aa1a..9d8f6f297c 100644 --- a/src/nvim/os/provider.c +++ b/src/nvim/os/provider.c @@ -102,7 +102,7 @@ Object provider_call(char *method, Array args) sizeof(buf), "Provider for method \"%s\" is not available", method); - report_error(buf); + vim_report_error(cstr_as_string(buf)); api_free_array(args); return NIL; } @@ -112,7 +112,7 @@ Object provider_call(char *method, Array args) channel_send_call(f->channel_id, method, args, &result, &error); if (error) { - report_error(result.data.string.data); + vim_report_error(result.data.string); api_free_object(result); return NIL; } @@ -151,9 +151,3 @@ static Feature * find_feature(char *name) return NULL; } - -static void report_error(char *str) -{ - vim_err_write((String) {.data = str, .size = strlen(str)}); - vim_err_write((String) {.data = "\n", .size = 1}); -} From cd2e46c0785d40b9ea15f6d722a3fad54c007b9b Mon Sep 17 00:00:00 2001 From: Thiago de Arruda Date: Thu, 11 Sep 2014 21:56:52 -0300 Subject: [PATCH 23/25] api/msgpack-rpc: Refactor metadata object construction Instead of building all metadata from msgpack-gen.lua, we now merge the generated part with manual information(such as types and features). The metadata is accessible through the api method `vim_get_api_info`. This was done to simplify the generator while also increasing flexibility(by being able to add more metadata) --- scripts/msgpack-gen.lua | 78 ++++++++++++---------------------- src/nvim/CMakeLists.txt | 4 +- src/nvim/api/private/defs.h | 4 -- src/nvim/api/private/helpers.c | 67 +++++++++++++++++++++++++++++ src/nvim/api/vim.c | 11 +++-- src/nvim/main.c | 18 +++++--- src/nvim/os/msgpack_rpc.c | 15 ------- src/nvim/os/msgpack_rpc.h | 1 + src/nvim/os/provider.c | 8 ++-- 9 files changed, 120 insertions(+), 86 deletions(-) diff --git a/scripts/msgpack-gen.lua b/scripts/msgpack-gen.lua index 5ef61267f2..8fbf64ebc0 100644 --- a/scripts/msgpack-gen.lua +++ b/scripts/msgpack-gen.lua @@ -35,30 +35,7 @@ grammar = Ct((c_proto + c_comment + c_preproc + ws) ^ 1) -- we need at least 2 arguments since the last one is the output file assert(#arg >= 1) --- api metadata -api = { - functions = {}, - types = {} -} - --- Extract type codes from api/private/defs.h. The codes are values between --- comment markers in the ObjectType enum -local typedefs_header = arg[1] -local input = io.open(typedefs_header, 'rb') -local reading_types = false -while true do - local line = input:read('*l'):gsub("^%s*(.-)%s*$", "%1") - if reading_types then - if line == '// end custom types' then - break - end - local type_name = line:gsub("^kObjectType(.-),$", "%1") - api.types[#api.types + 1] = type_name - else - reading_types = line == '// start custom types' - end -end -input:close() +functions = {} -- names of all headers relative to the source root(for inclusion in the -- generated file) @@ -67,7 +44,7 @@ headers = {} outputf = arg[#arg] -- read each input file, parse and append to the api metadata -for i = 2, #arg - 1 do +for i = 1, #arg - 1 do local full_path = arg[i] local parts = {} for part in string.gmatch(full_path, '[^/]+') do @@ -78,7 +55,7 @@ for i = 2, #arg - 1 do local input = io.open(full_path, 'rb') local tmp = grammar:match(input:read('*all')) for i = 1, #tmp do - api.functions[#api.functions + 1] = tmp[i] + functions[#functions + 1] = tmp[i] local fn = tmp[i] if #fn.parameters ~= 0 and fn.parameters[1][2] == 'channel_id' then -- this function should receive the channel id @@ -124,12 +101,12 @@ end output:write([[ -const uint8_t msgpack_metadata[] = { +static const uint8_t msgpack_metadata[] = { ]]) -- serialize the API metadata using msgpack and embed into the resulting -- binary for easy querying by clients -packed = msgpack.pack(api) +packed = msgpack.pack(functions) for i = 1, #packed do output:write(string.byte(packed, i)..', ') if i % 10 == 0 then @@ -138,17 +115,28 @@ for i = 1, #packed do end output:write([[ }; -const unsigned int msgpack_metadata_size = sizeof(msgpack_metadata); -msgpack_unpacked msgpack_unpacked_metadata; + +void msgpack_rpc_init_function_metadata(Dictionary *metadata) +{ + msgpack_unpacked unpacked; + msgpack_unpacked_init(&unpacked); + assert(msgpack_unpack_next(&unpacked, + (const char *)msgpack_metadata, + sizeof(msgpack_metadata), + NULL) == MSGPACK_UNPACK_SUCCESS); + Object functions; + msgpack_rpc_to_object(&unpacked.data, &functions); + msgpack_unpacked_destroy(&unpacked); + PUT(*metadata, "functions", functions); +} ]]) --- start the handler functions. First handler (method_id=0) is reserved for --- querying the metadata, usually it is the first function called by clients. --- Visit each function metadata to build the handler function with code --- generated for validating arguments and calling to the real API. -for i = 1, #api.functions do - local fn = api.functions[i] +-- start the handler functions. Visit each function metadata to build the +-- handler function with code generated for validating arguments and calling to +-- the real API. +for i = 1, #functions do + local fn = functions[i] local args = {} output:write('static Object handle_'..fn.name..'(uint64_t channel_id, msgpack_object *req, Error *error)') @@ -243,14 +231,6 @@ static Map(String, rpc_method_handler_fn) *methods = NULL; void msgpack_rpc_init(void) { - msgpack_unpacked_init(&msgpack_unpacked_metadata); - if (msgpack_unpack_next(&msgpack_unpacked_metadata, - (const char *)msgpack_metadata, - msgpack_metadata_size, - NULL) != MSGPACK_UNPACK_SUCCESS) { - abort(); - } - methods = map_new(String, rpc_method_handler_fn)(); ]]) @@ -258,8 +238,8 @@ void msgpack_rpc_init(void) -- Keep track of the maximum method name length in order to avoid walking -- strings longer than that when searching for a method handler local max_fname_len = 0 -for i = 1, #api.functions do - local fn = api.functions[i] +for i = 1, #functions do + local fn = functions[i] output:write(' map_put(String, rpc_method_handler_fn)(methods, '.. '(String) {.data = "'..fn.name..'", '.. '.size = sizeof("'..fn.name..'") - 1}, handle_'.. @@ -270,12 +250,6 @@ for i = 1, #api.functions do end end -local metadata_fn = 'get_api_metadata' -output:write(' map_put(String, rpc_method_handler_fn)(methods, '.. - '(String) {.data = "'..metadata_fn..'", '.. - '.size = sizeof("'..metadata_fn..'") - 1}, msgpack_rpc_handle_'.. - metadata_fn..');\n') - output:write('\n}\n\n') output:write([[ diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index 405cd20486..83de3347bd 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -3,7 +3,6 @@ include(CheckLibraryExists) set(GENERATED_DIR ${PROJECT_BINARY_DIR}/src/nvim/auto) set(DISPATCH_GENERATOR ${PROJECT_SOURCE_DIR}/scripts/msgpack-gen.lua) file(GLOB API_HEADERS api/*.h) -file(GLOB API_DEFS api/private/defs.h) set(MSGPACK_RPC_HEADER ${PROJECT_SOURCE_DIR}/src/nvim/os/msgpack_rpc.h) set(MSGPACK_DISPATCH ${GENERATED_DIR}/msgpack_dispatch.c) set(HEADER_GENERATOR ${PROJECT_SOURCE_DIR}/scripts/gendeclarations.lua) @@ -124,10 +123,9 @@ foreach(sfile ${NEOVIM_SOURCES} endforeach() add_custom_command(OUTPUT ${MSGPACK_DISPATCH} - COMMAND ${LUA_PRG} ${DISPATCH_GENERATOR} ${API_DEFS} ${API_HEADERS} ${MSGPACK_DISPATCH} + COMMAND ${LUA_PRG} ${DISPATCH_GENERATOR} ${API_HEADERS} ${MSGPACK_DISPATCH} DEPENDS ${API_HEADERS} - ${API_DEFS} ${MSGPACK_RPC_HEADER} ${DISPATCH_GENERATOR} ) diff --git a/src/nvim/api/private/defs.h b/src/nvim/api/private/defs.h index bae1819172..cf559a372e 100644 --- a/src/nvim/api/private/defs.h +++ b/src/nvim/api/private/defs.h @@ -44,13 +44,9 @@ typedef struct { } Dictionary; typedef enum { -// The following comments are markers that msgpack-gen.lua uses to extract -// types, don't remove! -// start custom types kObjectTypeBuffer, kObjectTypeWindow, kObjectTypeTabpage, -// end custom types kObjectTypeNil, kObjectTypeBoolean, kObjectTypeInteger, diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index de23481813..14a820aa1b 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -6,6 +6,7 @@ #include "nvim/api/private/helpers.h" #include "nvim/api/private/defs.h" #include "nvim/api/private/handle.h" +#include "nvim/os/provider.h" #include "nvim/ascii.h" #include "nvim/vim.h" #include "nvim/buffer.h" @@ -506,6 +507,72 @@ void api_free_dictionary(Dictionary value) free(value.items); } +Dictionary api_metadata(void) +{ + static Dictionary metadata = ARRAY_DICT_INIT; + + if (!metadata.size) { + msgpack_rpc_init_function_metadata(&metadata); + init_type_metadata(&metadata); + provider_init_feature_metadata(&metadata); + } + + return copy_object(DICTIONARY_OBJ(metadata)).data.dictionary; +} + +static void init_type_metadata(Dictionary *metadata) +{ + Dictionary types = ARRAY_DICT_INIT; + + Dictionary buffer_metadata = ARRAY_DICT_INIT; + PUT(buffer_metadata, "id", INTEGER_OBJ(kObjectTypeBuffer)); + + Dictionary window_metadata = ARRAY_DICT_INIT; + PUT(window_metadata, "id", INTEGER_OBJ(kObjectTypeWindow)); + + Dictionary tabpage_metadata = ARRAY_DICT_INIT; + PUT(tabpage_metadata, "id", INTEGER_OBJ(kObjectTypeTabpage)); + + PUT(types, "Buffer", DICTIONARY_OBJ(buffer_metadata)); + PUT(types, "Window", DICTIONARY_OBJ(window_metadata)); + PUT(types, "Tabpage", DICTIONARY_OBJ(tabpage_metadata)); + + PUT(*metadata, "types", DICTIONARY_OBJ(types)); +} + +/// Creates a deep clone of an object +static Object copy_object(Object obj) +{ + switch (obj.type) { + case kObjectTypeNil: + case kObjectTypeBoolean: + case kObjectTypeInteger: + case kObjectTypeFloat: + return obj; + + case kObjectTypeString: + return STRING_OBJ(cstr_to_string(obj.data.string.data)); + + case kObjectTypeArray: { + Array rv = ARRAY_DICT_INIT; + for (size_t i = 0; i < obj.data.array.size; i++) { + ADD(rv, copy_object(obj.data.array.items[i])); + } + return ARRAY_OBJ(rv); + } + + case kObjectTypeDictionary: { + Dictionary rv = ARRAY_DICT_INIT; + for (size_t i = 0; i < obj.data.dictionary.size; i++) { + KeyValuePair item = obj.data.dictionary.items[i]; + PUT(rv, item.key.data, copy_object(item.value)); + } + return DICTIONARY_OBJ(rv); + } + default: + abort(); + } +} /// Recursion helper for the `vim_to_object`. This uses a pointer table /// to avoid infinite recursion due to cyclic references diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 25454761ea..c0a9fe3410 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -528,10 +528,15 @@ void vim_register_provider(uint64_t channel_id, String feature, Error *err) } } -/// Returns a feature->method list dictionary for all pluggable features -Dictionary vim_discover_features(void) +Array vim_get_api_info(uint64_t channel_id) { - return provider_get_all(); + Array rv = ARRAY_DICT_INIT; + + assert(channel_id <= INT64_MAX); + ADD(rv, INTEGER_OBJ((int64_t)channel_id)); + ADD(rv, DICTIONARY_OBJ(api_metadata())); + + return rv; } /// Writes a message to vim output or error buffer. The string is split diff --git a/src/nvim/main.c b/src/nvim/main.c index 4b440ba92a..fc1826975a 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -12,6 +12,8 @@ #include #include +#include + #include "nvim/ascii.h" #include "nvim/vim.h" #include "nvim/main.h" @@ -57,6 +59,9 @@ #include "nvim/os/input.h" #include "nvim/os/os.h" #include "nvim/os/signal.h" +#include "nvim/os/msgpack_rpc_helpers.h" +#include "nvim/api/private/defs.h" +#include "nvim/api/private/helpers.h" /* Maximum number of commands from + or -c arguments. */ #define MAX_ARG_CMDS 10 @@ -116,9 +121,6 @@ static void init_locale(void); # endif #endif /* NO_VIM_MAIN */ -extern const uint8_t msgpack_metadata[]; -extern const unsigned int msgpack_metadata_size; - /* * Different types of error messages. */ @@ -1027,9 +1029,15 @@ static void command_line_scan(mparm_T *parmp) msg_didout = FALSE; mch_exit(0); } else if (STRICMP(argv[0] + argv_idx, "api-info") == 0) { - for (unsigned int i = 0; isize; i++) { + putchar(b->data[i]); } + mch_exit(0); } else if (STRICMP(argv[0] + argv_idx, "embed") == 0) { embedded_mode = true; diff --git a/src/nvim/os/msgpack_rpc.c b/src/nvim/os/msgpack_rpc.c index 2f347d9b15..d7e3d33c4b 100644 --- a/src/nvim/os/msgpack_rpc.c +++ b/src/nvim/os/msgpack_rpc.c @@ -17,8 +17,6 @@ # include "os/msgpack_rpc.c.generated.h" #endif -extern msgpack_unpacked msgpack_unpacked_metadata; - /// Validates the basic structure of the msgpack-rpc call and fills `res` /// with the basic response structure. /// @@ -83,19 +81,6 @@ Object msgpack_rpc_handle_missing_method(uint64_t channel_id, return NIL; } -/// Handler for retrieving API metadata through a msgpack-rpc call -Object msgpack_rpc_handle_get_api_metadata(uint64_t channel_id, - msgpack_object *req, - Error *error) -{ - Array rv = ARRAY_DICT_INIT; - Object metadata; - msgpack_rpc_to_object(&msgpack_unpacked_metadata.data, &metadata); - ADD(rv, INTEGER_OBJ((int64_t)channel_id)); - ADD(rv, metadata); - return ARRAY_OBJ(rv); -} - /// Serializes a msgpack-rpc request or notification(id == 0) WBuffer *serialize_request(uint64_t request_id, String method, diff --git a/src/nvim/os/msgpack_rpc.h b/src/nvim/os/msgpack_rpc.h index 35f175d2a0..3476d791ea 100644 --- a/src/nvim/os/msgpack_rpc.h +++ b/src/nvim/os/msgpack_rpc.h @@ -25,6 +25,7 @@ typedef Object (*rpc_method_handler_fn)(uint64_t channel_id, /// Initializes the msgpack-rpc method table void msgpack_rpc_init(void); +void msgpack_rpc_init_function_metadata(Dictionary *metadata); /// Dispatches to the actual API function after basic payload validation by /// `msgpack_rpc_call`. It is responsible for validating/converting arguments diff --git a/src/nvim/os/provider.c b/src/nvim/os/provider.c index 9d8f6f297c..2e7a677793 100644 --- a/src/nvim/os/provider.c +++ b/src/nvim/os/provider.c @@ -120,9 +120,9 @@ Object provider_call(char *method, Array args) return result; } -Dictionary provider_get_all(void) +void provider_init_feature_metadata(Dictionary *metadata) { - Dictionary rv = ARRAY_DICT_INIT; + Dictionary md = ARRAY_DICT_INIT; for (size_t i = 0; i < FEATURE_COUNT; i++) { Array methods = ARRAY_DICT_INIT; @@ -134,10 +134,10 @@ Dictionary provider_get_all(void) ADD(methods, STRING_OBJ(cstr_to_string(method))); } - PUT(rv, f->name, ARRAY_OBJ(methods)); + PUT(md, f->name, ARRAY_OBJ(methods)); } - return rv; + PUT(*metadata, "features", DICTIONARY_OBJ(md)); } static Feature * find_feature(char *name) From 545acf2024ea2653ae6937d570a37aa0340caa5e Mon Sep 17 00:00:00 2001 From: Thiago de Arruda Date: Fri, 12 Sep 2014 11:24:01 -0300 Subject: [PATCH 24/25] api metadata: Allow typed container information in api functions Adapt gendeclarations.lua/msgpack-gen.lua to allow the `ArrayOf(...)` and `DictionaryOf(...)` types in function headers. These are simple macros that expand to Array and Dictionary respectively, but the information is kept in the metadata object, which is useful for building clients in statically typed languages. --- scripts/gendeclarations.lua | 15 +++++++++++---- scripts/msgpack-gen.lua | 28 +++++++++++++++++++++++----- src/nvim/api/buffer.c | 21 ++++++++++++--------- src/nvim/api/private/defs.h | 5 +++++ src/nvim/api/tabpage.c | 2 +- src/nvim/api/vim.c | 8 ++++---- src/nvim/api/window.c | 6 +++--- 7 files changed, 59 insertions(+), 26 deletions(-) diff --git a/scripts/gendeclarations.lua b/scripts/gendeclarations.lua index 76711cc214..bc55b48a0a 100755 --- a/scripts/gendeclarations.lua +++ b/scripts/gendeclarations.lua @@ -59,9 +59,16 @@ local right_word = concat( raw_word, neg_look_ahead(aw) ) -local word = concat( - neg_look_behind(aw), - right_word +local word = branch( + concat( + branch(lit('ArrayOf('), lit('DictionaryOf(')), -- typed container macro + one_or_more(any_character - lit(')')), + lit(')') + ), + concat( + neg_look_behind(aw), + right_word + ) ) local spaces = any_amount(branch( s, @@ -204,7 +211,7 @@ while init ~= nil do declaration = declaration:gsub('\n', ' ') declaration = declaration:gsub('%s+', ' ') declaration = declaration:gsub(' ?%( ?', '(') - declaration = declaration:gsub(' ?%) ?', ')') + -- declaration = declaration:gsub(' ?%) ?', ')') declaration = declaration:gsub(' ?, ?', ', ') declaration = declaration:gsub(' ?(%*+) ?', ' %1') declaration = declaration:gsub(' ?(FUNC_ATTR_)', ' %1') diff --git a/scripts/msgpack-gen.lua b/scripts/msgpack-gen.lua index 8fbf64ebc0..68faa18c31 100644 --- a/scripts/msgpack-gen.lua +++ b/scripts/msgpack-gen.lua @@ -16,7 +16,12 @@ ws = S(' \t') + nl fill = ws ^ 0 c_comment = P('//') * (not_nl ^ 0) c_preproc = P('#') * (not_nl ^ 0) -c_id = letter * (alpha ^ 0) +typed_container = + (P('ArrayOf(') + P('DictionaryOf(')) * ((any - P(')')) ^ 1) * P(')') +c_id = ( + typed_container + + (letter * (alpha ^ 0)) +) c_void = P('void') c_param_type = ( ((P('Error') * fill * P('*') * fill) * Cc('error')) + @@ -90,6 +95,7 @@ output:write([[ #include "nvim/os/msgpack_rpc.h" #include "nvim/os/msgpack_rpc_helpers.h" #include "nvim/api/private/helpers.h" +#include "nvim/api/private/defs.h" ]]) for i = 1, #headers do @@ -132,6 +138,18 @@ void msgpack_rpc_init_function_metadata(Dictionary *metadata) ]]) +local function real_type(type) + local rv = type + if typed_container:match(rv) then + if rv:match('Array') then + rv = 'Array' + else + rv = 'Dictionary' + end + end + return rv +end + -- start the handler functions. Visit each function metadata to build the -- handler function with code generated for validating arguments and calling to -- the real API. @@ -146,7 +164,7 @@ for i = 1, #functions do for j = 1, #fn.parameters do local param = fn.parameters[j] local converted = 'arg_'..j - output:write('\n '..param[1]..' '..converted..' api_init_'..string.lower(param[1])..';') + output:write('\n '..param[1]..' '..converted..' api_init_'..string.lower(real_type(param[1]))..';') end output:write('\n') output:write('\n if (req->via.array.ptr[3].via.array.size != '..#fn.parameters..') {') @@ -161,7 +179,7 @@ for i = 1, #functions do param = fn.parameters[j] arg = '(req->via.array.ptr[3].via.array.ptr + '..(j - 1)..')' converted = 'arg_'..j - convert_arg = 'msgpack_rpc_to_'..string.lower(param[1]) + convert_arg = 'msgpack_rpc_to_'..real_type(param[1]):lower() output:write('\n if (!'..convert_arg..'('..arg..', &'..converted..')) {') output:write('\n snprintf(error->msg, sizeof(error->msg), "Wrong type for argument '..j..', expecting '..param[1]..'");') output:write('\n error->set = true;') @@ -208,7 +226,7 @@ for i = 1, #functions do end if fn.return_type ~= 'void' then - output:write('\n Object ret = '..string.upper(fn.return_type)..'_OBJ(rv);') + output:write('\n Object ret = '..string.upper(real_type(fn.return_type))..'_OBJ(rv);') end -- Now generate the cleanup label for freeing memory allocated for the -- arguments @@ -216,7 +234,7 @@ for i = 1, #functions do for j = 1, #fn.parameters do local param = fn.parameters[j] - output:write('\n api_free_'..string.lower(param[1])..'(arg_'..j..');') + output:write('\n api_free_'..string.lower(real_type(param[1]))..'(arg_'..j..');') end if fn.return_type ~= 'void' then output:write('\n return ret;\n}\n\n'); diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c index 383e13fd92..8355bfe868 100644 --- a/src/nvim/api/buffer.c +++ b/src/nvim/api/buffer.c @@ -95,12 +95,12 @@ void buffer_del_line(Buffer buffer, Integer index, Error *err) /// @param include_end True if the slice includes the `end` parameter /// @param[out] err Details of an error that may have occurred /// @return An array of lines -Array buffer_get_slice(Buffer buffer, - Integer start, - Integer end, - Boolean include_start, - Boolean include_end, - Error *err) +ArrayOf(String) buffer_get_slice(Buffer buffer, + Integer start, + Integer end, + Boolean include_start, + Boolean include_end, + Error *err) { Array rv = ARRAY_DICT_INIT; buf_T *buf = find_buffer_by_handle(buffer, err); @@ -161,7 +161,7 @@ void buffer_set_slice(Buffer buffer, Integer end, Boolean include_start, Boolean include_end, - Array replacement, + ArrayOf(String) replacement, Error *err) { buf_T *buf = find_buffer_by_handle(buffer, err); @@ -436,7 +436,10 @@ Boolean buffer_is_valid(Buffer buffer) /// to the end of the buffer. /// @param lines An array of lines /// @param[out] err Details of an error that may have occurred -void buffer_insert(Buffer buffer, Integer lnum, Array lines, Error *err) +void buffer_insert(Buffer buffer, + Integer lnum, + ArrayOf(String) lines, + Error *err) { buffer_set_slice(buffer, lnum, lnum, false, true, lines, err); } @@ -447,7 +450,7 @@ void buffer_insert(Buffer buffer, Integer lnum, Array lines, Error *err) /// @param name The mark's name /// @param[out] err Details of an error that may have occurred /// @return The (row, col) tuple -Array buffer_get_mark(Buffer buffer, String name, Error *err) +ArrayOf(Integer, 2) buffer_get_mark(Buffer buffer, String name, Error *err) { Array rv = ARRAY_DICT_INIT; buf_T *buf = find_buffer_by_handle(buffer, err); diff --git a/src/nvim/api/private/defs.h b/src/nvim/api/private/defs.h index cf559a372e..2dd229ec5f 100644 --- a/src/nvim/api/private/defs.h +++ b/src/nvim/api/private/defs.h @@ -10,6 +10,11 @@ #define OBJECT_INIT { .type = kObjectTypeNil } #define REMOTE_TYPE(type) typedef uint64_t type +#ifdef INCLUDE_GENERATED_DECLARATIONS + #define ArrayOf(...) Array + #define DictionaryOf(...) Dictionary +#endif + // Basic types typedef struct { char msg[256]; diff --git a/src/nvim/api/tabpage.c b/src/nvim/api/tabpage.c index 901f9a6c1a..91020d6850 100644 --- a/src/nvim/api/tabpage.c +++ b/src/nvim/api/tabpage.c @@ -13,7 +13,7 @@ /// @param tabpage The tabpage /// @param[out] err Details of an error that may have occurred /// @return The number of windows in `tabpage` -Array tabpage_get_windows(Tabpage tabpage, Error *err) +ArrayOf(Window) tabpage_get_windows(Tabpage tabpage, Error *err) { Array rv = ARRAY_DICT_INIT; tabpage_T *tab = find_tab_by_handle(tabpage, err); diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index c0a9fe3410..43f2aafdc8 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -149,7 +149,7 @@ Integer vim_strwidth(String str, Error *err) /// Returns a list of paths contained in 'runtimepath' /// /// @return The list of paths -Array vim_list_runtime_paths(void) +ArrayOf(String) vim_list_runtime_paths(void) { Array rv = ARRAY_DICT_INIT; uint8_t *rtp = p_rtp; @@ -321,7 +321,7 @@ void vim_report_error(String str) /// Gets the current list of buffer handles /// /// @return The number of buffers -Array vim_get_buffers(void) +ArrayOf(Buffer) vim_get_buffers(void) { Array rv = ARRAY_DICT_INIT; buf_T *b = firstbuf; @@ -381,7 +381,7 @@ void vim_set_current_buffer(Buffer buffer, Error *err) /// Gets the current list of window handles /// /// @return The number of windows -Array vim_get_windows(void) +ArrayOf(Window) vim_get_windows(void) { Array rv = ARRAY_DICT_INIT; tabpage_T *tp; @@ -437,7 +437,7 @@ void vim_set_current_window(Window window, Error *err) /// Gets the current list of tabpage handles /// /// @return The number of tab pages -Array vim_get_tabpages(void) +ArrayOf(Tabpage) vim_get_tabpages(void) { Array rv = ARRAY_DICT_INIT; tabpage_T *tp = first_tabpage; diff --git a/src/nvim/api/window.c b/src/nvim/api/window.c index 967d8acda4..dd256f2b6d 100644 --- a/src/nvim/api/window.c +++ b/src/nvim/api/window.c @@ -33,7 +33,7 @@ Buffer window_get_buffer(Window window, Error *err) /// @param window The window handle /// @param[out] err Details of an error that may have occurred /// @return the (row, col) tuple -Array window_get_cursor(Window window, Error *err) +ArrayOf(Integer, 2) window_get_cursor(Window window, Error *err) { Array rv = ARRAY_DICT_INIT; win_T *win = find_window_by_handle(window, err); @@ -51,7 +51,7 @@ Array window_get_cursor(Window window, Error *err) /// @param window The window handle /// @param pos the (row, col) tuple representing the new position /// @param[out] err Details of an error that may have occurred -void window_set_cursor(Window window, Array pos, Error *err) +void window_set_cursor(Window window, ArrayOf(Integer, 2) pos, Error *err) { win_T *win = find_window_by_handle(window, err); @@ -247,7 +247,7 @@ void window_set_option(Window window, String name, Object value, Error *err) /// @param window The window handle /// @param[out] err Details of an error that may have occurred /// @return The (row, col) tuple with the window position -Array window_get_position(Window window, Error *err) +ArrayOf(Integer, 2) window_get_position(Window window, Error *err) { Array rv = ARRAY_DICT_INIT; win_T *win = find_window_by_handle(window, err); From 2a67b847aa2074f688fce9f96e060eeb5ba29435 Mon Sep 17 00:00:00 2001 From: Thiago de Arruda Date: Fri, 12 Sep 2014 13:35:31 -0300 Subject: [PATCH 25/25] build/test: install vroom/python-client from the master branch --- .ci/common.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/common.sh b/.ci/common.sh index 09a6e24b9d..ba00011222 100644 --- a/.ci/common.sh +++ b/.ci/common.sh @@ -67,7 +67,7 @@ install_prebuilt_deps() { install_vroom() { ( - sudo pip install neovim + sudo pip install git+https://github.com/neovim/python-client.git git clone git://github.com/google/vroom cd vroom python setup.py build