Merge pull request #24399 from lambdalisue/fix-messagepack-rpc

feat(msgpack_rpc): add a new `msgpack-rpc` client type to fix behavior with MessagePack-RPC compliant clients
This commit is contained in:
bfredl
2023-08-26 13:39:31 +02:00
committed by GitHub
7 changed files with 69 additions and 11 deletions

View File

@@ -1336,7 +1336,11 @@ nvim_set_client_info({name}, {version}, {type}, {methods}, {attributes})
• {type} Must be one of the following values. Client libraries • {type} Must be one of the following values. Client libraries
should default to "remote" unless overridden by the should default to "remote" unless overridden by the
user. user.
• "remote" remote client connected to Nvim. • "remote" remote client connected "Nvim flavored"
MessagePack-RPC (responses must be in reverse order of
requests). |msgpack-rpc|
• "msgpack-rpc" remote client connected to Nvim via
fully MessagePack-RPC compliant protocol.
• "ui" gui frontend • "ui" gui frontend
• "embedder" application using Nvim as a component (for • "embedder" application using Nvim as a component (for
example, IDE/editor implementing a vim mode). example, IDE/editor implementing a vim mode).

View File

@@ -152,6 +152,9 @@ The following new APIs and features were added.
• Functions that take a severity as an optional parameter (e.g. • Functions that take a severity as an optional parameter (e.g.
|vim.diagnostic.get()|) now also accept a list of severities |vim.diagnostic.severity| |vim.diagnostic.get()|) now also accept a list of severities |vim.diagnostic.severity|
• New RPC client type `msgpack-rpc` is added for `nvim_set_client_info` to
support fully MessagePack-RPC compliant clients.
============================================================================== ==============================================================================
CHANGED FEATURES *news-changed* CHANGED FEATURES *news-changed*

View File

@@ -1679,7 +1679,11 @@ function vim.api.nvim_select_popupmenu_item(item, insert, finish, opts) end
--- @param type string Must be one of the following values. Client libraries --- @param type string Must be one of the following values. Client libraries
--- should default to "remote" unless overridden by the --- should default to "remote" unless overridden by the
--- user. --- user.
--- • "remote" remote client connected to Nvim. --- • "remote" remote client connected "Nvim flavored"
--- MessagePack-RPC (responses must be in reverse order of
--- requests). `msgpack-rpc`
--- • "msgpack-rpc" remote client connected to Nvim via
--- fully MessagePack-RPC compliant protocol.
--- • "ui" gui frontend --- • "ui" gui frontend
--- • "embedder" application using Nvim as a component (for --- • "embedder" application using Nvim as a component (for
--- example, IDE/editor implementing a vim mode). --- example, IDE/editor implementing a vim mode).

View File

@@ -1500,7 +1500,10 @@ Array nvim_get_api_info(uint64_t channel_id, Arena *arena)
/// - "commit" hash or similar identifier of commit /// - "commit" hash or similar identifier of commit
/// @param type Must be one of the following values. Client libraries should /// @param type Must be one of the following values. Client libraries should
/// default to "remote" unless overridden by the user. /// default to "remote" unless overridden by the user.
/// - "remote" remote client connected to Nvim. /// - "remote" remote client connected "Nvim flavored" MessagePack-RPC (responses
/// must be in reverse order of requests). |msgpack-rpc|
/// - "msgpack-rpc" remote client connected to Nvim via fully MessagePack-RPC
/// compliant protocol.
/// - "ui" gui frontend /// - "ui" gui frontend
/// - "embedder" application using Nvim as a component (for example, /// - "embedder" application using Nvim as a component (for example,
/// IDE/editor implementing a vim mode). /// IDE/editor implementing a vim mode).

View File

