refactor(api): deprecate nvim_call_atomic #28433

TODO:
FUNC_API_REMOTE_ONLY APIs such as `nvim_ui_*` cannot (yet) be used in
`nvim_exec_lua`. We can change FUNC_API_REMOTE_ONLY to allow
Vimscript/Lua to pass an explicit `channel_id`. #28437
This commit is contained in:
Justin M. Keyes 2024-04-22 04:28:16 -07:00 committed by GitHub
parent 013afc6863
commit fb5e2db4c7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 91 additions and 111 deletions

View File

@ -655,33 +655,6 @@ nvim__stats() *nvim__stats()*
Return: ~
Map of various internal stats.
nvim_call_atomic({calls}) *nvim_call_atomic()*
Calls many API methods atomically.
This has two main usages:
1. To perform several requests from an async context atomically, i.e.
without interleaving redraws, RPC requests from other clients, or user
interactions (however API methods may trigger autocommands or event
processing which have such side effects, e.g. |:sleep| may wake
timers).
2. To minimize RPC overhead (roundtrips) of a sequence of many requests.
Attributes: ~
|RPC| only
Parameters: ~
• {calls} an array of calls, where each call is described by an array
with two elements: the request name, and an array of
arguments.
Return: ~
Array of two elements. The first is an array of return values. The
second is NIL if all calls succeeded. If a call resulted in an error,
it is a three-element array with the zero-based index of the call
which resulted in an error, the error type and the error message. If
an error occurred, the values from all preceding calls will still be
returned.
nvim_chan_send({chan}, {data}) *nvim_chan_send()*
Send data to channel `id`. For a job, it writes it to the stdin of the
process. For the stdio channel |channel-stdio|, it writes to Nvim's

View File

@ -16,6 +16,7 @@ API
- *nvim_buf_clear_highlight()* Use |nvim_buf_clear_namespace()| instead.
- *nvim_buf_set_virtual_text()* Use |nvim_buf_set_extmark()| instead.
- *nvim_command_output()* Use |nvim_exec2()| instead.
- *nvim_call_atomic()* Use |nvim_exec_lua()| instead.
- *nvim_execute_lua()* Use |nvim_exec_lua()| instead.
- *nvim_get_hl_by_name()* Use |nvim_get_hl()| instead.
- *nvim_get_hl_by_id()* Use |nvim_get_hl()| instead.

View File

@ -533,6 +533,7 @@ release.
• |API| functions:
- |nvim_buf_get_option()| Use |nvim_get_option_value()| instead.
- |nvim_buf_set_option()| Use |nvim_set_option_value()| instead.
- nvim_call_atomic() Use |nvim_exec_lua()| instead.
- |nvim_get_option()| Use |nvim_get_option_value()| instead.
- |nvim_set_option()| Use |nvim_set_option_value()| instead.
- |nvim_win_get_option()| Use |nvim_get_option_value()| instead.

View File

@ -20,6 +20,9 @@
#include "nvim/lua/executor.h"
#include "nvim/memory.h"
#include "nvim/memory_defs.h"
#include "nvim/msgpack_rpc/channel.h"
#include "nvim/msgpack_rpc/channel_defs.h"
#include "nvim/msgpack_rpc/unpacker.h"
#include "nvim/option.h"
#include "nvim/option_defs.h"
#include "nvim/pos_defs.h"
@ -697,3 +700,89 @@ static void set_option_to(uint64_t channel_id, void *to, OptReqScope req_scope,
set_option_value_for(name.data, opt_idx, optval, opt_flags, req_scope, to, err);
});
}
/// @deprecated Use nvim_exec_lua() instead.
///
/// Calls many API methods atomically.
///
/// This has two main usages:
/// 1. To perform several requests from an async context atomically, i.e.
/// without interleaving redraws, RPC requests from other clients, or user
/// interactions (however API methods may trigger autocommands or event
/// processing which have such side effects, e.g. |:sleep| may wake timers).
/// 2. To minimize RPC overhead (roundtrips) of a sequence of many requests.
///
/// @param channel_id
/// @param calls an array of calls, where each call is described by an array
/// with two elements: the request name, and an array of arguments.
/// @param[out] err Validation error details (malformed `calls` parameter),
/// if any. Errors from batched calls are given in the return value.
///
/// @return Array of two elements. The first is an array of return
/// values. The second is NIL if all calls succeeded. If a call resulted in
/// an error, it is a three-element array with the zero-based index of the call
/// which resulted in an error, the error type and the error message. If an
/// error occurred, the values from all preceding calls will still be returned.
Array nvim_call_atomic(uint64_t channel_id, Array calls, Arena *arena, Error *err)
FUNC_API_SINCE(1) FUNC_API_DEPRECATED_SINCE(12) FUNC_API_REMOTE_ONLY
{
Array rv = arena_array(arena, 2);
Array results = arena_array(arena, calls.size);
Error nested_error = ERROR_INIT;
size_t i; // also used for freeing the variables
for (i = 0; i < calls.size; i++) {
VALIDATE_T("'calls' item", kObjectTypeArray, calls.items[i].type, {
goto theend;
});
Array call = calls.items[i].data.array;
VALIDATE_EXP((call.size == 2), "'calls' item", "2-item Array", NULL, {
goto theend;
});
VALIDATE_T("name", kObjectTypeString, call.items[0].type, {
goto theend;
});
String name = call.items[0].data.string;
VALIDATE_T("call args", kObjectTypeArray, call.items[1].type, {
goto theend;
});
Array args = call.items[1].data.array;
MsgpackRpcRequestHandler handler =
msgpack_rpc_get_handler_for(name.data,
name.size,
&nested_error);
if (ERROR_SET(&nested_error)) {
break;
}
Object result = handler.fn(channel_id, args, arena, &nested_error);
if (ERROR_SET(&nested_error)) {
// error handled after loop
break;
}
// TODO(bfredl): wasteful copy. It could be avoided to encoding to msgpack
// directly here. But `result` might become invalid when next api function
// is called in the loop.
ADD_C(results, copy_object(result, arena));
if (handler.ret_alloc) {
api_free_object(result);
}
}
ADD_C(rv, ARRAY_OBJ(results));
if (ERROR_SET(&nested_error)) {
Array errval = arena_array(arena, 3);
ADD_C(errval, INTEGER_OBJ((Integer)i));
ADD_C(errval, INTEGER_OBJ(nested_error.type));
ADD_C(errval, STRING_OBJ(copy_string(cstr_as_string(nested_error.msg), arena)));
ADD_C(rv, ARRAY_OBJ(errval));
} else {
ADD_C(rv, NIL);
}
theend:
api_clear_error(&nested_error);
return rv;
}

