diff options
Diffstat (limited to 'src/nvim/msgpack_rpc')
-rw-r--r-- | src/nvim/msgpack_rpc/channel.c | 171 | ||||
-rw-r--r-- | src/nvim/msgpack_rpc/channel.h | 1 | ||||
-rw-r--r-- | src/nvim/msgpack_rpc/defs.h | 28 | ||||
-rw-r--r-- | src/nvim/msgpack_rpc/helpers.c | 467 | ||||
-rw-r--r-- | src/nvim/msgpack_rpc/remote_ui.c | 363 | ||||
-rw-r--r-- | src/nvim/msgpack_rpc/remote_ui.h | 9 | ||||
-rw-r--r-- | src/nvim/msgpack_rpc/server.c | 7 |
7 files changed, 411 insertions, 635 deletions
diff --git a/src/nvim/msgpack_rpc/channel.c b/src/nvim/msgpack_rpc/channel.c index 34ff7c6374..98636263b9 100644 --- a/src/nvim/msgpack_rpc/channel.c +++ b/src/nvim/msgpack_rpc/channel.c @@ -7,8 +7,8 @@ #include "nvim/api/private/helpers.h" #include "nvim/api/vim.h" +#include "nvim/api/ui.h" #include "nvim/msgpack_rpc/channel.h" -#include "nvim/msgpack_rpc/remote_ui.h" #include "nvim/event/loop.h" #include "nvim/event/libuv_process.h" #include "nvim/event/rstream.h" @@ -16,8 +16,10 @@ #include "nvim/event/socket.h" #include "nvim/msgpack_rpc/helpers.h" #include "nvim/vim.h" +#include "nvim/main.h" #include "nvim/ascii.h" #include "nvim/memory.h" +#include "nvim/eval.h" #include "nvim/os_unix.h" #include "nvim/message.h" #include "nvim/map.h" @@ -54,12 +56,7 @@ typedef struct { msgpack_unpacker *unpacker; union { Stream stream; - struct { - LibuvProcess uvproc; - Stream in; - Stream out; - Stream err; - } process; + Process *proc; struct { Stream in; Stream out; @@ -68,7 +65,7 @@ typedef struct { uint64_t next_request_id; kvec_t(ChannelCallFrame *) call_stack; kvec_t(WBuffer *) delayed_notifications; - Queue *events; + MultiQueue *events; } Channel; typedef struct { @@ -78,7 +75,6 @@ typedef struct { uint64_t request_id; } RequestEvent; -static uint64_t next_id = 1; static PMap(uint64_t) *channels = NULL; static PMap(cstr_t) *event_strings = NULL; static msgpack_sbuffer out_buffer; @@ -111,33 +107,20 @@ void channel_teardown(void) } /// Creates an API channel by starting a process and connecting to its -/// stdin/stdout. stderr is forwarded to the editor error stream. +/// stdin/stdout. stderr is handled by the job infrastructure. /// /// @param argv The argument vector for the process. [consumed] /// @return The channel id (> 0), on success. /// 0, on error. -uint64_t channel_from_process(char **argv) -{ - Channel *channel = register_channel(kChannelTypeProc); - channel->data.process.uvproc = libuv_process_init(&loop, channel); - Process *proc = &channel->data.process.uvproc.process; - proc->argv = argv; - proc->in = &channel->data.process.in; - proc->out = &channel->data.process.out; - proc->err = &channel->data.process.err; - proc->cb = process_exit; - if (!process_spawn(proc)) { - loop_poll_events(&loop, 0); - decref(channel); - return 0; - } - +uint64_t channel_from_process(Process *proc, uint64_t id) +{ + Channel *channel = register_channel(kChannelTypeProc, id, proc->events); incref(channel); // process channels are only closed by the exit_cb + channel->data.proc = proc; + wstream_init(proc->in, 0); rstream_init(proc->out, 0); - rstream_start(proc->out, parse_msgpack); - rstream_init(proc->err, 0); - rstream_start(proc->err, forward_stderr); + rstream_start(proc->out, parse_msgpack, channel); return channel->id; } @@ -147,14 +130,14 @@ uint64_t channel_from_process(char **argv) /// @param watcher The SocketWatcher ready to accept the connection void channel_from_connection(SocketWatcher *watcher) { - Channel *channel = register_channel(kChannelTypeSocket); - socket_watcher_accept(watcher, &channel->data.stream, channel); + Channel *channel = register_channel(kChannelTypeSocket, 0, NULL); + socket_watcher_accept(watcher, &channel->data.stream); incref(channel); // close channel only after the stream is closed channel->data.stream.internal_close_cb = close_cb; channel->data.stream.internal_data = channel; wstream_init(&channel->data.stream, 0); rstream_init(&channel->data.stream, CHANNEL_BUFFER_SIZE); - rstream_start(&channel->data.stream, parse_msgpack); + rstream_start(&channel->data.stream, parse_msgpack, channel); } /// Sends event/arguments to channel @@ -179,7 +162,7 @@ bool channel_send_event(uint64_t id, char *name, Array args) // Pending request, queue the notification for later sending. String method = cstr_as_string(name); WBuffer *buffer = serialize_request(id, 0, method, args, &out_buffer, 1); - kv_push(WBuffer *, channel->delayed_notifications, buffer); + kv_push(channel->delayed_notifications, buffer); } else { send_event(channel, name, args); } @@ -217,10 +200,10 @@ Object channel_send_call(uint64_t id, send_request(channel, request_id, method_name, args); // Push the frame - ChannelCallFrame frame = {request_id, false, false, NIL}; - kv_push(ChannelCallFrame *, channel->call_stack, &frame); + ChannelCallFrame frame = { request_id, false, false, NIL }; + kv_push(channel->call_stack, &frame); channel->pending_requests++; - LOOP_PROCESS_EVENTS_UNTIL(&loop, channel->events, -1, frame.returned); + LOOP_PROCESS_EVENTS_UNTIL(&main_loop, channel->events, -1, frame.returned); (void)kv_pop(channel->call_stack); channel->pending_requests--; @@ -313,30 +296,21 @@ bool channel_close(uint64_t id) /// Neovim void channel_from_stdio(void) { - Channel *channel = register_channel(kChannelTypeStdio); + Channel *channel = register_channel(kChannelTypeStdio, 0, NULL); incref(channel); // stdio channels are only closed on exit // read stream - rstream_init_fd(&loop, &channel->data.std.in, 0, CHANNEL_BUFFER_SIZE, - channel); - rstream_start(&channel->data.std.in, parse_msgpack); + rstream_init_fd(&main_loop, &channel->data.std.in, 0, CHANNEL_BUFFER_SIZE); + rstream_start(&channel->data.std.in, parse_msgpack, channel); // write stream - wstream_init_fd(&loop, &channel->data.std.out, 1, 0, NULL); + wstream_init_fd(&main_loop, &channel->data.std.out, 1, 0); } -static void forward_stderr(Stream *stream, RBuffer *rbuf, size_t count, - void *data, bool eof) +void channel_process_exit(uint64_t id, int status) { - while (rbuffer_size(rbuf)) { - char buf[256]; - size_t read = rbuffer_read(rbuf, buf, sizeof(buf) - 1); - buf[read] = NUL; - ELOG("Channel %" PRIu64 " stderr: %s", ((Channel *)data)->id, buf); - } -} + Channel *channel = pmap_get(uint64_t)(channels, id); -static void process_exit(Process *proc, int status, void *data) -{ - decref(data); + channel->closed = true; + decref(channel); } static void parse_msgpack(Stream *stream, RBuffer *rbuf, size_t c, void *data, @@ -466,7 +440,7 @@ static void handle_request(Channel *channel, msgpack_object *request) if (handler.async) { on_request_event((void **)&event_data); } else { - queue_put(channel->events, on_request_event, 1, event_data); + multiqueue_put(channel->events, on_request_event, 1, event_data); } } @@ -478,7 +452,7 @@ static void on_request_event(void **argv) Array args = e->args; uint64_t request_id = e->request_id; Error error = ERROR_INIT; - Object result = handler.fn(channel->id, request_id, args, &error); + Object result = handler.fn(channel->id, args, &error); if (request_id != NO_RESPONSE) { // send the response msgpack_packer response; @@ -491,8 +465,7 @@ static void on_request_event(void **argv) } else { api_free_object(result); } - // All arguments were freed already, but we still need to free the array - xfree(args.items); + api_free_array(args); decref(channel); xfree(e); } @@ -511,7 +484,7 @@ static bool channel_write(Channel *channel, WBuffer *buffer) success = wstream_write(&channel->data.stream, buffer); break; case kChannelTypeProc: - success = wstream_write(&channel->data.process.in, buffer); + success = wstream_write(channel->data.proc->in, buffer); break; case kChannelTypeStdio: success = wstream_write(&channel->data.std.out, buffer); @@ -574,13 +547,12 @@ static void send_event(Channel *channel, static void broadcast_event(char *name, Array args) { - kvec_t(Channel *) subscribed; - kv_init(subscribed); + kvec_t(Channel *) subscribed = KV_INITIAL_VALUE; Channel *channel; map_foreach_value(channels, channel, { if (pmap_has(cstr_t)(channel->subscribed_events, name)) { - kv_push(Channel *, subscribed, channel); + kv_push(subscribed, channel); } }); @@ -600,7 +572,7 @@ static void broadcast_event(char *name, Array args) for (size_t i = 0; i < kv_size(subscribed); i++) { Channel *channel = kv_A(subscribed, i); if (channel->pending_requests) { - kv_push(WBuffer *, channel->delayed_notifications, buffer); + kv_push(channel->delayed_notifications, buffer); } else { channel_write(channel, buffer); } @@ -637,17 +609,18 @@ static void close_channel(Channel *channel) switch (channel->type) { case kChannelTypeSocket: - stream_close(&channel->data.stream, NULL); + stream_close(&channel->data.stream, NULL, NULL); break; case kChannelTypeProc: - if (!channel->data.process.uvproc.process.closed) { - process_stop(&channel->data.process.uvproc.process); - } + // Only close the rpc channel part, + // there could be an error message on the stderr stream + process_close_in(channel->data.proc); + process_close_out(channel->data.proc); break; case kChannelTypeStdio: - stream_close(&channel->data.std.in, NULL); - stream_close(&channel->data.std.out, NULL); - queue_put(loop.fast_events, exit_event, 1, channel); + stream_close(&channel->data.std.in, NULL, NULL); + stream_close(&channel->data.std.out, NULL, NULL); + multiqueue_put(main_loop.fast_events, exit_event, 1, channel); return; default: abort(); @@ -680,7 +653,9 @@ static void free_channel(Channel *channel) pmap_free(cstr_t)(channel->subscribed_events); kv_destroy(channel->call_stack); kv_destroy(channel->delayed_notifications); - queue_free(channel->events); + if (channel->type != kChannelTypeProc) { + multiqueue_free(channel->events); + } xfree(channel); } @@ -689,15 +664,16 @@ static void close_cb(Stream *stream, void *data) decref(data); } -static Channel *register_channel(ChannelType type) +static Channel *register_channel(ChannelType type, uint64_t id, + MultiQueue *events) { Channel *rv = xmalloc(sizeof(Channel)); - rv->events = queue_new_child(loop.events); + rv->events = events ? events : multiqueue_new_child(main_loop.events); rv->type = type; rv->refcount = 1; rv->closed = false; rv->unpacker = msgpack_unpacker_new(MSGPACK_UNPACKER_INIT_BUFFER_SIZE); - rv->id = next_id++; + rv->id = id > 0 ? id : next_chan_id++; rv->pending_requests = 0; rv->subscribed_events = pmap_new(cstr_t)(); rv->next_request_id = 1; @@ -816,20 +792,55 @@ static void decref(Channel *channel) #define REQ "[request] " #define RES "[response] " #define NOT "[notification] " +#define ERR "[error] " + +// Cannot define array with negative offsets, so this one is needed to be added +// to MSGPACK_UNPACK_\* values. +#define MUR_OFF 2 + +static const char *const msgpack_error_messages[] = { + [MSGPACK_UNPACK_EXTRA_BYTES + MUR_OFF] = "extra bytes found", + [MSGPACK_UNPACK_CONTINUE + MUR_OFF] = "incomplete string", + [MSGPACK_UNPACK_PARSE_ERROR + MUR_OFF] = "parse error", + [MSGPACK_UNPACK_NOMEM_ERROR + MUR_OFF] = "not enough memory", +}; static void log_server_msg(uint64_t channel_id, msgpack_sbuffer *packed) { msgpack_unpacked unpacked; msgpack_unpacked_init(&unpacked); - msgpack_unpack_next(&unpacked, packed->data, packed->size, NULL); - uint64_t type = unpacked.data.via.array.ptr[0].via.u64; DLOGN("[msgpack-rpc] nvim -> client(%" PRIu64 ") ", channel_id); - log_lock(); - FILE *f = open_log_file(); - fprintf(f, type ? (type == 1 ? RES : NOT) : REQ); - log_msg_close(f, unpacked.data); - msgpack_unpacked_destroy(&unpacked); + const msgpack_unpack_return result = + msgpack_unpack_next(&unpacked, packed->data, packed->size, NULL); + switch (result) { + case MSGPACK_UNPACK_SUCCESS: { + uint64_t type = unpacked.data.via.array.ptr[0].via.u64; + log_lock(); + FILE *f = open_log_file(); + fprintf(f, type ? (type == 1 ? RES : NOT) : REQ); + log_msg_close(f, unpacked.data); + msgpack_unpacked_destroy(&unpacked); + break; + } + case MSGPACK_UNPACK_EXTRA_BYTES: + case MSGPACK_UNPACK_CONTINUE: + case MSGPACK_UNPACK_PARSE_ERROR: + case MSGPACK_UNPACK_NOMEM_ERROR: { + log_lock(); + FILE *f = open_log_file(); + fprintf(f, ERR); + log_msg_close(f, (msgpack_object) { + .type = MSGPACK_OBJECT_STR, + .via.str = { + .ptr = (char *)msgpack_error_messages[result + MUR_OFF], + .size = (uint32_t)strlen( + msgpack_error_messages[result + MUR_OFF]), + }, + }); + break; + } + } } static void log_client_msg(uint64_t channel_id, diff --git a/src/nvim/msgpack_rpc/channel.h b/src/nvim/msgpack_rpc/channel.h index 104547a7b8..0d92976d02 100644 --- a/src/nvim/msgpack_rpc/channel.h +++ b/src/nvim/msgpack_rpc/channel.h @@ -6,6 +6,7 @@ #include "nvim/api/private/defs.h" #include "nvim/event/socket.h" +#include "nvim/event/process.h" #include "nvim/vim.h" #define METHOD_MAXLEN 512 diff --git a/src/nvim/msgpack_rpc/defs.h b/src/nvim/msgpack_rpc/defs.h deleted file mode 100644 index 5611636d4f..0000000000 --- a/src/nvim/msgpack_rpc/defs.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef NVIM_MSGPACK_RPC_DEFS_H -#define NVIM_MSGPACK_RPC_DEFS_H - - -/// The rpc_method_handlers table, used in msgpack_rpc_dispatch(), stores -/// functions of this type. -typedef struct { - Object (*fn)(uint64_t channel_id, - uint64_t request_id, - Array args, - Error *error); - bool async; // function is always safe to run immediately instead of being - // put in a request queue for handling when nvim waits for input. -} MsgpackRpcRequestHandler; - -/// Initializes the msgpack-rpc method table -void msgpack_rpc_init_method_table(void); - -// Add a handler to the method table -void msgpack_rpc_add_method_handler(String method, - MsgpackRpcRequestHandler handler); - -void msgpack_rpc_init_function_metadata(Dictionary *metadata); - -MsgpackRpcRequestHandler msgpack_rpc_get_handler_for(const char *name, - size_t name_len) - FUNC_ATTR_NONNULL_ARG(1); -#endif // NVIM_MSGPACK_RPC_DEFS_H diff --git a/src/nvim/msgpack_rpc/helpers.c b/src/nvim/msgpack_rpc/helpers.c index 0049ae6b95..5137b375f0 100644 --- a/src/nvim/msgpack_rpc/helpers.c +++ b/src/nvim/msgpack_rpc/helpers.c @@ -4,12 +4,14 @@ #include <msgpack.h> +#include "nvim/api/private/dispatch.h" #include "nvim/api/private/helpers.h" #include "nvim/msgpack_rpc/helpers.h" -#include "nvim/msgpack_rpc/defs.h" +#include "nvim/lib/kvec.h" #include "nvim/vim.h" #include "nvim/log.h" #include "nvim/memory.h" +#include "nvim/assert.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "msgpack_rpc/helpers.c.generated.h" @@ -18,39 +20,40 @@ 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) { \ - 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 o, msgpack_packer *res) \ - FUNC_ATTR_NONNULL_ARG(2) \ - { \ - 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); \ +#define HANDLE_TYPE_CONVERSION_IMPL(t, lt) \ + bool msgpack_rpc_to_##lt(const msgpack_object *const obj, \ + Integer *const arg) \ + FUNC_ATTR_NONNULL_ALL \ + { \ + 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 = (handle_T)data.via.i64; \ + return true; \ + } \ + \ + void msgpack_rpc_from_##lt(Integer o, msgpack_packer *res) \ + FUNC_ATTR_NONNULL_ARG(2) \ + { \ + msgpack_packer pac; \ + msgpack_packer_init(&pac, &sbuffer, msgpack_sbuffer_write); \ + msgpack_pack_int64(&pac, (handle_T)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) @@ -63,34 +66,182 @@ 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) +typedef struct { + const msgpack_object *mobj; + Object *aobj; + bool container; + size_t idx; +} MPToAPIObjectStackItem; + +/// Convert type used by msgpack parser to Neovim own API type +/// +/// @param[in] obj Msgpack value to convert. +/// @param[out] arg Location where result of conversion will be saved. +/// +/// @return true in case of success, false otherwise. +bool msgpack_rpc_to_object(const msgpack_object *const obj, Object *const 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) { - *arg = (int64_t)obj->via.u64; - return true; + bool ret = true; + kvec_t(MPToAPIObjectStackItem) stack = KV_INITIAL_VALUE; + kv_push(stack, ((MPToAPIObjectStackItem) { obj, arg, false, 0 })); + while (ret && kv_size(stack)) { + MPToAPIObjectStackItem cur = kv_last(stack); + if (!cur.container) { + *cur.aobj = NIL; + } + switch (cur.mobj->type) { + case MSGPACK_OBJECT_NIL: { + break; + } + case MSGPACK_OBJECT_BOOLEAN: { + *cur.aobj = BOOLEAN_OBJ(cur.mobj->via.boolean); + break; + } + case MSGPACK_OBJECT_NEGATIVE_INTEGER: { + STATIC_ASSERT(sizeof(Integer) == sizeof(cur.mobj->via.i64), + "Msgpack integer size does not match API integer"); + *cur.aobj = INTEGER_OBJ(cur.mobj->via.i64); + break; + } + case MSGPACK_OBJECT_POSITIVE_INTEGER: { + STATIC_ASSERT(sizeof(Integer) == sizeof(cur.mobj->via.u64), + "Msgpack integer size does not match API integer"); + if (cur.mobj->via.u64 > API_INTEGER_MAX) { + ret = false; + } else { + *cur.aobj = INTEGER_OBJ((Integer)cur.mobj->via.u64); + } + break; + } + case MSGPACK_OBJECT_FLOAT: { + STATIC_ASSERT(sizeof(Float) == sizeof(cur.mobj->via.f64), + "Msgpack floating-point size does not match API integer"); + *cur.aobj = FLOATING_OBJ(cur.mobj->via.f64); + break; + } +#define STR_CASE(type, attr, obj, dest, conv) \ + case type: { \ + dest = conv(((String) { \ + .size = obj->via.attr.size, \ + .data = (obj->via.attr.ptr == NULL || obj->via.attr.size == 0 \ + ? xmemdupz("", 0) \ + : xmemdupz(obj->via.attr.ptr, obj->via.attr.size)), \ + })); \ + break; \ + } + STR_CASE(MSGPACK_OBJECT_STR, str, cur.mobj, *cur.aobj, STRING_OBJ) + STR_CASE(MSGPACK_OBJECT_BIN, bin, cur.mobj, *cur.aobj, STRING_OBJ) + case MSGPACK_OBJECT_ARRAY: { + const size_t size = cur.mobj->via.array.size; + if (cur.container) { + if (cur.idx >= size) { + (void)kv_pop(stack); + } else { + const size_t idx = cur.idx; + cur.idx++; + kv_last(stack) = cur; + kv_push(stack, ((MPToAPIObjectStackItem) { + .mobj = &cur.mobj->via.array.ptr[idx], + .aobj = &cur.aobj->data.array.items[idx], + .container = false, + })); + } + } else { + *cur.aobj = ARRAY_OBJ(((Array) { + .size = size, + .capacity = size, + .items = (size > 0 + ? xcalloc(size, sizeof(*cur.aobj->data.array.items)) + : NULL), + })); + cur.container = true; + kv_last(stack) = cur; + } + break; + } + case MSGPACK_OBJECT_MAP: { + const size_t size = cur.mobj->via.map.size; + if (cur.container) { + if (cur.idx >= size) { + (void)kv_pop(stack); + } else { + const size_t idx = cur.idx; + cur.idx++; + kv_last(stack) = cur; + const msgpack_object *const key = &cur.mobj->via.map.ptr[idx].key; + switch (key->type) { +#define ID(x) x + STR_CASE(MSGPACK_OBJECT_STR, str, key, + cur.aobj->data.dictionary.items[idx].key, ID) + STR_CASE(MSGPACK_OBJECT_BIN, bin, key, + cur.aobj->data.dictionary.items[idx].key, ID) +#undef ID + case MSGPACK_OBJECT_NIL: + case MSGPACK_OBJECT_BOOLEAN: + case MSGPACK_OBJECT_POSITIVE_INTEGER: + case MSGPACK_OBJECT_NEGATIVE_INTEGER: + case MSGPACK_OBJECT_FLOAT: + case MSGPACK_OBJECT_EXT: + case MSGPACK_OBJECT_MAP: + case MSGPACK_OBJECT_ARRAY: { + ret = false; + break; + } + } + if (ret) { + kv_push(stack, ((MPToAPIObjectStackItem) { + .mobj = &cur.mobj->via.map.ptr[idx].val, + .aobj = &cur.aobj->data.dictionary.items[idx].value, + .container = false, + })); + } + } + } else { + *cur.aobj = DICTIONARY_OBJ(((Dictionary) { + .size = size, + .capacity = size, + .items = (size > 0 + ? xcalloc(size, sizeof(*cur.aobj->data.dictionary.items)) + : NULL), + })); + cur.container = true; + kv_last(stack) = cur; + } + break; + } + case MSGPACK_OBJECT_EXT: { + switch (cur.mobj->via.ext.type) { + case kObjectTypeBuffer: { + cur.aobj->type = kObjectTypeBuffer; + ret = msgpack_rpc_to_buffer(cur.mobj, &cur.aobj->data.integer); + break; + } + case kObjectTypeWindow: { + cur.aobj->type = kObjectTypeWindow; + ret = msgpack_rpc_to_window(cur.mobj, &cur.aobj->data.integer); + break; + } + case kObjectTypeTabpage: { + cur.aobj->type = kObjectTypeTabpage; + ret = msgpack_rpc_to_tabpage(cur.mobj, &cur.aobj->data.integer); + break; + } + } + break; + } +#undef STR_CASE + } + if (!cur.container) { + (void)kv_pop(stack); + } } - - *arg = obj->via.i64; - return obj->type == MSGPACK_OBJECT_NEGATIVE_INTEGER; -} - -bool msgpack_rpc_to_float(msgpack_object *obj, Float *arg) - FUNC_ATTR_NONNULL_ALL -{ - *arg = obj->via.f64; - return obj->type == MSGPACK_OBJECT_FLOAT; + kv_destroy(stack); + return ret; } -bool msgpack_rpc_to_string(msgpack_object *obj, String *arg) +static bool msgpack_rpc_to_string(const msgpack_object *const obj, + String *const arg) FUNC_ATTR_NONNULL_ALL { if (obj->type == MSGPACK_OBJECT_BIN || obj->type == MSGPACK_OBJECT_STR) { @@ -103,58 +254,7 @@ bool msgpack_rpc_to_string(msgpack_object *obj, String *arg) return false; } -bool msgpack_rpc_to_object(msgpack_object *obj, Object *arg) - FUNC_ATTR_NONNULL_ALL -{ - switch (obj->type) { - case MSGPACK_OBJECT_NIL: - arg->type = kObjectTypeNil; - return true; - - case MSGPACK_OBJECT_BOOLEAN: - arg->type = kObjectTypeBoolean; - return msgpack_rpc_to_boolean(obj, &arg->data.boolean); - - case MSGPACK_OBJECT_POSITIVE_INTEGER: - case MSGPACK_OBJECT_NEGATIVE_INTEGER: - arg->type = kObjectTypeInteger; - return msgpack_rpc_to_integer(obj, &arg->data.integer); - - case MSGPACK_OBJECT_FLOAT: - arg->type = kObjectTypeFloat; - 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); - - case MSGPACK_OBJECT_ARRAY: - arg->type = kObjectTypeArray; - return msgpack_rpc_to_array(obj, &arg->data.array); - - case MSGPACK_OBJECT_MAP: - arg->type = kObjectTypeDictionary; - return msgpack_rpc_to_dictionary(obj, &arg->data.dictionary); - - case MSGPACK_OBJECT_EXT: - switch (obj->via.ext.type) { - case kObjectTypeBuffer: - arg->type = kObjectTypeBuffer; - return msgpack_rpc_to_buffer(obj, &arg->data.buffer); - case kObjectTypeWindow: - arg->type = kObjectTypeWindow; - return msgpack_rpc_to_window(obj, &arg->data.window); - case kObjectTypeTabpage: - arg->type = kObjectTypeTabpage; - return msgpack_rpc_to_tabpage(obj, &arg->data.tabpage); - } - default: - return false; - } -} - -bool msgpack_rpc_to_array(msgpack_object *obj, Array *arg) +bool msgpack_rpc_to_array(const msgpack_object *const obj, Array *const arg) FUNC_ATTR_NONNULL_ALL { if (obj->type != MSGPACK_OBJECT_ARRAY) { @@ -173,7 +273,8 @@ bool msgpack_rpc_to_array(msgpack_object *obj, Array *arg) return true; } -bool msgpack_rpc_to_dictionary(msgpack_object *obj, Dictionary *arg) +bool msgpack_rpc_to_dictionary(const msgpack_object *const obj, + Dictionary *const arg) FUNC_ATTR_NONNULL_ALL { if (obj->type != MSGPACK_OBJECT_MAP) { @@ -225,53 +326,113 @@ void msgpack_rpc_from_string(String result, msgpack_packer *res) FUNC_ATTR_NONNULL_ARG(2) { msgpack_pack_str(res, result.size); - msgpack_pack_str_body(res, result.data, result.size); + if (result.size > 0) { + msgpack_pack_str_body(res, result.data, result.size); + } } -void msgpack_rpc_from_object(Object result, msgpack_packer *res) +typedef struct { + const Object *aobj; + bool container; + size_t idx; +} APIToMPObjectStackItem; + +/// Convert type used by Neovim API to msgpack +/// +/// @param[in] result Object to convert. +/// @param[out] res Structure that defines where conversion results are saved. +/// +/// @return true in case of success, false otherwise. +void msgpack_rpc_from_object(const Object result, msgpack_packer *const res) FUNC_ATTR_NONNULL_ARG(2) { - switch (result.type) { - case kObjectTypeNil: - msgpack_pack_nil(res); - break; - - case kObjectTypeBoolean: - msgpack_rpc_from_boolean(result.data.boolean, res); - break; - - case kObjectTypeInteger: - msgpack_rpc_from_integer(result.data.integer, res); - break; - - case kObjectTypeFloat: - msgpack_rpc_from_float(result.data.floating, res); - break; - - case kObjectTypeString: - msgpack_rpc_from_string(result.data.string, res); - break; - - case kObjectTypeArray: - msgpack_rpc_from_array(result.data.array, res); - break; - - case kObjectTypeBuffer: - msgpack_rpc_from_buffer(result.data.buffer, res); - break; - - case kObjectTypeWindow: - msgpack_rpc_from_window(result.data.window, res); - break; - - case kObjectTypeTabpage: - msgpack_rpc_from_tabpage(result.data.tabpage, res); - break; - - case kObjectTypeDictionary: - msgpack_rpc_from_dictionary(result.data.dictionary, res); - break; + kvec_t(APIToMPObjectStackItem) stack = KV_INITIAL_VALUE; + kv_push(stack, ((APIToMPObjectStackItem) { &result, false, 0 })); + while (kv_size(stack)) { + APIToMPObjectStackItem cur = kv_last(stack); + switch (cur.aobj->type) { + case kObjectTypeNil: { + msgpack_pack_nil(res); + break; + } + case kObjectTypeBoolean: { + msgpack_rpc_from_boolean(cur.aobj->data.boolean, res); + break; + } + case kObjectTypeInteger: { + msgpack_rpc_from_integer(cur.aobj->data.integer, res); + break; + } + case kObjectTypeFloat: { + msgpack_rpc_from_float(cur.aobj->data.floating, res); + break; + } + case kObjectTypeString: { + msgpack_rpc_from_string(cur.aobj->data.string, res); + break; + } + case kObjectTypeBuffer: { + msgpack_rpc_from_buffer(cur.aobj->data.integer, res); + break; + } + case kObjectTypeWindow: { + msgpack_rpc_from_window(cur.aobj->data.integer, res); + break; + } + case kObjectTypeTabpage: { + msgpack_rpc_from_tabpage(cur.aobj->data.integer, res); + break; + } + case kObjectTypeArray: { + const size_t size = cur.aobj->data.array.size; + if (cur.container) { + if (cur.idx >= size) { + (void)kv_pop(stack); + } else { + const size_t idx = cur.idx; + cur.idx++; + kv_last(stack) = cur; + kv_push(stack, ((APIToMPObjectStackItem) { + .aobj = &cur.aobj->data.array.items[idx], + .container = false, + })); + } + } else { + msgpack_pack_array(res, size); + cur.container = true; + kv_last(stack) = cur; + } + break; + } + case kObjectTypeDictionary: { + const size_t size = cur.aobj->data.dictionary.size; + if (cur.container) { + if (cur.idx >= size) { + (void)kv_pop(stack); + } else { + const size_t idx = cur.idx; + cur.idx++; + kv_last(stack) = cur; + msgpack_rpc_from_string(cur.aobj->data.dictionary.items[idx].key, + res); + kv_push(stack, ((APIToMPObjectStackItem) { + .aobj = &cur.aobj->data.dictionary.items[idx].value, + .container = false, + })); + } + } else { + msgpack_pack_map(res, size); + cur.container = true; + kv_last(stack) = cur; + } + break; + } + } + if (!cur.container) { + (void)kv_pop(stack); + } } + kv_destroy(stack); } void msgpack_rpc_from_array(Array result, msgpack_packer *res) @@ -297,7 +458,6 @@ void msgpack_rpc_from_dictionary(Dictionary result, msgpack_packer *res) /// Handler executed when an invalid method name is passed Object msgpack_rpc_handle_missing_method(uint64_t channel_id, - uint64_t request_id, Array args, Error *error) { @@ -308,7 +468,6 @@ Object msgpack_rpc_handle_missing_method(uint64_t channel_id, /// Handler executed when malformated arguments are passed Object msgpack_rpc_handle_invalid_arguments(uint64_t channel_id, - uint64_t request_id, Array args, Error *error) { diff --git a/src/nvim/msgpack_rpc/remote_ui.c b/src/nvim/msgpack_rpc/remote_ui.c deleted file mode 100644 index 6ffcffe2e1..0000000000 --- a/src/nvim/msgpack_rpc/remote_ui.c +++ /dev/null @@ -1,363 +0,0 @@ -#include <assert.h> -#include <stddef.h> -#include <stdint.h> -#include <stdbool.h> - -#include "nvim/vim.h" -#include "nvim/ui.h" -#include "nvim/memory.h" -#include "nvim/map.h" -#include "nvim/msgpack_rpc/remote_ui.h" -#include "nvim/msgpack_rpc/channel.h" -#include "nvim/api/private/defs.h" -#include "nvim/api/private/helpers.h" - -#ifdef INCLUDE_GENERATED_DECLARATIONS -# include "msgpack_rpc/remote_ui.c.generated.h" -#endif - -typedef struct { - uint64_t channel_id; - Array buffer; -} UIData; - -static PMap(uint64_t) *connected_uis = NULL; - -void remote_ui_init(void) -{ - connected_uis = pmap_new(uint64_t)(); - // Add handler for "attach_ui" - String method = cstr_as_string("ui_attach"); - MsgpackRpcRequestHandler handler = {.fn = remote_ui_attach, .async = false}; - msgpack_rpc_add_method_handler(method, handler); - method = cstr_as_string("ui_detach"); - handler.fn = remote_ui_detach; - msgpack_rpc_add_method_handler(method, handler); - method = cstr_as_string("ui_try_resize"); - handler.fn = remote_ui_try_resize; - msgpack_rpc_add_method_handler(method, handler); -} - -void remote_ui_disconnect(uint64_t channel_id) -{ - UI *ui = pmap_get(uint64_t)(connected_uis, channel_id); - if (!ui) { - return; - } - UIData *data = ui->data; - // destroy pending screen updates - api_free_array(data->buffer); - pmap_del(uint64_t)(connected_uis, channel_id); - xfree(ui->data); - ui_detach(ui); - xfree(ui); -} - -static Object remote_ui_attach(uint64_t channel_id, uint64_t request_id, - Array args, Error *error) -{ - if (pmap_has(uint64_t)(connected_uis, channel_id)) { - api_set_error(error, Exception, _("UI already attached for channel")); - return NIL; - } - - if (args.size != 3 || args.items[0].type != kObjectTypeInteger - || args.items[1].type != kObjectTypeInteger - || args.items[2].type != kObjectTypeBoolean - || args.items[0].data.integer <= 0 || args.items[1].data.integer <= 0) { - api_set_error(error, Validation, - _("Invalid arguments. Expected: " - "(uint width > 0, uint height > 0, bool enable_rgb)")); - return NIL; - } - UIData *data = xmalloc(sizeof(UIData)); - data->channel_id = channel_id; - data->buffer = (Array)ARRAY_DICT_INIT; - UI *ui = xcalloc(1, sizeof(UI)); - ui->width = (int)args.items[0].data.integer; - ui->height = (int)args.items[1].data.integer; - ui->rgb = args.items[2].data.boolean; - ui->data = data; - ui->resize = remote_ui_resize; - ui->clear = remote_ui_clear; - ui->eol_clear = remote_ui_eol_clear; - ui->cursor_goto = remote_ui_cursor_goto; - ui->update_menu = remote_ui_update_menu; - ui->busy_start = remote_ui_busy_start; - ui->busy_stop = remote_ui_busy_stop; - ui->mouse_on = remote_ui_mouse_on; - ui->mouse_off = remote_ui_mouse_off; - ui->mode_change = remote_ui_mode_change; - ui->set_scroll_region = remote_ui_set_scroll_region; - ui->scroll = remote_ui_scroll; - ui->highlight_set = remote_ui_highlight_set; - ui->put = remote_ui_put; - ui->bell = remote_ui_bell; - ui->visual_bell = remote_ui_visual_bell; - ui->update_fg = remote_ui_update_fg; - ui->update_bg = remote_ui_update_bg; - ui->update_sp = remote_ui_update_sp; - ui->flush = remote_ui_flush; - ui->suspend = remote_ui_suspend; - ui->set_title = remote_ui_set_title; - ui->set_icon = remote_ui_set_icon; - pmap_put(uint64_t)(connected_uis, channel_id, ui); - ui_attach(ui); - return NIL; -} - -static Object remote_ui_detach(uint64_t channel_id, uint64_t request_id, - Array args, Error *error) -{ - if (!pmap_has(uint64_t)(connected_uis, channel_id)) { - api_set_error(error, Exception, _("UI is not attached for channel")); - } - remote_ui_disconnect(channel_id); - - return NIL; -} - -static Object remote_ui_try_resize(uint64_t channel_id, uint64_t request_id, - Array args, Error *error) -{ - if (!pmap_has(uint64_t)(connected_uis, channel_id)) { - api_set_error(error, Exception, _("UI is not attached for channel")); - } - - if (args.size != 2 || args.items[0].type != kObjectTypeInteger - || args.items[1].type != kObjectTypeInteger - || args.items[0].data.integer <= 0 || args.items[1].data.integer <= 0) { - api_set_error(error, Validation, - _("Invalid arguments. Expected: " - "(uint width > 0, uint height > 0)")); - return NIL; - } - - UI *ui = pmap_get(uint64_t)(connected_uis, channel_id); - ui->width = (int)args.items[0].data.integer; - ui->height = (int)args.items[1].data.integer; - ui_refresh(); - return NIL; -} - - -static void push_call(UI *ui, char *name, Array args) -{ - Array call = ARRAY_DICT_INIT; - UIData *data = ui->data; - - // To optimize data transfer(especially for "put"), we bundle adjacent - // calls to same method together, so only add a new call entry if the last - // method call is different from "name" - if (kv_size(data->buffer)) { - call = kv_A(data->buffer, kv_size(data->buffer) - 1).data.array; - } - - if (!kv_size(call) || strcmp(kv_A(call, 0).data.string.data, name)) { - call = (Array)ARRAY_DICT_INIT; - ADD(data->buffer, ARRAY_OBJ(call)); - ADD(call, STRING_OBJ(cstr_to_string(name))); - } - - ADD(call, ARRAY_OBJ(args)); - kv_A(data->buffer, kv_size(data->buffer) - 1).data.array = call; -} - -static void remote_ui_resize(UI *ui, int width, int height) -{ - Array args = ARRAY_DICT_INIT; - ADD(args, INTEGER_OBJ(width)); - ADD(args, INTEGER_OBJ(height)); - push_call(ui, "resize", args); -} - -static void remote_ui_clear(UI *ui) -{ - Array args = ARRAY_DICT_INIT; - push_call(ui, "clear", args); -} - -static void remote_ui_eol_clear(UI *ui) -{ - Array args = ARRAY_DICT_INIT; - push_call(ui, "eol_clear", args); -} - -static void remote_ui_cursor_goto(UI *ui, int row, int col) -{ - Array args = ARRAY_DICT_INIT; - ADD(args, INTEGER_OBJ(row)); - ADD(args, INTEGER_OBJ(col)); - push_call(ui, "cursor_goto", args); -} - -static void remote_ui_update_menu(UI *ui) -{ - Array args = ARRAY_DICT_INIT; - push_call(ui, "update_menu", args); -} - -static void remote_ui_busy_start(UI *ui) -{ - Array args = ARRAY_DICT_INIT; - push_call(ui, "busy_start", args); -} - -static void remote_ui_busy_stop(UI *ui) -{ - Array args = ARRAY_DICT_INIT; - push_call(ui, "busy_stop", args); -} - -static void remote_ui_mouse_on(UI *ui) -{ - Array args = ARRAY_DICT_INIT; - push_call(ui, "mouse_on", args); -} - -static void remote_ui_mouse_off(UI *ui) -{ - Array args = ARRAY_DICT_INIT; - push_call(ui, "mouse_off", args); -} - -static void remote_ui_mode_change(UI *ui, int mode) -{ - Array args = ARRAY_DICT_INIT; - if (mode == INSERT) { - ADD(args, STRING_OBJ(cstr_to_string("insert"))); - } else if (mode == REPLACE) { - ADD(args, STRING_OBJ(cstr_to_string("replace"))); - } else { - assert(mode == NORMAL); - ADD(args, STRING_OBJ(cstr_to_string("normal"))); - } - push_call(ui, "mode_change", args); -} - -static void remote_ui_set_scroll_region(UI *ui, int top, int bot, int left, - int right) -{ - Array args = ARRAY_DICT_INIT; - ADD(args, INTEGER_OBJ(top)); - ADD(args, INTEGER_OBJ(bot)); - ADD(args, INTEGER_OBJ(left)); - ADD(args, INTEGER_OBJ(right)); - push_call(ui, "set_scroll_region", args); -} - -static void remote_ui_scroll(UI *ui, int count) -{ - Array args = ARRAY_DICT_INIT; - ADD(args, INTEGER_OBJ(count)); - push_call(ui, "scroll", args); -} - -static void remote_ui_highlight_set(UI *ui, HlAttrs attrs) -{ - Array args = ARRAY_DICT_INIT; - Dictionary hl = ARRAY_DICT_INIT; - - if (attrs.bold) { - PUT(hl, "bold", BOOLEAN_OBJ(true)); - } - - if (attrs.underline) { - PUT(hl, "underline", BOOLEAN_OBJ(true)); - } - - if (attrs.undercurl) { - PUT(hl, "undercurl", BOOLEAN_OBJ(true)); - } - - if (attrs.italic) { - PUT(hl, "italic", BOOLEAN_OBJ(true)); - } - - if (attrs.reverse) { - PUT(hl, "reverse", BOOLEAN_OBJ(true)); - } - - if (attrs.foreground != -1) { - PUT(hl, "foreground", INTEGER_OBJ(attrs.foreground)); - } - - if (attrs.background != -1) { - PUT(hl, "background", INTEGER_OBJ(attrs.background)); - } - - if (attrs.special != -1) { - PUT(hl, "special", INTEGER_OBJ(attrs.special)); - } - - ADD(args, DICTIONARY_OBJ(hl)); - push_call(ui, "highlight_set", args); -} - -static void remote_ui_put(UI *ui, uint8_t *data, size_t size) -{ - Array args = ARRAY_DICT_INIT; - String str = {.data = xmemdupz(data, size), .size = size}; - ADD(args, STRING_OBJ(str)); - push_call(ui, "put", args); -} - -static void remote_ui_bell(UI *ui) -{ - Array args = ARRAY_DICT_INIT; - push_call(ui, "bell", args); -} - -static void remote_ui_visual_bell(UI *ui) -{ - Array args = ARRAY_DICT_INIT; - push_call(ui, "visual_bell", args); -} - -static void remote_ui_update_fg(UI *ui, int fg) -{ - Array args = ARRAY_DICT_INIT; - ADD(args, INTEGER_OBJ(fg)); - push_call(ui, "update_fg", args); -} - -static void remote_ui_update_bg(UI *ui, int bg) -{ - Array args = ARRAY_DICT_INIT; - ADD(args, INTEGER_OBJ(bg)); - push_call(ui, "update_bg", args); -} - -static void remote_ui_update_sp(UI *ui, int sp) -{ - Array args = ARRAY_DICT_INIT; - ADD(args, INTEGER_OBJ(sp)); - push_call(ui, "update_sp", args); -} - -static void remote_ui_flush(UI *ui) -{ - UIData *data = ui->data; - channel_send_event(data->channel_id, "redraw", data->buffer); - data->buffer = (Array)ARRAY_DICT_INIT; -} - -static void remote_ui_suspend(UI *ui) -{ - Array args = ARRAY_DICT_INIT; - push_call(ui, "suspend", args); -} - -static void remote_ui_set_title(UI *ui, char *title) -{ - Array args = ARRAY_DICT_INIT; - ADD(args, STRING_OBJ(cstr_to_string(title))); - push_call(ui, "set_title", args); -} - -static void remote_ui_set_icon(UI *ui, char *icon) -{ - Array args = ARRAY_DICT_INIT; - ADD(args, STRING_OBJ(cstr_to_string(icon))); - push_call(ui, "set_icon", args); -} diff --git a/src/nvim/msgpack_rpc/remote_ui.h b/src/nvim/msgpack_rpc/remote_ui.h deleted file mode 100644 index 8af86dc1b8..0000000000 --- a/src/nvim/msgpack_rpc/remote_ui.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef NVIM_MSGPACK_RPC_REMOTE_UI_H -#define NVIM_MSGPACK_RPC_REMOTE_UI_H - -#include "nvim/ui.h" - -#ifdef INCLUDE_GENERATED_DECLARATIONS -# include "msgpack_rpc/remote_ui.h.generated.h" -#endif -#endif // NVIM_MSGPACK_RPC_REMOTE_UI_H diff --git a/src/nvim/msgpack_rpc/server.c b/src/nvim/msgpack_rpc/server.c index 6cc56ba3dd..d7c2926a0f 100644 --- a/src/nvim/msgpack_rpc/server.c +++ b/src/nvim/msgpack_rpc/server.c @@ -12,6 +12,7 @@ #include "nvim/eval.h" #include "nvim/garray.h" #include "nvim/vim.h" +#include "nvim/main.h" #include "nvim/memory.h" #include "nvim/log.h" #include "nvim/fileio.h" @@ -39,6 +40,10 @@ bool server_init(void) listen_address = server_address_new(); } + if (!listen_address) { + return false; + } + bool ok = (server_start(listen_address) == 0); if (must_free) { xfree((char *) listen_address); @@ -108,7 +113,7 @@ int server_start(const char *endpoint) } SocketWatcher *watcher = xmalloc(sizeof(SocketWatcher)); - socket_watcher_init(&loop, watcher, endpoint, NULL); + socket_watcher_init(&main_loop, watcher, endpoint, NULL); // Check if a watcher for the endpoint already exists for (int i = 0; i < watchers.ga_len; i++) { |