From 3a12737e6c13e9be774483f34655e7ac96e36c09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Linse?= Date: Sat, 2 Nov 2019 15:06:32 +0100 Subject: [PATCH 1/2] refactor(main): separate connection code from --remote execution code --- src/nvim/api/vim.c | 3 +- src/nvim/main.c | 164 ++++++++++++++++++++++++--------------------- 2 files changed, 90 insertions(+), 77 deletions(-) diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index b691dee2ef..a942c94f46 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -1997,9 +1997,8 @@ Array nvim_get_proc_children(Integer pid, Error *err) DLOG("fallback to vim._os_proc_children()"); Array a = ARRAY_DICT_INIT; ADD(a, INTEGER_OBJ(pid)); - String s = cstr_to_string("return vim._os_proc_children(...)"); + String s = STATIC_CSTR_AS_STRING("return vim._os_proc_children(...)"); Object o = nlua_exec(s, a, err); - api_free_string(s); api_free_array(a); if (o.type == kObjectTypeArray) { rvobj = o.data.array; diff --git a/src/nvim/main.c b/src/nvim/main.c index 230be9d9b9..f762160c05 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -269,7 +269,7 @@ int main(int argc, char **argv) server_init(params.listen_addr); if (params.remote) { - handle_remote_client(¶ms, params.remote, + remote_request(¶ms, params.remote, params.server_addr, argc, argv); } @@ -807,91 +807,105 @@ static void init_locale(void) } #endif -/// Handle remote subcommands -static void handle_remote_client(mparm_T *params, int remote_args, - char *server_addr, int argc, char **argv) + +static uint64_t server_connect(char *server_addr, const char **errmsg) { - Object rvobj = OBJECT_INIT; - rvobj.data.dictionary = (Dictionary)ARRAY_DICT_INIT; - rvobj.type = kObjectTypeDictionary; - CallbackReader on_data = CALLBACK_READER_INIT; - const char *connect_error = NULL; - uint64_t rc_id = 0; - if (server_addr != NULL) { - rc_id = channel_connect(false, server_addr, true, on_data, 50, &connect_error); - } + if (server_addr == NULL) { + *errmsg = "no address specified"; + return 0; + } + CallbackReader on_data = CALLBACK_READER_INIT; + const char *error = NULL; + bool is_tcp = strrchr(server_addr, ':') ? true : false; + // connected to channel + uint64_t chan = channel_connect(is_tcp, server_addr, true, on_data, 50, &error); + if (error) { + *errmsg = error; + return 0; + } + return chan; +} - int t_argc = remote_args; - Array args = ARRAY_DICT_INIT; - String arg_s; - for (; t_argc < argc; t_argc++) { - arg_s = cstr_to_string(argv[t_argc]); - ADD(args, STRING_OBJ(arg_s)); - } +/// Handle remote subcommands +static void remote_request(mparm_T *params, int remote_args, + char *server_addr, int argc, char **argv) +{ + const char *connect_error = NULL; + uint64_t chan = server_connect(server_addr, &connect_error); + Object rvobj = OBJECT_INIT; - Error err = ERROR_INIT; - Array a = ARRAY_DICT_INIT; - ADD(a, INTEGER_OBJ((int)rc_id)); - ADD(a, CSTR_TO_OBJ(server_addr)); - ADD(a, CSTR_TO_OBJ(connect_error)); - ADD(a, ARRAY_OBJ(args)); - String s = STATIC_CSTR_AS_STRING("return vim._cs_remote(...)"); - Object o = nlua_exec(s, a, &err); - api_free_array(a); - if (ERROR_SET(&err)) { - mch_errmsg(err.msg); + int t_argc = remote_args; + Array args = ARRAY_DICT_INIT; + String arg_s; + for (; t_argc < argc; t_argc++) { + arg_s = cstr_to_string(argv[t_argc]); + ADD(args, STRING_OBJ(arg_s)); + } + + Error err = ERROR_INIT; + Array a = ARRAY_DICT_INIT; + ADD(a, INTEGER_OBJ((int)chan)); + ADD(a, CSTR_TO_OBJ(server_addr)); + ADD(a, CSTR_TO_OBJ(connect_error)); + ADD(a, ARRAY_OBJ(args)); + String s = STATIC_CSTR_AS_STRING("return vim._cs_remote(...)"); + Object o = nlua_exec(s, a, &err); + api_free_array(a); + if (ERROR_SET(&err)) { + mch_errmsg(err.msg); + mch_errmsg("\n"); + os_exit(2); + } + + if (o.type == kObjectTypeDictionary) { + rvobj.data.dictionary = o.data.dictionary; + } else { + mch_errmsg("vim._cs_remote returned unexpected value\n"); + os_exit(2); + } + + TriState should_exit = kNone; + TriState tabbed = kNone; + + for (size_t i = 0; i < rvobj.data.dictionary.size ; i++) { + if (strcmp(rvobj.data.dictionary.items[i].key.data, "errmsg") == 0) { + if (rvobj.data.dictionary.items[i].value.type != kObjectTypeString) { + mch_errmsg("vim._cs_remote returned an unexpected type for 'errmsg'\n"); + os_exit(2); + } + mch_errmsg(rvobj.data.dictionary.items[i].value.data.string.data); mch_errmsg("\n"); os_exit(2); - } - - if (o.type == kObjectTypeDictionary) { - rvobj.data.dictionary = o.data.dictionary; - } else { - mch_errmsg("vim._cs_remote returned unexpected value\n"); - os_exit(2); - } - - TriState should_exit = kNone; - TriState tabbed = kNone; - - for (size_t i = 0; i < rvobj.data.dictionary.size ; i++) { - if (strcmp(rvobj.data.dictionary.items[i].key.data, "errmsg") == 0) { - if (rvobj.data.dictionary.items[i].value.type != kObjectTypeString) { - mch_errmsg("vim._cs_remote returned an unexpected type for 'errmsg'\n"); - os_exit(2); - } - mch_errmsg(rvobj.data.dictionary.items[i].value.data.string.data); - mch_errmsg("\n"); + } else if (strcmp(rvobj.data.dictionary.items[i].key.data, "tabbed") == 0) { + if (rvobj.data.dictionary.items[i].value.type != kObjectTypeBoolean) { + mch_errmsg("vim._cs_remote returned an unexpected type for 'tabbed'\n"); os_exit(2); - } else if (strcmp(rvobj.data.dictionary.items[i].key.data, "tabbed") == 0) { - if (rvobj.data.dictionary.items[i].value.type != kObjectTypeBoolean) { - mch_errmsg("vim._cs_remote returned an unexpected type for 'tabbed'\n"); - os_exit(2); - } - tabbed = rvobj.data.dictionary.items[i].value.data.boolean ? kTrue : kFalse; - } else if (strcmp(rvobj.data.dictionary.items[i].key.data, "should_exit") == 0) { - if (rvobj.data.dictionary.items[i].value.type != kObjectTypeBoolean) { - mch_errmsg("vim._cs_remote returned an unexpected type for 'should_exit'\n"); - os_exit(2); - } - should_exit = rvobj.data.dictionary.items[i].value.data.boolean ? kTrue : kFalse; } + tabbed = rvobj.data.dictionary.items[i].value.data.boolean ? kTrue : kFalse; + } else if (strcmp(rvobj.data.dictionary.items[i].key.data, "should_exit") == 0) { + if (rvobj.data.dictionary.items[i].value.type != kObjectTypeBoolean) { + mch_errmsg("vim._cs_remote returned an unexpected type for 'should_exit'\n"); + os_exit(2); + } + should_exit = rvobj.data.dictionary.items[i].value.data.boolean ? kTrue : kFalse; } - if (should_exit == kNone || tabbed == kNone) { - mch_errmsg("vim._cs_remote didn't return a value for should_exit or tabbed, bailing\n"); - os_exit(2); - } - api_free_object(o); + } + if (should_exit == kNone || tabbed == kNone) { + mch_errmsg("vim._cs_remote didn't return a value for should_exit or tabbed, bailing\n"); + os_exit(2); + } + api_free_object(o); - if (should_exit == kTrue) { - os_exit(0); - } - if (tabbed == kTrue) { - params->window_count = argc - remote_args - 1; - params->window_layout = WIN_TABS; - } + if (should_exit == kTrue) { + os_exit(0); + } + if (tabbed == kTrue) { + params->window_count = argc - remote_args - 1; + params->window_layout = WIN_TABS; + } } + /// Decides whether text (as opposed to commands) will be read from stdin. /// @see EDIT_STDIN static bool edit_stdin(bool explicit, mparm_T *parmp) From a4400bf8cda8ace4c4aab67bc73a1820478f46f1 Mon Sep 17 00:00:00 2001 From: bfredl Date: Sat, 12 Mar 2022 13:47:50 +0100 Subject: [PATCH 2/2] feat(ui): connect to remote ui (only debug messages for now) co-authored-by: hlpr98 --- src/nvim/api/private/dispatch.c | 8 ++++ src/nvim/globals.h | 3 ++ src/nvim/main.c | 18 +++++++-- src/nvim/msgpack_rpc/channel.c | 5 +++ src/nvim/ui_client.c | 70 +++++++++++++++++++++++++++++++++ src/nvim/ui_client.h | 9 +++++ 6 files changed, 109 insertions(+), 4 deletions(-) create mode 100644 src/nvim/ui_client.c create mode 100644 src/nvim/ui_client.h diff --git a/src/nvim/api/private/dispatch.c b/src/nvim/api/private/dispatch.c index f670f06357..ba2e560d63 100644 --- a/src/nvim/api/private/dispatch.c +++ b/src/nvim/api/private/dispatch.c @@ -30,6 +30,7 @@ #include "nvim/api/vimscript.h" #include "nvim/api/win_config.h" #include "nvim/api/window.h" +#include "nvim/ui_client.h" static Map(String, MsgpackRpcRequestHandler) methods = MAP_INIT; @@ -38,6 +39,13 @@ static void msgpack_rpc_add_method_handler(String method, MsgpackRpcRequestHandl map_put(String, MsgpackRpcRequestHandler)(&methods, method, handler); } +void msgpack_rpc_add_redraw(void) +{ + msgpack_rpc_add_method_handler(STATIC_CSTR_AS_STRING("redraw"), + (MsgpackRpcRequestHandler) { .fn = ui_client_handle_redraw, + .fast = true }); +} + /// @param name API method name /// @param name_len name size (includes terminating NUL) MsgpackRpcRequestHandler msgpack_rpc_get_handler_for(const char *name, size_t name_len, diff --git a/src/nvim/globals.h b/src/nvim/globals.h index 35ad57906b..b64ed7c758 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -342,6 +342,9 @@ EXTERN sctx_T current_sctx INIT(= { 0 COMMA 0 COMMA 0 }); // ID of the current channel making a client API call EXTERN uint64_t current_channel_id INIT(= 0); +// ID of the client channel. Used by ui client +EXTERN uint64_t ui_client_channel_id INIT(= 0); + EXTERN bool did_source_packages INIT(= false); // Scope information for the code that indirectly triggered the current diff --git a/src/nvim/main.c b/src/nvim/main.c index f762160c05..95ef306745 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -29,6 +29,7 @@ #include "nvim/if_cscope.h" #include "nvim/lua/executor.h" #include "nvim/main.h" +#include "nvim/ui_client.h" #include "nvim/vim.h" #ifdef HAVE_LOCALE_H # include @@ -269,8 +270,7 @@ int main(int argc, char **argv) server_init(params.listen_addr); if (params.remote) { - remote_request(¶ms, params.remote, - params.server_addr, argc, argv); + remote_request(¶ms, params.remote, params.server_addr, argc, argv); } if (GARGCOUNT > 0) { @@ -834,10 +834,20 @@ static void remote_request(mparm_T *params, int remote_args, uint64_t chan = server_connect(server_addr, &connect_error); Object rvobj = OBJECT_INIT; - int t_argc = remote_args; + if (strequal(argv[remote_args], "--remote-ui-test")) { + if (!chan) { + emsg(connect_error); + exit(1); + } + + ui_client_init(chan); + ui_client_execute(chan); + abort(); // unreachable + } + Array args = ARRAY_DICT_INIT; String arg_s; - for (; t_argc < argc; t_argc++) { + for (int t_argc = remote_args; t_argc < argc; t_argc++) { arg_s = cstr_to_string(argv[t_argc]); ADD(args, STRING_OBJ(arg_s)); } diff --git a/src/nvim/msgpack_rpc/channel.c b/src/nvim/msgpack_rpc/channel.c index 299651ee97..f4e836fa81 100644 --- a/src/nvim/msgpack_rpc/channel.c +++ b/src/nvim/msgpack_rpc/channel.c @@ -547,6 +547,11 @@ void rpc_close(Channel *channel) channel->rpc.closed = true; channel_decref(channel); + if (channel->id == ui_client_channel_id) { + // TODO(bfredl): handle this in ui_client, where os_exit() is safe + exit(0); + } + if (channel->streamtype == kChannelStreamStdio) { multiqueue_put(main_loop.fast_events, exit_event, 0); } diff --git a/src/nvim/ui_client.c b/src/nvim/ui_client.c new file mode 100644 index 0000000000..4a435aac4d --- /dev/null +++ b/src/nvim/ui_client.c @@ -0,0 +1,70 @@ +// This is an open source non-commercial project. Dear PVS-Studio, please check +// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com + +#include +#include +#include + +#include "nvim/vim.h" +#include "nvim/ui_client.h" +#include "nvim/api/private/helpers.h" +#include "nvim/msgpack_rpc/channel.h" +#include "nvim/api/private/dispatch.h" +#include "nvim/ui.h" + +void ui_client_init(uint64_t chan) +{ + Array args = ARRAY_DICT_INIT; + int width = 80; + int height = 25; + Dictionary opts = ARRAY_DICT_INIT; + + PUT(opts, "rgb", BOOLEAN_OBJ(true)); + PUT(opts, "ext_linegrid", BOOLEAN_OBJ(true)); + PUT(opts, "ext_termcolors", BOOLEAN_OBJ(true)); + + // TODO(bfredl): use the size of the client UI + ADD(args, INTEGER_OBJ((int)width)); + ADD(args, INTEGER_OBJ((int)height)); + ADD(args, DICTIONARY_OBJ(opts)); + + rpc_send_event(chan, "nvim_ui_attach", args); + msgpack_rpc_add_redraw(); // GAME! + ui_client_channel_id = chan; +} + +/// Handler for "redraw" events sent by the NVIM server +/// +/// This is just a stub. The mentioned functionality will be implemented. +/// +/// This function will be called by handle_request (in msgpack_rpc/channle.c) +/// The individual ui_events sent by the server are individually handled +/// by their respective handlers defined in ui_events_redraw.generated.h +/// +/// @note The "flush" event is called only once and only after handling all +/// the other events +/// @param channel_id: The id of the rpc channel +/// @param uidata: The dense array containing the ui_events sent by the server +/// @param[out] err Error details, if any +Object ui_client_handle_redraw(uint64_t channel_id, Array args, Error *error) +{ + for (size_t i = 0; i < args.size; i++) { + Array call = args.items[i].data.array; + char *method_name = call.items[0].data.string.data; + + fprintf(stderr, "%s: %zu\n", method_name, call.size-1); + } + return NIL; +} + +/// run the main thread in ui client mode +/// +/// This is just a stub. the full version will handle input, resizing, etc +void ui_client_execute(uint64_t chan) +{ + while (true) { + loop_poll_events(&main_loop, -1); + } + + getout(0); +} diff --git a/src/nvim/ui_client.h b/src/nvim/ui_client.h new file mode 100644 index 0000000000..067f78d5c5 --- /dev/null +++ b/src/nvim/ui_client.h @@ -0,0 +1,9 @@ +#ifndef NVIM_UI_CLIENT_H +#define NVIM_UI_CLIENT_H + +#include "nvim/api/private/defs.h" + +#ifdef INCLUDE_GENERATED_DECLARATIONS +#include "ui_client.h.generated.h" +#endif +#endif // NVIM_UI_CLIENT_H