@@ -6730,7 +6730,7 @@ static void f_rpcrequest(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
const char *name = NULL; const char *name = NULL;
Channel *chan = find_channel(chan_id); Channel *chan = find_channel(chan_id);
if (chan) { if (chan) {
name = rpc_client_name(chan); name = get_client_info(chan, "name");
} }
msg_ext_set_kind("rpc_error"); msg_ext_set_kind("rpc_error");
if (name) { if (name) {

View File

@@ -306,6 +306,17 @@ end:
channel_decref(channel); channel_decref(channel);
} }
static ChannelCallFrame *find_call_frame(RpcState *rpc, uint32_t request_id)
{
for (size_t i = 0; i < kv_size(rpc->call_stack); i++) {
ChannelCallFrame *frame = kv_Z(rpc->call_stack, i);
if (frame->request_id == request_id) {
return frame;
}
}
return NULL;
}
static void parse_msgpack(Channel *channel) static void parse_msgpack(Channel *channel)
{ {
Unpacker *p = channel->rpc.unpacker; Unpacker *p = channel->rpc.unpacker;
@@ -321,13 +332,15 @@ static void parse_msgpack(Channel *channel)
} }
arena_mem_free(arena_finish(&p->arena)); arena_mem_free(arena_finish(&p->arena));
} else if (p->type == kMessageTypeResponse) { } else if (p->type == kMessageTypeResponse) {
ChannelCallFrame *frame = kv_last(channel->rpc.call_stack); ChannelCallFrame *frame = channel->rpc.client_type == kClientTypeMsgpackRpc
if (p->request_id != frame->request_id) { ? find_call_frame(&channel->rpc, p->request_id)
: kv_last(channel->rpc.call_stack);
if (frame == NULL || p->request_id != frame->request_id) {
char buf[256]; char buf[256];
snprintf(buf, sizeof(buf), snprintf(buf, sizeof(buf),
"ch %" PRIu64 " returned a response with an unknown request " "ch %" PRIu64 " (type=%" PRIu32 ") returned a response with an unknown request "
"id. Ensure the client is properly synchronized", "id %" PRIu32 ". Ensure the client is properly synchronized",
channel->id); channel->id, (unsigned)channel->rpc.client_type, p->request_id);
chan_close_with_error(channel, buf, LOGLVL_ERR); chan_close_with_error(channel, buf, LOGLVL_ERR);
} }
frame->returned = true; frame->returned = true;
@@ -691,6 +704,25 @@ void rpc_set_client_info(uint64_t id, Dictionary info)
api_free_dictionary(chan->rpc.info); api_free_dictionary(chan->rpc.info);
chan->rpc.info = info; chan->rpc.info = info;
// Parse "type" on "info" and set "client_type"
const char *type = get_client_info(chan, "type");
if (type == NULL || strequal(type, "remote")) {
chan->rpc.client_type = kClientTypeRemote;
} else if (strequal(type, "msgpack-rpc")) {
chan->rpc.client_type = kClientTypeMsgpackRpc;
} else if (strequal(type, "ui")) {
chan->rpc.client_type = kClientTypeUi;
} else if (strequal(type, "embedder")) {
chan->rpc.client_type = kClientTypeEmbedder;
} else if (strequal(type, "host")) {
chan->rpc.client_type = kClientTypeHost;
} else if (strequal(type, "plugin")) {
chan->rpc.client_type = kClientTypePlugin;
} else {
chan->rpc.client_type = kClientTypeUnknown;
}
channel_info_changed(chan, false); channel_info_changed(chan, false);
} }
@@ -699,14 +731,15 @@ Dictionary rpc_client_info(Channel *chan)
return copy_dictionary(chan->rpc.info, NULL); return copy_dictionary(chan->rpc.info, NULL);
} }
const char *rpc_client_name(Channel *chan) const char *get_client_info(Channel *chan, const char *key)
FUNC_ATTR_NONNULL_ALL
{ {
if (!chan->is_rpc) { if (!chan->is_rpc) {
return NULL; return NULL;
} }
Dictionary info = chan->rpc.info; Dictionary info = chan->rpc.info;
for (size_t i = 0; i < info.size; i++) { for (size_t i = 0; i < info.size; i++) {
if (strequal("name", info.items[i].key.data) if (strequal(key, info.items[i].key.data)
&& info.items[i].value.type == kObjectTypeString) { && info.items[i].value.type == kObjectTypeString) {
return info.items[i].value.data.string.data; return info.items[i].value.data.string.data;
} }

View File

@@ -14,6 +14,16 @@
typedef struct Channel Channel; typedef struct Channel Channel;
typedef struct Unpacker Unpacker; typedef struct Unpacker Unpacker;
typedef enum {
kClientTypeUnknown = -1,
kClientTypeRemote = 0,
kClientTypeMsgpackRpc = 5,
kClientTypeUi = 1,
kClientTypeEmbedder = 2,
kClientTypeHost = 3,
kClientTypePlugin = 4,
} ClientType;
typedef struct { typedef struct {
uint32_t request_id; uint32_t request_id;
bool returned, errored; bool returned, errored;
@@ -37,6 +47,7 @@ typedef struct {
uint32_t next_request_id; uint32_t next_request_id;
kvec_t(ChannelCallFrame *) call_stack; kvec_t(ChannelCallFrame *) call_stack;
Dictionary info; Dictionary info;
ClientType client_type;
} RpcState; } RpcState;
#endif // NVIM_MSGPACK_RPC_CHANNEL_DEFS_H #endif // NVIM_MSGPACK_RPC_CHANNEL_DEFS_H