View File

@ -1698,90 +1698,6 @@ Array nvim_list_chans(Arena *arena)
return channel_all_info(arena);
}
/// Calls many API methods atomically.
///
/// This has two main usages:
/// 1. To perform several requests from an async context atomically, i.e.
/// without interleaving redraws, RPC requests from other clients, or user
/// interactions (however API methods may trigger autocommands or event
/// processing which have such side effects, e.g. |:sleep| may wake timers).
/// 2. To minimize RPC overhead (roundtrips) of a sequence of many requests.
///
/// @param channel_id
/// @param calls an array of calls, where each call is described by an array
/// with two elements: the request name, and an array of arguments.
/// @param[out] err Validation error details (malformed `calls` parameter),
/// if any. Errors from batched calls are given in the return value.
///
/// @return Array of two elements. The first is an array of return
/// values. The second is NIL if all calls succeeded. If a call resulted in
/// an error, it is a three-element array with the zero-based index of the call
/// which resulted in an error, the error type and the error message. If an
/// error occurred, the values from all preceding calls will still be returned.
Array nvim_call_atomic(uint64_t channel_id, Array calls, Arena *arena, Error *err)
FUNC_API_SINCE(1) FUNC_API_REMOTE_ONLY
{
Array rv = arena_array(arena, 2);
Array results = arena_array(arena, calls.size);
Error nested_error = ERROR_INIT;
size_t i; // also used for freeing the variables
for (i = 0; i < calls.size; i++) {
VALIDATE_T("'calls' item", kObjectTypeArray, calls.items[i].type, {
goto theend;
});
Array call = calls.items[i].data.array;
VALIDATE_EXP((call.size == 2), "'calls' item", "2-item Array", NULL, {
goto theend;
});
VALIDATE_T("name", kObjectTypeString, call.items[0].type, {
goto theend;
});
String name = call.items[0].data.string;
VALIDATE_T("call args", kObjectTypeArray, call.items[1].type, {
goto theend;
});
Array args = call.items[1].data.array;
MsgpackRpcRequestHandler handler =
msgpack_rpc_get_handler_for(name.data,
name.size,
&nested_error);
if (ERROR_SET(&nested_error)) {
break;
}
Object result = handler.fn(channel_id, args, arena, &nested_error);
if (ERROR_SET(&nested_error)) {
// error handled after loop
break;
}
// TODO(bfredl): wasteful copy. It could be avoided to encoding to msgpack
// directly here. But `result` might become invalid when next api function
// is called in the loop.
ADD_C(results, copy_object(result, arena));
if (handler.ret_alloc) {
api_free_object(result);
}
}
ADD_C(rv, ARRAY_OBJ(results));
if (ERROR_SET(&nested_error)) {
Array errval = arena_array(arena, 3);
ADD_C(errval, INTEGER_OBJ((Integer)i));
ADD_C(errval, INTEGER_OBJ(nested_error.type));
ADD_C(errval, STRING_OBJ(copy_string(cstr_as_string(nested_error.msg), arena)));
ADD_C(rv, ARRAY_OBJ(errval));
} else {
ADD_C(rv, NIL);
}
theend:
api_clear_error(&nested_error);
return rv;
}
/// 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.